diff --git a/shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.android.kt b/shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.android.kt new file mode 100644 index 00000000..43e1b411 --- /dev/null +++ b/shared/presentation/src/androidMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.android.kt @@ -0,0 +1,11 @@ +package network.bisq.mobile.presentation.ui.components + +import androidx.activity.compose.BackHandler as AndroidBackHandler +import androidx.compose.runtime.Composable + +@Composable +actual fun BackHandler(onBackPressed: () -> Unit) { + AndroidBackHandler { + onBackPressed() + } +} diff --git a/shared/presentation/src/commonMain/composeResources/drawable/icon_add_filled_green.png b/shared/presentation/src/commonMain/composeResources/drawable/icon_add_filled_green.png new file mode 100755 index 00000000..21ea4a5c Binary files /dev/null and b/shared/presentation/src/commonMain/composeResources/drawable/icon_add_filled_green.png differ diff --git a/shared/presentation/src/commonMain/composeResources/drawable/icon_flash_light.png b/shared/presentation/src/commonMain/composeResources/drawable/icon_flash_light.png new file mode 100644 index 00000000..d9f2a4cf Binary files /dev/null and b/shared/presentation/src/commonMain/composeResources/drawable/icon_flash_light.png differ diff --git a/shared/presentation/src/commonMain/composeResources/drawable/icon_gallery.png b/shared/presentation/src/commonMain/composeResources/drawable/icon_gallery.png new file mode 100644 index 00000000..bb5630af Binary files /dev/null and b/shared/presentation/src/commonMain/composeResources/drawable/icon_gallery.png differ diff --git a/shared/presentation/src/commonMain/composeResources/drawable/icon_info.png b/shared/presentation/src/commonMain/composeResources/drawable/icon_info.png index 6c304de8..9bd7d0d3 100644 Binary files a/shared/presentation/src/commonMain/composeResources/drawable/icon_info.png and b/shared/presentation/src/commonMain/composeResources/drawable/icon_info.png differ diff --git a/shared/presentation/src/commonMain/composeResources/drawable/icon_warning.png b/shared/presentation/src/commonMain/composeResources/drawable/icon_warning.png new file mode 100644 index 00000000..b9d44054 Binary files /dev/null and b/shared/presentation/src/commonMain/composeResources/drawable/icon_warning.png differ diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStrings.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStrings.kt index acb6d4c9..74e88e67 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStrings.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStrings.kt @@ -9,6 +9,10 @@ data class CommonStrings( val common_offers: String, val common_search: String, + val common_buy: String, + val common_sell: String, + val common_yes: String, + val common_no: String, val offers_list_buy_from: String, val offers_list_sell_to: String, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsEn.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsEn.kt index 05357792..ef036c74 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsEn.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsEn.kt @@ -9,6 +9,10 @@ val EnCommonStrings = CommonStrings( common_offers = "Offers", common_search = "Search", + common_buy = "Buy", + common_sell = "Sell", + common_yes = "Yes", + common_no = "No", offers_list_buy_from = "Buy from", offers_list_sell_to = "Sell to", diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsFr.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsFr.kt index 47f891b2..24cbdb43 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsFr.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/i18n/CommonStringsFr.kt @@ -10,6 +10,10 @@ val FrCommonStrings = CommonStrings( common_offers = "[FR] offers", common_search = "[FR] Search", + common_buy = "[FR] Buy", + common_sell = "[FR] Sell", + common_yes = "[FR] Yes", + common_no = "[FR] No", offers_list_buy_from = "[FR] Buy from", offers_list_sell_to = "[FR] Sell to", 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 c7e0c908..e114d3c7 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 @@ -1,15 +1,19 @@ package network.bisq.mobile.presentation +import androidx.compose.material3.SnackbarHostState import androidx.annotation.CallSuper import androidx.navigation.NavHostController import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +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.i18n.AppStrings import network.bisq.mobile.domain.utils.Logging - /** * Presenter methods accesible by all views. Views should extend this interface when defining the behaviour expected for their presenter. */ @@ -29,6 +33,9 @@ interface ViewPresenter { */ fun getRootTabNavController(): NavHostController + fun getSnackState(): SnackbarHostState + fun showSnackbar(message: String, isError: Boolean = true) + /** * Navigate back in the stack */ @@ -71,6 +78,17 @@ abstract class BasePresenter(private val rootPresenter: MainPresenter?): ViewPre // Presenter is interactive by default private val _isInteractive = MutableStateFlow(true) override val isInteractive: StateFlow = _isInteractive + val snackbarHostState: SnackbarHostState = SnackbarHostState() + + override fun getSnackState(): SnackbarHostState { + return snackbarHostState + } + + override fun showSnackbar(message: String, isError: Boolean) { + uiScope.launch { + snackbarHostState.showSnackbar(message, withDismissAction = true) + } + } /** * @throws IllegalStateException if this presenter has no root 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 ee9a9067..2e40a471 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 @@ -1,14 +1,12 @@ package network.bisq.mobile.presentation.di import network.bisq.mobile.client.ClientMainPresenter -import network.bisq.mobile.domain.UrlLauncher 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.offer.MarketListPresenter -import network.bisq.mobile.presentation.ui.uicases.offer.OffersListPresenter import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferAmountPresenter import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferDirectionPresenter import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferMarketPresenter @@ -16,6 +14,10 @@ import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOffe import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferPresenter import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferPricePresenter import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferReviewPresenter +import network.bisq.mobile.presentation.ui.uicases.ITabContainerPresenter +import network.bisq.mobile.presentation.ui.uicases.TabContainerPresenter +import network.bisq.mobile.presentation.ui.uicases.offer.IOffersListPresenter +import network.bisq.mobile.presentation.ui.uicases.offer.OffersListPresenter import network.bisq.mobile.presentation.ui.uicases.settings.IPaymentAccountSettingsPresenter import network.bisq.mobile.presentation.ui.uicases.settings.ISettingsPresenter import network.bisq.mobile.presentation.ui.uicases.settings.IUserProfileSettingsPresenter @@ -32,10 +34,10 @@ import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferAmo import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferPaymentMethodPresenter import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferPresenter import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferReviewPresenter -import network.bisq.mobile.presentation.ui.uicases.trades.IMyTrades -import network.bisq.mobile.presentation.ui.uicases.trades.ITradeFlowPresenter -import network.bisq.mobile.presentation.ui.uicases.trades.MyTradesPresenter -import network.bisq.mobile.presentation.ui.uicases.trades.TradeFlowPresenter +import network.bisq.mobile.presentation.ui.uicases.trade.IMyTrades +import network.bisq.mobile.presentation.ui.uicases.trade.ITradeFlowPresenter +import network.bisq.mobile.presentation.ui.uicases.trade.MyTradesPresenter +import network.bisq.mobile.presentation.ui.uicases.trade.TradeFlowPresenter import org.koin.dsl.bind import org.koin.dsl.module @@ -54,9 +56,8 @@ val presentationModule = module { ) } -// single { TabContainerPresenter(get()) } bind ITabContainerPresenter::class - single { OnBoardingPresenter(get(), get(), get()) } bind IOnboardingPresenter::class + single { TabContainerPresenter(get()) } bind ITabContainerPresenter::class single { SettingsPresenter(get(), get()) } bind ISettingsPresenter::class @@ -81,12 +82,13 @@ val presentationModule = module { single { MarketListPresenter(get(), get()) } - single { OffersListPresenter(get(), get(), get()) } + single { OffersListPresenter(get(), get(), get(), get()) } bind IOffersListPresenter::class single { MyTradesPresenter( get(), - myTradesRepository = get() + get(), + get(), ) } bind IMyTrades::class diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.kt new file mode 100644 index 00000000..a2d95beb --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.kt @@ -0,0 +1,6 @@ +package network.bisq.mobile.presentation.ui.components + +import androidx.compose.runtime.Composable + +@Composable +expect fun BackHandler(onBackPressed: () -> Unit) \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/Button.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/Button.kt index 5a650444..17b7e25a 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/Button.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/Button.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants enum class BisqButtonType { Default, @@ -31,10 +32,13 @@ enum class BisqButtonType { @Composable fun BisqButton( text: String? = "Button", - onClick: () -> Unit, + onClick: (() -> Unit)? = null, color: Color = BisqTheme.colors.light1, backgroundColor: Color = BisqTheme.colors.primary, - padding: PaddingValues = PaddingValues(horizontal = 48.dp, vertical = 4.dp), + padding: PaddingValues = PaddingValues( + horizontal = BisqUIConstants.ScreenPadding4X, + vertical = BisqUIConstants.ScreenPaddingHalf + ), iconOnly: (@Composable () -> Unit)? = null, leftIcon: (@Composable () -> Unit)? = null, rightIcon: (@Composable () -> Unit)? = null, @@ -64,7 +68,7 @@ fun BisqButton( val finalContentColor = color Button( - onClick = { onClick() }, + onClick = { onClick?.invoke() }, contentPadding = if (iconOnly != null) PaddingValues(horizontal = 0.dp, vertical = 0.dp) else padding, colors = ButtonColors( containerColor = finalBackgroundColor, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/CopyIconButton.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/CopyIconButton.kt new file mode 100644 index 00000000..e4e22817 --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/CopyIconButton.kt @@ -0,0 +1,19 @@ +package network.bisq.mobile.presentation.ui.components.atoms + +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.text.buildAnnotatedString +import network.bisq.mobile.presentation.ui.components.atoms.icons.CopyIcon + +@Composable +fun CopyIconButton(value: String) { + val clipboardManager = LocalClipboardManager.current + IconButton( + onClick = { + clipboardManager.setText(buildAnnotatedString { append(value) }) + } + ) { + CopyIcon() + } +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/TextField.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/TextField.kt index 9895cfc1..e03cf0b5 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/TextField.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/atoms/TextField.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -26,12 +27,15 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import network.bisq.mobile.presentation.ui.components.atoms.icons.CopyIcon import network.bisq.mobile.presentation.ui.components.atoms.icons.SearchIcon import network.bisq.mobile.presentation.ui.theme.BisqTheme @@ -50,6 +54,7 @@ fun BisqTextField( isTextArea: Boolean = false, paddingValues: PaddingValues = PaddingValues(all = 12.dp), disabled: Boolean = false, + showCopy: Boolean = false, modifier: Modifier = Modifier, ) { var isFocused by remember { mutableStateOf(false) } @@ -134,6 +139,11 @@ fun BisqTextField( innerTextField() } + + if (showCopy) { + CopyIconButton(value) + } + if (rightSuffix != null) { Box(modifier = Modifier.width(50.dp)) { rightSuffix() 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 755e21f0..e5a5b476 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 @@ -2,10 +2,15 @@ package network.bisq.mobile.presentation.ui.components.atoms.icons import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.dp +import bisqapps.shared.presentation.generated.resources.* import bisqapps.shared.presentation.generated.resources.Res import bisqapps.shared.presentation.generated.resources.exchange_h_arrow import bisqapps.shared.presentation.generated.resources.exchange_v_arrow @@ -13,6 +18,7 @@ import bisqapps.shared.presentation.generated.resources.icon_arrow_down import bisqapps.shared.presentation.generated.resources.icon_bell import bisqapps.shared.presentation.generated.resources.icon_chat_outlined import bisqapps.shared.presentation.generated.resources.icon_copy +import bisqapps.shared.presentation.generated.resources.icon_gallery import bisqapps.shared.presentation.generated.resources.icon_info import bisqapps.shared.presentation.generated.resources.icon_language_grey import bisqapps.shared.presentation.generated.resources.icon_qr @@ -29,6 +35,21 @@ import org.jetbrains.compose.resources.painterResource expect fun rememberPlatformImagePainter(platformImage: PlatformImage): Painter +@Composable +fun CloseIcon(modifier: Modifier = Modifier.size(24.dp)) { + Icon( + Icons.Filled.Close, + "close", + modifier = modifier, + tint = Color.White + ) +} + +@Composable +fun AddIcon(modifier: Modifier = Modifier.size(16.dp)) { + Image(painterResource(Res.drawable.icon_add_filled_green), "Add icon", modifier = modifier) +} + @Composable fun ArrowDownIcon(modifier: Modifier = Modifier.size(12.dp)) { Image(painterResource(Res.drawable.icon_arrow_down), "Down arrow icon", modifier = modifier) @@ -50,7 +71,12 @@ fun CopyIcon(modifier: Modifier = Modifier) { } @Composable -fun LanguageIcon(modifier: Modifier = Modifier.size(12.dp)) { +fun FlashLightIcon(modifier: Modifier = Modifier.size(24.dp)) { + Image(painterResource(Res.drawable.icon_flash_light), "Flash light icon", modifier = modifier) +} + +@Composable +fun LanguageIcon(modifier: Modifier = Modifier.size(16.dp)) { Image(painterResource(Res.drawable.icon_language_grey), "Language icon", modifier = modifier) } @@ -59,6 +85,11 @@ fun InfoIcon(modifier: Modifier = Modifier.size(16.dp)) { Image(painterResource(Res.drawable.icon_info), "Info icon", modifier = modifier) } +@Composable +fun GalleryIcon(modifier: Modifier = Modifier.size(24.dp)) { + Image(painterResource(Res.drawable.icon_gallery), "Gallery icon", modifier = modifier) +} + @Composable fun SwapHArrowIcon(modifier: Modifier = Modifier.size(16.dp)) { Image(painterResource(Res.drawable.exchange_h_arrow), "Swap horizontal icon", modifier = modifier) @@ -119,4 +150,9 @@ fun UserIcon(platformImage: PlatformImage?, modifier: Modifier = Modifier) { val painter = rememberPlatformImagePainter(platformImage) Image(painter = painter, contentDescription = "User icon", modifier = modifier) } -} \ No newline at end of file +} + +//@Composable +//fun WarningIcon(modifier: Modifier = Modifier.size(36.dp)) { +// Image(painterResource(Res.drawable.icon_warning), "Warning icon", modifier = modifier) +//} diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/MultiScreenWizardScaffold.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/MultiScreenWizardScaffold.kt index ba4caa7c..90065a1d 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/MultiScreenWizardScaffold.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/MultiScreenWizardScaffold.kt @@ -2,6 +2,7 @@ package network.bisq.mobile.presentation.ui.components.layout import androidx.compose.foundation.layout.* import androidx.compose.material3.BottomAppBar +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -24,6 +25,7 @@ fun MultiScreenWizardScaffold( nextOnClick: (() -> Unit)? = null, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, useStaticScaffold: Boolean = false, + snackbarHostState: SnackbarHostState? = null, content: @Composable ColumnScope.() -> Unit ) { @@ -33,24 +35,27 @@ fun MultiScreenWizardScaffold( bottomBar: @Composable (() -> Unit)?, hAlignment: Alignment.Horizontal, vArrangement: Arrangement.Vertical, + snackbarHostState: SnackbarHostState?, content: @Composable ColumnScope.() -> Unit ) -> Unit = - if (useStaticScaffold) { padding, topBar, bottomBar, hAlignment, verticalArrangement, innerContent -> + if (useStaticScaffold) { padding, topBar, bottomBar, hAlignment, verticalArrangement, snackState, innerContent -> BisqStaticScaffold( padding = padding, topBar = topBar, bottomBar = bottomBar, horizontalAlignment = hAlignment, verticalArrangement = verticalArrangement, + snackbarHostState = snackState, content = innerContent ) - } else { padding, topBar, bottomBar, hAlignment, verticalArrangement, innerContent -> + } else { padding, topBar, bottomBar, hAlignment, verticalArrangement, snackState, innerContent -> BisqScrollScaffold( padding = padding, topBar = topBar, bottomBar = bottomBar, horizontalAlignment = hAlignment, verticalArrangement = verticalArrangement, + snackbarHostState = snackState, content = innerContent ) } @@ -98,6 +103,7 @@ fun MultiScreenWizardScaffold( }, horizontalAlignment, Arrangement.Top, + snackbarHostState, ) { BisqProgressBar( diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/ScrollScaffold.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/ScrollScaffold.kt index 64a64ac4..d1190322 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/ScrollScaffold.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/ScrollScaffold.kt @@ -2,13 +2,17 @@ package network.bisq.mobile.presentation.ui.components.layout import androidx.compose.foundation.layout.* import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import network.bisq.mobile.presentation.ui.components.organisms.BisqSnackbar import network.bisq.mobile.presentation.ui.theme.BisqTheme import network.bisq.mobile.presentation.ui.theme.BisqUIConstants +// FinalTODO: Merge StaticScaffold and ScrollScaffold @Composable fun BisqScrollScaffold( padding: PaddingValues = PaddingValues( @@ -19,14 +23,23 @@ fun BisqScrollScaffold( ), topBar: @Composable (() -> Unit)? = null, bottomBar: @Composable (() -> Unit)? = null, + snackbarHostState: SnackbarHostState? = null, + fab: @Composable (() -> Unit)? = null, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, verticalArrangement: Arrangement.Vertical = Arrangement.Top, content: @Composable ColumnScope.() -> Unit ) { + Scaffold( containerColor = BisqTheme.colors.backgroundColor, topBar = topBar ?: {}, bottomBar = bottomBar ?: {}, + snackbarHost = { + if (snackbarHostState != null) { + BisqSnackbar(snackbarHostState = snackbarHostState) + } + }, + floatingActionButton = fab ?: {}, content = { BisqScrollLayout( padding = if (topBar != null) it else padding, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/StaticScaffold.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/StaticScaffold.kt index d98eba62..0f5d1772 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/StaticScaffold.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/layout/StaticScaffold.kt @@ -2,13 +2,17 @@ package network.bisq.mobile.presentation.ui.components.layout import androidx.compose.foundation.layout.* import androidx.compose.material3.Scaffold -import androidx.compose.material3.TopAppBar +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import network.bisq.mobile.presentation.ui.components.organisms.BisqSnackbar import network.bisq.mobile.presentation.ui.theme.BisqTheme import network.bisq.mobile.presentation.ui.theme.BisqUIConstants +// FinalTODO: Merge StaticScaffold and ScrollScaffold @Composable fun BisqStaticScaffold( padding: PaddingValues = PaddingValues( @@ -19,14 +23,23 @@ fun BisqStaticScaffold( ), topBar: @Composable (() -> Unit)? = null, bottomBar: @Composable (() -> Unit)? = null, + snackbarHostState: SnackbarHostState? = null, + fab: @Composable (() -> Unit)? = null, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, verticalArrangement: Arrangement.Vertical = Arrangement.Top, content: @Composable ColumnScope.() -> Unit ) { + Scaffold( containerColor = BisqTheme.colors.backgroundColor, topBar = topBar ?: {}, bottomBar = bottomBar ?: {}, + snackbarHost = { + if (snackbarHostState != null) { + BisqSnackbar(snackbarHostState = snackbarHostState) + } + }, + floatingActionButton = fab ?: {}, content = { it -> BisqStaticLayout( padding = if (topBar != null) it else padding, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/AmountSelector.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/AmountSelector.kt index dcf2d558..a90c71ef 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/AmountSelector.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/AmountSelector.kt @@ -15,7 +15,13 @@ import network.bisq.mobile.presentation.ui.components.atoms.BisqSlider import network.bisq.mobile.presentation.ui.components.atoms.BisqText import network.bisq.mobile.presentation.ui.components.atoms.BtcSatsText import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants +// ToDiscuss: +// buddha: Ideally this component should deal only with Fiat values (as Double) and have one valueChange() event +// so `initialSliderPosition` will become `defaultValue`, +// which will be some value between `formattedMinAmount` and `formattedMaxAmount` +// onSliderValueChange() / onTextValueChange() will become onValueChange(value: Double) -> Unit @Composable fun BisqAmountSelector( fiatCurrencyCode: String, @@ -48,7 +54,7 @@ fun BisqAmountSelector( Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(16.dp), + verticalArrangement = Arrangement.spacedBy(BisqUIConstants.ScreenPadding), horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/ConfirmationDialog.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/ConfirmationDialog.kt index a94f63a0..4c14ef65 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/ConfirmationDialog.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/ConfirmationDialog.kt @@ -16,43 +16,43 @@ import network.bisq.mobile.presentation.ui.theme.BisqUIConstants fun ConfirmationDialog( title: String = "", message: String = "Are you sure?", + subMessage: String = "", confirmButtonText: String = "Yes", cancelButtonText: String = "No", onConfirm: () -> Unit, - onDismissRequest: () -> Unit, + onDismiss: () -> Unit, ) { BisqDialog { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 24.dp, horizontal = 20.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(20.dp) - ) { - BisqText.h6Regular( - text = message, + BisqText.h6Regular( + text = message, + color = BisqTheme.colors.light1, + modifier = Modifier.padding(vertical = 12.dp) + ) + if (subMessage.isNotEmpty()) { + BisqText.baseRegular( + text = subMessage, color = BisqTheme.colors.light1, modifier = Modifier.padding(vertical = 12.dp) ) - Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { - BisqButton( - text = cancelButtonText, - backgroundColor = BisqTheme.colors.dark5, - onClick = { onDismissRequest() }, - padding = PaddingValues( - horizontal = BisqUIConstants.ScreenPadding4X, - vertical = BisqUIConstants.ScreenPaddingHalf - ) + } + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + BisqButton( + text = cancelButtonText, + backgroundColor = BisqTheme.colors.dark5, + onClick = onDismiss, + padding = PaddingValues( + horizontal = BisqUIConstants.ScreenPadding4X, + vertical = BisqUIConstants.ScreenPaddingHalf ) - BisqButton( - text = confirmButtonText, - onClick = { onConfirm() }, - padding = PaddingValues( - horizontal = BisqUIConstants.ScreenPadding4X, - vertical = BisqUIConstants.ScreenPaddingHalf - ) + ) + BisqButton( + text = confirmButtonText, + onClick = onConfirm, + padding = PaddingValues( + horizontal = BisqUIConstants.ScreenPadding4X, + vertical = BisqUIConstants.ScreenPaddingHalf ) - } + ) } } } \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/Dialog.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/Dialog.kt index 5edb4211..69a823cb 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/Dialog.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/Dialog.kt @@ -1,11 +1,9 @@ package network.bisq.mobile.presentation.ui.components.molecules -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Card import androidx.compose.material3.CardColors import androidx.compose.runtime.Composable @@ -16,17 +14,22 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants @Composable fun BisqDialog( onDismissRequest: () -> Unit = {}, + horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, content: @Composable ColumnScope.() -> Unit = {} ) { - Dialog(onDismissRequest = onDismissRequest, properties = DialogProperties(dismissOnClickOutside = true)) { + Dialog( + onDismissRequest = onDismissRequest, + properties = DialogProperties(dismissOnClickOutside = true) + ) { Box( modifier = Modifier .fillMaxSize() - .padding(top = 86.dp) + .padding(top = BisqUIConstants.ScreenPadding5X) ) { Card( modifier = Modifier @@ -40,7 +43,15 @@ fun BisqDialog( ), shape = RoundedCornerShape(16.dp), ) { - content() + // TODO: When content is too long, footer buttons will be hidden at bottom now. + // Dialog should have Header, Footer with scrollable only for content + Column( + modifier = Modifier.padding(BisqUIConstants.ScreenPadding2X).verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(BisqUIConstants.ScreenPadding), + horizontalAlignment = horizontalAlignment, + ) { + content() + } } } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/MyOfferCard.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/MyOfferCard.kt new file mode 100644 index 00000000..aff8e99c --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/MyOfferCard.kt @@ -0,0 +1,151 @@ +package network.bisq.mobile.presentation.ui.components.molecules + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import network.bisq.mobile.domain.replicated.offer.bisq_easy.OfferListItemVO +import network.bisq.mobile.presentation.ui.components.atoms.BisqText +import network.bisq.mobile.presentation.ui.components.atoms.icons.LanguageIcon +import cafe.adriel.lyricist.LocalStrings +import network.bisq.mobile.domain.data.model.MockOffer +import network.bisq.mobile.presentation.ui.components.atoms.* +import network.bisq.mobile.presentation.ui.components.atoms.icons.ChatIcon +import network.bisq.mobile.presentation.ui.components.atoms.icons.LanguageIcon +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqVDivider +import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants + +@Composable +fun MyOfferCard( + offerListItem: MockOffer, + myTrade: Boolean = false, + onClick: () -> Unit, + onChatClick: () -> Unit, +) { + val strings = LocalStrings.current.common + + Column( + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(8.dp)) + .background(color = BisqTheme.colors.dark5) + ) { + Row( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .padding(BisqUIConstants.ScreenPadding) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = onClick + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.weight(3f), + ) { + BisqText.baseRegular("TODO: User profile") + BisqText.baseRegular("TODO: Payment methods") + /* + UserProfile(offerListItem) + PaymentMethods(offerListItem) + */ + } + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.weight(2f) + ) { + // Len: 13 - "300 - 600 USD" + // Len: 17 - "3,000 - 6,000 XYZ" + // Len: 23 - "150,640 - 1,200,312 CRC" + //if (offerListItem.formattedQuoteAmount.length < 18) { + BisqText.baseRegular( + text = "mockOffer.formattedQuoteAmount", + color = BisqTheme.colors.primary + ) + BisqGap.H1() + Row { + BisqText.smallRegular( + text = "@ ", + color = BisqTheme.colors.grey2 + ) + BisqText.smallRegular( + text = "mockOffer.formattedPriceSpec", + color = BisqTheme.colors.light1 + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + LanguageIcon() + BisqText.smallRegular( + text = " : ", + color = BisqTheme.colors.grey2 + ) + BisqText.smallRegular( + text = "mockOffer.bisqEasyOffer.supportedLanguageCodes.joinToString().uppercase()", + color = BisqTheme.colors.light1 + ) + BisqGap.H1() + } + } + /* + // TODO: Keeping this code for later. If we want to have Chat button here again + if (!myTrade) { + BisqVDivider() + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.width(36.dp).height(108.dp).background(color = BisqTheme.colors.dark4) + ) { + IconButton(onClick = onChatClick) { + ChatIcon(modifier = Modifier.size(24.dp)) + } + } + } + */ + } + + if (myTrade) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + start = BisqUIConstants.ScreenPadding, + end = BisqUIConstants.ScreenPadding, + top = 0.dp, + bottom = BisqUIConstants.ScreenPadding, + ) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = onClick + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + BisqText.baseRegular("3 days ago", color = BisqTheme.colors.light1) + BisqButton( + text = strings.common_buy, + disabled = true, + padding = PaddingValues( + horizontal = BisqUIConstants.ScreenPadding2X, + vertical = BisqUIConstants.ScreenPaddingHalf + ), + backgroundColor = BisqTheme.colors.warning + ) + } + } + } +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/OfferCard.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/OfferCard.kt index d6c63252..3c14ce76 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/OfferCard.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/molecules/OfferCard.kt @@ -3,11 +3,7 @@ package network.bisq.mobile.presentation.ui.components.molecules import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -18,16 +14,25 @@ import androidx.compose.ui.unit.dp import network.bisq.mobile.domain.replicated.offer.bisq_easy.OfferListItemVO import network.bisq.mobile.presentation.ui.components.atoms.BisqText import network.bisq.mobile.presentation.ui.components.atoms.icons.LanguageIcon +import cafe.adriel.lyricist.LocalStrings +import network.bisq.mobile.presentation.ui.components.atoms.* +import network.bisq.mobile.presentation.ui.components.atoms.icons.ChatIcon +import network.bisq.mobile.presentation.ui.components.atoms.icons.LanguageIcon import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqVDivider import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants @Composable fun OfferCard( offerListItem: OfferListItemVO, + myTrade: Boolean = false, onClick: () -> Unit, onChatClick: () -> Unit, ) { - Row( + val strings = LocalStrings.current.common + + Column( modifier = Modifier .fillMaxWidth() .clip(shape = RoundedCornerShape(8.dp)) @@ -35,9 +40,8 @@ fun OfferCard( ) { Row( modifier = Modifier - .weight(1f) .fillMaxWidth() - .padding(12.dp) + .padding(BisqUIConstants.ScreenPadding) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null, @@ -48,7 +52,7 @@ fun OfferCard( ) { Column( verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.weight(2f) + modifier = Modifier.weight(3f), ) { UserProfile(offerListItem) PaymentMethods(offerListItem) @@ -56,33 +60,13 @@ fun OfferCard( Column( horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.weight(2f) + modifier = Modifier.weight(3f) ) { - // Len: 13 - "300 - 600 USD" - // Len: 17 - "3,000 - 6,000 XYZ" - // Len: 23 - "150,640 - 1,200,312 CRC" - //if (offerListItem.formattedQuoteAmount.length < 18) { BisqText.baseRegular( - text = offerListItem.formattedQuoteAmount, + text = "${offerListItem.formattedPrice} ${offerListItem.bisqEasyOffer.market.quoteCurrencyCode}", color = BisqTheme.colors.primary ) - /* } else { - BisqText.smallRegular( - text = offerListItem.formattedQuoteAmount, - color = BisqTheme.colors.primary - ) - }*/ BisqGap.H1() - Row { - BisqText.smallRegular( - text = "@ ", - color = BisqTheme.colors.grey2 - ) - BisqText.smallRegular( - text = offerListItem.formattedPriceSpec, - color = BisqTheme.colors.light1 - ) - } Row(verticalAlignment = Alignment.CenterVertically) { LanguageIcon() BisqText.smallRegular( @@ -93,18 +77,76 @@ fun OfferCard( text = offerListItem.bisqEasyOffer.supportedLanguageCodes.joinToString(", ").uppercase(), color = BisqTheme.colors.light1 ) + BisqGap.H1() + BisqText.smallRegular( + text = offerListItem.formattedPriceSpec, + color = BisqTheme.colors.light1 + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + BisqGap.H1() + // Len: 13 - "300 - 600 USD" + // Len: 17 - "3,000 - 6,000 XYZ" + // Len: 23 - "150,640 - 1,200,312 CRC" + if (offerListItem.formattedQuoteAmount.length < 18) { + BisqText.baseRegular( + text = offerListItem.formattedQuoteAmount, + color = BisqTheme.colors.light1 + ) + } else { + BisqText.smallRegular( + text = offerListItem.formattedQuoteAmount, + color = BisqTheme.colors.light1 + ) + } + } + } + /* + // TODO: This code is kept here, so ChatButton can be added back in future, when required + if (!myTrade) { + BisqVDivider() + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.width(36.dp).height(108.dp).background(color = BisqTheme.colors.dark4) + ) { + IconButton(onClick = onChatClick) { + ChatIcon(modifier = Modifier.size(24.dp)) + } } } + */ + } + + if (myTrade) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + start = BisqUIConstants.ScreenPadding, + end = BisqUIConstants.ScreenPadding, + top = 0.dp, + bottom = BisqUIConstants.ScreenPadding, + ) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = onClick + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + BisqText.baseRegular("3 days ago", color = BisqTheme.colors.light1) + BisqButton( + text = strings.common_buy, + disabled = true, + padding = PaddingValues( + horizontal = BisqUIConstants.ScreenPadding2X, + vertical = BisqUIConstants.ScreenPaddingHalf + ), + backgroundColor = BisqTheme.colors.warning + ) + } } - /* BisqVDivider() - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.width(36.dp).height(108.dp).background(color = BisqTheme.colors.dark4) - ) { - IconButton(onClick = onChatClick) { - ChatIcon(modifier = Modifier.size(24.dp)) - } - }*/ } } \ 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 58ecd2c6..8e13d363 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 @@ -19,20 +19,23 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.draw.alpha import androidx.navigation.compose.currentBackStackEntryAsState +import network.bisq.mobile.presentation.ui.components.BackHandler import network.bisq.mobile.presentation.ui.components.atoms.animations.ShineOverlay import network.bisq.mobile.presentation.ui.components.atoms.icons.BisqLogoSmall import network.bisq.mobile.presentation.ui.components.atoms.icons.UserIcon import network.bisq.mobile.presentation.ui.navigation.Routes import network.bisq.mobile.presentation.ui.theme.BisqTheme import org.koin.compose.koinInject -import org.koin.core.qualifier.named - -interface ITopBarPresenter: ViewPresenter { - fun onAvatarClicked() +interface ITopBarPresenter : ViewPresenter { val uniqueAvatar: StateFlow + fun onAvatarClicked() } @OptIn(ExperimentalMaterial3Api::class) @@ -41,6 +44,8 @@ fun TopBar( title: String = "", isHome: Boolean = false, customBackButton: @Composable (() -> Unit)? = null, + backConfirmation: Boolean = false, + backBehavior: (() -> Unit)? = null, isFlowScreen: Boolean = false, stepText: String = "" ) { @@ -49,6 +54,7 @@ fun TopBar( val tabNavController: NavHostController = presenter.getRootTabNavController() val interactionEnabled = presenter.isInteractive.collectAsState().value + var showBackConfirmationDialog by remember { mutableStateOf(false) } val currentTab = tabNavController.currentBackStackEntryAsState().value?.destination?.route @@ -57,7 +63,13 @@ fun TopBar( val defaultBackButton: @Composable () -> Unit = { IconButton(onClick = { if (navController.previousBackStackEntry != null) { - presenter.goBack() + if (backConfirmation) { + if (!showBackConfirmationDialog) { + showBackConfirmationDialog = true + } + } else { + presenter.goBack() + } } }) { Icon( @@ -106,24 +118,51 @@ fun TopBar( }, actions = { Row( - modifier = Modifier.padding(top = if (isFlowScreen) 15.dp else 0.dp, end = 16.dp), + modifier = Modifier.padding(top = if (isFlowScreen) 15.dp else 0.dp, end = 16.dp), verticalAlignment = Alignment.CenterVertically ) { // TODO implement full feature after MVP // BellIcon() Spacer(modifier = Modifier.width(12.dp)) ShineOverlay { - UserIcon(presenter.uniqueAvatar.value, - modifier = Modifier.size(30.dp) -// .fillMaxSize() - .alpha(if (currentTab == Routes.TabSettings.name) 0.5f else 1.0f) - .clickable { - if (currentTab != Routes.TabSettings.name && interactionEnabled) { - presenter.onAvatarClicked() - } - }) + UserIcon( + presenter.uniqueAvatar.value, + modifier = Modifier.size(30.dp) + .alpha(if (currentTab == Routes.TabSettings.name) 0.5f else 1.0f) + .clickable { + if (currentTab != Routes.TabSettings.name) { + navController.navigate(Routes.UserProfileSettings.name) + } + }) } } }, ) + + // TODO: What if both backBehavior and backConfirmation are set + if (backBehavior != null) { + BackHandler(onBackPressed = { + backBehavior.invoke() + }) + } + + if (backConfirmation) { + BackHandler(onBackPressed = { + showBackConfirmationDialog = true + }) + } + + if (showBackConfirmationDialog) { + ConfirmationDialog( + message = "Are you sure want to exit the Trade?", + subMessage = "You can resume later", + onConfirm = { + showBackConfirmationDialog = false + presenter.goBack() + }, + onDismiss = { + showBackConfirmationDialog = false + } + ) + } } \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/SnackBar.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/SnackBar.kt new file mode 100644 index 00000000..51bff8f8 --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/SnackBar.kt @@ -0,0 +1,27 @@ +package network.bisq.mobile.presentation.ui.components.organisms + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants + +@Composable +fun BisqSnackbar(snackbarHostState: SnackbarHostState) { + SnackbarHost( + hostState = snackbarHostState, + snackbar = { data -> + Snackbar( + snackbarData = data, + // containerColor = BisqTheme.colors.primary, + containerColor = BisqTheme.colors.light1.copy(alpha = 0.95f), + contentColor = BisqTheme.colors.grey5, + dismissActionContentColor = BisqTheme.colors.grey5, + modifier = Modifier.padding(bottom = BisqUIConstants.ScreenPadding2X), + ) + } + ) +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CancelTradeDialog.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CancelTradeDialog.kt new file mode 100644 index 00000000..86dfb125 --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CancelTradeDialog.kt @@ -0,0 +1,81 @@ +package network.bisq.mobile.presentation.ui.components.organisms.trades + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.text.style.LineHeightStyle +import androidx.compose.ui.unit.dp +import cafe.adriel.lyricist.LocalStrings +import network.bisq.mobile.presentation.ui.components.atoms.BisqButton +import network.bisq.mobile.presentation.ui.components.atoms.BisqText +// import network.bisq.mobile.presentation.ui.components.atoms.icons.WarningIcon +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap +import network.bisq.mobile.presentation.ui.components.molecules.BisqDialog +import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants + +@Composable +fun CancelTradeDialog( + onCancelConfirm: () -> Unit, + onDismiss: () -> Unit +) { + val strings = LocalStrings.current.bisqEasy + val stringsApplication = LocalStrings.current.application + val stringsCommon = LocalStrings.current.common + + val isBuyer: Boolean = true + + // TODO: Use this if trade steps is not started yet: bisqEasy_openTrades_rejectTrade_warning + val warningText1 = if (isBuyer) + strings.bisqEasy_openTrades_cancelTrade_warning_buyer + else + strings.bisqEasy_openTrades_cancelTrade_warning_seller + + val warningText2 = strings.bisqEasy_openTrades_cancelTrade_warning_part2 + + BisqDialog(horizontalAlignment = Alignment.Start) { + Row( + modifier = Modifier.padding(bottom = BisqUIConstants.ScreenPadding), + verticalAlignment = Alignment.CenterVertically + ) { + // WarningIcon() + BisqGap.H1() + BisqText.h6Medium( + text = stringsApplication.popup_headline_warning, + color = BisqTheme.colors.warning + ) + } + + BisqText.baseRegular(text = warningText1 + warningText2) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + BisqButton( + text = stringsCommon.common_no, + onClick = onCancelConfirm, + padding = PaddingValues(horizontal = BisqUIConstants.ScreenPadding, vertical = 8.dp), + backgroundColor = BisqTheme.colors.dark5 + ) + BisqGap.H1() + BisqButton( + text = stringsCommon.common_yes, + onClick = onDismiss, + padding = PaddingValues(horizontal = BisqUIConstants.ScreenPadding, vertical = 8.dp) + ) + } + } +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CloseTradeCard.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CloseTradeDialog.kt similarity index 78% rename from shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CloseTradeCard.kt rename to shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CloseTradeDialog.kt index 1f544442..f47a69e4 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CloseTradeCard.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/CloseTradeDialog.kt @@ -1,55 +1,58 @@ -package network.bisq.mobile.presentation.ui.components.organisms.trades - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import cafe.adriel.lyricist.LocalStrings -import network.bisq.mobile.presentation.ui.components.atoms.BisqButton -import network.bisq.mobile.presentation.ui.components.atoms.BisqText -import network.bisq.mobile.presentation.ui.theme.BisqTheme - -@Composable -fun CloseTradeCard( - onDismissRequest: () -> Unit, - onConfirm: () -> Unit -) { - val strings = LocalStrings.current.bisqEasyTradeState - val stringsBisqEasy = LocalStrings.current.bisqEasy - val stringsCommon = LocalStrings.current.common - - Column( - modifier = Modifier.padding(24.dp), - verticalArrangement = Arrangement.spacedBy(24.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - - BisqText.h4Regular(text = strings.bisqEasy_tradeState_phase4) - - BisqText.baseRegular( - text = stringsBisqEasy.bisqEasy_openTrades_closeTrade_warning_completed, - color = BisqTheme.colors.grey1, - textAlign = TextAlign.Center - ) - - Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { - BisqButton( - text = stringsCommon.buttons_cancel, - backgroundColor = BisqTheme.colors.dark5, - onClick = onDismissRequest, - padding = PaddingValues(horizontal = 20.dp, vertical = 8.dp) - ) - BisqButton( - text = stringsBisqEasy.bisqEasy_openTrades_confirmCloseTrade, - onClick = onConfirm, - padding = PaddingValues(horizontal = 12.dp, vertical = 8.dp) - ) - } - } +package network.bisq.mobile.presentation.ui.components.organisms.trades + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import cafe.adriel.lyricist.LocalStrings +import network.bisq.mobile.presentation.ui.components.atoms.BisqButton +import network.bisq.mobile.presentation.ui.components.atoms.BisqText +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap +import network.bisq.mobile.presentation.ui.components.molecules.BisqDialog +import network.bisq.mobile.presentation.ui.theme.BisqTheme +import network.bisq.mobile.presentation.ui.theme.BisqUIConstants + +@Composable +fun CloseTradeDialog( + onDismissRequest: () -> Unit, + onConfirm: () -> Unit +) { + val strings = LocalStrings.current.bisqEasyTradeState + val stringsBisqEasy = LocalStrings.current.bisqEasy + val stringsCommon = LocalStrings.current.common + + BisqDialog { + + BisqText.h4Regular( + text = strings.bisqEasy_tradeState_phase4, + modifier = Modifier.padding(bottom= BisqUIConstants.ScreenPadding) + ) + + BisqText.baseRegular( + text = stringsBisqEasy.bisqEasy_openTrades_closeTrade_warning_completed, + color = BisqTheme.colors.grey1, + textAlign = TextAlign.Center + ) + + Row { + BisqButton( + text = stringsCommon.buttons_cancel, + backgroundColor = BisqTheme.colors.dark5, + onClick = onDismissRequest, + padding = PaddingValues(horizontal = 20.dp, vertical = 8.dp) + ) + BisqGap.H1() + BisqButton( + text = stringsBisqEasy.bisqEasy_openTrades_confirmCloseTrade, + onClick = onConfirm, + padding = PaddingValues(horizontal = 12.dp, vertical = 8.dp) + ) + } + } } \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/MediationRequestDialog.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/MediationRequestDialog.kt new file mode 100644 index 00000000..eb8cebd6 --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/MediationRequestDialog.kt @@ -0,0 +1,58 @@ +package network.bisq.mobile.presentation.ui.components.organisms.trades + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import network.bisq.mobile.presentation.ui.components.atoms.BisqButton +import network.bisq.mobile.presentation.ui.components.atoms.BisqText +import network.bisq.mobile.presentation.ui.components.atoms.icons.InfoIcon +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap +import network.bisq.mobile.presentation.ui.components.molecules.BisqDialog +import network.bisq.mobile.presentation.ui.theme.BisqTheme + +@Composable +fun MediationRequestDialog( + onConfirm: () -> Unit, + onDismiss: () -> Unit, +) { + BisqDialog { + Row { + InfoIcon() + BisqGap.H1() + BisqText.h6Regular( + text = "Request mediation", + color = BisqTheme.colors.primary + ) + } + Column { + BisqText.baseRegular( + text = "If you have problems which you cannot resolve with your trade partner your can request assistance from a mediator.", + textAlign = TextAlign.Center + ) + BisqGap.V1() + BisqText.smallRegular( + text = "Please do not request mediation for general questions. In the support section there are chat rooms where you can get general advice and help.", + color = BisqTheme.colors.grey1, + textAlign = TextAlign.Center + ) + } + Row { + BisqButton( + text = "Cancel", + backgroundColor = BisqTheme.colors.dark5, + onClick = onDismiss, + padding = PaddingValues(horizontal = 42.dp, vertical = 8.dp) + ) + BisqGap.H1() + BisqButton( + text = "Open mediation", + backgroundColor = BisqTheme.colors.primary, + onClick = onConfirm, + padding = PaddingValues(horizontal = 18.dp, vertical = 8.dp) + ) + } + } +} \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowAccountDetails.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowAccountDetails.kt index 850fac32..8b1a1d59 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowAccountDetails.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowAccountDetails.kt @@ -9,7 +9,7 @@ import cafe.adriel.lyricist.LocalStrings import network.bisq.mobile.presentation.ui.components.atoms.* import network.bisq.mobile.presentation.ui.components.atoms.layout.* import network.bisq.mobile.presentation.ui.theme.BisqUIConstants -import network.bisq.mobile.presentation.ui.uicases.trades.ITradeFlowPresenter +import network.bisq.mobile.presentation.ui.uicases.trade.ITradeFlowPresenter import org.koin.compose.koinInject /** diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowFiatPayment.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowFiatPayment.kt index 4db81545..eaa18661 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowFiatPayment.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeFlowFiatPayment.kt @@ -16,7 +16,7 @@ import network.bisq.mobile.presentation.ui.components.atoms.* import network.bisq.mobile.presentation.ui.components.atoms.layout.* import network.bisq.mobile.presentation.ui.theme.BisqTheme import network.bisq.mobile.presentation.ui.theme.BisqUIConstants -import network.bisq.mobile.presentation.ui.uicases.trades.ITradeFlowPresenter +import network.bisq.mobile.presentation.ui.uicases.trade.ITradeFlowPresenter import org.koin.compose.koinInject /** diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeHeader.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeHeader.kt index b9c56ce4..3c6e853e 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeHeader.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/components/organisms/trades/TradeHeader.kt @@ -44,6 +44,7 @@ import network.bisq.mobile.presentation.ui.theme.BisqTheme @Composable fun TradeHeader( offer: OfferListItemVO, + onCancel: () -> Unit, ) { val strings = LocalStrings.current.bisqEasyTradeState val stringsBisqEasy = LocalStrings.current.bisqEasy @@ -148,7 +149,7 @@ fun TradeHeader( BisqButton( text = stringsBisqEasy.bisqEasy_openTrades_cancelTrade, color = BisqTheme.colors.grey1, - onClick = {}, + onClick = onCancel, backgroundColor = Color.Transparent, padding = PaddingValues(horizontal = 70.dp, vertical = 6.dp) ) diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/RootNavGraph.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/RootNavGraph.kt index b457f7d9..97684386 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/RootNavGraph.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/RootNavGraph.kt @@ -12,6 +12,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import network.bisq.mobile.presentation.ui.navigation.Routes +import network.bisq.mobile.presentation.ui.navigation.* import network.bisq.mobile.presentation.ui.theme.BisqTheme import network.bisq.mobile.presentation.ui.uicases.TabContainerScreen import network.bisq.mobile.presentation.ui.uicases.offer.OffersListScreen @@ -29,7 +30,7 @@ import network.bisq.mobile.presentation.ui.uicases.startup.TrustedNodeSetupScree import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferPaymentMethodScreen import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferReviewTradeScreen import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferTradeAmountScreen -import network.bisq.mobile.presentation.ui.uicases.trades.TradeFlowScreen +import network.bisq.mobile.presentation.ui.uicases.trade.TradeFlowScreen @Composable fun RootNavGraph(rootNavController: NavHostController) { @@ -105,7 +106,6 @@ fun RootNavGraph(rootNavController: NavHostController) { composable(Routes.UserProfileSettings.name) { UserProfileSettingsScreen(showBackNavigation = true) } - } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/TabNavGraph.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/TabNavGraph.kt index 8a493795..a344e833 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/TabNavGraph.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/navigation/graph/TabNavGraph.kt @@ -15,7 +15,7 @@ import network.bisq.mobile.presentation.ui.theme.BisqTheme import network.bisq.mobile.presentation.ui.uicases.GettingStartedScreen import network.bisq.mobile.presentation.ui.uicases.offer.MarketListScreen import network.bisq.mobile.presentation.ui.uicases.settings.SettingsScreen -import network.bisq.mobile.presentation.ui.uicases.trades.MyTradesScreen +import network.bisq.mobile.presentation.ui.uicases.trade.MyTradesScreen import org.koin.compose.koinInject @Composable diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/theme/UIConstants.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/theme/UIConstants.kt index 778de33b..e1756ec7 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/theme/UIConstants.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/theme/UIConstants.kt @@ -2,6 +2,7 @@ package network.bisq.mobile.presentation.ui.theme import androidx.compose.ui.unit.dp +// FinalTODO: Refactor and remove the prefix 'ScreenPadding' object BisqUIConstants { val ScreenPaddingQuarter = 3.dp val ScreenPaddingHalf = 6.dp diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedPresenter.kt index 5b49729c..8c603efe 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedPresenter.kt @@ -1,18 +1,15 @@ package network.bisq.mobile.presentation.ui.uicases -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import network.bisq.mobile.domain.data.repository.BisqStatsRepository import network.bisq.mobile.domain.service.market_price.MarketPriceServiceFacade import network.bisq.mobile.domain.service.offerbook.OfferbookServiceFacade import network.bisq.mobile.presentation.BasePresenter import network.bisq.mobile.presentation.MainPresenter import network.bisq.mobile.presentation.ui.navigation.Routes -import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferPresenter open class GettingStartedPresenter( mainPresenter: MainPresenter, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedScreen.kt index c2ff449e..8da98ab7 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/GettingStartedScreen.kt @@ -46,6 +46,7 @@ interface IGettingStarted : ViewPresenter { @Composable fun GettingStartedScreen() { val presenter: GettingStartedPresenter = koinInject() + val tabPresenter: ITabContainerPresenter = koinInject() val offersOnline: Number = presenter.offersOnline.collectAsState().value val publishedProfiles: Number = presenter.publishedProfiles.collectAsState().value diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerPresenter.kt new file mode 100644 index 00000000..957a096a --- /dev/null +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/TabContainerPresenter.kt @@ -0,0 +1,17 @@ +package network.bisq.mobile.presentation.ui.uicases + +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import network.bisq.mobile.domain.data.repository.BisqStatsRepository +import network.bisq.mobile.domain.service.market_price.MarketPriceServiceFacade +import network.bisq.mobile.presentation.BasePresenter +import network.bisq.mobile.presentation.MainPresenter +import network.bisq.mobile.presentation.ui.navigation.Routes + +class TabContainerPresenter( + mainPresenter: MainPresenter, +) : BasePresenter(mainPresenter), ITabContainerPresenter { + +} \ 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 c1f9f5b4..d24a8851 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 @@ -5,6 +5,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import bisqapps.shared.presentation.generated.resources.* import bisqapps.shared.presentation.generated.resources.Res +import network.bisq.mobile.presentation.ViewPresenter import network.bisq.mobile.presentation.ui.AppPresenter import network.bisq.mobile.presentation.ui.components.layout.BisqStaticScaffold import network.bisq.mobile.presentation.ui.components.molecules.TopBar @@ -22,11 +23,12 @@ val navigationListItem = listOf( BottomNavigationItem("Settings", Routes.TabSettings.name, Res.drawable.icon_settings), ) +interface ITabContainerPresenter : ViewPresenter {} @Composable fun TabContainerScreen() { - val mainPresenter: AppPresenter = koinInject() - val navController: NavHostController = mainPresenter.getRootTabNavController() + val presenter: ITabContainerPresenter = koinInject() + val navController: NavHostController = presenter.getRootTabNavController() val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute by remember(navBackStackEntry) { derivedStateOf { @@ -50,6 +52,20 @@ fun TabContainerScreen() { Routes.TabSettings.name -> "Settings" else -> "App" }, + backBehavior = { + if (currentRoute != Routes.TabHome.name) { + navController.navigate(Routes.TabHome.name) { + navController.graph.startDestinationRoute?.let { route -> + popUpTo(route) { saveState = false } + } + launchSingleTop = true + restoreState = false + } + } else { + presenter.showSnackbar("Press back again to exit") + presenter.goBack() + } + } ) }, @@ -69,6 +85,7 @@ fun TabContainerScreen() { } }) }, + snackbarHostState = presenter.getSnackState(), content = { TabNavGraph() } ) diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListPresenter.kt index 5356793b..d082aba0 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListPresenter.kt @@ -8,22 +8,24 @@ import network.bisq.mobile.domain.service.offerbook.OfferbookServiceFacade import network.bisq.mobile.presentation.BasePresenter import network.bisq.mobile.presentation.MainPresenter import network.bisq.mobile.presentation.ui.navigation.Routes +import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferPresenter import network.bisq.mobile.presentation.ui.uicases.trade.take_offer.TakeOfferPresenter class OffersListPresenter( mainPresenter: MainPresenter, offerbookServiceFacade: OfferbookServiceFacade, - private val takeOfferPresenter: TakeOfferPresenter -) : BasePresenter(mainPresenter) { - val offerListItems: StateFlow> = + private val takeOfferPresenter: TakeOfferPresenter, + private val createOfferPresenter: CreateOfferPresenter +) : BasePresenter(mainPresenter), IOffersListPresenter { + override val offerListItems: StateFlow> = offerbookServiceFacade.offerListItems private val _selectedDirection = MutableStateFlow(DirectionEnum.SELL) - val selectedDirection: StateFlow = _selectedDirection + override val selectedDirection: StateFlow = _selectedDirection - fun takeOffer(offerListItem: OfferListItemVO) { - takeOfferPresenter.selectOfferToTake(offerListItem) + override fun takeOffer(offer: OfferListItemVO) { + takeOfferPresenter.selectOfferToTake(offer) if (takeOfferPresenter.showAmountScreen()) { rootNavigator.navigate(Routes.TakeOfferTradeAmount.name) @@ -34,11 +36,16 @@ class OffersListPresenter( } } - fun chatForOffer(offerListItem: OfferListItemVO) { + override fun createOffer() { + createOfferPresenter.onStartCreateOffer() + rootNavigator.navigate(Routes.CreateOfferDirection.name) + } + + override fun chatForOffer(offer: OfferListItemVO) { log.i { "chat for offer clicked " } } - fun onSelectDirection(direction: DirectionEnum) { + override fun onSelectDirection(direction: DirectionEnum) { _selectedDirection.value = direction } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListScreen.kt index 93a1d684..0f96656d 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/OffersListScreen.kt @@ -1,26 +1,45 @@ package network.bisq.mobile.presentation.ui.uicases.offer import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.FloatingActionButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import cafe.adriel.lyricist.LocalStrings import network.bisq.mobile.domain.replicated.offer.DirectionEnum +import kotlinx.coroutines.flow.StateFlow +import network.bisq.mobile.domain.replicated.offer.bisq_easy.OfferListItemVO +import network.bisq.mobile.presentation.ViewPresenter +import network.bisq.mobile.presentation.ui.components.atoms.BisqText +import network.bisq.mobile.presentation.ui.components.atoms.icons.AddIcon import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap import network.bisq.mobile.presentation.ui.components.layout.BisqStaticScaffold 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 org.koin.compose.koinInject +interface IOffersListPresenter : ViewPresenter { + val offerListItems: StateFlow> + val selectedDirection: StateFlow + + fun takeOffer(offer: OfferListItemVO) + fun createOffer() + fun chatForOffer(offer: OfferListItemVO) + fun onSelectDirection(direction: DirectionEnum) +} + @Composable fun OffersListScreen() { val commonStrings = LocalStrings.current.common - val presenter: OffersListPresenter = koinInject() + val presenter: IOffersListPresenter = koinInject() RememberPresenterLifecycle(presenter) @@ -38,9 +57,16 @@ fun OffersListScreen() { RememberPresenterLifecycle(presenter) BisqStaticScaffold( - topBar = { - TopBar(title = commonStrings.common_offers) - }, + topBar = { TopBar(title = commonStrings.common_offers) }, + fab = { + FloatingActionButton( + onClick = { presenter.createOffer() }, + containerColor = BisqTheme.colors.primary, + contentColor = BisqTheme.colors.light1, + ) { + AddIcon(modifier = Modifier.size(24.dp)) + } + } ) { DirectionToggle( offerDirections, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferPaymentMethodScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferPaymentMethodScreen.kt index 96feece2..4f4e4d9a 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferPaymentMethodScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferPaymentMethodScreen.kt @@ -37,7 +37,7 @@ fun CreateOfferPaymentMethodSelectorScreen() { color = BisqTheme.colors.light1 ) - BisqGap.V1() + BisqGap.V2() PaymentMethodCard( title = presenter.quoteSideHeadline, diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferReviewPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferReviewPresenter.kt index 773fb4da..d791c460 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferReviewPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/offer/create_offer/CreateOfferReviewPresenter.kt @@ -116,6 +116,6 @@ class CreateOfferReviewPresenter( } fun onGoToOfferList() { - rootNavigator.navigate(Routes.Offerbook.name) + rootNavigator.popBackStack(Routes.Offerbook.name, false, false) } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/settings/PaymentAccountSettingsScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/settings/PaymentAccountSettingsScreen.kt index 4837197b..2fd5d999 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/settings/PaymentAccountSettingsScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/settings/PaymentAccountSettingsScreen.kt @@ -151,7 +151,7 @@ fun PaymentAccountSettingsScreen() { accountDescription = presenter.selectedAccount.value.description showConfirmationDialog = false }, - onDismissRequest = { + onDismiss = { showConfirmationDialog = false } ) 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 b7be29e9..bb0c86ca 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 @@ -102,7 +102,7 @@ open class CreateProfilePresenter( popUpTo(Routes.CreateProfile.name) { inclusive = true } } */ rootNavigator.navigate(Routes.TabContainer.name) { - popUpTo(Routes.CreateProfile.name) { inclusive = true } + popUpTo(Routes.Onboarding.name) { inclusive = true } } }.onFailure { e -> // TODO give user feedback (we could have a general error screen covering usual diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupPresenter.kt index d8df9323..867bd230 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupPresenter.kt @@ -59,10 +59,17 @@ class TrustedNodeSetupPresenter( } settingsRepository.update(updatedSettings) + + showSnackbar("Connected successfully") + // showSnackbar("Connected successfully and long text message with long list of english words") } } override fun navigateToNextScreen() { rootNavigator.navigate(Routes.CreateProfile.name) } + + override fun goBackToSetupScreen() { + rootNavigator.popBackStack() + } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupScreen.kt index 1bdd7a68..b270e8ae 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/startup/TrustedNodeSetupScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import network.bisq.mobile.presentation.ui.components.atoms.icons.BisqLogo @@ -41,6 +42,8 @@ interface ITrustedNodeSetupPresenter: ViewPresenter { fun testConnection(isTested: Boolean) fun navigateToNextScreen() + + fun goBackToSetupScreen() } @OptIn(ExperimentalResourceApi::class) @@ -52,10 +55,13 @@ fun TrustedNodeSetupScreen(isWorkflow: Boolean = true) { val bisqApiUrl = presenter.bisqApiUrl.collectAsState().value val isConnected = presenter.isConnected.collectAsState().value + val clipboardManager = LocalClipboardManager.current RememberPresenterLifecycle(presenter) - BisqScrollScaffold { + BisqScrollScaffold( + snackbarHostState = presenter.getSnackState() + ) { BisqLogo() Spacer(modifier = Modifier.height(24.dp)) Column( @@ -82,7 +88,11 @@ fun TrustedNodeSetupScreen(isWorkflow: Boolean = true) { ) { BisqButton( text = "Paste", - onClick = {}, + onClick = { + val annotatedString = clipboardManager.getText() + if(annotatedString != null) { + presenter.updateBisqApiUrl(annotatedString.text) + } }, backgroundColor = BisqTheme.colors.dark5, color = BisqTheme.colors.light1, leftIcon= { CopyIcon() } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/MyTradesPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/MyTradesPresenter.kt similarity index 63% rename from shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/MyTradesPresenter.kt rename to shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/MyTradesPresenter.kt index b0449f2a..ade80755 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/MyTradesPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/MyTradesPresenter.kt @@ -1,5 +1,6 @@ -package network.bisq.mobile.presentation.ui.uicases.trades +package network.bisq.mobile.presentation.ui.uicases.trade +import androidx.compose.runtime.mutableStateOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -8,13 +9,17 @@ import kotlinx.coroutines.launch import network.bisq.mobile.domain.data.BackgroundDispatcher import network.bisq.mobile.domain.data.model.MockOffer import network.bisq.mobile.domain.data.repository.MyTradesRepository +import network.bisq.mobile.domain.replicated.offer.bisq_easy.OfferListItemVO +import network.bisq.mobile.domain.service.offerbook.OfferbookServiceFacade import network.bisq.mobile.presentation.BasePresenter import network.bisq.mobile.presentation.MainPresenter import network.bisq.mobile.presentation.ui.navigation.Routes +import network.bisq.mobile.presentation.ui.uicases.offer.create_offer.CreateOfferPresenter class MyTradesPresenter( mainPresenter: MainPresenter, - private val myTradesRepository: MyTradesRepository + private val offerbookServiceFacade: OfferbookServiceFacade, + private val createOfferPresenter: CreateOfferPresenter ) : BasePresenter(mainPresenter), IMyTrades { private val _myTrades = MutableStateFlow>(emptyList()) @@ -33,17 +38,19 @@ class MyTradesPresenter( } } + + override fun createOffer() { + log.i { "Goto create offer" } + createOfferPresenter.onStartCreateOffer() + rootNavigator.navigate(Routes.CreateOfferDirection.name) + } + + override fun gotoTradeScreen(offer: MockOffer) { + log.i { "Goto trade screen" } + rootNavigator.navigate(Routes.TradeFlow.name) + } + private fun refresh() { - CoroutineScope(BackgroundDispatcher).launch { - try { - delay(1000) // TODO: To simulate loading. Yet to be handled - val trades = myTradesRepository.fetch() - _myTrades.value = trades?.trades ?: emptyList() - } catch (e: Exception) { - // Handle errors - println("Error: ${e.message}") - } - } } override fun onViewAttached() { diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/MyTradesScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/MyTradesScreen.kt similarity index 71% rename from shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/MyTradesScreen.kt rename to shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/MyTradesScreen.kt index d6fb57b2..b443e9ce 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/MyTradesScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/MyTradesScreen.kt @@ -1,4 +1,4 @@ -package network.bisq.mobile.presentation.ui.uicases.trades +package network.bisq.mobile.presentation.ui.uicases.trade import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -21,9 +21,12 @@ import network.bisq.mobile.domain.data.model.MockOffer import network.bisq.mobile.presentation.ViewPresenter import network.bisq.mobile.presentation.ui.components.atoms.BisqButton import network.bisq.mobile.presentation.ui.components.atoms.BisqText +import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap import network.bisq.mobile.presentation.ui.components.layout.BisqScrollLayout +import network.bisq.mobile.presentation.ui.components.molecules.MyOfferCard 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.jetbrains.compose.resources.painterResource import org.koin.compose.koinInject @@ -31,6 +34,10 @@ interface IMyTrades : ViewPresenter { val myTrades: StateFlow> fun navigateToCurrencyList() + + fun createOffer() + + fun gotoTradeScreen(offer: MockOffer) } @Composable @@ -53,9 +60,17 @@ fun MyTradesScreen() { @Composable fun TradeList(presenter: IMyTrades, myTrades: List) { - LazyColumn(modifier = Modifier.padding(top = 48.dp)) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(BisqUIConstants.ScreenPadding), + horizontalAlignment = Alignment.CenterHorizontally, + ) { items(myTrades) { offer -> - //OfferCard( onClick = {} ) + MyOfferCard( + offerListItem = offer, + myTrade = true, + onClick = { presenter.gotoTradeScreen(offer) }, + onChatClick = {}, + ) } } @@ -65,22 +80,28 @@ fun TradeList(presenter: IMyTrades, myTrades: List) { fun NoTradesSection(presenter: IMyTrades) { BisqScrollLayout(verticalArrangement = Arrangement.Center) { Column( - modifier = Modifier.padding(vertical = 52.dp), + modifier = Modifier.padding(vertical = BisqUIConstants.ScreenPadding2X), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(64.dp) ) { Image( painterResource(Res.drawable.img_no_trades), "", - modifier = Modifier.height(272.dp).width(350.dp) + modifier = Modifier.height(220.dp).width(284.dp) ) + BisqGap.V2() BisqText.h3Regular( text = "A journey of a thousand miles begins with a first step!", color = BisqTheme.colors.light1, textAlign = TextAlign.Center ) + BisqGap.V4() BisqButton( text = "Start your first trade", - onClick = { presenter.navigateToCurrencyList() } + onClick = { presenter.navigateToCurrencyList() }, + ) + BisqGap.V1() + BisqButton( + text = "Create a offer", + onClick = { presenter.createOffer() } ) } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/TradeFlowPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/TradeFlowPresenter.kt similarity index 83% rename from shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/TradeFlowPresenter.kt rename to shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/TradeFlowPresenter.kt index e2f329ec..e73e6faa 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/TradeFlowPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/TradeFlowPresenter.kt @@ -1,4 +1,4 @@ -package network.bisq.mobile.presentation.ui.uicases.trades +package network.bisq.mobile.presentation.ui.uicases.trade import androidx.compose.runtime.Composable import cafe.adriel.lyricist.LocalStrings @@ -69,6 +69,22 @@ open class TradeFlowPresenter( setShowCloseTradeDialog(true) } + private val _showCancelTradeDialog = MutableStateFlow(false) + override val showCancelTradeDialog: StateFlow get() = _showCancelTradeDialog + override fun setShowCancelTradeDialog(value: Boolean) { + _showCancelTradeDialog.value = value + } + + override fun cancelTrade() { + setShowCancelTradeDialog(true) + } + + private val _showMediationDialog = MutableStateFlow(false) + override val showMediationDialog: StateFlow get() = _showMediationDialog + override fun setShowMediationDialog(value: Boolean) { + _showMediationDialog.value = value + } + override fun closeTradeConfirm() { setShowCloseTradeDialog(false) rootNavigator.popBackStack(Routes.Offerbook.name, inclusive = false, saveState = false) diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/TradeFlowScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/TradeFlowScreen.kt similarity index 75% rename from shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/TradeFlowScreen.kt rename to shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/TradeFlowScreen.kt index 8de32dd4..7f5fb3c5 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trades/TradeFlowScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/TradeFlowScreen.kt @@ -1,4 +1,4 @@ -package network.bisq.mobile.presentation.ui.uicases.trades +package network.bisq.mobile.presentation.ui.uicases.trade import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Box @@ -23,15 +23,14 @@ import network.bisq.mobile.presentation.ViewPresenter import network.bisq.mobile.presentation.ui.components.atoms.BisqText import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap import network.bisq.mobile.presentation.ui.components.layout.BisqStaticScaffold -import network.bisq.mobile.presentation.ui.components.molecules.BisqDialog import network.bisq.mobile.presentation.ui.components.molecules.TopBar -import network.bisq.mobile.presentation.ui.components.organisms.trades.CloseTradeCard import network.bisq.mobile.presentation.ui.components.organisms.trades.StepperSection import network.bisq.mobile.presentation.ui.components.organisms.trades.TradeFlowAccountDetails import network.bisq.mobile.presentation.ui.components.organisms.trades.TradeFlowBtcPayment import network.bisq.mobile.presentation.ui.components.organisms.trades.TradeFlowCompleted import network.bisq.mobile.presentation.ui.components.organisms.trades.TradeFlowFiatPayment import network.bisq.mobile.presentation.ui.components.organisms.trades.TradeHeader +import network.bisq.mobile.presentation.ui.components.organisms.trades.* import network.bisq.mobile.presentation.ui.theme.BisqTheme import org.koin.compose.koinInject @@ -55,6 +54,13 @@ interface ITradeFlowPresenter : ViewPresenter { fun closeTradeConfirm() + val showCancelTradeDialog: StateFlow + fun setShowCancelTradeDialog(value: Boolean) + fun cancelTrade() + + val showMediationDialog: StateFlow + fun setShowMediationDialog(value: Boolean) + fun openWalletGuideLink() fun openTradeGuideLink() @@ -70,38 +76,57 @@ fun TradeFlowScreen() { val offer = presenter.offerListItems.collectAsState().value.first() val showCloseTradeDialog = presenter.showCloseTradeDialog.collectAsState().value + val showCancelTradeDialog = presenter.showCancelTradeDialog.collectAsState().value + val showMediationDialog = presenter.showMediationDialog.collectAsState().value + + val showDialog = showCloseTradeDialog || showCancelTradeDialog || showMediationDialog BisqStaticScaffold( - topBar = { TopBar("Trade - 07b9bab1") } + topBar = { TopBar("Trade - 07b9bab1", backConfirmation = true) } ) { - Box(modifier = Modifier.fillMaxSize().blur(if (showCloseTradeDialog) 12.dp else 0.dp)) { + Box( + modifier = Modifier + .fillMaxSize() + .blur(if (showDialog) 12.dp else 0.dp) + ) { Column( modifier = Modifier .verticalScroll(rememberScrollState()) .fillMaxSize() ) { - TradeHeader(offer) + TradeHeader( + offer, + onCancel = { presenter.setShowCancelTradeDialog(true) } + ) BisqGap.V2() TradeFlowStepper() } + if (showCloseTradeDialog) { - BisqDialog() { - CloseTradeCard( - onDismissRequest = { - presenter.setShowCloseTradeDialog(false) - }, - onConfirm = { - presenter.closeTradeConfirm() - } - ) + CloseTradeDialog( + onConfirm = { presenter.closeTradeConfirm() }, + onDismissRequest = { presenter.setShowCloseTradeDialog(false) }, + ) + } - } + if (showCancelTradeDialog) { + CancelTradeDialog( + onCancelConfirm= { presenter.setShowCancelTradeDialog(false) }, + onDismiss= { presenter.setShowCancelTradeDialog(false) } + ) + } + if (showMediationDialog) { + MediationRequestDialog( + onConfirm= { presenter.setShowMediationDialog(false) }, + onDismiss= { presenter.setShowMediationDialog(false) }, + ) } + } } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferAmountScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferAmountScreen.kt index acb7e259..700c5282 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferAmountScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferAmountScreen.kt @@ -1,7 +1,6 @@ package network.bisq.mobile.presentation.ui.uicases.trade.take_offer -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -25,7 +24,8 @@ fun TakeOfferTradeAmountScreen() { stepIndex = 1, stepsLength = 3, prevOnClick = { presenter.onBack() }, - nextOnClick = { presenter.onNext() } + nextOnClick = { presenter.onNext() }, + useStaticScaffold = true ) { BisqText.h3Regular( text = strings.bisqEasy_takeOffer_amount_headline_buyer, @@ -41,17 +41,20 @@ fun TakeOfferTradeAmountScreen() { color = BisqTheme.colors.grey2 ) - Spacer(modifier = Modifier.height(128.dp)) - - BisqAmountSelector( - presenter.quoteCurrencyCode, - presenter.formattedMinAmountWithCode, - presenter.formattedMaxAmountWithCode, - presenter.sliderPosition, - presenter.formattedQuoteAmount, - presenter.formattedBaseAmount, - { sliderValue -> presenter.onSliderValueChanged(sliderValue) }, - { textInput -> presenter.onTextValueChanged(textInput) } - ) + Column( + modifier = Modifier.fillMaxHeight(), + verticalArrangement = Arrangement.Center, + ) { + BisqAmountSelector( + presenter.quoteCurrencyCode, + presenter.formattedMinAmountWithCode, + presenter.formattedMaxAmountWithCode, + presenter.sliderPosition, + presenter.formattedQuoteAmount, + presenter.formattedBaseAmount, + { sliderValue -> presenter.onSliderValueChanged(sliderValue) }, + { textInput -> presenter.onTextValueChanged(textInput) } + ) + } } } \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodPresenter.kt index 6c618f7b..4ad218e9 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodPresenter.kt @@ -1,5 +1,6 @@ package network.bisq.mobile.presentation.ui.uicases.trade.take_offer +import kotlinx.coroutines.flow.MutableStateFlow import network.bisq.mobile.presentation.BasePresenter import network.bisq.mobile.presentation.MainPresenter import network.bisq.mobile.presentation.ui.navigation.Routes @@ -15,6 +16,7 @@ class TakeOfferPaymentMethodPresenter( lateinit var baseSidePaymentMethods: List var quoteSidePaymentMethod: String? = null var baseSidePaymentMethod: String? = null + var quoteCurrencyCode: String = "USD" private lateinit var takeOfferModel: TakeOfferPresenter.TakeOfferModel @@ -32,6 +34,8 @@ class TakeOfferPaymentMethodPresenter( if (offerListItem.baseSidePaymentMethods.size == 1) { baseSidePaymentMethod = offerListItem.baseSidePaymentMethods[0] } + + quoteCurrencyCode = takeOfferModel.priceQuote.market.quoteCurrencyCode } fun onQuoteSidePaymentMethodSelected(paymentMethod: String) { @@ -52,6 +56,13 @@ class TakeOfferPaymentMethodPresenter( commitToModel() rootNavigator.navigate(Routes.TakeOfferReviewTrade.name) } else { + var warningMessage = "Please select both Fiat and Bitcoin payment methods" + if (quoteSidePaymentMethod == null && baseSidePaymentMethod != null) { + warningMessage = "Please select fiat payment method" + } else if (quoteSidePaymentMethod != null && baseSidePaymentMethod == null) { + warningMessage = "Please select settlement method" + } + showSnackbar(warningMessage) //TODO show user feedback if one or both are not selected. // Note the data is set at the service layer, so if there is only one payment method we // have it set at the service. We do not need to check here if we have the multiple options. @@ -65,4 +76,29 @@ class TakeOfferPaymentMethodPresenter( } private fun isValid() = quoteSidePaymentMethod != null && baseSidePaymentMethod != null + + fun getPaymentMethodAsSet(paymentMethod: String?): MutableStateFlow> { + return if (paymentMethod == null) + MutableStateFlow(emptySet()) + else { + MutableStateFlow(setOf(paymentMethod)) + } + } + + fun getQuoteSidePaymentMethodsImagePaths(): List { + return quoteSidePaymentMethods.map { payment -> + getPaymentMethodImagePath(payment, "fiat") + } + } + + fun getBaseSidePaymentMethodsImagePaths(): List { + return baseSidePaymentMethods.map { payment -> + getPaymentMethodImagePath(payment, "bitcoin") + } + } + + private fun getPaymentMethodImagePath(paymentMethod: String, directory: String): String { + val fileName = paymentMethod.lowercase().replace("-", "_") + return "drawable/payment/$directory/$fileName.png" + } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodScreen.kt index eb6c68bd..e6481ad4 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferPaymentMethodScreen.kt @@ -1,56 +1,39 @@ package network.bisq.mobile.presentation.ui.uicases.trade.take_offer -import androidx.compose.animation.animateColorAsState -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp import cafe.adriel.lyricist.LocalStrings -import network.bisq.mobile.i18n.toDisplayString import network.bisq.mobile.presentation.ui.components.atoms.BisqText -import network.bisq.mobile.presentation.ui.components.atoms.DynamicImage import network.bisq.mobile.presentation.ui.components.atoms.layout.BisqGap import network.bisq.mobile.presentation.ui.components.layout.MultiScreenWizardScaffold +import network.bisq.mobile.presentation.ui.components.organisms.PaymentMethodCard 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 @Composable fun TakeOfferPaymentMethodScreen() { val strings = LocalStrings.current.bisqEasy - val paymentMethodStrings = LocalStrings.current.paymentMethod val presenter: TakeOfferPaymentMethodPresenter = koinInject() val baseSidePaymentMethod = remember { mutableStateOf(presenter.baseSidePaymentMethod) } val quoteSidePaymentMethod = remember { mutableStateOf(presenter.quoteSidePaymentMethod) } + val quoteCurrencyCode = remember { mutableStateOf(presenter.quoteCurrencyCode) } RememberPresenterLifecycle(presenter, { baseSidePaymentMethod.value = presenter.baseSidePaymentMethod quoteSidePaymentMethod.value = presenter.quoteSidePaymentMethod + quoteCurrencyCode.value = presenter.quoteCurrencyCode }) - var customMethodCounter = 1 - MultiScreenWizardScaffold( strings.bisqEasy_takeOffer_progress_method, stepIndex = 2, stepsLength = 3, prevOnClick = { presenter.onBack() }, - nextOnClick = { presenter.onNext() } + nextOnClick = { presenter.onNext() }, + snackbarHostState = presenter.getSnackState(), ) { BisqText.h3Regular( @@ -59,112 +42,39 @@ fun TakeOfferPaymentMethodScreen() { ) if (presenter.hasMultipleQuoteSidePaymentMethods) { + BisqGap.V2() - BisqGap.V2() - Column( - modifier = Modifier.padding(horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - BisqText.largeLight( - text = strings.bisqEasy_takeOffer_paymentMethods_subtitle_fiat_buyer("USD"), - color = BisqTheme.colors.grey2 - ) - BisqGap.V2() - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = 38.dp), - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(BisqUIConstants.ScreenPadding) - ) { - presenter.quoteSidePaymentMethods.forEach { paymentMethod -> - // TODO: Make this to Toggle buttons. Can get paymentMethod as some Enum? + PaymentMethodCard( + title = strings.bisqEasy_takeOffer_paymentMethods_subtitle_fiat_buyer(quoteCurrencyCode.value), + imagePaths = presenter.getQuoteSidePaymentMethodsImagePaths(), + availablePaymentMethods = presenter.quoteSidePaymentMethods, + i18n = LocalStrings.current.paymentMethod, + selectedPaymentMethods = presenter.getPaymentMethodAsSet(quoteSidePaymentMethod.value), + onToggle = { paymentMethod -> + quoteSidePaymentMethod.value = paymentMethod + presenter.onQuoteSidePaymentMethodSelected(paymentMethod) + }, + ) - val isSelected = paymentMethod == quoteSidePaymentMethod.value - val backgroundColor by animateColorAsState( - targetValue = if (isSelected) BisqTheme.colors.primary else BisqTheme.colors.dark5 - ) - Row( - modifier = Modifier - .fillMaxWidth() - .clip(shape = RoundedCornerShape(6.dp)) - .background(color = backgroundColor) - .clickable { - quoteSidePaymentMethod.value = paymentMethod - presenter.onQuoteSidePaymentMethodSelected(paymentMethod) - } - .padding(start = 18.dp) - .padding(vertical = 10.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) - { - DynamicImage( - path = "drawable/payment/fiat/${ - paymentMethod - .lowercase() - .replace("-", "_") - }.png", - fallbackPath = "drawable/payment/fiat/custom_payment_${customMethodCounter++}.png", - modifier = Modifier.size(15.dp), - ) - BisqText.baseRegular(text = paymentMethodStrings.toDisplayString(paymentMethod)) - } - } - } - } } if (presenter.hasMultipleBaseSidePaymentMethods) { + BisqGap.V2() - BisqGap.V2() - Column( - modifier = Modifier.padding(horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - BisqText.largeLight( - text = strings.bisqEasy_takeOffer_paymentMethods_subtitle_bitcoin_seller, - color = BisqTheme.colors.grey2 - ) - BisqGap.V1() - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = 38.dp), - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(BisqUIConstants.ScreenPadding) - ) { - presenter.baseSidePaymentMethods.forEach { paymentMethod -> - // TODO: Make this to Toggle buttons. Can get paymentMethod as some Enum? - val isSelected = paymentMethod == baseSidePaymentMethod.value - val backgroundColor by animateColorAsState( - targetValue = if (isSelected) BisqTheme.colors.primary else BisqTheme.colors.dark5 - ) - Row( - modifier = Modifier - .fillMaxWidth() - .clip(shape = RoundedCornerShape(6.dp)) - .background(color = backgroundColor) - .clickable { - baseSidePaymentMethod.value = paymentMethod - presenter.onBaseSidePaymentMethodSelected(paymentMethod) - } - .padding(horizontal = 10.dp) - .padding(vertical = 10.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - DynamicImage( - "drawable/payment/bitcoin/${ - paymentMethod - .lowercase() - .replace("-", "_") - }.png", - modifier = Modifier.size(15.dp) - ) - BisqText.baseRegular(text = paymentMethodStrings.toDisplayString(paymentMethod)) - } - } - } - } + PaymentMethodCard( + title = strings.bisqEasy_takeOffer_paymentMethods_subtitle_bitcoin_seller, + imagePaths = presenter.getBaseSidePaymentMethodsImagePaths(), + availablePaymentMethods = presenter.baseSidePaymentMethods, + i18n = LocalStrings.current.paymentMethod, + selectedPaymentMethods = presenter.getPaymentMethodAsSet(baseSidePaymentMethod.value), + onToggle = { paymentMethod -> + baseSidePaymentMethod.value = paymentMethod + presenter.onBaseSidePaymentMethodSelected(paymentMethod) + }, + ) + } } } \ No newline at end of file diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewPresenter.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewPresenter.kt index 110cd37f..50cb9533 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewPresenter.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewPresenter.kt @@ -111,7 +111,9 @@ class TakeOfferReviewPresenter( } fun onGoToOpenTrades() { - rootNavigator.navigate(Routes.TradeFlow.name) + rootNavigator.navigate(Routes.TradeFlow.name) { + popUpTo(Routes.Offerbook.name) + } } diff --git a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewScreen.kt b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewScreen.kt index cfce0e30..34d22b85 100644 --- a/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewScreen.kt +++ b/shared/presentation/src/commonMain/kotlin/network/bisq/mobile/presentation/ui/uicases/trade/take_offer/TakeOfferReviewScreen.kt @@ -57,7 +57,7 @@ fun TakeOfferReviewTradeScreen() { } BisqHDivider() - Column(verticalArrangement = Arrangement.spacedBy(24.dp)) { + Column(verticalArrangement = Arrangement.spacedBy(BisqUIConstants.ScreenPadding2X)) { InfoBox( label = strings.bisqEasy_tradeWizard_review_priceDescription_taker, valueComposable = { diff --git a/shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.ios.kt b/shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.ios.kt new file mode 100644 index 00000000..dc5005e4 --- /dev/null +++ b/shared/presentation/src/iosMain/kotlin/network/bisq/mobile/presentation/ui/components/BackHandler.ios.kt @@ -0,0 +1,42 @@ +package network.bisq.mobile.presentation.ui.components + +import androidx.compose.runtime.Composable +//import androidx.compose.runtime.DisposableEffect +//import androidx.compose.ui.interop.LocalUIViewController +//import platform.UIKit.UINavigationController +//import platform.UIKit.UINavigationControllerDelegateProtocol +//import platform.UIKit.UIViewController +//import platform.UIKit.navigationController +//import platform.darwin.NSObject + +@Composable +actual fun BackHandler(onBackPressed: () -> Unit) { +// TODO for iOS there is no back button concept as in android, however when we do implement the back +// gesture handling we will need this. One option is something like the following: + +// val viewController = LocalUIViewController.current +// +// DisposableEffect(viewController) { +// val navigationController = viewController.navigationController +// +// val backPressHandler = object : NSObject(), UINavigationControllerDelegateProtocol { +// override fun navigationController( +// navigationController: UINavigationController, +// willShowViewController: UIViewController, +// animated: Boolean +// ) { +// if (navigationController.viewControllers.contains(viewController).not()) { +// onBackPressed() +// } +// } +// } +// +// navigationController?.delegate = backPressHandler +// +// onDispose { +// if (navigationController?.delegate == backPressHandler) { +// navigationController?.delegate = null +// } +// } +// } +}