From a4cfa2ae97e7e13eb6f68f13c45325ed55127166 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Wed, 17 Jun 2026 18:21:55 +0000 Subject: [PATCH] android, desktop: fix overlapping warning texts after finalizing migration (#7071) * android, desktop: fix overlapping warning texts after finalizing migration * plans: justify migration finished screen overlap fix --- .../views/migration/MigrateFromDevice.kt | 4 +- plans/2026-06-12-fix-migrate-text-overlap.md | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 plans/2026-06-12-fix-migrate-text-overlap.md diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateFromDevice.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateFromDevice.kt index 39c4cb0b7f..fd6b27482d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateFromDevice.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateFromDevice.kt @@ -413,12 +413,12 @@ private fun MutableState.FinishedView(chatDeletion: Boolean) } ) {} } - SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_you_must_not_start_database_on_two_device)) - SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_using_on_two_device_breaks_encryption)) if (chatDeletion) { ProgressView() } } + SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_you_must_not_start_database_on_two_device)) + SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_using_on_two_device_breaks_encryption)) } @Composable diff --git a/plans/2026-06-12-fix-migrate-text-overlap.md b/plans/2026-06-12-fix-migrate-text-overlap.md new file mode 100644 index 0000000000..1bb1d3a5c6 --- /dev/null +++ b/plans/2026-06-12-fix-migrate-text-overlap.md @@ -0,0 +1,71 @@ +# Fix overlapping warning texts after finalizing migration + +Branch: `nd/fix-migrate-text` · regression from PR [#6777](https://github.com/simplex-chat/simplex-chat/pull/6777) (`df5ea3d46`, new settings section design). + +## 1. Problem statement + +On the "Migrate device" screen (Android and desktop), after tapping **Finalize migration** the finished state renders broken: the two warning texts — "You **must not** use the same database on two devices." and "**Please note**: using the same database on two devices will break the decryption of messages…" — are painted on top of each other and on top of the "Migration complete" section card, directly under the section header. + +Reproduced on desktop with default settings. The screen immediately before (`LinkShownView`, with the QR code) renders correctly. + +## 2. Solution summary + +Move the two `SectionTextFooter` calls in `FinishedView` out of the `Box` and place them after it, so they render as sequential children of the screen's scroll `Column` — the same placement `LinkShownView` already uses for its footers. + +```diff + } +- SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_you_must_not_start_database_on_two_device)) +- SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_using_on_two_device_breaks_encryption)) + if (chatDeletion) { + ProgressView() + } + } ++ SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_you_must_not_start_database_on_two_device)) ++ SectionTextFooter(annotatedStringResource(MR.strings.migrate_from_device_using_on_two_device_breaks_encryption)) + } +``` + +Total diff: 1 file, 2 lines moved (+2 / −2 at different indentation). + +## 3. Root cause + +PR #6777 added card chrome to `SectionView` and, in a sub-commit ("Migrate views: move all SectionTextFooter / SectionSpacer out of SectionView lambdas"), moved footers out of the card lambdas so they read as captions below the cards. In `FinishedView` (`MigrateFromDevice.kt`) the footers were moved out of the `SectionView` — but left **inside the wrapping `Box`**: + +```kotlin +Box { + SectionView(stringResource(MR.strings.migrate_from_device_migration_complete)) { + // "Start chat" / "Delete database" buttons + } + SectionTextFooter(…you_must_not_start_database_on_two_device…) // Box child 2 + SectionTextFooter(…using_on_two_device_breaks_encryption…) // Box child 3 + if (chatDeletion) { + ProgressView() // Box child 4 (overlay) + } +} +``` + +That `Box` exists for exactly one reason: to overlay `ProgressView` (a fullscreen-centered spinner) over the section while the chat database is being deleted. `Box` stacks its children at `TopStart`, so both footers render at the Box's top-left corner — over the card's top edge and over each other. This is the only migration sub-view where the refactor produced this shape: the other `Box`-wrapped states keep all flow content inside one child (a `SectionView` or an inner `Column`), and `LinkShownView` has no `Box` at all. + +`FinishedView` is composed inside `ColumnWithScrollBar` (via `SectionByState`), so composables emitted at the function's top level land in the scroll `Column` and stack vertically — which is where the footers belong. + +## 4. The fix in detail, and why this shape + +Three candidate fixes were compared: + +- **Move the 2 footer lines after the `Box`** (chosen). Smallest possible diff, zero re-indentation. Footers become siblings of the Box in the scroll `Column`, identical to the working `LinkShownView` pattern in the same file. `ProgressView` keeps its overlay semantics unchanged (same as `DatabaseInitView`, `ArchivingView`, `LinkCreationView`). Only behavioural delta beyond the bug fix: during the transient `chatDeletion` spinner, the overlay centers over the card rather than card + footers — matching every other migration sub-view. +- **Wrap card + footers in a `Column` inside the Box.** Behaviorally near-identical, but ~40 lines of indentation churn and a layout shape no sibling view uses. Rejected: larger diff, no benefit. +- **Also hoist `ProgressView` out of the Box.** Changes overlay semantics (spinner would flow below content instead of over it). Rejected: touches behavior the bug report doesn't concern. + +Regression risk: the change is placement-only — no logic, no state, no measurement changes. The new arrangement is the proven pattern of the adjacent view. + +## 5. Scope verification — no other instances of the bug class + +The class ("flow content as direct children of an overlay `Box`") was searched for across all Kotlin source sets (`commonMain`, `androidMain`, `desktopMain`, `android`, `desktop`) with three complementary structural scans: + +1. Every `Box` block with ≥2 stacking flow children (section views, footers, spacers, settings items): **only** `FinishedView`. +2. All 384 footer/spacer call sites classified by nearest enclosing block: the only ones directly inside a `Box` are the two fixed lines. +3. All ~25 composable functions that emit footers at function top level (placement decided by caller): no caller invokes them inside a `Box`. + +iOS is structurally immune: SwiftUI footers are part of `Section { } footer: { }` inside a `List`; `MigrateFromDevice.swift`'s `finishedView` was verified correct. + +Related but distinct (not fixed here): 10 `SectionTextFooter` calls app-wide still sit *inside* `SectionView` card lambdas (6 in migration views, plus `LinkAMobileView`, `ConnectMobileView`, 2 in `NetworkAndServers`), rendering inside the white card instead of as captions below it. Cosmetic placement inconsistency with #6777's stated pattern, no overlap — left for a separate change if desired.