From a5b2999ad984ad7f4c7a7cba5d09c33e83a04dfc Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Fri, 5 Jun 2026 09:00:42 +0200 Subject: [PATCH 1/3] fix: align widget preview defaults with ios --- .../ui/screens/widgets/WidgetSizeDraft.kt | 12 ++-- .../ui/screens/widgets/price/PriceCard.kt | 2 +- .../java/to/bitkit/ui/sheets/WidgetsSheet.kt | 5 -- .../ui/screens/widgets/WidgetSizeDraftTest.kt | 72 +++++++++++++++++++ 4 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/WidgetSizeDraft.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/WidgetSizeDraft.kt index 13f270129e..2881079486 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/WidgetSizeDraft.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/WidgetSizeDraft.kt @@ -14,8 +14,8 @@ import to.bitkit.models.WidgetType /** * Tracks the widget size chosen in a preview sheet's size carousel. Before the user picks a size it - * reflects the persisted size (or the type default for a not-yet-saved widget); once the user swipes - * the carousel the draft takes over. [current] is read by the widget's save action. + * reflects the persisted size (or small for a not-yet-saved widget, matching iOS); once the user + * swipes the carousel the draft takes over. [current] is read by the widget's save action. */ class WidgetSizeDraft( scope: CoroutineScope, @@ -23,16 +23,14 @@ class WidgetSizeDraft( widgetsDataFlow: StateFlow, subscriptionTimeoutMs: Long = SUBSCRIPTION_TIMEOUT, ) { - private val default = WidgetSize.default(type) - private val savedSize: StateFlow = widgetsDataFlow - .map { data -> data.widgets.firstOrNull { it.type == type }?.size ?: default } - .stateIn(scope, SharingStarted.WhileSubscribed(subscriptionTimeoutMs), default) + .map { data -> data.widgets.firstOrNull { it.type == type }?.size ?: WidgetSize.SMALL } + .stateIn(scope, SharingStarted.WhileSubscribed(subscriptionTimeoutMs), WidgetSize.SMALL) private val _draft = MutableStateFlow(null) val size: StateFlow = combine(_draft, savedSize) { draft, saved -> draft ?: saved } - .stateIn(scope, SharingStarted.WhileSubscribed(subscriptionTimeoutMs), default) + .stateIn(scope, SharingStarted.WhileSubscribed(subscriptionTimeoutMs), WidgetSize.SMALL) val current: WidgetSize get() = size.value diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceCard.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceCard.kt index 26552e5a7a..23547b2ad3 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceCard.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceCard.kt @@ -138,7 +138,7 @@ fun PriceCardSmall( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .testTag("price_card_small_pair_row_${widgetData.pair.displayName}") + .testTag("PriceWidgetRow-${widgetData.pair.displayName}") ) { Caption13Up( text = widgetData.pair.displayName, diff --git a/app/src/main/java/to/bitkit/ui/sheets/WidgetsSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/WidgetsSheet.kt index 7b8fd47cdf..150d3915b2 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/WidgetsSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/WidgetsSheet.kt @@ -101,15 +101,10 @@ private fun WidgetsSheetContent( val galleryViewModelStoreOwner = rememberSheetViewModelStoreOwner() val galleryScrollState = rememberScrollState() val navBackStackEntry by navController.currentBackStackEntryAsState() - val isGalleryRoute = navBackStackEntry?.destination?.hasRoute() == true val widgetFlowKey = navBackStackEntry?.destination?.widgetFlowKey() ?: startRoute.widgetFlowKey().takeIf { navBackStackEntry == null } val widgetViewModelStoreOwner = rememberWidgetFlowViewModelStoreOwner(widgetFlowKey) - LaunchedEffect(isGalleryRoute) { - galleryScrollState.scrollTo(0) - } - Column( modifier = Modifier .fillMaxWidth() diff --git a/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt b/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt new file mode 100644 index 0000000000..1768939ed8 --- /dev/null +++ b/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt @@ -0,0 +1,72 @@ +package to.bitkit.ui.screens.widgets + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceUntilIdle +import to.bitkit.data.WidgetsData +import to.bitkit.models.WidgetSize +import to.bitkit.models.WidgetType +import to.bitkit.models.WidgetWithPosition +import org.junit.Before +import to.bitkit.test.BaseUnitTest +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalCoroutinesApi::class) +class WidgetSizeDraftTest : BaseUnitTest() { + + private val widgetsData = MutableStateFlow(WidgetsData(widgets = emptyList())) + + @Before + fun setUp() { + widgetsData.value = WidgetsData(widgets = emptyList()) + } + + @Test + fun `unsaved widget defaults to small for wide types`() = test { + val draft = WidgetSizeDraft(backgroundScope, WidgetType.PRICE, widgetsData) + val collector = backgroundScope.launch { draft.size.collect {} } + + advanceUntilIdle() + + assertEquals(WidgetSize.SMALL, draft.size.first()) + collector.cancel() + } + + @Test + fun `saved widget reflects persisted size`() = test { + widgetsData.value = WidgetsData( + widgets = listOf( + WidgetWithPosition(type = WidgetType.PRICE, position = 0, size = WidgetSize.WIDE), + ), + ) + val draft = WidgetSizeDraft(backgroundScope, WidgetType.PRICE, widgetsData) + val collector = backgroundScope.launch { draft.size.collect {} } + + advanceUntilIdle() + + assertEquals(WidgetSize.WIDE, draft.size.first()) + collector.cancel() + } + + @Test + fun `user draft overrides saved size`() = test { + widgetsData.value = WidgetsData( + widgets = listOf( + WidgetWithPosition(type = WidgetType.FACTS, position = 0, size = WidgetSize.SMALL), + ), + ) + val draft = WidgetSizeDraft(backgroundScope, WidgetType.FACTS, widgetsData) + val collector = backgroundScope.launch { draft.size.collect {} } + + advanceUntilIdle() + draft.set(WidgetSize.WIDE) + + advanceUntilIdle() + + assertEquals(WidgetSize.WIDE, draft.size.first()) + collector.cancel() + } +} From 7dcd7f26cdcf083c8974477ecea160039b21a127 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Fri, 5 Jun 2026 09:06:00 +0200 Subject: [PATCH 2/3] chore: add changelog fragment for widget preview fix Co-authored-by: Cursor --- changelog.d/next/990.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/next/990.fixed.md diff --git a/changelog.d/next/990.fixed.md b/changelog.d/next/990.fixed.md new file mode 100644 index 0000000000..adfcfcbac0 --- /dev/null +++ b/changelog.d/next/990.fixed.md @@ -0,0 +1 @@ +New widgets now open on compact size in the preview carousel, matching iOS, and the add-widgets list keeps its scroll position when navigating back. From 558acc11cfc1c7fd6acabcd5755690a3d39f40b7 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Fri, 5 Jun 2026 09:29:59 +0200 Subject: [PATCH 3/3] chore: fix import order in widget size draft test Co-authored-by: Cursor --- .../java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt b/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt index 1768939ed8..24ac7aadf4 100644 --- a/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt +++ b/app/src/test/java/to/bitkit/ui/screens/widgets/WidgetSizeDraftTest.kt @@ -5,11 +5,11 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.test.advanceUntilIdle +import org.junit.Before import to.bitkit.data.WidgetsData import to.bitkit.models.WidgetSize import to.bitkit.models.WidgetType import to.bitkit.models.WidgetWithPosition -import org.junit.Before import to.bitkit.test.BaseUnitTest import kotlin.test.Test import kotlin.test.assertEquals