diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ServersSummaryView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ServersSummaryView.kt index 5d94b9c2d6..1c2f34e88d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ServersSummaryView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ServersSummaryView.kt @@ -367,7 +367,7 @@ private fun SMPSubscriptionsSection(subs: SMPServerSubs, summary: SMPServerSumma ) SubscriptionStatusIndicatorView(subs, summary.sessionsOrNew.hasSess) } - Column(Modifier.padding(PaddingValues()).fillMaxWidth()) { + SectionView { InfoRow( generalGetString(MR.strings.servers_info_subscriptions_connections_subscribed), numOrDash(subs.ssActive) @@ -540,20 +540,16 @@ fun XFTPServerSummaryLayout(summary: XFTPServerSummary, statsStartedAt: Instant, ) ) } - if (summary.stats != null || summary.sessions != null) { - SectionDividerSpaced() - } + } - if (summary.stats != null) { - XFTPStatsView(stats = summary.stats, rh = rh, statsStartedAt = statsStartedAt) - if (summary.sessions != null) { - SectionDividerSpaced() - } - } + if (summary.stats != null) { + SectionDividerSpaced() + XFTPStatsView(stats = summary.stats, rh = rh, statsStartedAt = statsStartedAt) + } - if (summary.sessions != null) { - ServerSessionsView(summary.sessions) - } + if (summary.sessions != null) { + SectionDividerSpaced() + ServerSessionsView(summary.sessions) } SectionBottomSpacer() @@ -572,27 +568,21 @@ fun SMPServerSummaryLayout(summary: SMPServerSummary, statsStartedAt: Instant, r ) ) } - if (summary.stats != null || summary.subs != null || summary.sessions != null) { - SectionDividerSpaced() - } + } - if (summary.stats != null) { - SMPStatsView(stats = summary.stats, remoteHostInfo = rh, statsStartedAt = statsStartedAt) - if (summary.subs != null || summary.sessions != null) { - SectionDividerSpaced() - } - } + if (summary.stats != null) { + SectionDividerSpaced() + SMPStatsView(stats = summary.stats, remoteHostInfo = rh, statsStartedAt = statsStartedAt) + } - if (summary.subs != null) { - SMPSubscriptionsSection(subs = summary.subs, summary = summary, rh = rh) - if (summary.sessions != null) { - SectionDividerSpaced() - } - } + if (summary.subs != null) { + SectionDividerSpaced() + SMPSubscriptionsSection(subs = summary.subs, summary = summary, rh = rh) + } - if (summary.sessions != null) { - ServerSessionsView(summary.sessions) - } + if (summary.sessions != null) { + SectionDividerSpaced() + ServerSessionsView(summary.sessions) } SectionBottomSpacer() 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 fd6b27482d..87784f009e 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 @@ -196,7 +196,6 @@ private fun MutableState.ChatStopInProgressView() { private fun MutableState.ChatStopFailedView(reason: String) { SectionView(stringResource(MR.strings.error_stopping_chat)) { Text(reason) - SectionDividerSpaced() SettingsActionItemWithContent( icon = painterResource(MR.images.ic_report_filled), text = stringResource(MR.strings.auth_stop_chat), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt index 86be99d1bf..2d2de2881f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt @@ -147,8 +147,11 @@ private fun ConnectDesktop(deviceName: String, remoteCtrls: SnapshotStateList) { AppBarTitle(stringResource(MR.strings.verify_connection)) SectionView(stringResource(MR.strings.connected_to_desktop), contentPadding = PaddingValues(horizontal = DEFAULT_PADDING)) { - CtrlDeviceNameText(session, rc) - CtrlDeviceVersionText(session) + Column { + CtrlDeviceNameText(session, rc) + Spacer(Modifier.height(DEFAULT_PADDING_HALF)) + CtrlDeviceVersionText(session) + } } SectionDividerSpaced() @@ -308,8 +316,11 @@ private fun CtrlDeviceVersionText(session: RemoteCtrlSession) { private fun ActiveSession(session: RemoteCtrlSession, rc: RemoteCtrlInfo, close: () -> Unit) { AppBarTitle(stringResource(MR.strings.connected_to_desktop)) SectionView(stringResource(MR.strings.connected_desktop), contentPadding = PaddingValues(horizontal = DEFAULT_PADDING)) { - Text(rc.deviceViewName) - CtrlDeviceVersionText(session) + Column { + Text(rc.deviceViewName) + Spacer(Modifier.height(DEFAULT_PADDING_HALF)) + CtrlDeviceVersionText(session) + } } if (session.sessionCode != null) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt index 1c9e602f1b..93c5eb7756 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt @@ -561,7 +561,6 @@ object AppearanceScope { color = if (chatModel.remoteHostId != null && themeUserDestination.value != null) MaterialTheme.colors.secondary else MaterialTheme.colors.primary ) } - SectionDividerSpaced() } val state: State = remember(appPrefs.currentTheme.get()) { diff --git a/plans/2026-06-22-fix-card-section-stray-dividers.md b/plans/2026-06-22-fix-card-section-stray-dividers.md new file mode 100644 index 0000000000..72beec92bd --- /dev/null +++ b/plans/2026-06-22-fix-card-section-stray-dividers.md @@ -0,0 +1,40 @@ +# Fix stray card dividers in server info, connect-to-desktop, appearance and migration screens + +Branch: `nd/fix-ui-lines` · base: `master`. + +## Problem + +On several card screens (Android and desktop, current `master`), near-black horizontal lines appear inside section cards where they don't belong — most visible in dark/black themes: + +- **Servers info → tap an SMP or XFTP server**: the server-detail card is sliced by stray lines around its nested stats / subscriptions / sessions blocks. +- **Use from desktop → "Connected to desktop"** (also the connecting / found / verify states): two black lines between the desktop name and its version (e.g. "Desktop" and "v7.0.0.1"). +- **Settings → Appearance** with a custom image wallpaper: a double line with a gap between "Remove image" and "Color mode". +- **Migrate from this device**, "error stopping chat" state: a double line between the error text and the stop-chat action. + +Behaviour is correct everywhere; the bugs are purely visual. + +## Cause + +PR #6777 (`df5ea3d46`, "android, desktop: new settings section design") wrapped `SectionView`'s content in `CardColumnLayout`, which draws a 2 dp divider (`canvasColorForCurrentTheme()`, near-black in dark/black themes) at the bottom of every direct child except the last. The same PR turned these screens into card screens (`ModalView(cardScreen = true)`) but left three content patterns that pre-date the card chrome, each of which now produces stray lines: + +1. **Nested `SectionView`s** inside one outer `SectionView` (server detail) → card-in-card, plus a line around every nested block and spacer. +2. **Loose `Text` + `Spacer` + `Text`** as separate children of a card `SectionView` (connect-to-desktop name + version) → a line after the text and after the spacer. +3. **`SectionDividerSpaced()` as a middle child** (Appearance, migration) → on a card screen this is a 30 dp `Spacer`, so it gets a line above *and* below = a double line bracketing a gap. + +This is the same bug class as `2026-05-25-fix-e2e-encryption-section-divider.md` (#7012); #6777 reintroduced further instances that earlier fix did not cover. + +## Fix + +`apps/multiplatform/common/...`, layout-only, matching the reference sibling pattern already used by `DetailedSMPStatsLayout`: + +- **`ServersSummaryView.kt`** — `SMPServerSummaryLayout` / `XFTPServerSummaryLayout`: stop wrapping everything in one outer `SectionView`; make the address its own card and each sub-section a top-level sibling separated by `SectionDividerSpaced()` placed *between* them. `SMPSubscriptionsSection`'s rows are wrapped in their own `SectionView` so they keep card chrome. +- **`ConnectDesktopView.kt`** — wrap each device-name + version block in a single `Column` (4 states), so the card sees one child and draws no internal divider. +- **`Appearance.kt`** / **`MigrateFromDevice.kt`** — delete the in-card `SectionDividerSpaced()`; the auto-divider between the two now-adjacent rows already separates them (the #6777 precedent for in-card spacers). + +## Risk + +- Layout-only: no logic, state, side-effect, or click-handler change. +- iOS is a separate codebase (native grouped lists) and is unaffected. +- Verified: `:common:compileKotlinDesktop` and the full desktop AppImage build succeed, and the running app renders the screens correctly. Independent adversarial review confirmed every conditional branch and separator is preserved across all stats/subs/sessions combinations. +- Deliberately out of scope: `SectionTextFooter`-inside-card instances (NetworkAndServers, Migrate*) — these render as a single normal row-separator above an in-card caption, not the stray-line bug; a separate #6777-style cleanup. +- Rollback: `git revert` the fix commit.