From 6d85e7c2881733adf763aa22ee1a0506bcd7c691 Mon Sep 17 00:00:00 2001 From: none23 Date: Tue, 13 Oct 2026 14:54:51 +0400 Subject: [PATCH 1/4] feat(mobile): float Android workspace tabs beside New thread --- .../T3WorkspaceNavigationModule.kt | 78 ++++++++++++------- .../MaterialNewThreadButton.shared.tsx | 1 + .../MaterialScrollComposeButton.android.tsx | 34 ++++---- .../features/home/AndroidHomeFab.android.tsx | 14 ++++ apps/mobile/src/features/home/HomeScreen.tsx | 7 +- .../AndroidWorkspaceNavigation.android.tsx | 15 +++- .../workspace/WorkspaceRootScreen.android.tsx | 54 +++++++------ ...ndroid-workspace-navigation-colors.test.ts | 2 +- .../android-workspace-navigation-colors.ts | 10 ++- .../workspace/workspace-tabs-state.ts | 1 + 10 files changed, 136 insertions(+), 80 deletions(-) diff --git a/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt b/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt index e13c1fbbae501..6b3cbaaaa3ed5 100644 --- a/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt +++ b/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt @@ -2,17 +2,24 @@ package expo.modules.t3nativecontrols import android.content.Context import android.view.ViewGroup +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.NavigationRail import androidx.compose.material3.NavigationRailItem import androidx.compose.material3.NavigationRailItemDefaults +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme @@ -20,11 +27,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import expo.modules.kotlin.AppContext import expo.modules.kotlin.modules.Module @@ -59,7 +70,7 @@ class T3WorkspaceNavigationModule : Module() { } } -/** One Material3 selection surface moves from below the list to its leading edge. */ +/** Compact windows use an icon pill; expanded windows retain the Material navigation rail. */ class T3WorkspaceNavigationView( context: Context, appContext: AppContext @@ -100,8 +111,12 @@ class T3WorkspaceNavigationView( } init { + clipChildren = false + clipToPadding = false addView( ComposeView(context).apply { + clipChildren = false + clipToPadding = false setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool ) @@ -136,29 +151,39 @@ class T3WorkspaceNavigationView( Spacer(Modifier.weight(1f)) } } else { - NavigationBar( + Surface( modifier = Modifier.fillMaxSize(), - containerColor = Color(containerColor), - tonalElevation = androidx.compose.ui.unit.Dp(0f), - windowInsets = WindowInsets(0), + shape = CircleShape, + color = Color(containerColor), + // Match the resting elevation of the Compose New thread FAB. + shadowElevation = 6.dp, ) { - tabs.forEach { tab -> - NavigationBarItem( - selected = selectedTab == tab, - onClick = { onTabSelected(mapOf("tab" to tab)) }, - icon = { tabIcon(tab) }, - label = { - val title = if (tab == "threads") "Chats" else "Pull requests" - Text(title, fontSize = (12 * textScale).sp) - }, - colors = NavigationBarItemDefaults.colors( - selectedIconColor = Color(selectedColor), - selectedTextColor = Color(selectedColor), - indicatorColor = Color(indicatorColor), - unselectedIconColor = Color(unselectedColor), - unselectedTextColor = Color(unselectedColor), - ), - ) + Row( + modifier = Modifier.fillMaxSize().selectableGroup(), + verticalAlignment = Alignment.CenterVertically, + ) { + tabs.forEach { tab -> + val selected = selectedTab == tab + Box( + modifier = Modifier + .weight(1f) + .fillMaxHeight() + .clip(CircleShape) + .selectable( + selected = selected, + role = Role.Tab, + onClick = { onTabSelected(mapOf("tab" to tab)) }, + ) + .padding(4.dp) + .background( + if (selected) Color(indicatorColor) else Color.Transparent, + CircleShape, + ), + contentAlignment = Alignment.Center, + ) { + tabIcon(tab, Color(if (selected) selectedColor else unselectedColor)) + } + } } } } @@ -170,7 +195,7 @@ class T3WorkspaceNavigationView( } @Composable - private fun tabIcon(tab: String) { + private fun tabIcon(tab: String, tint: Color = LocalContentColor.current) { val drawable = if (tab == "threads") { R.drawable.t3_chats } else { @@ -179,6 +204,7 @@ class T3WorkspaceNavigationView( Icon( painter = painterResource(drawable), contentDescription = if (tab == "threads") "Chats" else "Pull requests", + tint = tint, ) } } diff --git a/apps/mobile/src/components/MaterialNewThreadButton.shared.tsx b/apps/mobile/src/components/MaterialNewThreadButton.shared.tsx index 2c887ce78a71c..a3fb535db8d15 100644 --- a/apps/mobile/src/components/MaterialNewThreadButton.shared.tsx +++ b/apps/mobile/src/components/MaterialNewThreadButton.shared.tsx @@ -9,6 +9,7 @@ export function MaterialNewThreadButton(props: { readonly onPress: () => void; readonly extended?: boolean; readonly expanded?: boolean; + readonly maxWidth?: number; readonly className?: string; readonly style?: StyleProp; }) { diff --git a/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx b/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx index 8c653e67ee9aa..562b73aabead2 100644 --- a/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx +++ b/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx @@ -2,7 +2,6 @@ import { Box, ExtendedFloatingActionButton, Host, Icon, Text } from "@expo/ui/je import { defaultMinSize, fillMaxWidth, - graphicsLayer, height, onSizeChanged, size, @@ -16,6 +15,7 @@ import { resolveScaledTextRole } from "../lib/appearancePreferences"; /** Keep the animated width and icon positioning entirely inside Compose, not Yoga. */ export function MaterialScrollComposeButton(props: { readonly expanded: boolean; + readonly maxWidth?: number; readonly onPress: () => void; readonly className?: string; readonly style?: StyleProp; @@ -23,11 +23,9 @@ export function MaterialScrollComposeButton(props: { const { appearance, themeAppearance, themeVariables: colors } = useAppearancePreferences(); const typography = resolveScaledTextRole("footnote", appearance.baseFontSize); const { iconSize, fabSize } = useAndroidControlSizing(); - // Scale the native 56dp minimum; keep text and icons at their requested sizes. - const nativeSize = Math.max(56, fabSize); - const scale = fabSize / nativeSize; - const nativeIconSize = Math.round(iconSize / scale); - const [buttonWidth, setButtonWidth] = useState(nativeSize); + const [buttonWidth, setButtonWidth] = useState(fabSize); + const expanded = + props.expanded && (props.maxWidth === undefined || buttonWidth <= props.maxWidth); const rememberWidth = useCallback(({ width }: { width: number }) => { setButtonWidth((previous) => Math.max(previous, width)); }, []); @@ -46,25 +44,19 @@ export function MaterialScrollComposeButton(props: { > - + @@ -73,8 +65,8 @@ export function MaterialScrollComposeButton(props: { @@ -97,8 +89,8 @@ export function MaterialScrollComposeButton(props: { top: 0, bottom: 0, // Release the label area as soon as collapse starts, before native measurements arrive. - width: props.expanded ? buttonWidth * scale : fabSize, - borderRadius: 16 * scale, + width: expanded ? buttonWidth : fabSize, + borderRadius: 16, overflow: "hidden", }} /> diff --git a/apps/mobile/src/features/home/AndroidHomeFab.android.tsx b/apps/mobile/src/features/home/AndroidHomeFab.android.tsx index 423c76d91ea3c..4a7f2425e9fb1 100644 --- a/apps/mobile/src/features/home/AndroidHomeFab.android.tsx +++ b/apps/mobile/src/features/home/AndroidHomeFab.android.tsx @@ -7,16 +7,21 @@ import { } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { MaterialNewThreadButton } from "../../components/MaterialNewThreadButton"; +import { useAndroidControlSizing } from "../../components/useAndroidControlSizing"; import type { AndroidHomeFabLayout as SharedAndroidHomeFabLayout } from "./AndroidHomeFab.shared"; import { useWorkspaceState } from "../../state/workspace"; import { MaterialFabScrollContext } from "./MaterialFabScrollContext"; import { updateMaterialFabScroll } from "./material-fab-scroll"; import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; +import { useWorkspaceTabs } from "../workspace/WorkspaceTabsContext"; +import { ANDROID_WORKSPACE_DOCK } from "../workspace/workspace-tabs-state"; export function AndroidHomeFabLayout(props: ComponentProps) { const insets = useSafeAreaInsets(); const { appearance } = useAppearancePreferences(); const { fontScale } = useWindowDimensions(); + const { scale } = useAndroidControlSizing(); + const workspace = useWorkspaceTabs(); const [layoutWidth, setLayoutWidth] = useState(null); const { state } = useWorkspaceState(); const [expanded, setExpanded] = useState(true); @@ -41,6 +46,15 @@ export function AndroidHomeFabLayout(props: ComponentProps {v2ListEmpty} diff --git a/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx b/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx index b6cddfbd46391..e05549eaf861a 100644 --- a/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx +++ b/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx @@ -15,7 +15,7 @@ import { useAndroidControlSizing } from "../../components/useAndroidControlSizin import { androidWorkspaceNavigationColors } from "./android-workspace-navigation-colors"; import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; import { useWorkspaceTabs } from "./WorkspaceTabsContext"; -import type { WorkspaceTab } from "./workspace-tabs-state"; +import { ANDROID_WORKSPACE_DOCK, type WorkspaceTab } from "./workspace-tabs-state"; export const ANDROID_WORKSPACE_RAIL_WIDTH = 80; @@ -47,7 +47,7 @@ export function AndroidWorkspaceNavigation(props: { const insets = useSafeAreaInsets(); const keyboardVisible = useKeyboardState((state) => state.isVisible); const { themeAppearance, themeVariables: colors } = useAppearancePreferences(); - const { scale } = useAndroidControlSizing(); + const { scale, fabSize } = useAndroidControlSizing(); const rail = Boolean(props.rail); const { containerColor, indicatorColor, selectedColor, unselectedColor } = androidWorkspaceNavigationColors(colors, rail); @@ -64,7 +64,7 @@ export function AndroidWorkspaceNavigation(props: { paddingTop: insets.top, paddingBottom: insets.bottom, } - : { paddingBottom: insets.bottom }), + : { borderRadius: 999 }), }} > ); diff --git a/apps/mobile/src/features/workspace/WorkspaceRootScreen.android.tsx b/apps/mobile/src/features/workspace/WorkspaceRootScreen.android.tsx index 3e6f31cb59024..8003e5f0acf64 100644 --- a/apps/mobile/src/features/workspace/WorkspaceRootScreen.android.tsx +++ b/apps/mobile/src/features/workspace/WorkspaceRootScreen.android.tsx @@ -1,6 +1,7 @@ import { useMemo } from "react"; import { View } from "react-native"; -import { SafeAreaInsetsContext, useSafeAreaInsets } from "react-native-safe-area-context"; +import { useKeyboardState } from "react-native-keyboard-controller"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeStackScreenOptions } from "../../native/StackHeader"; import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; import { EmptyState } from "../../components/EmptyState"; @@ -12,19 +13,17 @@ import { MaterialPullRequestToolbar } from "../pull-requests/MaterialPullRequest import { useAdaptiveWorkspaceLayout } from "../layout/AdaptiveWorkspaceLayout"; import { AndroidWorkspaceNavigation } from "./AndroidWorkspaceNavigation"; import { WorkspaceTabsContext, useWorkspaceTabs } from "./WorkspaceTabsContext"; +import { ANDROID_WORKSPACE_DOCK } from "./workspace-tabs-state"; export function WorkspaceRootScreen() { const { layout } = useAdaptiveWorkspaceLayout(); const workspace = useWorkspaceTabs()!; const insets = useSafeAreaInsets(); - const { fabClearance } = useAndroidControlSizing(); - const context = useMemo( - () => ({ - ...workspace, - bottomClearance: workspace.state.selectedTab === "threads" ? fabClearance + 16 : 16, - }), - [workspace, fabClearance], - ); + const keyboardVisible = useKeyboardState((keyboard) => keyboard.isVisible); + const { fabSize } = useAndroidControlSizing(); + const bottom = Math.max(insets.bottom, 16) + 16; + const bottomClearance = bottom + fabSize + 16; + const context = useMemo(() => ({ ...workspace, bottomClearance }), [workspace, bottomClearance]); // Split lists live beside the navigator; Home remains an empty detail pane. if (layout.usesSplitView) { @@ -49,21 +48,30 @@ export function WorkspaceRootScreen() { - - - {workspace.state.selectedTab === "threads" ? ( - - ) : ( - <> - - - - - - )} + + {workspace.state.selectedTab === "threads" ? ( + + ) : ( + <> + + + + + + )} + + {!keyboardVisible ? ( + + - - + ) : null} ); diff --git a/apps/mobile/src/features/workspace/android-workspace-navigation-colors.test.ts b/apps/mobile/src/features/workspace/android-workspace-navigation-colors.test.ts index 2216ed6b4d631..533fe79b42c47 100644 --- a/apps/mobile/src/features/workspace/android-workspace-navigation-colors.test.ts +++ b/apps/mobile/src/features/workspace/android-workspace-navigation-colors.test.ts @@ -27,7 +27,7 @@ function contrast(first: string, second: string) { function expectVisibleSelection(colors: ReturnType) { expect(contrast(colors.indicatorColor, colors.containerColor)).toBeGreaterThanOrEqual(1.5); - // Native labels sit below the indicator, while their icons sit within it. + // Rail labels sit outside the indicator; both layouts place selected icons within it. for (const surface of [colors.containerColor, colors.indicatorColor]) { expect(contrast(colors.selectedColor, surface)).toBeGreaterThanOrEqual(4.5); } diff --git a/apps/mobile/src/features/workspace/android-workspace-navigation-colors.ts b/apps/mobile/src/features/workspace/android-workspace-navigation-colors.ts index a43f7e574af2c..5890a9f376ef9 100644 --- a/apps/mobile/src/features/workspace/android-workspace-navigation-colors.ts +++ b/apps/mobile/src/features/workspace/android-workspace-navigation-colors.ts @@ -1,6 +1,5 @@ import { flattenThemeColor, - themeColorWithAlpha, themeColorWithContrast, type MobileThemeVariables, } from "../../lib/mobileTheme"; @@ -10,7 +9,7 @@ export function androidWorkspaceNavigationColors(colors: MobileThemeVariables, r const header = flattenThemeColor(colors["--color-header"], colors["--color-screen"]); const containerColor = rail ? header - : flattenThemeColor(themeColorWithAlpha(colors["--color-header-foreground"], 0.05), header); + : flattenThemeColor(colors["--color-secondary"], colors["--color-screen"]); const indicatorColor = themeColorWithContrast( flattenThemeColor(colors["--color-secondary"], containerColor), containerColor, @@ -23,6 +22,11 @@ export function androidWorkspaceNavigationColors(colors: MobileThemeVariables, r flattenThemeColor(colors["--color-secondary-foreground"], indicatorColor), [indicatorColor, containerColor], ), - unselectedColor: flattenThemeColor(colors["--color-header-foreground"], containerColor), + unselectedColor: rail + ? flattenThemeColor(colors["--color-header-foreground"], containerColor) + : themeColorWithContrast( + flattenThemeColor(colors["--color-secondary-foreground"], containerColor), + containerColor, + ), }; } diff --git a/apps/mobile/src/features/workspace/workspace-tabs-state.ts b/apps/mobile/src/features/workspace/workspace-tabs-state.ts index 6cc70362a1ff9..050e2dfa96d51 100644 --- a/apps/mobile/src/features/workspace/workspace-tabs-state.ts +++ b/apps/mobile/src/features/workspace/workspace-tabs-state.ts @@ -84,6 +84,7 @@ export function workspaceTabsReducer( export const WORKSPACE_TOOLBAR = { height: 48, expandedGap: 10, edge: 16, gap: 8 }; export const WORKSPACE_DOCK = { height: 48, tabsWidth: 176, gap: 12, bottomGap: 6 }; +export const ANDROID_WORKSPACE_DOCK = { tabsWidth: 128, edge: 20, gap: 12 }; export function workspaceToolbarHeight(mode: WorkspaceSearchMode): number { return mode === "expanded" From 7a8764f9a28e060000509222caa3e1b3097a3ca2 Mon Sep 17 00:00:00 2001 From: none23 Date: Tue, 13 Oct 2026 15:17:45 +0400 Subject: [PATCH 2/4] fix(mobile): constrain New thread before its first measurement --- .../components/MaterialScrollComposeButton.android.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx b/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx index 562b73aabead2..a3262c46e3a7a 100644 --- a/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx +++ b/apps/mobile/src/components/MaterialScrollComposeButton.android.tsx @@ -24,8 +24,10 @@ export function MaterialScrollComposeButton(props: { const typography = resolveScaledTextRole("footnote", appearance.baseFontSize); const { iconSize, fabSize } = useAndroidControlSizing(); const [buttonWidth, setButtonWidth] = useState(fabSize); + // Treat measurements at the width limit as overflow, allowing for native pixel rounding. const expanded = - props.expanded && (props.maxWidth === undefined || buttonWidth <= props.maxWidth); + props.expanded && + (props.maxWidth === undefined || Math.ceil(buttonWidth) < Math.floor(props.maxWidth)); const rememberWidth = useCallback(({ width }: { width: number }) => { setButtonWidth((previous) => Math.max(previous, width)); }, []); @@ -33,7 +35,11 @@ export function MaterialScrollComposeButton(props: { Date: Tue, 13 Oct 2026 15:33:34 +0400 Subject: [PATCH 3/4] fix(mobile): scale floating tab icons with text size --- .../T3WorkspaceNavigationModule.kt | 16 ++++++++++++++-- .../AndroidWorkspaceNavigation.android.tsx | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt b/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt index 6b3cbaaaa3ed5..4dc94f2d66439 100644 --- a/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt +++ b/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.shape.CircleShape @@ -53,6 +54,7 @@ class T3WorkspaceNavigationModule : Module() { Prop("rail") { view: T3WorkspaceNavigationView, value: Boolean -> view.rail = value } Prop("dark") { view: T3WorkspaceNavigationView, value: Boolean -> view.dark = value } Prop("textScale") { view: T3WorkspaceNavigationView, value: Float -> view.textScale = value } + Prop("iconSize") { view: T3WorkspaceNavigationView, value: Float -> view.iconSize = value } Prop("containerColor") { view: T3WorkspaceNavigationView, value: Int -> view.containerColor = value } @@ -83,6 +85,7 @@ class T3WorkspaceNavigationView( var rail by mutableStateOf(false) var dark by mutableStateOf(false) var textScale by mutableStateOf(1f) + var iconSize by mutableStateOf(24f) var containerColor by mutableStateOf(0) var selectedColor by mutableStateOf(0) var indicatorColor by mutableStateOf(0) @@ -181,7 +184,11 @@ class T3WorkspaceNavigationView( ), contentAlignment = Alignment.Center, ) { - tabIcon(tab, Color(if (selected) selectedColor else unselectedColor)) + tabIcon( + tab, + Color(if (selected) selectedColor else unselectedColor), + Modifier.size(iconSize.dp), + ) } } } @@ -195,7 +202,11 @@ class T3WorkspaceNavigationView( } @Composable - private fun tabIcon(tab: String, tint: Color = LocalContentColor.current) { + private fun tabIcon( + tab: String, + tint: Color = LocalContentColor.current, + modifier: Modifier = Modifier, + ) { val drawable = if (tab == "threads") { R.drawable.t3_chats } else { @@ -204,6 +215,7 @@ class T3WorkspaceNavigationView( Icon( painter = painterResource(drawable), contentDescription = if (tab == "threads") "Chats" else "Pull requests", + modifier = modifier, tint = tint, ) } diff --git a/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx b/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx index e05549eaf861a..74a9ecad5b41a 100644 --- a/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx +++ b/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx @@ -24,6 +24,7 @@ interface NativeNavigationProps extends ViewProps { readonly selectedTab: WorkspaceTab; readonly dark: boolean; readonly textScale: number; + readonly iconSize: number; readonly containerColor: number; readonly selectedColor: number; readonly indicatorColor: number; @@ -47,7 +48,7 @@ export function AndroidWorkspaceNavigation(props: { const insets = useSafeAreaInsets(); const keyboardVisible = useKeyboardState((state) => state.isVisible); const { themeAppearance, themeVariables: colors } = useAppearancePreferences(); - const { scale, fabSize } = useAndroidControlSizing(); + const { scale, iconSize, fabSize } = useAndroidControlSizing(); const rail = Boolean(props.rail); const { containerColor, indicatorColor, selectedColor, unselectedColor } = androidWorkspaceNavigationColors(colors, rail); @@ -72,6 +73,7 @@ export function AndroidWorkspaceNavigation(props: { selectedTab={workspace.state.selectedTab} dark={themeAppearance === "dark"} textScale={scale} + iconSize={iconSize} containerColor={nativeColor(containerColor)} selectedColor={nativeColor(selectedColor)} indicatorColor={nativeColor(indicatorColor)} From 731a5f140e1f453ca48a7d4adaedc1cde17450b9 Mon Sep 17 00:00:00 2001 From: none23 Date: Tue, 13 Oct 2026 15:36:44 +0400 Subject: [PATCH 4/4] style(mobile): follow Kotlin parameter formatting --- .../modules/t3nativecontrols/T3WorkspaceNavigationModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt b/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt index 4dc94f2d66439..ac27a69783077 100644 --- a/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt +++ b/apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3WorkspaceNavigationModule.kt @@ -205,7 +205,7 @@ class T3WorkspaceNavigationView( private fun tabIcon( tab: String, tint: Color = LocalContentColor.current, - modifier: Modifier = Modifier, + modifier: Modifier = Modifier ) { val drawable = if (tab == "threads") { R.drawable.t3_chats