mobile: fix orientation changing and fullscreen mode on tablets

This commit is contained in:
Ammar Ahmed
2025-12-02 14:01:51 +05:00
parent 8e5743f417
commit c957094b1e
5 changed files with 97 additions and 137 deletions

View File

@@ -111,7 +111,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
const containerWidth = widths const containerWidth = widths
? widths.sidebar + widths.list + widths.editor ? widths.sidebar + widths.list + widths.editor
: dimensions.width; : dimensions.width;
const drawerPosition = 0; const drawerPosition = 0;
const homePosition = widths.sidebar; const homePosition = widths.sidebar;
const editorPosition = widths.sidebar + widths.list; const editorPosition = widths.sidebar + widths.list;
@@ -440,15 +439,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
width: containerWidth, width: containerWidth,
flexDirection: "row" flexDirection: "row"
}, },
deviceMode === "tablet" animatedStyles
? {
transform: [
{
translateX: 0
}
]
}
: animatedStyles
]} ]}
> >
{children} {children}

View File

@@ -58,6 +58,7 @@ import { Button } from "../ui/button";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import { isFeatureAvailable } from "@notesnook/common"; import { isFeatureAvailable } from "@notesnook/common";
import PaywallSheet from "../sheets/paywall"; import PaywallSheet from "../sheets/paywall";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
const renderScene = SceneMap({ const renderScene = SceneMap({
home: SideMenuHome, home: SideMenuHome,
notebooks: SideMenuNotebooks, notebooks: SideMenuNotebooks,
@@ -68,6 +69,7 @@ const renderScene = SceneMap({
export const SideMenu = React.memo( export const SideMenu = React.memo(
function SideMenu() { function SideMenu() {
const { colors } = useThemeColors(); const { colors } = useThemeColors();
const insets = useGlobalSafeAreaInsets();
const [index, setIndex] = React.useState( const [index, setIndex] = React.useState(
SettingsService.getProperty("defaultSidebarTab") SettingsService.getProperty("defaultSidebarTab")
); );
@@ -87,10 +89,13 @@ export const SideMenu = React.memo(
]); ]);
return ( return (
<SafeAreaView <View
style={{ style={{
flex: 1, flex: 1,
backgroundColor: colors.primary.background backgroundColor: colors.primary.background,
paddingTop: insets.top,
paddingBottom: insets.bottom,
paddingLeft: insets.left
}} }}
> >
<TabView <TabView
@@ -103,7 +108,7 @@ export const SideMenu = React.memo(
animationEnabled={false} animationEnabled={false}
lazy lazy
/> />
</SafeAreaView> </View>
); );
}, },
() => true () => true
@@ -351,9 +356,8 @@ const TabBar = (
color={colors.primary.icon} color={colors.primary.icon}
onPress={async () => { onPress={async () => {
if (props.navigationState.index === 1) { if (props.navigationState.index === 1) {
const notebooksFeature = await isFeatureAvailable( const notebooksFeature =
"notebooks" await isFeatureAvailable("notebooks");
);
if (!notebooksFeature.isAllowed) { if (!notebooksFeature.isAllowed) {
PaywallSheet.present(notebooksFeature); PaywallSheet.present(notebooksFeature);
return; return;

View File

@@ -29,7 +29,7 @@ import React, {
useRef, useRef,
useState useState
} from "react"; } from "react";
import { Dimensions, LayoutChangeEvent, Platform, View } from "react-native"; import { LayoutChangeEvent, View } from "react-native";
import Orientation, { import Orientation, {
OrientationType, OrientationType,
useDeviceOrientationChange useDeviceOrientationChange
@@ -39,7 +39,6 @@ import Animated, {
useSharedValue, useSharedValue,
withTiming withTiming
} from "react-native-reanimated"; } from "react-native-reanimated";
import { SafeAreaView } from "react-native-safe-area-context";
import { notesnook } from "../../e2e/test.ids"; import { notesnook } from "../../e2e/test.ids";
import { db } from "../common/database"; import { db } from "../common/database";
import { FluidPanels } from "../components/fluid-panels"; import { FluidPanels } from "../components/fluid-panels";
@@ -69,15 +68,13 @@ import {
eOpenFullscreenEditor, eOpenFullscreenEditor,
eUnlockNote eUnlockNote
} from "../utils/events"; } from "../utils/events";
import { editorRef, fluidTabsRef } from "../utils/global-refs"; import { valueLimiter } from "../utils/functions";
import { fluidTabsRef } from "../utils/global-refs";
import { AppNavigationStack } from "./navigation-stack"; import { AppNavigationStack } from "./navigation-stack";
import type { PaneWidths } from "../screens/editor/wrapper";
const MOBILE_SIDEBAR_SIZE = 0.85; const MOBILE_SIDEBAR_SIZE = 0.85;
const valueLimiter = (value: number, min: number, max: number) => {
return value < min ? min : value > max ? max : value;
};
let SideMenu: any = null; let SideMenu: any = null;
let EditorWrapper: any = null; let EditorWrapper: any = null;
@@ -93,7 +90,6 @@ export const FluidPanelsView = React.memo(
const insets = useGlobalSafeAreaInsets(); const insets = useGlobalSafeAreaInsets();
const animatedOpacity = useSharedValue(0); const animatedOpacity = useSharedValue(0);
const animatedTranslateY = useSharedValue(-9999); const animatedTranslateY = useSharedValue(-9999);
const overlayRef = useRef<Animated.View>(null); const overlayRef = useRef<Animated.View>(null);
const [orientation, setOrientation] = useState<OrientationType>( const [orientation, setOrientation] = useState<OrientationType>(
Orientation.getInitialOrientation() Orientation.getInitialOrientation()
@@ -102,10 +98,17 @@ export const FluidPanelsView = React.memo(
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useDeviceOrientationChange((o) => { useDeviceOrientationChange((o) => {
setOrientation(o); if (
o !== OrientationType.UNKNOWN &&
o !== OrientationType["FACE-UP"] &&
o !== OrientationType["FACE-DOWN"] &&
o !== OrientationType["PORTRAIT-UPSIDEDOWN"]
) {
setOrientation(o);
}
}); });
const isLandscape = orientation.includes("LANDSCAPE"); console.log(orientation);
useEffect(() => { useEffect(() => {
if (!appLoading) { if (!appLoading) {
@@ -147,16 +150,6 @@ export const FluidPanelsView = React.memo(
if (deviceMode === "smallTablet") { if (deviceMode === "smallTablet") {
fluidTabsRef.current?.openDrawer(false); fluidTabsRef.current?.openDrawer(false);
} }
editorRef.current?.setNativeProps({
style: {
width: dimensions.width,
zIndex: 999,
paddingHorizontal:
deviceMode === "smallTablet"
? dimensions.width * 0
: dimensions.width * 0.15
}
});
}, [deviceMode, dimensions.width, setFullscreen]); }, [deviceMode, dimensions.width, setFullscreen]);
const closeFullScreenEditor = useCallback( const closeFullScreenEditor = useCallback(
@@ -169,17 +162,6 @@ export const FluidPanelsView = React.memo(
editorController.current?.commands.updateSettings({ editorController.current?.commands.updateSettings({
fullscreen: false fullscreen: false
}); });
editorRef.current?.setNativeProps({
style: {
width:
_deviceMode === "smallTablet"
? dimensions.width -
valueLimiter(dimensions.width * 0.4, 300, 450)
: dimensions.width * 0.48,
zIndex: null,
paddingHorizontal: 0
}
});
if (_deviceMode === "smallTablet") { if (_deviceMode === "smallTablet") {
fluidTabsRef.current?.goToIndex(1, false); fluidTabsRef.current?.goToIndex(1, false);
} }
@@ -221,38 +203,8 @@ export const FluidPanelsView = React.memo(
(current: string | null, size: { width: number; height: number }) => { (current: string | null, size: { width: number; height: number }) => {
setDeviceModeState(current); setDeviceModeState(current);
const needsLayout = current !== deviceMode;
if (fullscreen && current !== "mobile") { if (fullscreen && current !== "mobile") {
// Runs after size is set via state. eSendEvent(eCloseFullscreenEditor, current);
setTimeout(() => {
editorRef.current?.setNativeProps({
style: {
width: size.width,
zIndex: 999,
paddingHorizontal:
current === "smallTablet" ? size.width * 0 : size.width * 0.15
}
});
}, 1);
} else {
if (fullscreen) eSendEvent(eCloseFullscreenEditor, current);
editorRef.current?.setNativeProps({
style: {
position: "relative",
width:
current === "tablet"
? size.width * 0.48
: current === "smallTablet"
? size.width - valueLimiter(size.width * 0.4, 300, 450)
: size.width,
zIndex: null,
paddingHorizontal: 0
}
});
}
if (!needsLayout) {
return;
} }
const state = getAppState(); const state = getAppState();
@@ -280,40 +232,42 @@ export const FluidPanelsView = React.memo(
} }
break; break;
} }
}, 32); }, 400);
}, },
[deviceMode, fullscreen, setDeviceModeState] [deviceMode, fullscreen, setDeviceModeState]
); );
const checkDeviceType = React.useCallback( const checkDeviceType = React.useCallback(
(size: { width: number; height: number }) => { (size: { width: number; height: number }) => {
setDimensions({
width: size.width,
height: size.height
});
DDS.setSize(size, orientation); DDS.setSize(size, orientation);
const nextDeviceMode = DDS.isLargeTablet() const nextDeviceMode = DDS.isLargeTablet()
? "tablet" ? "tablet"
: DDS.isSmallTab : DDS.isSmallTab
? "smallTablet" ? "smallTablet"
: "mobile"; : "mobile";
setDeviceMode(nextDeviceMode, size); setDeviceMode(nextDeviceMode, size);
}, },
[orientation, setDeviceMode, setDimensions] [orientation, setDeviceMode, setDimensions]
); );
useEffect(() => {
if (orientation !== "UNKNOWN") {
checkDeviceType(dimensions);
}
}, [orientation, dimensions]);
const _onLayout = React.useCallback( const _onLayout = React.useCallback(
(event: LayoutChangeEvent) => { (event: LayoutChangeEvent) => {
const size = event?.nativeEvent?.layout; const size = event?.nativeEvent?.layout;
if (!size || (size.width === dimensions.width && deviceMode !== null)) { setDimensions({
DDS.setSize(size, orientation); width: size.width,
setDeviceMode(deviceMode, size); height: size.height
checkDeviceType(size); });
return; if (size.width > size.height) {
setOrientation(OrientationType["LANDSCAPE-RIGHT"]);
} else {
setOrientation(OrientationType["PORTRAIT"]);
} }
checkDeviceType(size);
}, },
[ [
checkDeviceType, checkDeviceType,
@@ -324,11 +278,6 @@ export const FluidPanelsView = React.memo(
] ]
); );
if (!deviceMode) {
const size = Dimensions.get("window");
checkDeviceType(size);
}
const PANE_OFFSET = useMemo( const PANE_OFFSET = useMemo(
() => ({ () => ({
mobile: { mobile: {
@@ -356,7 +305,7 @@ export const FluidPanelsView = React.memo(
[dimensions.width, fullscreen] [dimensions.width, fullscreen]
); );
const PANE_WIDTHS = useMemo( const PANE_WIDTHS: PaneWidths = useMemo(
() => ({ () => ({
mobile: { mobile: {
sidebar: dimensions.width * MOBILE_SIDEBAR_SIZE, sidebar: dimensions.width * MOBILE_SIDEBAR_SIZE,
@@ -382,6 +331,7 @@ export const FluidPanelsView = React.memo(
(scrollOffset: number) => { (scrollOffset: number) => {
if (!deviceMode) return; if (!deviceMode) return;
hideAllTooltips(); hideAllTooltips();
if ( if (
scrollOffset > scrollOffset >
PANE_OFFSET[deviceMode as keyof typeof PANE_OFFSET].sidebar - 10 PANE_OFFSET[deviceMode as keyof typeof PANE_OFFSET].sidebar - 10
@@ -409,7 +359,7 @@ export const FluidPanelsView = React.memo(
}; };
}, []); }, []);
if (!isLoading) { if (!isLoading && !SideMenu && !EditorWrapper) {
SideMenu = require("../components/side-menu").SideMenu; SideMenu = require("../components/side-menu").SideMenu;
EditorWrapper = require("../screens/editor/wrapper").EditorWrapper; EditorWrapper = require("../screens/editor/wrapper").EditorWrapper;
} }
@@ -421,15 +371,7 @@ export const FluidPanelsView = React.memo(
style={{ style={{
height: "100%", height: "100%",
width: "100%", width: "100%",
backgroundColor: colors.primary.background, backgroundColor: colors.primary.background
marginRight:
orientation === "LANDSCAPE-RIGHT" && Platform.OS === "ios"
? insets.right
: 0,
marginLeft:
orientation === "LANDSCAPE-LEFT" && Platform.OS === "ios"
? insets.left
: 0
}} }}
> >
{deviceMode && PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS] ? ( {deviceMode && PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS] ? (
@@ -503,9 +445,7 @@ export const FluidPanelsView = React.memo(
style={{ style={{
flex: 1, flex: 1,
paddingTop: insets.top, paddingTop: insets.top,
paddingBottom: insets.bottom, paddingBottom: insets.bottom
paddingLeft: insets.left,
paddingRight: insets.right
}} }}
> >
<AppNavigationStack /> <AppNavigationStack />

View File

@@ -23,21 +23,36 @@ import {
AppState, AppState,
AppStateStatus, AppStateStatus,
KeyboardAvoidingView, KeyboardAvoidingView,
Platform,
TextInput, TextInput,
View View
} from "react-native"; } from "react-native";
import Editor from "."; import Editor from ".";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets"; import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard"; import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import useKeyboard from "../../hooks/use-keyboard";
import { DDS } from "../../services/device-detection"; import { DDS } from "../../services/device-detection";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import { editorRef } from "../../utils/global-refs"; import { editorRef } from "../../utils/global-refs";
import { editorController, textInput } from "./tiptap/utils"; import { editorController, textInput } from "./tiptap/utils";
import deviceInfo from "react-native-device-info";
export const EditorWrapper = ({ widths }: { widths: any }) => { export type PaneWidths = {
mobile: {
sidebar: number;
list: number;
editor: number;
};
smallTablet: {
sidebar: number;
list: number;
editor: number;
};
tablet: {
sidebar: number;
list: number;
editor: number;
};
};
export const EditorWrapper = ({ widths }: { widths: PaneWidths }) => {
const { colors } = useThemeColors(); const { colors } = useThemeColors();
const { colors: toolBarColors } = useThemeColors("editorToolbar"); const { colors: toolBarColors } = useThemeColors("editorToolbar");
const deviceMode = useSettingStore((state) => state.deviceMode); const deviceMode = useSettingStore((state) => state.deviceMode);
@@ -47,8 +62,9 @@ export const EditorWrapper = ({ widths }: { widths: any }) => {
const introCompleted = useSettingStore( const introCompleted = useSettingStore(
(state) => state.settings.introCompleted (state) => state.settings.introCompleted
); );
const keyboard = useKeyboard();
const prevState = useRef<AppStateStatus>(undefined); const prevState = useRef<AppStateStatus>(undefined);
const isFullscreen = useSettingStore((state) => state.fullscreen);
const dimensions = useSettingStore((state) => state.dimensions);
const onAppStateChanged = async (state: AppStateStatus) => { const onAppStateChanged = async (state: AppStateStatus) => {
if (!prevState.current) { if (!prevState.current) {
@@ -72,32 +88,37 @@ export const EditorWrapper = ({ widths }: { widths: any }) => {
}; };
}, [loading]); }, [loading]);
const getMarginBottom = () => {
const bottomInsets =
Platform.OS === "android" ? 12 : insets.bottom + 16 || 14;
if (!keyboard.keyboardShown) return bottomInsets / 1.5;
if (deviceInfo.isTablet() && Platform.OS === "ios" && !floating)
return bottomInsets;
if (Platform.OS === "ios") return bottomInsets / 1.5;
return 0;
};
return ( return (
<View <View
testID="editor-wrapper" testID="editor-wrapper"
ref={editorRef} ref={editorRef}
style={{ style={[
width: widths[!introCompleted ? "mobile" : (deviceMode as any)]?.editor, {
height: "100%", width: isFullscreen
minHeight: "100%", ? dimensions.width
backgroundColor: toolBarColors.primary.background, : widths[
borderLeftWidth: DDS.isTab ? 1 : 0, !introCompleted ? "mobile" : (deviceMode as keyof PaneWidths)
borderLeftColor: DDS.isTab ]?.editor,
? colors.secondary.background height: "100%",
: "transparent", minHeight: "100%",
paddingBottom: backgroundColor: toolBarColors.primary.background,
Platform.OS === "android" ? insets.bottom + 10 : insets.bottom paddingLeft: isFullscreen
}} ? deviceMode === "smallTablet"
? 0
: dimensions.width * 0.15
: null,
paddingRight: isFullscreen
? deviceMode === "smallTablet"
? 0
: dimensions.width * 0.15
: insets.right,
borderLeftWidth: DDS.isTab ? 1 : 0,
borderLeftColor: DDS.isTab
? colors.secondary.background
: "transparent",
paddingBottom: insets.bottom
}
]}
> >
{loading || !introCompleted ? null : ( {loading || !introCompleted ? null : (
<KeyboardAvoidingView <KeyboardAvoidingView

View File

@@ -35,6 +35,10 @@ import { useTagStore } from "../stores/use-tag-store";
import { eUpdateNoteInEditor } from "./events"; import { eUpdateNoteInEditor } from "./events";
import { unlockVault } from "./unlock-vault"; import { unlockVault } from "./unlock-vault";
export const valueLimiter = (value: number, min: number, max: number) => {
return value < min ? min : value > max ? max : value;
};
export function getObfuscatedEmail(email: string) { export function getObfuscatedEmail(email: string) {
if (!email) return ""; if (!email) return "";
const [username, provider] = email.split("@"); const [username, provider] = email.split("@");