From 63b3ea7a663db1d569779e6b3bb617a1693e2dbd Mon Sep 17 00:00:00 2001 From: wgqqqqq Date: Sun, 20 Sep 2026 14:44:05 +0800 Subject: [PATCH] fix(mobile): back the open drawer with a full-bleed fill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drawer painted its sidebar colour as a panel exactly as wide as the content card's left edge. The card is a 28dp rounded rectangle, so its corners had nothing to curve onto: the page white showed through the radius as a wedge, and the panel's own square corners sat flush against the card's rounded ones. Paint the sidebar colour as a floor under the whole shell instead, and fade it with the drawer. The card's corners now curve onto the same colour on every edge, and the fill reaches the screen bottom rather than stopping at the panel's height. The fade is timed to the content card, not to the sidebar panel, on all three clients — the panel settles 20ms sooner, and a floor that finished first would flash white behind the card's corners on close. --- .../app/ui/shell/OpenBitFunCompactDrawer.kt | 15 +++++++++++++ .../main/ets/pages/components/AppShell.ets | 21 +++++++++++++++++++ .../Features/Shell/MobileShellView.swift | 18 ++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/apps/mobile/android/app/src/main/kotlin/com/openbitfun/mobile/app/ui/shell/OpenBitFunCompactDrawer.kt b/src/apps/mobile/android/app/src/main/kotlin/com/openbitfun/mobile/app/ui/shell/OpenBitFunCompactDrawer.kt index c5a5f04ed4..2a7a8589e0 100644 --- a/src/apps/mobile/android/app/src/main/kotlin/com/openbitfun/mobile/app/ui/shell/OpenBitFunCompactDrawer.kt +++ b/src/apps/mobile/android/app/src/main/kotlin/com/openbitfun/mobile/app/ui/shell/OpenBitFunCompactDrawer.kt @@ -35,6 +35,7 @@ import com.openbitfun.mobile.app.ui.theme.MotionDrawerHideMillis import com.openbitfun.mobile.app.ui.theme.MotionDrawerOpenMillis import com.openbitfun.mobile.app.ui.theme.MotionDrawerRevealMillis import com.openbitfun.mobile.app.ui.theme.MotionDrawerScrimMillis +import com.openbitfun.mobile.app.ui.theme.openBitFunColors import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -218,7 +219,21 @@ internal fun OpenBitFunCompactDrawer( } Box(Modifier.fillMaxSize()) { + // The open drawer's fill is a floor under the whole shell, not a panel + // the width of the sidebar. The content card's rounded corners have to + // curve onto something: a fill that stopped at the card's left edge + // left them curving onto the page white, so a square-cornered grey + // block sat against a rounded card with a white wedge between them. + // Driven by contentProgress rather than drawerProgress so the floor is + // never thinner than the card has already moved, in either direction. if (compact && drawerComposed) { + Box( + Modifier + .fillMaxSize() + .background(openBitFunColors.sidebar.background) + .graphicsLayer { alpha = contentProgress.value }, + ) + Box( modifier = Modifier .width(drawerWidth) diff --git a/src/apps/mobile/harmonyos/entry/src/main/ets/pages/components/AppShell.ets b/src/apps/mobile/harmonyos/entry/src/main/ets/pages/components/AppShell.ets index ef319b5dfd..3a22388991 100644 --- a/src/apps/mobile/harmonyos/entry/src/main/ets/pages/components/AppShell.ets +++ b/src/apps/mobile/harmonyos/entry/src/main/ets/pages/components/AppShell.ets @@ -12,6 +12,7 @@ import { PAGE_BG, SCRIM, SHELL_SCRIM, + SIDEBAR_BG, TRANSPARENT, } from './Theme'; @@ -48,6 +49,26 @@ export struct AppShell { } }) + // The open drawer's fill is a floor under the whole shell, not a panel + // the width of the sidebar. The content card's rounded corners have to + // curve onto something: when the fill stopped at the card's left edge + // they curved onto the page white instead, so a square-cornered grey + // block sat against a rounded card with a white wedge between them. + Column() + .width('100%') + .height('100%') + .backgroundColor(SIDEBAR_BG) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + .hitTestBehavior(HitTestMode.None) + .opacity((this.allowSidebar && this.shellState.showSidebar) ? 1 : 0) + // Matched to the content card rather than to the sidebar panel: the + // card must never finish moving before the floor it curves onto has + // finished fading, in either direction. + .animation({ + duration: (this.allowSidebar && this.shellState.showSidebar) ? 320 : 250, + curve: Curve.EaseOut + }) + Column() { // The drawer is a compact affordance. Wide layouts put the same session // list in the master pane instead, and this copy stayed mounted behind diff --git a/src/apps/mobile/ios/OpenBitFun/Features/Shell/MobileShellView.swift b/src/apps/mobile/ios/OpenBitFun/Features/Shell/MobileShellView.swift index 4bf74f7aea..00091ffaac 100644 --- a/src/apps/mobile/ios/OpenBitFun/Features/Shell/MobileShellView.swift +++ b/src/apps/mobile/ios/OpenBitFun/Features/Shell/MobileShellView.swift @@ -176,7 +176,25 @@ struct MobileShellView: View { let compactSidebarWidth = min(280, max(220, viewportWidth * 0.68)) ZStack(alignment: .leading) { + // The open drawer's fill is a floor under the whole shell, not a + // panel the width of the sidebar. The content card's rounded + // corners have to curve onto something: a fill that stopped at the + // card's left edge left them curving onto the page white, so a + // square-cornered grey block sat against a rounded card with a + // white wedge between them. if !sidebarVisible { + OpenBitFunTheme.sidebarBg + .ignoresSafeArea() + .opacity(model.drawerOpen ? 1 : 0) + .allowsHitTesting(false) + // Matched to the content card rather than to the sidebar + // panel: the card must never finish moving before the floor + // it curves onto has finished fading, in either direction. + .animation( + .easeOut(duration: model.drawerOpen ? 0.32 : 0.25), + value: model.drawerOpen + ) + SidebarView(model: model) .frame(width: compactSidebarWidth) .opacity(model.drawerOpen ? 1 : 0)