diff --git a/.eslintignore b/.eslintignore
index 9e05addb20..1c7a6c3245 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -713,6 +713,7 @@ packages/app-mobile/components/NoteList.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js
+packages/app-mobile/components/SafeAreaView.js
packages/app-mobile/components/ScreenHeader/Menu.js
packages/app-mobile/components/ScreenHeader/WarningBanner.test.js
packages/app-mobile/components/ScreenHeader/WarningBanner.js
diff --git a/.gitignore b/.gitignore
index 35a956848b..63600b7217 100644
--- a/.gitignore
+++ b/.gitignore
@@ -688,6 +688,7 @@ packages/app-mobile/components/NoteList.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js
+packages/app-mobile/components/SafeAreaView.js
packages/app-mobile/components/ScreenHeader/Menu.js
packages/app-mobile/components/ScreenHeader/WarningBanner.test.js
packages/app-mobile/components/ScreenHeader/WarningBanner.js
diff --git a/packages/app-mobile/components/SafeAreaView.js b/packages/app-mobile/components/SafeAreaView.js
deleted file mode 100644
index 6285fff23d..0000000000
--- a/packages/app-mobile/components/SafeAreaView.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { View, Platform, SafeAreaView, StyleSheet /* , StatusBar */ } from 'react-native';
-const React = require('react');
-// import DeviceInfo from 'react-native-device-info';
-
-// Untested! This should check if the device has a notch and, if it does, apply
-// an extra padding on top of the screen.
-const styles = StyleSheet.create({
- AndroidSafeArea: {
- // Disabled for now because it seems that even when there's a notch the system status bar
- // covers it, and thus we should add our own gap.
- // Can only test on emulator though
- // Fixes: https://github.com/laurent22/joplin/issues/2694
-
- // paddingTop: Platform.OS === 'android' && DeviceInfo.hasNotch() ? StatusBar.currentHeight : 0,
- paddingTop: 0,
- },
-});
-
-function JoplinSafeAreaView(props) {
- if (Platform.OS === 'ios') {
- return {props.children};
- } else {
- const viewProps = { ...props };
-
- const style = [];
-
- if (viewProps.style) {
- style.push(viewProps.style);
- delete viewProps.style;
- }
-
- style.push(styles.AndroidSafeArea);
-
- return {props.children};
- }
-}
-
-module.exports = JoplinSafeAreaView;
diff --git a/packages/app-mobile/components/SafeAreaView.tsx b/packages/app-mobile/components/SafeAreaView.tsx
new file mode 100644
index 0000000000..d51f266006
--- /dev/null
+++ b/packages/app-mobile/components/SafeAreaView.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react';
+import { View, Platform, StyleSheet } from 'react-native';
+import { SafeAreaViewProps } from 'react-native-safe-area-context';
+import useSafeAreaPadding from '../utils/hooks/useSafeAreaPadding';
+import { useMemo } from 'react';
+
+interface Props extends SafeAreaViewProps {
+ titleBarUnderlayColor?: string;
+}
+
+const useStyles = (titleBarUnderlayColor: string) => {
+ const padding = useSafeAreaPadding();
+ return useMemo(() => {
+ return StyleSheet.create({
+ titleBarUnderlay: {
+ height: padding.paddingTop,
+ position: 'absolute',
+ left: padding.paddingLeft,
+ right: padding.paddingRight,
+ top: 0,
+ backgroundColor: titleBarUnderlayColor,
+ },
+ safeArea: {
+ ...padding,
+ },
+ });
+ }, [titleBarUnderlayColor, padding]);
+};
+
+const JoplinSafeAreaView: React.FC = ({ titleBarUnderlayColor, ...forwardedProps }) => {
+ const styles = useStyles(titleBarUnderlayColor);
+
+ if (Platform.OS !== 'web') {
+ return <>
+ {titleBarUnderlayColor ? : null}
+ {forwardedProps.children}
+ >;
+ } else {
+ return {forwardedProps.children};
+ }
+};
+
+export default JoplinSafeAreaView;
diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx
index 64af8192ef..05ef30599a 100644
--- a/packages/app-mobile/root.tsx
+++ b/packages/app-mobile/root.tsx
@@ -33,7 +33,7 @@ import getResponsiveValue from './components/getResponsiveValue';
import NetInfo, { NetInfoSubscription } from '@react-native-community/netinfo';
const DropdownAlert = require('react-native-dropdownalert').default;
const AlarmServiceDriver = require('./services/AlarmServiceDriver').default;
-const SafeAreaView = require('./components/SafeAreaView');
+import SafeAreaView from './components/SafeAreaView';
const { connect, Provider } = require('react-redux');
import fastDeepEqual = require('fast-deep-equal');
import { Provider as PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper';
@@ -147,6 +147,7 @@ import SsoLoginScreen from './components/screens/SsoLoginScreen';
import SamlShared from '@joplin/lib/components/shared/SamlShared';
import NoteRevisionViewer from './components/screens/NoteRevisionViewer';
import DocumentScanner from './components/screens/DocumentScanner/DocumentScanner';
+import { SafeAreaProvider } from 'react-native-safe-area-context';
const logger = Logger.create('root');
@@ -1360,8 +1361,7 @@ class AppComponent extends React.Component
disableGestures={disableSideMenuGestures}
>
-
-
+
{ shouldShowMainContent && }
@@ -1421,17 +1421,19 @@ class AppComponent extends React.Component
style={{ flex: 1 }}
closeButtonLabel={_('Dismiss')}
>
-
- {shouldShowMainContent ? mainContent : (
-
-
-
- )}
-
+
+
+ {shouldShowMainContent ? mainContent : (
+
+
+
+ )}
+
+