Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions apps/mobile/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
[
"expo-splash-screen",
{
"backgroundColor": "#faf9f5",
"backgroundColor": "#f7f5f0",
"dark": {
"backgroundColor": "#121110"
"backgroundColor": "#000000"
},
"image": "./assets/images/splash-icon.png",
"imageWidth": 76
Expand All @@ -42,6 +42,12 @@
{
"photosPermission": "DailyNote adds the photos you pick to the note you are writing. They are copied into the app and stay on this device."
}
],
[
"expo-local-authentication",
{
"faceIDPermission": "DailyNote uses Face ID to open notes you have locked."
}
]
],
"experiments": {
Expand Down
2 changes: 2 additions & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@dailynote/core": "*",
"@expo-google-fonts/newsreader": "^0.4.1",
"@expo/ui": "~57.0.8",
"@expo/vector-icons": "^15.0.2",
"expo": "~57.0.8",
"expo-constants": "~57.0.7",
"expo-crypto": "~57.0.1",
Expand All @@ -26,6 +27,7 @@
"expo-image": "~57.0.1",
"expo-image-picker": "~57.0.7",
"expo-linking": "~57.0.4",
"expo-local-authentication": "~57.0.2",
"expo-router": "~57.0.9",
"expo-splash-screen": "~57.0.5",
"expo-sqlite": "~57.0.1",
Expand Down
49 changes: 49 additions & 0 deletions apps/mobile/src/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Tabs } from 'expo-router';

import { Icon } from '@/components/icon';
import { useTheme } from '@/theme';

/**
* Notes and To-dos, the reference's two tabs.
*
* To-dos is not a second store: a to-do is a note that has been promoted to a
* commitment, so the tab is a view over the same table rather than a parallel
* feature with its own data.
*/
export default function TabsLayout() {
const theme = useTheme();

return (
<Tabs
screenOptions={{
headerShown: false,
tabBarActiveTintColor: theme.ink,
tabBarInactiveTintColor: theme.faint,
tabBarStyle: {
backgroundColor: theme.canvas,
borderTopColor: theme.line,
borderTopWidth: 1,
height: 62,
paddingTop: 6,
elevation: 0,
},
tabBarLabelStyle: { fontSize: 11, paddingBottom: 6 },
Comment on lines +22 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

React Navigation bottom tabs tabBarStyle height safe area insets bottom behavior

💡 Result:

In React Navigation, the bottom tab bar automatically calculates and applies safe area insets to its own height to ensure it is not obscured by system UI elements like the home indicator on iOS [1]. Key behavioral details: Automatic Handling: The built-in bottom tab bar uses react-native-safe-area-context to detect device insets [1][2]. When the tab bar is positioned at the bottom, it automatically adds the bottom safe area inset to its calculated height, preventing content overlap [1]. Customization and Styling: - If you use the standard tab bar, you should avoid manually overriding its layout to account for safe areas, as this can conflict with the internal automatic calculations [1][3]. - When implementing a custom tab bar via the tabBarStyle property, you are responsible for managing these insets. The library's internal logic calculates height based on a base height plus the bottom safe area inset [1]. - If you are setting a custom height in tabBarStyle, note that the tab bar's internal logic may still apply additional padding or inset logic depending on the implementation [1]. Managing Content Overlap: Because the tab bar occupies space at the bottom of the screen, your screen content may be hidden behind it. To manage this: - Use the useSafeAreaInsets hook from react-native-safe-area-context within your screens to apply appropriate padding to your containers, ensuring your content remains visible [2][4]. - On iOS, for scrollable lists (ScrollView, FlatList, etc.), use contentInsetAdjustmentBehavior="automatic" as a preferred, automated way to handle insets without manual padding calculations [2][4]. - Avoid wrapping your entire application in a single SafeAreaView; instead, apply safe area handling to the specific screen content that requires it [2][4]. Native Bottom Tabs Note: If you are using native bottom tabs, the tab bar is rendered outside the standard React Native JS layout tree [5]. Consequently, there is no direct equivalent to useBottomTabBarHeight, and you cannot measure the tab bar height directly from JS [5]. You must rely on safe area insets to apply necessary bottom padding to your content to prevent overlap [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files matching _layout.tsx / bottom tab screens:\n'
fd -a '_layout\.tsx$|Tabs|tab' . | sed 's#^\./##' | head -80

printf '\nTarget file excerpt:\n'
cat -n "apps/mobile/src/app/(tabs)/_layout.tsx" | sed -n '1,120p'

printf '\nSearch for safe-area usage and screenOptions edges:\n'
rg -n "SafeAreaView|useSafeAreaInsets|edges=\\{|edges:" apps/mobile/src -S || true

Repository: zacktiger/DailyNote

Length of output: 1924


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect React Navigation dependency versions/package manifests and BottomTabBar/source if included.
printf 'Package references:\n'
rg -n '"react-navigation/bottom-tabs"|"react-navigation"|react-navigation-bottom-tabs|BottomTabBar' package.json apps/mobile/package.json . -S 2>/dev/null | head -120 || true

printf '\nSearch for local vendored BottomTabBar TypeScript:\n'
fd -a 'BottomTabBar\.tsx$|BottomTabBar\.js$' . | sed 's#^\./##' | head -50

# Read-only behavioral/source probe: parse local BottomTabBar if present to see how safeAreaInsetBottom affects height/padding.
python3 - <<'PY'
from pathlib import Path
roots = [Path('packages/bottom-tabs/src/views'), Path('apps/mobile/node_modules/react-navigation/bottom-tabs')]
found=[]
for p in roots:
    for f in p.glob('BottomTabBar.tsx'):
        found.append(f)
    for f in p.glob('BottomTabBar.js'):
        found.append(f)
if not found:
    print('No local BottomTabBar source found in expected roots')
    raise SystemExit
for f in found:
    print(f'## {f}')
    lines=f.read_text().splitlines()
    start=max(0, next((i for i,l in enumerate(lines) if 'calculateHeight' in l or 'safeArea' in l or 'tabBarStyle' in l), 0)-20)
    end=min(len(lines), start+140)
    for i in range(start, end):
        if any(t in lines[i] for t in ['safeAreaInsetBottom','calculateHeight','tabBarStyle','style={{','StyleProvider']):
            print(f'{i+1:5d}: {lines[i]}')
PY

Repository: zacktiger/DailyNote

Length of output: 1924


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files matching _layout.tsx / bottom tab screens:\n'
fd -a '_layout\.tsx$' . | sed 's#^\./##' | head -80

printf '\nTarget file excerpt:\n'
cat -n "apps/mobile/src/app/(tabs)/_layout.tsx" | sed -n '1,120p'

printf '\nSearch for safe-area usage and screenOptions edges:\n'
rg -n "SafeAreaView|useSafeAreaInsets|edges=\\{|edges:" apps/mobile/src -S || true

printf '\nPackage references:\n'
rg -n '"react-navigation/bottom-tabs"|"react-navigation"|react-navigation-bottom-tabs|BottomTabBar' package.json apps/mobile/package.json . -S 2>/dev/null | head -120 || true

printf '\nSearch for local vendored BottomTabBar TypeScript:\n'
fd -a 'BottomTabBar\.tsx$|BottomTabBar\.js$' . | sed 's#^\./##' | head -50

python3 - <<'PY'
from pathlib import Path
roots = [Path('packages/bottom-tabs/src/views'), Path('apps/mobile/node_modules/react-navigation/bottom-tabs')]
found=[]
for p in roots:
    for f in p.glob('BottomTabBar.tsx'):
        found.append(f)
    for f in p.glob('BottomTabBar.js'):
        found.append(f)
if not found:
    print('No local BottomTabBar source found in expected roots')
    raise SystemExit
for f in found:
    print(f'## {f}')
    lines=f.read_text().splitlines()
    start=max(0, next((i for i,l in enumerate(lines) if 'calculateHeight' in l or 'safeArea' in l or 'tabBarStyle' in l), 0)-20)
    end=min(len(lines), start+140)
    for i in range(start, end):
        if any(t in lines[i] for t in ['safeAreaInsetBottom','calculateHeight','tabBarStyle','style={{','StyleProvider']):
            print(f'{i+1:5d}: {lines[i]}')
PY

Repository: zacktiger/DailyNote

Length of output: 1924


Verify the fixed tab bar height against the bottom safe-area inset.

tabBarStyle.height is hard-coded to 62. If the navigator does not add insets.bottom on top of an explicit custom layout height, tab screens with edges={['top', 'left', 'right']} can render overlap the bottom safe area. Add insets.bottom with useSafeAreaInsets(), or remove the explicit height.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/mobile/src/app/`(tabs)/_layout.tsx around lines 22 - 30, The
tabBarStyle.height is hard-coded to 62 without accounting for the bottom safe
area inset, which can cause tab screens with restricted edges to overlap the
safe area. Use the useSafeAreaInsets() hook to retrieve the bottom inset and add
it to the hard-coded height value in the tabBarStyle object, or alternatively
remove the explicit height property and let the navigator handle the layout.

}}
>
<Tabs.Screen
name="index"
options={{
title: 'Notes',
tabBarIcon: ({ color }) => <Icon name="notes" size={22} color={color} />,
}}
/>
<Tabs.Screen
name="todos"
options={{
title: 'To-dos',
tabBarIcon: ({ color }) => <Icon name="todos" size={20} color={color} />,
}}
/>
</Tabs>
);
}
Loading
Loading