From 20ecfb166cbc017733a1496eb3e14f06e1cd1051 Mon Sep 17 00:00:00 2001 From: Rodrigo Varela Date: Wed, 4 Dec 2024 09:24:04 +1100 Subject: [PATCH] - connect new repository to TopBar navigation so that it can read user saved profile. Added same code for iOS though not tested yet --- .../bisq/mobile/domain/data/model/User.kt | 5 +-- .../domain/data/repository/Repositories.kt | 1 + .../bisq/mobile/domain/di/DomainModule.kt | 7 +--- .../components/atoms/icons/Icons.android.kt | 9 +++++ .../bisq/mobile/presentation/BasePresenter.kt | 2 + .../presentation/di/PresentationModule.kt | 5 +++ .../ui/components/atoms/icons/Icons.kt | 14 ++++++- .../ui/components/molecules/TopBar.kt | 12 ++++-- .../components/molecules/TopBarPresenter.kt | 39 +++++++++++++++++++ .../ui/uicases/TabContainerScreen.kt | 2 - .../ui/uicases/offers/OffersListScreen.kt | 1 - .../uicases/startup/CreateProfilePresenter.kt | 13 +++++++ .../ui/components/atoms/icons/Icons.ios.kt | 23 +++++++++++ 13 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.android.kt create mode 100644 shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBarPresenter.kt create mode 100644 shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.ios.kt diff --git a/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/model/User.kt b/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/model/User.kt index 9ebd8b32..5c5b5fc7 100644 --- a/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/model/User.kt +++ b/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/model/User.kt @@ -1,10 +1,9 @@ package network.bisq.mobile.domain.data.model +import kotlinx.serialization.Serializable import network.bisq.mobile.domain.PlatformImage -/** - * - */ +@Serializable open class User: BaseModel() { var uniqueAvatar: PlatformImage? = null } \ No newline at end of file diff --git a/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/repository/Repositories.kt b/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/repository/Repositories.kt index 933ec1e5..6b276197 100644 --- a/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/repository/Repositories.kt +++ b/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/data/repository/Repositories.kt @@ -39,3 +39,4 @@ open class MyTradesRepository : SingleObjectRepository() { } } open class SettingsRepository(keyValueStorage: KeyValueStorage): SingleObjectRepository(keyValueStorage, Settings()) +open class UserRepository(keyValueStorage: KeyValueStorage): SingleObjectRepository(keyValueStorage, User()) diff --git a/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/di/DomainModule.kt b/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/di/DomainModule.kt index 8efef2c5..c62ad71d 100644 --- a/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/di/DomainModule.kt +++ b/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/di/DomainModule.kt @@ -5,11 +5,7 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import network.bisq.mobile.domain.data.model.Greeting import network.bisq.mobile.domain.data.persistance.KeyValueStorage -import network.bisq.mobile.domain.data.repository.BisqStatsRepository -import network.bisq.mobile.domain.data.repository.BtcPriceRepository -import network.bisq.mobile.domain.data.repository.GreetingRepository -import network.bisq.mobile.domain.data.repository.MyTradesRepository -import network.bisq.mobile.domain.data.repository.SettingsRepository +import network.bisq.mobile.domain.data.repository.* import network.bisq.mobile.domain.getPlatformSettings import org.koin.dsl.module @@ -32,4 +28,5 @@ val domainModule = module { single { BtcPriceRepository() } single { MyTradesRepository() } single { SettingsRepository(get()) } + single { UserRepository(get()) } } diff --git a/shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.android.kt b/shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.android.kt new file mode 100644 index 00000000..2dcb3328 --- /dev/null +++ b/shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.android.kt @@ -0,0 +1,9 @@ +package network.bisq.mobile.presentation.ui.components.atoms.icons + +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.painter.Painter +import network.bisq.mobile.domain.PlatformImage + +actual fun rememberPlatformImagePainter(platformImage: PlatformImage): Painter { + return BitmapPainter(platformImage.bitmap) +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/BasePresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/BasePresenter.kt index 55cca874..8cf89b4f 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/BasePresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/BasePresenter.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import network.bisq.mobile.domain.data.BackgroundDispatcher import network.bisq.mobile.domain.data.model.BaseModel import network.bisq.mobile.utils.Logging @@ -47,6 +48,7 @@ abstract class BasePresenter(private val rootPresenter: MainPresenter?): ViewPre protected var view: Any? = null // Coroutine scope for the presenter protected val presenterScope = CoroutineScope(Dispatchers.Main + Job()) + protected val backgroundScope = CoroutineScope(BackgroundDispatcher) private val dependants = if (isRoot()) mutableListOf() else null diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/di/PresentationModule.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/di/PresentationModule.kt index 63b3a8d9..3f00d944 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/di/PresentationModule.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/di/PresentationModule.kt @@ -5,6 +5,8 @@ import androidx.navigation.NavHostController import network.bisq.mobile.client.ClientMainPresenter import network.bisq.mobile.presentation.MainPresenter import network.bisq.mobile.presentation.ui.AppPresenter +import network.bisq.mobile.presentation.ui.components.molecules.ITopBarPresenter +import network.bisq.mobile.presentation.ui.components.molecules.TopBarPresenter import network.bisq.mobile.presentation.ui.uicases.GettingStartedPresenter import network.bisq.mobile.presentation.ui.uicases.IGettingStarted import network.bisq.mobile.presentation.ui.uicases.offers.MarketListPresenter @@ -27,6 +29,8 @@ val presentationModule = module { single { ClientMainPresenter(get(), get(), get()) } bind AppPresenter::class + single { TopBarPresenter(get(), get()) } bind ITopBarPresenter::class + single { SplashPresenter( get(), @@ -51,6 +55,7 @@ val presentationModule = module { single { CreateProfilePresenter( + get(), get(), get() ) diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.kt index 79cadbb3..61df3399 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.kt @@ -3,10 +3,14 @@ package network.bisq.mobile.presentation.ui.components.atoms.icons import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter import bisqapps.shared.presentation.generated.resources.Res import bisqapps.shared.presentation.generated.resources.* +import network.bisq.mobile.domain.PlatformImage import org.jetbrains.compose.resources.painterResource +expect fun rememberPlatformImagePainter(platformImage: PlatformImage): Painter + @Composable fun BellIcon(modifier: Modifier = Modifier) { Image(painterResource(Res.drawable.icon_bell), "Bell icon", modifier = modifier) @@ -33,6 +37,12 @@ fun SortIcon(modifier: Modifier = Modifier) { } @Composable -fun UserIcon(modifier: Modifier = Modifier) { - Image(painterResource(Res.drawable.img_bot_image), "User icon", modifier = modifier) +fun UserIcon(platformImage: PlatformImage?, modifier: Modifier = Modifier) { + if (platformImage == null) { + // show default + Image(painterResource(Res.drawable.img_bot_image), "User icon", modifier = modifier) + } else { + val painter = rememberPlatformImagePainter(platformImage) + Image(painter = painter, contentDescription = "User icon", modifier = modifier) + } } \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBar.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBar.kt index d6539db8..45a33f90 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBar.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBar.kt @@ -1,6 +1,5 @@ package network.bisq.mobile.presentation.ui.components.molecules -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -13,9 +12,11 @@ import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController +import kotlinx.coroutines.flow.StateFlow +import network.bisq.mobile.domain.PlatformImage +import network.bisq.mobile.presentation.ViewPresenter import network.bisq.mobile.presentation.ui.components.atoms.BisqText import network.bisq.mobile.presentation.ui.components.atoms.icons.BellIcon import network.bisq.mobile.presentation.ui.components.atoms.icons.BisqLogoSmall @@ -24,6 +25,10 @@ import network.bisq.mobile.presentation.ui.theme.BisqTheme import org.koin.compose.koinInject import org.koin.core.qualifier.named +interface ITopBarPresenter: ViewPresenter { + val uniqueAvatar: StateFlow +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun TopBar( @@ -32,6 +37,7 @@ fun TopBar( customBackButton: @Composable (() -> Unit)? = null ) { val navController: NavHostController = koinInject(named("RootNavController")) + val presenter: ITopBarPresenter = koinInject() val showBackButton = customBackButton == null && navController.previousBackStackEntry != null @@ -74,7 +80,7 @@ fun TopBar( Row(modifier = Modifier.padding(end = 16.dp), verticalAlignment = Alignment.CenterVertically) { BellIcon(modifier = Modifier.size(30.dp)) Spacer(modifier = Modifier.width(12.dp)) - UserIcon(modifier = Modifier.size(30.dp)) + UserIcon(presenter.uniqueAvatar.value, modifier = Modifier.size(30.dp)) } }, ) diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBarPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBarPresenter.kt new file mode 100644 index 00000000..dfbe0e92 --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/TopBarPresenter.kt @@ -0,0 +1,39 @@ +package network.bisq.mobile.presentation.ui.components.molecules + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import network.bisq.mobile.domain.PlatformImage +import network.bisq.mobile.domain.data.repository.UserRepository +import network.bisq.mobile.presentation.BasePresenter +import network.bisq.mobile.presentation.MainPresenter + +open class TopBarPresenter( + private val userRepository: UserRepository, + mainPresenter: MainPresenter +): BasePresenter(mainPresenter), ITopBarPresenter { + + private val _uniqueAvatar = MutableStateFlow(userRepository.data.value?.uniqueAvatar) + override val uniqueAvatar: StateFlow get() = _uniqueAvatar + + private fun setUniqueAvatar(value: PlatformImage?) { + _uniqueAvatar.value = value + } + + init { + refresh() + } + + override fun onViewAttached() { + super.onViewAttached() + refresh() + } + + private fun refresh() { + backgroundScope.launch { + userRepository.fetch().let { + setUniqueAvatar(it?.uniqueAvatar) + } + } + } +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerScreen.kt index eb8223dc..f3f37229 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerScreen.kt @@ -1,8 +1,6 @@ package network.bisq.mobile.presentation.ui.uicases -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.* -import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import bisqapps.shared.presentation.generated.resources.* diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offers/OffersListScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offers/OffersListScreen.kt index 9efd45cf..a412d7fe 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offers/OffersListScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offers/OffersListScreen.kt @@ -30,7 +30,6 @@ import network.bisq.mobile.presentation.ui.components.molecules.DirectionToggle import network.bisq.mobile.presentation.ui.components.molecules.OfferCard import network.bisq.mobile.presentation.ui.components.molecules.TopBar import network.bisq.mobile.presentation.ui.helpers.RememberPresenterLifecycle -import network.bisq.mobile.presentation.ui.theme.BisqTheme import network.bisq.mobile.presentation.ui.theme.BisqUIConstants import org.koin.compose.koinInject import org.koin.core.qualifier.named diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/CreateProfilePresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/CreateProfilePresenter.kt index 2949dfc0..1555f111 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/CreateProfilePresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/CreateProfilePresenter.kt @@ -8,6 +8,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import network.bisq.mobile.domain.PlatformImage +import network.bisq.mobile.domain.data.model.User +import network.bisq.mobile.domain.data.repository.UserRepository import network.bisq.mobile.domain.service.user_profile.UserProfileServiceFacade import network.bisq.mobile.presentation.BasePresenter import network.bisq.mobile.presentation.MainPresenter @@ -15,6 +17,7 @@ import network.bisq.mobile.presentation.ui.navigation.Routes open class CreateProfilePresenter( mainPresenter: MainPresenter, + private val userRepository: UserRepository, private val userProfileService: UserProfileServiceFacade ) : BasePresenter(mainPresenter) { @@ -69,6 +72,13 @@ open class CreateProfilePresenter( cancelJob() } + init { + // if this presenter gets to work, it means there is no profile saved + backgroundScope.launch { + userRepository.create(User()) + } + } + // UI handlers fun onGenerateKeyPair() { generateKeyPair() @@ -110,6 +120,9 @@ open class CreateProfilePresenter( setId(id) setNym(nym) setProfileIcon(profileIcon) + backgroundScope.launch { + userRepository.update(User().apply { uniqueAvatar = profileIcon }) + } } setGenerateKeyPairInProgress(false) log.i { "Hide busy animation for generateKeyPair" } diff --git a/shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.ios.kt b/shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.ios.kt new file mode 100644 index 00000000..3036aef0 --- /dev/null +++ b/shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/icons/Icons.ios.kt @@ -0,0 +1,23 @@ +package network.bisq.mobile.presentation.ui.components.atoms.icons + +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.toComposeImageBitmap +import network.bisq.mobile.domain.PlatformImage +import network.bisq.mobile.service.IosImageUtil.toByteArray +import org.jetbrains.skia.Image +import platform.UIKit.UIImage +import platform.UIKit.UIImagePNGRepresentation + +actual fun rememberPlatformImagePainter(platformImage: PlatformImage): Painter { + val uiImage = platformImage as UIImage + val skiaImage = uiImage.toSkiaImage() + return BitmapPainter(skiaImage.toComposeImageBitmap()) +} + +// Helper function to convert UIImage to Skia Image +fun UIImage.toSkiaImage(): Image { + val nsData = UIImagePNGRepresentation(this)!! + val byteArray = nsData.toByteArray() + return Image.makeFromEncoded(byteArray) +} \ No newline at end of file