mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
mobile: add simple tab bar implementation
This fixes most bugs faced by users in side-menu on ios like swipe gesture working when it is disabled and navigation screen stuck on older ios versions.
This commit is contained in:
@@ -21,15 +21,6 @@ import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import {
|
||||
NavigationState,
|
||||
Route,
|
||||
SceneMap,
|
||||
SceneRendererProps,
|
||||
TabDescriptor,
|
||||
TabView
|
||||
} from "react-native-tab-view";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { useGroupOptions } from "../../hooks/use-group-options";
|
||||
@@ -59,7 +50,104 @@ import SettingsService from "../../services/settings";
|
||||
import { isFeatureAvailable } from "@notesnook/common";
|
||||
import PaywallSheet from "../sheets/paywall";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
const renderScene = SceneMap({
|
||||
|
||||
/**
|
||||
* Simple Tab View Implementation for the Side bar
|
||||
*/
|
||||
type SimpleRoute = {
|
||||
key: string;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
type SimpleNavigationState = {
|
||||
index: number;
|
||||
routes: SimpleRoute[];
|
||||
};
|
||||
|
||||
type SimpleTabBarProps = {
|
||||
navigationState: SimpleNavigationState;
|
||||
jumpTo: (key: string) => void;
|
||||
};
|
||||
|
||||
type SimpleTabViewProps = {
|
||||
navigationState: SimpleNavigationState;
|
||||
renderScene: ({ route }: { route: SimpleRoute }) => React.ReactNode;
|
||||
renderTabBar?: (props: SimpleTabBarProps) => React.ReactNode;
|
||||
onIndexChange?: (index: number) => void;
|
||||
};
|
||||
|
||||
const createSceneMap = (
|
||||
scenes: Record<string, React.ComponentType<any>>
|
||||
): ((props: { route: SimpleRoute }) => React.ReactNode) => {
|
||||
return ({ route }: { route: SimpleRoute }) => {
|
||||
const SceneComponent = scenes[route.key];
|
||||
if (!SceneComponent) return null;
|
||||
return <SceneComponent />;
|
||||
};
|
||||
};
|
||||
|
||||
const SimpleTabView = ({
|
||||
navigationState,
|
||||
renderScene,
|
||||
renderTabBar,
|
||||
onIndexChange
|
||||
}: SimpleTabViewProps) => {
|
||||
const loadedKeysRef = React.useRef(new Set<string>());
|
||||
const scenesRef = React.useRef(new Map<string, React.ReactNode>());
|
||||
const jumpTo = React.useCallback(
|
||||
(key: string) => {
|
||||
const nextIndex = navigationState.routes.findIndex(
|
||||
(route) => route.key === key
|
||||
);
|
||||
if (nextIndex !== -1 && nextIndex !== navigationState.index) {
|
||||
onIndexChange?.(nextIndex);
|
||||
}
|
||||
},
|
||||
[navigationState.index, navigationState.routes, onIndexChange]
|
||||
);
|
||||
|
||||
const activeKey = navigationState.routes[navigationState.index]?.key;
|
||||
if (activeKey && !loadedKeysRef.current.has(activeKey)) {
|
||||
loadedKeysRef.current.add(activeKey);
|
||||
}
|
||||
|
||||
const getSceneForRoute = React.useCallback(
|
||||
(route: SimpleRoute) => {
|
||||
const cached = scenesRef.current.get(route.key);
|
||||
if (cached) return cached;
|
||||
const created = renderScene({ route });
|
||||
scenesRef.current.set(route.key, created);
|
||||
return created;
|
||||
},
|
||||
[renderScene]
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<View style={{ flex: 1 }}>
|
||||
{navigationState.routes.map((route, routeIndex) => (
|
||||
<View
|
||||
key={route.key}
|
||||
style={{
|
||||
flex: 1,
|
||||
display: navigationState.index === routeIndex ? "flex" : "none"
|
||||
}}
|
||||
>
|
||||
{loadedKeysRef.current.has(route.key)
|
||||
? getSceneForRoute(route)
|
||||
: navigationState.index === routeIndex
|
||||
? getSceneForRoute(route)
|
||||
: null}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{renderTabBar ? renderTabBar({ navigationState, jumpTo }) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const renderScene = createSceneMap({
|
||||
home: SideMenuHome,
|
||||
notebooks: SideMenuNotebooks,
|
||||
tags: SideMenuTags,
|
||||
@@ -73,7 +161,7 @@ export const SideMenu = React.memo(
|
||||
const [index, setIndex] = React.useState(
|
||||
SettingsService.getProperty("defaultSidebarTab")
|
||||
);
|
||||
const [routes] = React.useState<Route[]>([
|
||||
const [routes] = React.useState<SimpleRoute[]>([
|
||||
{
|
||||
key: "home",
|
||||
title: "Home"
|
||||
@@ -98,15 +186,11 @@ export const SideMenu = React.memo(
|
||||
paddingLeft: insets.left
|
||||
}}
|
||||
>
|
||||
<TabView
|
||||
<SimpleTabView
|
||||
navigationState={{ index, routes }}
|
||||
renderTabBar={(props) => <TabBar {...props} />}
|
||||
tabBarPosition="bottom"
|
||||
renderScene={renderScene}
|
||||
onIndexChange={setIndex}
|
||||
swipeEnabled={false}
|
||||
animationEnabled={false}
|
||||
lazy
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
@@ -114,12 +198,7 @@ export const SideMenu = React.memo(
|
||||
() => true
|
||||
);
|
||||
|
||||
const TabBar = (
|
||||
props: SceneRendererProps & {
|
||||
navigationState: NavigationState<Route>;
|
||||
options: Record<string, TabDescriptor<Route>> | undefined;
|
||||
}
|
||||
) => {
|
||||
const TabBar = (props: SimpleTabBarProps) => {
|
||||
const dragging = useSideBarDraggingStore((state) => state.dragging);
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const groupOptions = useGroupOptions(
|
||||
|
||||
@@ -85,6 +85,7 @@ export function SideMenuHome() {
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
keyExtractor={() => "scroll-items"}
|
||||
bounces={false}
|
||||
renderItem={() => (
|
||||
<>
|
||||
<ReorderableList
|
||||
|
||||
@@ -1724,7 +1724,10 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
@@ -1789,7 +1792,10 @@
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
|
||||
@@ -2004,35 +2004,6 @@ PODS:
|
||||
- React
|
||||
- react-native-orientation-locker (1.7.0):
|
||||
- React-Core
|
||||
- react-native-pager-view (8.0.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
- fmt
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly
|
||||
- RCT-Folly/Fabric
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-jsi
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-renderercss
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- SwiftUIIntrospect (~> 1.0)
|
||||
- Yoga
|
||||
- react-native-pdf (7.0.3):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -3062,10 +3033,10 @@ PODS:
|
||||
- Yoga
|
||||
- RNKeychain (4.0.5):
|
||||
- React
|
||||
- RNNotifee (7.4.10):
|
||||
- RNNotifee (7.4.11):
|
||||
- React-Core
|
||||
- RNNotifee/NotifeeCore (= 7.4.10)
|
||||
- RNNotifee/NotifeeCore (7.4.10):
|
||||
- RNNotifee/NotifeeCore (= 7.4.11)
|
||||
- RNNotifee/NotifeeCore (7.4.11):
|
||||
- React-Core
|
||||
- RNPrivacySnapshot (1.0.0):
|
||||
- React-Core
|
||||
@@ -3161,7 +3132,7 @@ PODS:
|
||||
- RNWorklets
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNScreens (4.18.0):
|
||||
- RNScreens (4.19.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3188,10 +3159,10 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNScreens/common (= 4.18.0)
|
||||
- RNScreens/common (= 4.19.0)
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNScreens/common (4.18.0):
|
||||
- RNScreens/common (4.19.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3414,7 +3385,6 @@ PODS:
|
||||
- pop (~> 1.0)
|
||||
- SocketRocket (0.7.1)
|
||||
- SSZipArchive (2.4.3)
|
||||
- SwiftUIIntrospect (1.3.0)
|
||||
- SwiftyRSA (1.7.0):
|
||||
- SwiftyRSA/ObjC (= 1.7.0)
|
||||
- SwiftyRSA/ObjC (1.7.0)
|
||||
@@ -3486,7 +3456,6 @@ DEPENDENCIES:
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-notification-sounds (from `../node_modules/react-native-notification-sounds`)
|
||||
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
|
||||
- react-native-pager-view (from `../node_modules/react-native-pager-view`)
|
||||
- react-native-pdf (from `../node_modules/react-native-pdf`)
|
||||
- react-native-quick-sqlite (from `../node_modules/react-native-quick-sqlite`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
@@ -3570,7 +3539,6 @@ SPEC REPOS:
|
||||
- SDWebImage
|
||||
- SocketRocket
|
||||
- SSZipArchive
|
||||
- SwiftUIIntrospect
|
||||
- SwiftyRSA
|
||||
- TOCropViewController
|
||||
|
||||
@@ -3694,8 +3662,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-notification-sounds"
|
||||
react-native-orientation-locker:
|
||||
:path: "../node_modules/react-native-orientation-locker"
|
||||
react-native-pager-view:
|
||||
:path: "../node_modules/react-native-pager-view"
|
||||
react-native-pdf:
|
||||
:path: "../node_modules/react-native-pdf"
|
||||
react-native-quick-sqlite:
|
||||
@@ -3906,7 +3872,6 @@ SPEC CHECKSUMS:
|
||||
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
|
||||
react-native-notification-sounds: ce106d58df0dd384bccbd2e84fb53accab7cc068
|
||||
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
|
||||
react-native-pager-view: d7d2aa47f54343bf55fdcee3973503dd27c2bd37
|
||||
react-native-pdf: edc236298f13f1609e42d41e45b8b6ea88ed10f9
|
||||
react-native-quick-sqlite: 1ed8d3db1e22a8604d006be69f06053382e93bb0
|
||||
react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616
|
||||
@@ -3962,10 +3927,10 @@ SPEC CHECKSUMS:
|
||||
RNIap: 61f183ac917792fae42b0326b1bef33598c1adf6
|
||||
RNImageCropPicker: 5fd4ceaead64d8c53c787e4e559004f97bc76df7
|
||||
RNKeychain: ffd0513e676445c637410b47249460cbf56bc9cb
|
||||
RNNotifee: 3840cc81add9954a5dec040f96442498f3f95d7b
|
||||
RNNotifee: c4827fa2cec60ab634d62bafdb5cf57cd2999d54
|
||||
RNPrivacySnapshot: ccad3a548338c2f526bb7b1789af3fb0618b7d1d
|
||||
RNReanimated: f1868b36f4b2b52a0ed00062cfda69506f75eaee
|
||||
RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479
|
||||
RNScreens: ffbb0296608eb3560de641a711bbdb663ed1f6b4
|
||||
RNSecureRandom: b64d263529492a6897e236a22a2c4249aa1b53dc
|
||||
RNShare: 5d39a36f2d3d9a6c9344bfb3232c8ed607924a35
|
||||
RNSVG: 8c0bbfa480a24b24468f1c76bd852a4aac3178e6
|
||||
@@ -3976,7 +3941,6 @@ SPEC CHECKSUMS:
|
||||
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
|
||||
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
|
||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||
SwiftUIIntrospect: fee9aa07293ee280373a591e1824e8ddc869ba5d
|
||||
SwiftyRSA: 8c6dd1ea7db1b8dc4fb517a202f88bb1354bc2c6
|
||||
TOCropViewController: 797deaf39c90e6e9ddd848d88817f6b9a8a09888
|
||||
toolbar-android: c426ed5bd3dcccfed20fd79533efc0d1ae0ef018
|
||||
|
||||
34
apps/mobile/package-lock.json
generated
34
apps/mobile/package-lock.json
generated
@@ -102,7 +102,6 @@
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-orientation-locker": "^1.7.0",
|
||||
"react-native-pager-view": "^8.0.0",
|
||||
"react-native-pdf": "^7.0.3",
|
||||
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
|
||||
"react-native-progress": "5.0.0",
|
||||
@@ -113,12 +112,11 @@
|
||||
"react-native-safe-area-context": "^5.6.1",
|
||||
"react-native-scoped-storage": "^1.9.5",
|
||||
"react-native-screenguard": "1.0.0",
|
||||
"react-native-screens": "^4.16.0",
|
||||
"react-native-screens": "^4.19.0",
|
||||
"react-native-securerandom": "^1.0.1",
|
||||
"react-native-share": "^12.0.3",
|
||||
"react-native-svg": "^15.12.0",
|
||||
"react-native-swiper-flatlist": "3.2.2",
|
||||
"react-native-tab-view": "^4.2.2",
|
||||
"react-native-theme-switch-animation": "^0.6.0",
|
||||
"react-native-tooltips": "^1.0.3",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
@@ -17742,16 +17740,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-pager-view": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-8.0.0.tgz",
|
||||
"integrity": "sha512-oAwlWT1lhTkIs9HhODnjNNl/owxzn9DP1MbP+az6OTUdgbmzA16Up83sBH8NRKwrH8rNm7iuWnX1qMqiiWOLhg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-pdf": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-pdf/-/react-native-pdf-7.0.3.tgz",
|
||||
@@ -17900,9 +17888,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-screens": {
|
||||
"version": "4.18.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.18.0.tgz",
|
||||
"integrity": "sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ==",
|
||||
"version": "4.19.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.19.0.tgz",
|
||||
"integrity": "sha512-qSDAO3AL5bti0Ri7KZRSVmWlhDr8MV86N5GruiKVQfEL7Zx2nUi3Dl62lqHUAD/LnDvOPuDDsMHCfIpYSv3hPQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-freeze": "^1.0.0",
|
||||
@@ -17958,20 +17946,6 @@
|
||||
"react-native": ">=0.59.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-tab-view": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-4.2.2.tgz",
|
||||
"integrity": "sha512-NXtrG6OchvbGjsvbySJGVocXxo4Y2vA17ph4rAaWtA2jh+AasD8OyikKBRg2SmllEfeQ+GEhcKe8kulHv8BhTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-latest-callback": "^0.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-pager-view": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-theme-switch-animation": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-theme-switch-animation/-/react-native-theme-switch-animation-0.6.0.tgz",
|
||||
|
||||
@@ -118,7 +118,6 @@
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-orientation-locker": "^1.7.0",
|
||||
"react-native-pager-view": "^8.0.0",
|
||||
"react-native-pdf": "^7.0.3",
|
||||
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
|
||||
"react-native-progress": "5.0.0",
|
||||
@@ -129,12 +128,11 @@
|
||||
"react-native-safe-area-context": "^5.6.1",
|
||||
"react-native-scoped-storage": "^1.9.5",
|
||||
"react-native-screenguard": "1.0.0",
|
||||
"react-native-screens": "^4.16.0",
|
||||
"react-native-screens": "^4.19.0",
|
||||
"react-native-securerandom": "^1.0.1",
|
||||
"react-native-share": "^12.0.3",
|
||||
"react-native-svg": "^15.12.0",
|
||||
"react-native-swiper-flatlist": "3.2.2",
|
||||
"react-native-tab-view": "^4.2.2",
|
||||
"react-native-theme-switch-animation": "^0.6.0",
|
||||
"react-native-tooltips": "^1.0.3",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
|
||||
Reference in New Issue
Block a user