mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-21 05:59:35 +01:00
mobile: ui redesign
This commit is contained in:
committed by
Abdullah Atta
parent
8f84c85c23
commit
b315a834c2
@@ -19,20 +19,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { i18n } from "@lingui/core";
|
||||
import { I18nProvider } from "@lingui/react";
|
||||
import {
|
||||
ScopedThemeProvider,
|
||||
THEME_COMPATIBILITY_VERSION,
|
||||
useThemeEngineStore
|
||||
} from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import { I18nManager, View } from "react-native";
|
||||
import { I18nManager, StatusBar } from "react-native";
|
||||
import "react-native-gesture-handler";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
import AppLockedOverlay from "./components/app-lock-overlay";
|
||||
import DialogProvider from "./components/dialog-provider";
|
||||
import { withErrorBoundry } from "./components/exception-handler";
|
||||
import GlobalSafeAreaProvider from "./components/globalsafearea";
|
||||
import { Toast } from "./components/toast";
|
||||
import { useAppEvents } from "./hooks/use-app-events";
|
||||
import { ApplicationHolder } from "./navigation";
|
||||
import { NotePreviewConfigure } from "./screens/note-preview-configure";
|
||||
import { RootNavigation } from "./navigation/navigation-stack";
|
||||
import { themeTrpcClient } from "./screens/settings/theme-selector";
|
||||
import Notifications from "./services/notifications";
|
||||
import SettingsService from "./services/settings";
|
||||
@@ -64,42 +66,26 @@ const App = (props: { configureMode: "note-preview" }) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: -1
|
||||
}}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<SafeAreaProvider>
|
||||
<GlobalSafeAreaProvider />
|
||||
</SafeAreaProvider>
|
||||
</View>
|
||||
|
||||
<StatusBar translucent={true} backgroundColor="transparent" />
|
||||
<GestureHandlerRootView
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<GlobalSafeAreaProvider />
|
||||
{props.configureMode === "note-preview" ? (
|
||||
<NotePreviewConfigure />
|
||||
) : (
|
||||
<ApplicationHolder />
|
||||
<RootNavigation />
|
||||
)}
|
||||
<ScopedThemeProvider value="dialog">
|
||||
<Toast />
|
||||
</ScopedThemeProvider>
|
||||
<DialogProvider />
|
||||
</GestureHandlerRootView>
|
||||
<AppLockedOverlay />
|
||||
</View>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ const verifyUserPassword = async (password: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const AppLockedOverlay = () => {
|
||||
const AppLockedScreen = () => {
|
||||
const initialLaunchBiometricRequest = useRef(true);
|
||||
const { colors } = useThemeColors();
|
||||
const user = getUser();
|
||||
@@ -203,14 +203,12 @@ const AppLockedOverlay = () => {
|
||||
}
|
||||
}, [appState, onUnlockAppRequested, appLocked]);
|
||||
|
||||
return appLocked ? (
|
||||
return (
|
||||
<KeyboardAwareScrollView
|
||||
style={{
|
||||
backgroundColor: colors.primary.background,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
zIndex: 999
|
||||
height: "100%"
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
justifyContent: "center",
|
||||
@@ -333,7 +331,7 @@ const AppLockedOverlay = () => {
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLockedOverlay;
|
||||
export default AppLockedScreen;
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { Keyboard, View } from "react-native";
|
||||
import { Keyboard, TouchableOpacity, View } from "react-native";
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedStyle,
|
||||
@@ -34,7 +34,7 @@ import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
interface FloatingButtonProps {
|
||||
onPress: () => void;
|
||||
@@ -108,20 +108,24 @@ const FloatingButton = ({
|
||||
style={[
|
||||
{
|
||||
position: "absolute",
|
||||
right: 12,
|
||||
right: DefaultAppStyles.GAP,
|
||||
bottom: 20,
|
||||
zIndex: 10
|
||||
},
|
||||
animatedStyle
|
||||
]}
|
||||
>
|
||||
<Pressable
|
||||
<TouchableOpacity
|
||||
testID={notesnook.buttons.add}
|
||||
type="accent"
|
||||
accentColor={color}
|
||||
activeOpacity={0.95}
|
||||
style={{
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 100
|
||||
...getElevationStyle(10),
|
||||
borderRadius: 20,
|
||||
borderTopWidth: 0,
|
||||
borderBottomWidth: 0,
|
||||
borderLeftWidth: 0,
|
||||
borderRightWidth: 0,
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
onPress={onPress}
|
||||
>
|
||||
@@ -130,7 +134,9 @@ const FloatingButton = ({
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: normalize(60),
|
||||
width: normalize(60)
|
||||
width: normalize(60),
|
||||
backgroundColor: colors.primary.shade,
|
||||
borderRadius: 20
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
@@ -141,11 +147,11 @@ const FloatingButton = ({
|
||||
? "delete"
|
||||
: "plus"
|
||||
}
|
||||
color={colors.primary.accentForeground}
|
||||
size={SIZE.xxl}
|
||||
color={colors.primary.accent}
|
||||
size={SIZE.xxxl}
|
||||
/>
|
||||
</View>
|
||||
</Pressable>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -46,8 +46,6 @@ const DialogButtons = ({
|
||||
{
|
||||
backgroundColor: colors.secondary.background,
|
||||
height: 60,
|
||||
borderBottomRightRadius: 5,
|
||||
borderBottomLeftRadius: 5,
|
||||
paddingHorizontal: 12,
|
||||
borderTopWidth: 0.7,
|
||||
borderTopColor: getColorLinearShade(
|
||||
|
||||
@@ -39,6 +39,7 @@ import { Button } from "../ui/button";
|
||||
import { getContainerBorder } from "../../utils/colors";
|
||||
import { Notice } from "../ui/notice";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { br } from "../../utils/size";
|
||||
|
||||
export const Dialog = ({ context = "global" }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -125,10 +126,11 @@ export const Dialog = ({ context = "global" }) => {
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 400 : "85%",
|
||||
maxHeight: 450,
|
||||
borderRadius: 5,
|
||||
borderRadius: br,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12,
|
||||
...getContainerBorder(colors.primary.border, 0.5)
|
||||
...getContainerBorder(colors.primary.border, 0.5),
|
||||
overflow: "hidden"
|
||||
};
|
||||
|
||||
return visible ? (
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
eUpdateNoteInEditor
|
||||
} from "../../../utils/events";
|
||||
import { deleteItems } from "../../../utils/functions";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
import { convertNoteToText } from "../../../utils/note-to-text";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
@@ -553,7 +553,7 @@ export class VaultDialog extends Component {
|
||||
item: note
|
||||
});
|
||||
if (!DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,12 +37,12 @@ import Animated, {
|
||||
WithSpringConfig,
|
||||
withTiming
|
||||
} from "react-native-reanimated";
|
||||
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
|
||||
import { getAppState } from "../../screens/editor/tiptap/utils";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { eClearEditor } from "../../utils/events";
|
||||
import { useSideBarDraggingStore } from "../side-menu/dragging-store";
|
||||
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
|
||||
|
||||
interface TabProps extends ViewProps {
|
||||
dimensions: { width: number; height: number };
|
||||
@@ -66,7 +66,7 @@ export interface TabsRef {
|
||||
node: RefObject<Animated.View>;
|
||||
}
|
||||
|
||||
export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
{
|
||||
children,
|
||||
dimensions,
|
||||
@@ -81,9 +81,6 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const appState = useMemo(() => getAppState(), []);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const fullscreen = useSettingStore((state) => state.fullscreen);
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const translateX = useSharedValue(
|
||||
widths
|
||||
? appState &&
|
||||
@@ -118,7 +115,6 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const isIPhone = Platform.OS === "ios";
|
||||
|
||||
useEffect(() => {
|
||||
if (introCompleted) {
|
||||
if (deviceMode === "tablet" || fullscreen) {
|
||||
translateX.value = 0;
|
||||
} else {
|
||||
@@ -134,10 +130,19 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
}
|
||||
isLoaded.current = true;
|
||||
prevWidths.current = widths;
|
||||
}
|
||||
}, [
|
||||
deviceMode,
|
||||
widths,
|
||||
fullscreen,
|
||||
translateX,
|
||||
editorPosition,
|
||||
appState,
|
||||
onChangeTab
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = BackHandler.addEventListener("hardwareBackPress", () => {
|
||||
if (isDrawerOpen.value) {
|
||||
if (isDrawerOpen.value && !forcedLock.value) {
|
||||
translateX.value = withTiming(homePosition);
|
||||
onDrawerStateChange(false);
|
||||
isDrawerOpen.value = false;
|
||||
@@ -149,16 +154,11 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
sub && sub.remove();
|
||||
};
|
||||
}, [
|
||||
introCompleted,
|
||||
deviceMode,
|
||||
widths,
|
||||
fullscreen,
|
||||
translateX,
|
||||
isDrawerOpen,
|
||||
forcedLock.value,
|
||||
homePosition,
|
||||
isDrawerOpen,
|
||||
onDrawerStateChange,
|
||||
editorPosition,
|
||||
appState
|
||||
translateX
|
||||
]);
|
||||
|
||||
useImperativeHandle(
|
||||
@@ -294,8 +294,8 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const gesture = Gesture.Pan()
|
||||
.maxPointers(1)
|
||||
.enabled(enabled && !disabled)
|
||||
.activeOffsetX([-5, 5])
|
||||
.failOffsetY([-15, 15])
|
||||
.activeOffsetX([-20, 20])
|
||||
.failOffsetY([-10, 10])
|
||||
.onBegin((event) => {
|
||||
locked.value = false;
|
||||
gestureStartValue.value = {
|
||||
@@ -17,60 +17,52 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { View } from "react-native";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import useNavigationStore, {
|
||||
RouteName
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { IconButtonProps } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { LeftMenus } from "./left-menus";
|
||||
import { RightMenus } from "./right-menus";
|
||||
import { Title } from "./title";
|
||||
|
||||
type HeaderRightButton = {
|
||||
title: string;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
export const Header = ({
|
||||
renderedInRoute,
|
||||
onLeftMenuButtonPress,
|
||||
title,
|
||||
titleHiddenOnRender,
|
||||
headerRightButtons,
|
||||
id,
|
||||
accentColor,
|
||||
isBeta,
|
||||
canGoBack,
|
||||
onPressDefaultRightButton,
|
||||
hasSearch,
|
||||
onSearch
|
||||
onSearch,
|
||||
rightButton
|
||||
}: {
|
||||
onLeftMenuButtonPress?: () => void;
|
||||
renderedInRoute?: RouteName;
|
||||
id?: string;
|
||||
title: string;
|
||||
headerRightButtons?: HeaderRightButton[];
|
||||
titleHiddenOnRender?: boolean;
|
||||
accentColor?: string;
|
||||
isBeta?: boolean;
|
||||
canGoBack?: boolean;
|
||||
onPressDefaultRightButton?: () => void;
|
||||
hasSearch?: boolean;
|
||||
onSearch?: () => void;
|
||||
rightButton?: IconButtonProps;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const [borderHidden, setBorderHidden] = useState(true);
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
const isFocused = useNavigationStore((state) => state.focusedRouteId === id);
|
||||
const [selectedItemsList, selectionMode] = useSelectionStore((state) => [
|
||||
state.selectedItemsList,
|
||||
state.selectionMode
|
||||
]);
|
||||
|
||||
const onScroll = useCallback(
|
||||
(data: { x: number; y: number; id?: string; route: string }) => {
|
||||
@@ -93,88 +85,47 @@ export const Header = ({
|
||||
};
|
||||
}, [borderHidden, onScroll]);
|
||||
|
||||
return selectionMode && isFocused ? null : (
|
||||
<>
|
||||
const HeaderWrapper = hasSearch ? Pressable : View;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
marginTop: Platform.OS === "android" ? insets.top : null,
|
||||
backgroundColor: colors.primary.background,
|
||||
overflow: "hidden",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: borderHidden
|
||||
? "transparent"
|
||||
: colors.secondary.background,
|
||||
justifyContent: "space-between"
|
||||
}
|
||||
]}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<HeaderWrapper
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: DefaultAppStyles.GAP_SMALL,
|
||||
borderRadius: 10,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
borderWidth: hasSearch ? 1 : 0,
|
||||
borderColor: colors.secondary.border,
|
||||
paddingHorizontal: !hasSearch ? 0 : DefaultAppStyles.GAP_SMALL,
|
||||
alignItems: "center"
|
||||
}}
|
||||
onPress={() => {
|
||||
onSearch?.();
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<View style={styles.leftBtnContainer}>
|
||||
<LeftMenus
|
||||
canGoBack={canGoBack}
|
||||
onLeftButtonPress={onLeftMenuButtonPress}
|
||||
/>
|
||||
|
||||
<Title
|
||||
isHiddenOnRender={titleHiddenOnRender}
|
||||
renderedInRoute={renderedInRoute}
|
||||
id={id}
|
||||
accentColor={accentColor}
|
||||
title={title}
|
||||
isBeta={isBeta}
|
||||
/>
|
||||
{hasSearch ? (
|
||||
<Paragraph>
|
||||
{selectionMode
|
||||
? `${selectedItemsList.length} selected`
|
||||
: strings.searchInRoute(title)}
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Heading size={SIZE.lg}>{title}</Heading>
|
||||
)}
|
||||
|
||||
<RightMenus rightButton={rightButton} />
|
||||
</HeaderWrapper>
|
||||
</View>
|
||||
<RightMenus
|
||||
renderedInRoute={renderedInRoute}
|
||||
id={id}
|
||||
headerRightButtons={headerRightButtons}
|
||||
onPressDefaultRightButton={onPressDefaultRightButton}
|
||||
search={hasSearch}
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
</>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
zIndex: 11,
|
||||
height: 50,
|
||||
maxHeight: 50,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
width: "100%"
|
||||
},
|
||||
leftBtnContainer: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
flexShrink: 1
|
||||
},
|
||||
leftBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 100,
|
||||
marginLeft: -5,
|
||||
marginRight: 25
|
||||
},
|
||||
rightBtnContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
},
|
||||
rightBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
height: 40,
|
||||
width: 40,
|
||||
paddingRight: 0
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,10 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
|
||||
export const LeftMenus = ({
|
||||
@@ -41,7 +40,7 @@ export const LeftMenus = ({
|
||||
if (onLeftButtonPress) return onLeftButtonPress();
|
||||
|
||||
if (!canGoBack) {
|
||||
if (tabBarRef.current?.isDrawerOpen()) {
|
||||
if (fluidTabsRef.current?.isDrawerOpen()) {
|
||||
Navigation.closeDrawer();
|
||||
} else {
|
||||
Navigation.openDrawer();
|
||||
@@ -54,23 +53,14 @@ export const LeftMenus = ({
|
||||
return isTablet && !canGoBack ? null : (
|
||||
<IconButton
|
||||
testID={notesnook.ids.default.header.buttons.left}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 100,
|
||||
marginRight: DDS.isLargeTablet() ? 10 : 7
|
||||
}}
|
||||
left={40}
|
||||
top={40}
|
||||
right={DDS.isLargeTablet() ? 10 : 10}
|
||||
onPress={_onLeftButtonPress}
|
||||
onLongPress={() => {
|
||||
Navigation.popToTop();
|
||||
}}
|
||||
name={canGoBack ? "arrow-left" : "menu"}
|
||||
color={colors.primary.paragraph}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,127 +17,30 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { Menu } from "react-native-material-menu";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import {
|
||||
HeaderRightButton,
|
||||
RouteName
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { IconButton, IconButtonProps } from "../ui/icon-button";
|
||||
|
||||
export const RightMenus = ({
|
||||
headerRightButtons,
|
||||
renderedInRoute,
|
||||
id,
|
||||
onPressDefaultRightButton,
|
||||
search,
|
||||
onSearch
|
||||
rightButton
|
||||
}: {
|
||||
headerRightButtons?: HeaderRightButton[];
|
||||
renderedInRoute?: RouteName;
|
||||
id?: string;
|
||||
onPressDefaultRightButton?: () => void;
|
||||
search?: boolean;
|
||||
onSearch?: () => void;
|
||||
rightButton?: IconButtonProps;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const { colors: contextMenuColors } = useThemeColors("contextMenu");
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const menuRef = useRef<Menu>(null);
|
||||
|
||||
return (
|
||||
<View style={styles.rightBtnContainer}>
|
||||
{search ? (
|
||||
<IconButton
|
||||
onPress={onSearch}
|
||||
testID="icon-search"
|
||||
name="magnify"
|
||||
color={colors.primary.paragraph}
|
||||
style={styles.rightBtn}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{deviceMode !== "mobile" ? (
|
||||
<Button
|
||||
onPress={onPressDefaultRightButton}
|
||||
testID={notesnook.ids.default.addBtn}
|
||||
icon={renderedInRoute === "Trash" ? "delete" : "plus"}
|
||||
iconSize={SIZE.xl}
|
||||
type="accent"
|
||||
hitSlop={{
|
||||
top: 10,
|
||||
right: 10,
|
||||
bottom: 10,
|
||||
left: 0
|
||||
}}
|
||||
{rightButton ? (
|
||||
<IconButton {...rightButton} color={colors.primary.icon} />
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 10,
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.accent
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{headerRightButtons && headerRightButtons.length > 0 ? (
|
||||
<Menu
|
||||
ref={menuRef}
|
||||
animationDuration={200}
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: contextMenuColors.primary.background,
|
||||
marginTop: 35
|
||||
}}
|
||||
onRequestClose={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
name="dots-vertical"
|
||||
color={colors.primary.paragraph}
|
||||
style={styles.rightBtn}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{headerRightButtons.map((item) => (
|
||||
<Button
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderRadius: 0,
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
type="plain"
|
||||
buttonType={{
|
||||
text: contextMenuColors.primary.paragraph
|
||||
}}
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
if (Platform.OS === "ios") await sleep(300);
|
||||
item.onPress();
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
) : null}
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -149,10 +52,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
rightBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
marginLeft: 10,
|
||||
paddingRight: 0
|
||||
alignItems: "center"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,122 +17,43 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
import { Notebook } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../../common/database";
|
||||
import { ToastManager } from "../../../services/event-manager";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import AppIcon from "../../ui/AppIcon";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const NotebookHeader = ({
|
||||
notebook,
|
||||
onEditNotebook,
|
||||
totalNotes = 0
|
||||
}: {
|
||||
notebook: Notebook;
|
||||
onEditNotebook: () => void;
|
||||
totalNotes: number;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [isPinnedToMenu, setIsPinnedToMenu] = useState(
|
||||
db.shortcuts.exists(notebook.id)
|
||||
);
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
|
||||
const onPinNotebook = async () => {
|
||||
try {
|
||||
if (isPinnedToMenu) {
|
||||
await db.shortcuts.remove(notebook.id);
|
||||
} else {
|
||||
await db.shortcuts.add({
|
||||
item: {
|
||||
id: notebook.id,
|
||||
type: "notebook"
|
||||
}
|
||||
});
|
||||
ToastManager.show({
|
||||
heading: strings.shortcutCreated(),
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
setIsPinnedToMenu(db.shortcuts.exists(notebook.id));
|
||||
setMenuPins();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
marginBottom: 5,
|
||||
padding: 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginVertical: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingVertical: 15,
|
||||
paddingHorizontal: 12,
|
||||
alignSelf: "center",
|
||||
borderRadius: 10,
|
||||
paddingTop: 25
|
||||
gap: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingVertical: 25
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
|
||||
{getFormattedDate(notebook.dateModified, "date-time")}
|
||||
</Paragraph>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
>
|
||||
{notebook.title}
|
||||
</Heading>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name={isPinnedToMenu ? "link-variant-off" : "link-variant"}
|
||||
onPress={onPinNotebook}
|
||||
tooltipText={"Create shortcut in side menu"}
|
||||
style={{
|
||||
marginRight: 15,
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
type="transparent"
|
||||
color={isPinnedToMenu ? colors.primary.accent : colors.primary.icon}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
onPress={onEditNotebook}
|
||||
tooltipText="Edit this notebook"
|
||||
name="pencil"
|
||||
type="transparent"
|
||||
color={colors.primary.icon}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<AppIcon name="notebook" size={SIZE.xxl} />
|
||||
<Heading size={SIZE.lg}>{notebook.title}</Heading>
|
||||
|
||||
{notebook.description ? (
|
||||
<Paragraph size={SIZE.sm} color={colors.primary.paragraph}>
|
||||
@@ -140,17 +61,21 @@ export const NotebookHeader = ({
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
<Paragraph
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10,
|
||||
fontStyle: "italic",
|
||||
fontFamily: undefined
|
||||
flexDirection: "row",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
<Paragraph size={SIZE.xxs} color={colors.secondary.paragraph}>
|
||||
{strings.notes(totalNotes || 0)}
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xxs}>
|
||||
{getFormattedDate(notebook.dateModified, "date-time")}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,16 +21,16 @@ import { GroupHeader, GroupOptions, ItemType } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
import { useWindowDimensions, View } from "react-native";
|
||||
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { RouteName } from "../../../stores/use-navigation-store";
|
||||
import { getContainerBorder } from "../../../utils/colors";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import Sort from "../../sheets/sort";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
|
||||
type SectionHeaderProps = {
|
||||
@@ -65,91 +65,88 @@ export const SectionHeader = React.memo<
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: "95%",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 12,
|
||||
height: 35 * fontScale,
|
||||
backgroundColor: colors.secondary.background,
|
||||
alignSelf: "center",
|
||||
borderRadius: 5,
|
||||
marginVertical: 5,
|
||||
...getContainerBorder(colors.secondary.background, 0.8)
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginVertical: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
justifyContent: "space-between",
|
||||
borderBottomWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
paddingBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
onOpenJumpToDialog();
|
||||
}}
|
||||
activeOpacity={0.9}
|
||||
hitSlop={{ top: 10, left: 10, right: 30, bottom: 15 }}
|
||||
style={{
|
||||
height: "100%",
|
||||
justifyContent: "center"
|
||||
justifyContent: "flex-start",
|
||||
flexDirection: "row",
|
||||
width: "auto"
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
color={color || colors.primary.accent}
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
textAlignVertical: "center"
|
||||
}}
|
||||
color={colors.primary.accent}
|
||||
>
|
||||
{!item.title || item.title === "" ? strings.pinned() : item.title}
|
||||
</Heading>
|
||||
</TouchableOpacity>
|
||||
</Pressable>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{index === 0 ? (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
presentSheet({
|
||||
component: <Sort screen={screen} type={dataType} />
|
||||
});
|
||||
}}
|
||||
title={groupBy}
|
||||
icon={
|
||||
<IconButton
|
||||
name={
|
||||
groupOptions.sortDirection === "asc"
|
||||
? "sort-ascending"
|
||||
: "sort-descending"
|
||||
}
|
||||
height={25}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 0,
|
||||
backgroundColor: "transparent",
|
||||
marginRight:
|
||||
dataType === "note" ||
|
||||
screen === "Notes" ||
|
||||
dataType === "notebook"
|
||||
? 10
|
||||
: 0
|
||||
onPress={() => {
|
||||
if (!screen) return;
|
||||
presentSheet({
|
||||
component: <Sort screen={screen} type={dataType} />
|
||||
});
|
||||
}}
|
||||
type="plain"
|
||||
iconPosition="right"
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25
|
||||
}}
|
||||
size={SIZE.lg - 2}
|
||||
/>
|
||||
<IconButton
|
||||
hidden={
|
||||
dataType !== "note" &&
|
||||
dataType !== "notebook" &&
|
||||
screen !== "Notes"
|
||||
}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25
|
||||
}}
|
||||
testID="icon-compact-mode"
|
||||
color={colors.secondary.icon}
|
||||
name={isCompactModeEnabled ? "view-list" : "view-list-outline"}
|
||||
name={
|
||||
isCompactModeEnabled ? "view-list" : "view-list-outline"
|
||||
}
|
||||
onPress={() => {
|
||||
SettingsService.set({
|
||||
[dataType !== "notebook"
|
||||
@@ -163,6 +160,16 @@ export const SectionHeader = React.memo<
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<IconButton
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25
|
||||
}}
|
||||
name={"chevron-down"}
|
||||
size={SIZE.lg - 2}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -29,24 +29,25 @@ import { EntityLevel, decode } from "entities";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
|
||||
import NotebookScreen from "../../../screens/notebook";
|
||||
import { TaggedNotes } from "../../../screens/notes/tagged";
|
||||
import useNavigationStore from "../../../stores/use-navigation-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
|
||||
import { useTabStore } from "../../../screens/editor/tiptap/use-tab-store";
|
||||
import { NotebooksWithDateEdited, TagsWithDateEdited } from "@notesnook/common";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import useIsSelected from "../../../hooks/use-selected";
|
||||
import { useTabStore } from "../../../screens/editor/tiptap/use-tab-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Properties } from "../../properties";
|
||||
import { Button } from "../../ui/button";
|
||||
import AppIcon from "../../ui/AppIcon";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { ReminderTime } from "../../ui/reminder-time";
|
||||
import { TimeSince } from "../../ui/time-since";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
type NoteItemProps = {
|
||||
item: Note | BaseTrashItem<Note>;
|
||||
@@ -85,6 +86,8 @@ const NoteItem = ({
|
||||
);
|
||||
const _update = useRelationStore((state) => state.updater);
|
||||
const primaryColors = isEditingNote ? colors.selected : colors.primary;
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
const [selected] = useIsSelected(item);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -94,64 +97,6 @@ const NoteItem = ({
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
{!compactMode ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
zIndex: 10,
|
||||
elevation: 10,
|
||||
marginBottom: 2.5,
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{notebooks?.items
|
||||
?.filter(
|
||||
(item) =>
|
||||
item.id !== useNavigationStore.getState().focusedRouteId
|
||||
)
|
||||
.map((item) => (
|
||||
<Button
|
||||
title={
|
||||
item.title.length > 25
|
||||
? item.title.slice(0, 25) + "..."
|
||||
: item.title
|
||||
}
|
||||
tooltipText={item.title}
|
||||
key={item.id}
|
||||
height={25}
|
||||
icon="book-outline"
|
||||
type="secondary"
|
||||
fontSize={SIZE.xs}
|
||||
iconSize={SIZE.sm}
|
||||
textStyle={{
|
||||
marginRight: 0
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
marginRight: 5,
|
||||
paddingHorizontal: 6,
|
||||
marginBottom: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
NotebookScreen.navigate(item, true);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ReminderTime
|
||||
reminder={reminder}
|
||||
color={color?.colorCode}
|
||||
onPress={() => {
|
||||
Properties.present(reminder);
|
||||
}}
|
||||
style={{
|
||||
height: 25
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{compactMode ? (
|
||||
<Paragraph
|
||||
numberOfLines={1}
|
||||
@@ -167,7 +112,7 @@ const NoteItem = ({
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
color={color?.colorCode || primaryColors.heading}
|
||||
size={SIZE.md}
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
paddingRight: 10
|
||||
}}
|
||||
@@ -198,7 +143,9 @@ const NoteItem = ({
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginTop: 5,
|
||||
height: SIZE.md + 2
|
||||
columnGap: 8,
|
||||
rowGap: 4,
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{!isTrash ? (
|
||||
@@ -206,9 +153,6 @@ const NoteItem = ({
|
||||
{item.conflicted ? (
|
||||
<Icon
|
||||
name="alert-circle"
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
color={colors.error.accent}
|
||||
/>
|
||||
@@ -219,9 +163,6 @@ const NoteItem = ({
|
||||
testID="sync-off"
|
||||
name="sync-off"
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
) : null}
|
||||
@@ -231,18 +172,14 @@ const NoteItem = ({
|
||||
testID="pencil-lock"
|
||||
name="pencil-lock"
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<TimeSince
|
||||
style={{
|
||||
fontSize: SIZE.xs,
|
||||
color: colors.secondary.paragraph,
|
||||
marginRight: 6
|
||||
fontSize: SIZE.xxs,
|
||||
color: colors.secondary.paragraph
|
||||
}}
|
||||
time={date}
|
||||
updateFrequency={Date.now() - date < 60000 ? 2000 : 60000}
|
||||
@@ -253,7 +190,6 @@ const NoteItem = ({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginRight: 6,
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
@@ -264,7 +200,7 @@ const NoteItem = ({
|
||||
/>
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
size={SIZE.xs}
|
||||
size={SIZE.xxs}
|
||||
>
|
||||
{attachmentsCount}
|
||||
</Paragraph>
|
||||
@@ -275,10 +211,7 @@ const NoteItem = ({
|
||||
<Icon
|
||||
testID="icon-pinned"
|
||||
name="pin-outline"
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
color={color?.colorCode || primaryColors.accent}
|
||||
/>
|
||||
) : null}
|
||||
@@ -288,9 +221,6 @@ const NoteItem = ({
|
||||
name="lock"
|
||||
testID="note-locked-icon"
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
) : null}
|
||||
@@ -300,36 +230,80 @@ const NoteItem = ({
|
||||
testID="icon-star"
|
||||
name="star-outline"
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
color="orange"
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{reminder ? (
|
||||
<ReminderTime
|
||||
reminder={reminder}
|
||||
disabled
|
||||
color={color?.colorCode}
|
||||
textStyle={{
|
||||
fontSize: SIZE.xxxs
|
||||
}}
|
||||
iconSize={SIZE.xxxs}
|
||||
style={{
|
||||
height: "auto"
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{notebooks?.items
|
||||
?.filter(
|
||||
(item) =>
|
||||
item.id !== useNavigationStore.getState().focusedRouteId
|
||||
)
|
||||
.map((item) => (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingHorizontal: 4,
|
||||
borderWidth: 0.5,
|
||||
borderColor: primaryColors.border,
|
||||
paddingVertical: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL / 2
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="book-outline"
|
||||
size={SIZE.xxxs}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Paragraph
|
||||
size={SIZE.xxxs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
))}
|
||||
|
||||
{!isTrash && !compactMode && tags
|
||||
? tags.items?.map((item) =>
|
||||
item.id ? (
|
||||
<Button
|
||||
title={"#" + item.title}
|
||||
<View
|
||||
key={item.id}
|
||||
height={23}
|
||||
type="plain"
|
||||
textStyle={{
|
||||
textDecorationLine: "underline",
|
||||
color: colors.secondary.paragraph
|
||||
}}
|
||||
hitSlop={{ top: 8, bottom: 12, left: 0, right: 0 }}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
paddingHorizontal: 6,
|
||||
marginRight: 4,
|
||||
zIndex: 10,
|
||||
maxWidth: tags.items?.length > 1 ? 130 : null
|
||||
borderRadius: 4,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingHorizontal: 4,
|
||||
borderWidth: 0.5,
|
||||
borderColor: primaryColors.border,
|
||||
paddingVertical: 1
|
||||
}}
|
||||
onPress={() => TaggedNotes.navigate(item, true)}
|
||||
/>
|
||||
>
|
||||
<Paragraph
|
||||
size={SIZE.xxxs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
#{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null
|
||||
)
|
||||
: null}
|
||||
@@ -338,7 +312,7 @@ const NoteItem = ({
|
||||
<>
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
size={SIZE.xs}
|
||||
size={SIZE.xxs}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
@@ -352,7 +326,7 @@ const NoteItem = ({
|
||||
|
||||
<Paragraph
|
||||
color={primaryColors.accent}
|
||||
size={SIZE.xs}
|
||||
size={SIZE.xxs}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
@@ -409,7 +383,7 @@ const NoteItem = ({
|
||||
|
||||
<TimeSince
|
||||
style={{
|
||||
fontSize: SIZE.xs,
|
||||
fontSize: SIZE.xxs,
|
||||
color: colors.secondary.paragraph,
|
||||
marginRight: 6
|
||||
}}
|
||||
@@ -419,11 +393,20 @@ const NoteItem = ({
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{selectionMode ? (
|
||||
<>
|
||||
<AppIcon
|
||||
name={selected ? "checkbox-outline" : "checkbox-blank-outline"}
|
||||
color={selected ? colors.selected.icon : colors.primary.icon}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<IconButton
|
||||
testID={notesnook.listitem.menu}
|
||||
color={primaryColors.paragraph}
|
||||
color={colors.secondary.icon}
|
||||
name="dots-horizontal"
|
||||
size={SIZE.xl}
|
||||
size={SIZE.lg}
|
||||
onPress={() => !noOpen && Properties.present(item)}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
@@ -433,6 +416,7 @@ const NoteItem = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
|
||||
import { NotebooksWithDateEdited, TagsWithDateEdited } from "@notesnook/common";
|
||||
import NotePreview from "../../note-history/preview";
|
||||
@@ -66,7 +66,7 @@ export const openNote = async (
|
||||
item: note
|
||||
});
|
||||
if (!DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -101,6 +101,7 @@ export const NoteWrapper = React.memo<
|
||||
onPress={() => openNote(item as Note, isTrash, isRenderedInActionSheet)}
|
||||
isSheet={isRenderedInActionSheet}
|
||||
item={item}
|
||||
index={index}
|
||||
color={restProps.color?.colorCode}
|
||||
>
|
||||
<NoteItem {...restProps} item={item} index={index} isTrash={isTrash} />
|
||||
|
||||
@@ -39,16 +39,15 @@ export const Filler = ({ item, color }: { item: Item; color?: string }) => {
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "110%",
|
||||
height: "150%",
|
||||
backgroundColor: colors.selected.background,
|
||||
borderLeftWidth: 5,
|
||||
borderLeftColor: isEditingNote
|
||||
? color
|
||||
? color
|
||||
: colors.selected.accent
|
||||
: "transparent"
|
||||
height: "100%",
|
||||
backgroundColor: colors.selected.accent
|
||||
// borderLeftWidth: 5,
|
||||
// borderLeftColor: isEditingNote
|
||||
// ? color
|
||||
// ? color
|
||||
// : colors.selected.accent
|
||||
// : "transparent"
|
||||
}}
|
||||
collapsable={false}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -17,14 +17,14 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Item, TrashItem } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { PropsWithChildren, useRef } from "react";
|
||||
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
|
||||
import { useTabStore } from "../../../screens/editor/tiptap/use-tab-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import { Filler } from "./back-fill";
|
||||
import { SelectionIcon } from "./selection";
|
||||
import { Item, TrashItem } from "@notesnook/core";
|
||||
|
||||
export function selectItem(item: Item) {
|
||||
if (useSelectionStore.getState().selectionMode === item.type) {
|
||||
@@ -49,6 +49,7 @@ type SelectionWrapperProps = PropsWithChildren<{
|
||||
testID?: string;
|
||||
isSheet?: boolean;
|
||||
color?: string;
|
||||
index?: number;
|
||||
}>;
|
||||
|
||||
const SelectionWrapper = ({
|
||||
@@ -57,10 +58,16 @@ const SelectionWrapper = ({
|
||||
testID,
|
||||
isSheet,
|
||||
children,
|
||||
color
|
||||
color,
|
||||
index = 0
|
||||
}: SelectionWrapperProps) => {
|
||||
const itemId = useRef(item.id);
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const isEditingNote = useTabStore(
|
||||
(state) =>
|
||||
state.tabs.find((t) => t.id === state.currentTab)?.session?.noteId ===
|
||||
item.id
|
||||
);
|
||||
const compactMode = useIsCompactModeEnabled(
|
||||
(item as TrashItem).itemType || item.type
|
||||
);
|
||||
@@ -79,7 +86,13 @@ const SelectionWrapper = ({
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
customColor={isSheet ? colors.secondary.background : "transparent"}
|
||||
customColor={
|
||||
isEditingNote
|
||||
? colors.selected.background
|
||||
: isSheet
|
||||
? colors.primary.hover
|
||||
: "transparent"
|
||||
}
|
||||
testID={testID}
|
||||
onLongPress={onLongPress}
|
||||
onPress={onPress}
|
||||
@@ -91,15 +104,14 @@ const SelectionWrapper = ({
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
overflow: "hidden",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: compactMode ? 4 : 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: compactMode ? 4 : DefaultAppStyles.GAP_VERTICAL,
|
||||
borderRadius: isSheet ? 10 : 0,
|
||||
marginBottom: isSheet ? 12 : undefined
|
||||
}}
|
||||
>
|
||||
{item.type === "note" ? <Filler item={item} color={color} /> : null}
|
||||
<SelectionIcon item={item} />
|
||||
{children}
|
||||
</Pressable>
|
||||
);
|
||||
|
||||
@@ -17,13 +17,13 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useMessageStore } from "../../stores/use-message-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getContainerBorder, hexToRGBA } from "../../utils/colors";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
@@ -38,19 +38,24 @@ export const Card = ({ color }: { color?: string }) => {
|
||||
(announcements && announcements.length) ? null : (
|
||||
<View
|
||||
style={{
|
||||
width: "95%"
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
onPress={messageBoardState.onPress}
|
||||
type="plain"
|
||||
style={{
|
||||
paddingVertical: 12,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 0,
|
||||
width: "100%"
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
width: "100%",
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.border
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -63,24 +68,14 @@ export const Card = ({ color }: { color?: string }) => {
|
||||
<View
|
||||
style={{
|
||||
width: 40 * fontScale,
|
||||
backgroundColor:
|
||||
messageBoardState.type === "error"
|
||||
? hexToRGBA(colors.error.accent, 0.15)
|
||||
: hexToRGBA(color, 0.15),
|
||||
height: 40 * fontScale,
|
||||
borderRadius: 100,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
...getContainerBorder(
|
||||
messageBoardState.type === "error"
|
||||
? colors.error.accent
|
||||
: color || colors.primary.accent,
|
||||
0.4
|
||||
)
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
size={SIZE.lg}
|
||||
size={SIZE.xxxl}
|
||||
color={
|
||||
messageBoardState.type === "error" ? colors.error.icon : color
|
||||
}
|
||||
@@ -95,9 +90,6 @@ export const Card = ({ color }: { color?: string }) => {
|
||||
marginRight: 10
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
|
||||
{messageBoardState.message}
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "nowrap",
|
||||
@@ -107,6 +99,9 @@ export const Card = ({ color }: { color?: string }) => {
|
||||
>
|
||||
{messageBoardState.actionText}
|
||||
</Paragraph>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xxs}>
|
||||
{messageBoardState.message}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ import Sync from "../../services/sync";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { Footer } from "../list-items/footer";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { Header } from "../list-items/headers/header";
|
||||
import { Empty, PlaceholderData } from "./empty";
|
||||
import { ListItemWrapper } from "./list-item.wrapper";
|
||||
@@ -43,6 +42,7 @@ import { ListItemWrapper } from "./list-item.wrapper";
|
||||
type ListProps = {
|
||||
data: VirtualizedGrouping<Item> | undefined;
|
||||
dataType: Item["type"];
|
||||
mode?: "drawer" | "sheet";
|
||||
onRefresh?: () => void;
|
||||
loading?: boolean;
|
||||
headerTitle?: string;
|
||||
@@ -160,7 +160,7 @@ export default function List(props: ListProps) {
|
||||
onScroll={onListScroll}
|
||||
nestedScrollEnabled={true}
|
||||
onMomentumScrollEnd={() => {
|
||||
tabBarRef.current?.unlock();
|
||||
fluidTabsRef.current?.unlock();
|
||||
}}
|
||||
getItemType={(item: number, index: number) => {
|
||||
return props.data?.type(index);
|
||||
@@ -192,9 +192,6 @@ export default function List(props: ListProps) {
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
ListFooterComponent={
|
||||
<Footer height={props.renderedInRoute === "Notebook" ? 300 : 150} />
|
||||
}
|
||||
ListHeaderComponent={
|
||||
<>
|
||||
{props.CustomLisHeader ? (
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
DraxListRenderItemContent
|
||||
} from "react-native-drax";
|
||||
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { useSideBarDraggingStore } from "../side-menu/dragging-store";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
@@ -61,9 +61,9 @@ function ReorderableList<T extends { id: string }>({
|
||||
const listRef = useRef<FlatList | null>(null);
|
||||
|
||||
if (dragging) {
|
||||
tabBarRef.current?.lock();
|
||||
fluidTabsRef.current?.lock();
|
||||
} else {
|
||||
tabBarRef.current?.unlock();
|
||||
fluidTabsRef.current?.unlock();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -78,8 +78,7 @@ function ReorderableList<T extends { id: string }>({
|
||||
return isHidden && !dragging ? null : (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -16,18 +16,19 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import { db } from "../../common/database";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent, presentSheet } from "../../services/event-manager";
|
||||
import { ColorValues } from "../../utils/colors";
|
||||
import { eOnLoadNote } from "../../utils/events";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { useSideBarDraggingStore } from "../side-menu/dragging-store";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { ReminderTime } from "../ui/reminder-time";
|
||||
@@ -36,10 +37,8 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
import { DateMeta } from "./date-meta";
|
||||
import { Items } from "./items";
|
||||
import Notebooks from "./notebooks";
|
||||
import { Synced } from "./synced";
|
||||
import { TagStrip, Tags } from "./tags";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
|
||||
const Line = ({ top = 6, bottom = 6 }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -101,7 +100,8 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1
|
||||
flexShrink: 1,
|
||||
gap: 5
|
||||
}}
|
||||
>
|
||||
{item.type === "color" ? (
|
||||
@@ -116,16 +116,15 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
marginRight: 10
|
||||
}}
|
||||
/>
|
||||
) : item.type === "tag" ? (
|
||||
<Icon
|
||||
name="pound"
|
||||
size={SIZE.lg}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Heading size={SIZE.lg}>
|
||||
{item.type === "tag" && !isColor ? (
|
||||
<Heading size={SIZE.xl} color={colors.primary.accent}>
|
||||
#
|
||||
</Heading>
|
||||
) : null}
|
||||
{item.title}
|
||||
</Heading>
|
||||
<Heading size={SIZE.lg}>{item.title}</Heading>
|
||||
</View>
|
||||
|
||||
{item.type === "note" ? (
|
||||
@@ -144,7 +143,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
newTab: true
|
||||
});
|
||||
if (!DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -202,8 +201,6 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Synced item={item} close={close} />
|
||||
|
||||
{DDS.isTab ? (
|
||||
<View
|
||||
style={{
|
||||
@@ -218,87 +215,36 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
);
|
||||
};
|
||||
|
||||
Properties.present = async (item, buttons = [], isSheet) => {
|
||||
Properties.present = async (item, isSheet) => {
|
||||
if (!item) return;
|
||||
let type = item?.type;
|
||||
let props = [];
|
||||
let android = [];
|
||||
let dbItem;
|
||||
switch (type) {
|
||||
case "trash":
|
||||
props[0] = item;
|
||||
props.push(["delete", "restore"]);
|
||||
dbItem = item;
|
||||
break;
|
||||
case "note":
|
||||
android = Platform.OS === "android" ? ["pin-to-notifications"] : [];
|
||||
props[0] = await db.notes.note(item.id);
|
||||
props.push([
|
||||
"notebooks",
|
||||
"add-reminder",
|
||||
"share",
|
||||
"export",
|
||||
"copy",
|
||||
"publish",
|
||||
"pin",
|
||||
"favorite",
|
||||
"attachments",
|
||||
"lock-unlock",
|
||||
"trash",
|
||||
"remove-from-notebook",
|
||||
"history",
|
||||
"read-only",
|
||||
"reminders",
|
||||
"local-only",
|
||||
"duplicate",
|
||||
"copy-link",
|
||||
"references",
|
||||
...android,
|
||||
...buttons
|
||||
]);
|
||||
dbItem = await db.notes.note(item.id);
|
||||
break;
|
||||
case "notebook":
|
||||
props[0] = await db.notebooks.notebook(item.id);
|
||||
props.push([
|
||||
"edit-notebook",
|
||||
"pin",
|
||||
"add-shortcut",
|
||||
"trash",
|
||||
"default-notebook",
|
||||
"add-notebook",
|
||||
"move-notes",
|
||||
"move-notebook"
|
||||
]);
|
||||
dbItem = await db.notebooks.notebook(item.id);
|
||||
break;
|
||||
case "tag":
|
||||
props[0] = await db.tags.tag(item.id);
|
||||
props.push(["add-shortcut", "trash", "rename-tag"]);
|
||||
dbItem = await db.tags.tag(item.id);
|
||||
break;
|
||||
case "color":
|
||||
props[0] = await db.colors.color(item.id);
|
||||
|
||||
props.push([
|
||||
"trash",
|
||||
"rename-color",
|
||||
...(useSideBarDraggingStore.getState().dragging ? [] : ["reorder"])
|
||||
]);
|
||||
dbItem = await db.colors.color(item.id);
|
||||
break;
|
||||
case "reminder": {
|
||||
props[0] = await db.reminders.reminder(item.id);
|
||||
props.push(["edit-reminder", "trash", "disable-reminder"]);
|
||||
dbItem = await db.reminders.reminder(item.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!props[0]) return;
|
||||
|
||||
presentSheet({
|
||||
context: isSheet ? "local" : undefined,
|
||||
component: (ref, close) => (
|
||||
<Properties
|
||||
close={() => {
|
||||
close();
|
||||
}}
|
||||
actionSheetRef={ref}
|
||||
item={props[0]}
|
||||
buttons={props[1]}
|
||||
/>
|
||||
<Properties close={close} actionSheetRef={ref} item={dbItem} />
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Dimensions, ScrollView, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useActions } from "../../hooks/use-actions";
|
||||
import { useStoredRef } from "../../hooks/use-stored-ref";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
export const Items = ({ item, buttons, close }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const topBarSorting = useStoredRef("topbar-sorting-ref", {});
|
||||
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const actions = useActions({ item, close });
|
||||
const data = actions.filter((i) => buttons.indexOf(i.id) > -1 && !i.hidden);
|
||||
let width = dimensions.width > 600 ? 600 : dimensions.width;
|
||||
const shouldShrink =
|
||||
Dimensions.get("window").fontScale > 1 &&
|
||||
Dimensions.get("window").width < 450;
|
||||
let columnItemsCount = DDS.isLargeTablet() ? 7 : shouldShrink ? 4 : 5;
|
||||
let columnItemWidth = DDS.isTab
|
||||
? (width - 12) / columnItemsCount
|
||||
: (width - 12) / columnItemsCount;
|
||||
|
||||
const _renderRowItem = ({ item }) => (
|
||||
<View
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
width: columnItemWidth,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
onPress={item.func}
|
||||
type={item.on ? "shade" : "secondary"}
|
||||
style={{
|
||||
height: columnItemWidth - 12,
|
||||
width: columnItemWidth - 12,
|
||||
borderRadius: 10,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
textAlignVertical: "center",
|
||||
marginBottom: DDS.isTab ? 7 : 3.5
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
allowFontScaling
|
||||
name={item.icon}
|
||||
size={DDS.isTab ? SIZE.xxl : shouldShrink ? SIZE.xxl : SIZE.lg}
|
||||
color={
|
||||
item.on
|
||||
? colors.primary.accent
|
||||
: item.id.match(/(delete|trash)/g)
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
textBreakStrategy="simple"
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
);
|
||||
|
||||
const renderColumnItem = (item) => (
|
||||
<Button
|
||||
key={item.name + item.title}
|
||||
buttonType={{
|
||||
text: item.on
|
||||
? colors.primary.accent
|
||||
: item.name === "Delete" || item.name === "PermDelete"
|
||||
? colors.error.paragraph
|
||||
: colors.primary.paragraph
|
||||
}}
|
||||
onPress={item.func}
|
||||
title={item.title}
|
||||
icon={item.icon}
|
||||
type={item.on ? "shade" : "plain"}
|
||||
fontSize={SIZE.sm}
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
justifyContent: "flex-start",
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderTopBarItem = (item, index) => {
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
item.func();
|
||||
setImmediate(() => {
|
||||
const currentValue = topBarSorting.current[item.id] || 0;
|
||||
topBarSorting.current = {
|
||||
...topBarSorting.current,
|
||||
[item.id]: currentValue + 1
|
||||
};
|
||||
});
|
||||
}}
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
activeOpacity={1}
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 0,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<View
|
||||
onPress={item.func}
|
||||
style={{
|
||||
height: topBarItemWidth,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
textAlignVertical: "center",
|
||||
marginBottom: DDS.isTab ? 7 : 3.5,
|
||||
borderRadius: 100
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
allowFontScaling
|
||||
size={DDS.isTab ? SIZE.xxl : SIZE.md + 4}
|
||||
color={
|
||||
item.on
|
||||
? colors.primary.accent
|
||||
: item.name === "Delete" || item.name === "PermDelete"
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Paragraph
|
||||
textBreakStrategy="simple"
|
||||
size={SIZE.xxs}
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
const topBarItemsList = [
|
||||
"pin",
|
||||
"favorite",
|
||||
"copy",
|
||||
"share",
|
||||
"lock-unlock",
|
||||
"publish",
|
||||
"export",
|
||||
"copy-link",
|
||||
"duplicate",
|
||||
"local-only",
|
||||
"read-only"
|
||||
];
|
||||
|
||||
const bottomBarItemsList = [
|
||||
"notebooks",
|
||||
"add-reminder",
|
||||
"pin-to-notifications",
|
||||
"history",
|
||||
"reminders",
|
||||
"attachments",
|
||||
"references",
|
||||
"trash"
|
||||
];
|
||||
|
||||
const topBarItems = data
|
||||
.filter((item) => topBarItemsList.indexOf(item.id) > -1)
|
||||
.sort((a, b) =>
|
||||
topBarItemsList.indexOf(a.id) > topBarItemsList.indexOf(b.id) ? 1 : -1
|
||||
)
|
||||
.sort((a, b) => {
|
||||
return (
|
||||
(topBarSorting.current[b.id] || 0) - (topBarSorting.current[a.id] || 0)
|
||||
);
|
||||
});
|
||||
|
||||
const bottomGridItems = data
|
||||
.filter((item) => bottomBarItemsList.indexOf(item.id) > -1)
|
||||
.sort((a, b) =>
|
||||
bottomBarItemsList.indexOf(a.id) > bottomBarItemsList.indexOf(b.id)
|
||||
? 1
|
||||
: -1
|
||||
);
|
||||
|
||||
let topBarItemWidth =
|
||||
(width - (topBarItems.length * 10 + 14)) / topBarItems.length;
|
||||
topBarItemWidth;
|
||||
|
||||
if (topBarItemWidth < 60) {
|
||||
topBarItemWidth = 60;
|
||||
}
|
||||
|
||||
return item.type === "note" ? (
|
||||
<>
|
||||
<ScrollView
|
||||
horizontal
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
marginTop: 6,
|
||||
marginBottom: 6
|
||||
}}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{
|
||||
paddingRight: 25,
|
||||
gap: 15
|
||||
}}
|
||||
>
|
||||
{topBarItems.map(renderTopBarItem)}
|
||||
</ScrollView>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignContent: "flex-start",
|
||||
marginTop: item.type !== "note" ? 10 : 0,
|
||||
paddingTop: 10,
|
||||
marginLeft: 6
|
||||
}}
|
||||
>
|
||||
{bottomGridItems.map((item) => _renderRowItem({ item }))}
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View data={data}>{data.map(renderColumnItem)}</View>
|
||||
);
|
||||
};
|
||||
301
apps/mobile/app/components/properties/items.tsx
Normal file
301
apps/mobile/app/components/properties/items.tsx
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Item } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Dimensions, ScrollView, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { Action, ActionId, useActions } from "../../hooks/use-actions";
|
||||
import { useStoredRef } from "../../hooks/use-stored-ref";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Button } from "../ui/button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
const TOP_BAR_ITEMS: ActionId[] = [
|
||||
"pin",
|
||||
"favorite",
|
||||
"copy",
|
||||
"share",
|
||||
"lock-unlock",
|
||||
"publish",
|
||||
"export",
|
||||
"copy-link",
|
||||
"duplicate",
|
||||
"local-only",
|
||||
"read-only"
|
||||
];
|
||||
|
||||
const BOTTOM_BAR_ITEMS: ActionId[] = [
|
||||
"notebooks",
|
||||
"add-reminder",
|
||||
"pin-to-notifications",
|
||||
"history",
|
||||
"reminders",
|
||||
"attachments",
|
||||
"references",
|
||||
"trash"
|
||||
];
|
||||
|
||||
const COLUMN_BAR_ITEMS: ActionId[] = [
|
||||
"select",
|
||||
"add-notebook",
|
||||
"edit-notebook",
|
||||
"move-notes",
|
||||
"move-notebook",
|
||||
"pin",
|
||||
"default-notebook",
|
||||
"add-shortcut",
|
||||
"trash"
|
||||
];
|
||||
|
||||
export const Items = ({ item, close }: { item: Item; close: () => void }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const topBarSorting = useStoredRef<{ [name: string]: number }>(
|
||||
"topbar-sorting-ref",
|
||||
{}
|
||||
);
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const actions = useActions({ item, close });
|
||||
const selectedActions = actions.filter((i) => !i.hidden);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const width = Math.min(dimensions.width, 600);
|
||||
|
||||
const shouldShrink =
|
||||
Dimensions.get("window").fontScale > 1 &&
|
||||
Dimensions.get("window").width < 450;
|
||||
|
||||
const columnItemsCount = deviceMode === "tablet" ? 7 : shouldShrink ? 4 : 5;
|
||||
|
||||
const columnItemWidth =
|
||||
deviceMode !== "mobile"
|
||||
? (width - 16) / columnItemsCount
|
||||
: (width - 16) / columnItemsCount;
|
||||
|
||||
const topBarItems = selectedActions
|
||||
.filter((item) => TOP_BAR_ITEMS.indexOf(item.id) > -1)
|
||||
.sort((a, b) =>
|
||||
TOP_BAR_ITEMS.indexOf(a.id) > TOP_BAR_ITEMS.indexOf(b.id) ? 1 : -1
|
||||
)
|
||||
.sort((a, b) => {
|
||||
return (
|
||||
(topBarSorting.current[b.id] || 0) - (topBarSorting.current[a.id] || 0)
|
||||
);
|
||||
});
|
||||
|
||||
const bottomGridItems = selectedActions
|
||||
.filter((item) => BOTTOM_BAR_ITEMS.indexOf(item.id) > -1)
|
||||
.sort((a, b) =>
|
||||
BOTTOM_BAR_ITEMS.indexOf(a.id) > BOTTOM_BAR_ITEMS.indexOf(b.id) ? 1 : -1
|
||||
);
|
||||
|
||||
const columnItems = selectedActions
|
||||
.filter((item) => COLUMN_BAR_ITEMS.indexOf(item.id) > -1)
|
||||
.sort((a, b) =>
|
||||
COLUMN_BAR_ITEMS.indexOf(a.id) > COLUMN_BAR_ITEMS.indexOf(b.id) ? 1 : -1
|
||||
);
|
||||
|
||||
const topBarItemHeight = Math.min(
|
||||
(width - (topBarItems.length * 10 + 14)) / topBarItems.length,
|
||||
60
|
||||
);
|
||||
|
||||
const renderRowItem = React.useCallback(
|
||||
({ item }: { item: Action }) => (
|
||||
<View
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
width: columnItemWidth - 8
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
onPress={item.onPress}
|
||||
type={item.checked ? "shade" : "secondary"}
|
||||
style={{
|
||||
height: columnItemWidth - 8,
|
||||
width: columnItemWidth - 8,
|
||||
borderRadius: 10,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: DDS.isTab ? 7 : 3.5
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
allowFontScaling
|
||||
name={item.icon}
|
||||
size={DDS.isTab ? SIZE.xxl : shouldShrink ? SIZE.xxl : SIZE.lg}
|
||||
color={
|
||||
item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: item.id.match(/(delete|trash)/g)
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
|
||||
<Paragraph
|
||||
size={SIZE.xxs}
|
||||
textBreakStrategy="simple"
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
),
|
||||
[
|
||||
colors.error.icon,
|
||||
colors.primary.accent,
|
||||
colors.secondary.icon,
|
||||
columnItemWidth,
|
||||
shouldShrink
|
||||
]
|
||||
);
|
||||
|
||||
const renderColumnItem = React.useCallback(
|
||||
(item: Action) => (
|
||||
<Button
|
||||
key={item.id}
|
||||
buttonType={{
|
||||
text: item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: item.id === "delete" || item.id === "trash"
|
||||
? colors.error.paragraph
|
||||
: colors.primary.paragraph
|
||||
}}
|
||||
onPress={item.onPress}
|
||||
title={item.title}
|
||||
icon={item.icon}
|
||||
type={item.checked ? "inverted" : "plain"}
|
||||
fontSize={SIZE.sm}
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
justifyContent: "flex-start",
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[colors.error.paragraph, colors.primary.accent, colors.primary.paragraph]
|
||||
);
|
||||
|
||||
const renderTopBarItem = React.useCallback(
|
||||
(item: Action) => {
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
item.onPress();
|
||||
setImmediate(() => {
|
||||
const currentValue = topBarSorting.current[item.id] || 0;
|
||||
topBarSorting.current = {
|
||||
...topBarSorting.current,
|
||||
[item.id]: currentValue + 1
|
||||
};
|
||||
});
|
||||
}}
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 0,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
height: topBarItemHeight,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: DDS.isTab ? 7 : 3.5,
|
||||
borderRadius: 100
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
allowFontScaling
|
||||
size={DDS.isTab ? SIZE.xxl : SIZE.md + 4}
|
||||
color={
|
||||
item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: item.id === "delete" || item.id === "trash"
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Paragraph
|
||||
textBreakStrategy="simple"
|
||||
size={SIZE.xxs}
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
},
|
||||
[
|
||||
colors.error.icon,
|
||||
colors.primary.accent,
|
||||
colors.secondary.icon,
|
||||
topBarItemHeight,
|
||||
topBarSorting
|
||||
]
|
||||
);
|
||||
|
||||
return item.type === "note" ? (
|
||||
<>
|
||||
<ScrollView
|
||||
horizontal
|
||||
style={{
|
||||
paddingHorizontal: 16,
|
||||
marginTop: 6,
|
||||
marginBottom: 6
|
||||
}}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{
|
||||
paddingRight: 25,
|
||||
gap: 15
|
||||
}}
|
||||
>
|
||||
{topBarItems.map(renderTopBarItem)}
|
||||
</ScrollView>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
marginTop: 10,
|
||||
gap: 5,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{bottomGridItems.map((item) => renderRowItem({ item }))}
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View>{columnItems.map(renderColumnItem)}</View>
|
||||
);
|
||||
};
|
||||
@@ -19,75 +19,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View, useWindowDimensions } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
export const Synced = ({ item, close }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const user = useUserStore((state) => state.user);
|
||||
const lastSynced = useUserStore((state) => state.lastSynced);
|
||||
|
||||
const dimensions = useWindowDimensions();
|
||||
const shouldShrink = dimensions.fontScale > 1 && dimensions.width < 450;
|
||||
return user && lastSynced >= item.dateModified ? (
|
||||
<View
|
||||
<Button
|
||||
style={{
|
||||
paddingVertical: 0,
|
||||
width: "100%",
|
||||
paddingHorizontal: 12,
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignSelf: "center",
|
||||
paddingTop: 10,
|
||||
marginTop: 10,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.primary.border
|
||||
alignSelf: "flex-start",
|
||||
height: "auto",
|
||||
padding: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="shield-key-outline"
|
||||
color={colors.primary.accent}
|
||||
size={shouldShrink ? SIZE.xxl : SIZE.xxxl}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
marginLeft: 5,
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
color={colors.primary.heading}
|
||||
size={SIZE.xs}
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{strings.noteSyncedNoticeHeading()}
|
||||
</Heading>
|
||||
{shouldShrink ? null : (
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
color={colors.primary.paragraph}
|
||||
>
|
||||
{strings.noteSyncedNoticeDesc(item.itemType || item.type)}
|
||||
</Paragraph>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<Button
|
||||
fontSize={SIZE.xxxs}
|
||||
iconSize={SIZE.xs}
|
||||
icon="shield-key-outline"
|
||||
type="shade"
|
||||
title="Encrypted and synced"
|
||||
onPress={async () => {
|
||||
try {
|
||||
close();
|
||||
@@ -100,11 +57,6 @@ export const Synced = ({ item, close }) => {
|
||||
console.error(e);
|
||||
}
|
||||
}}
|
||||
title={strings.learnMore()}
|
||||
fontSize={SIZE.xs}
|
||||
height={30}
|
||||
type="secondaryAccented"
|
||||
/>
|
||||
</View>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -35,18 +35,17 @@ import Navigation from "../../services/navigation";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { deleteItems } from "../../utils/functions";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { updateNotebook } from "../../utils/notebooks";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import MoveNoteSheet from "../sheets/add-to";
|
||||
import ExportNotesSheet from "../sheets/export-notes";
|
||||
import ManageTagsSheet from "../sheets/manage-tags";
|
||||
import { MoveNotebookSheet } from "../sheets/move-notebook";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
|
||||
export const SelectionHeader = React.memo(
|
||||
({
|
||||
@@ -75,9 +74,9 @@ export const SelectionHeader = React.memo(
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionMode) {
|
||||
tabBarRef.current?.lock();
|
||||
fluidTabsRef.current?.lock();
|
||||
} else {
|
||||
tabBarRef.current?.unlock();
|
||||
fluidTabsRef.current?.unlock();
|
||||
}
|
||||
}, [selectionMode]);
|
||||
|
||||
@@ -139,61 +138,24 @@ export const SelectionHeader = React.memo(
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: Platform.OS === "android" ? 50 + insets.top : 50,
|
||||
paddingTop: Platform.OS === "android" ? insets.top : null,
|
||||
backgroundColor: colors.primary.background,
|
||||
justifyContent: "space-between",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
zIndex: 999,
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
borderTopWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
borderRadius: 100
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 100,
|
||||
marginRight: 10
|
||||
}}
|
||||
onPress={() => {
|
||||
clearSelection();
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
name="close"
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: "center",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.lg} color={colors.primary.paragraph}>
|
||||
{selectedItemsList.length}
|
||||
</Heading>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center"
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
@@ -202,42 +164,14 @@ export const SelectionHeader = React.memo(
|
||||
.getState()
|
||||
.setAll(allSelected ? [] : [...((await items?.ids()) || [])]);
|
||||
}}
|
||||
tooltipText="Select all"
|
||||
tooltipPosition={4}
|
||||
style={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
color={
|
||||
allSelected ? colors.primary.accent : colors.primary.paragraph
|
||||
}
|
||||
size={SIZE.lg}
|
||||
color={allSelected ? colors.primary.accent : colors.primary.icon}
|
||||
name="select-all"
|
||||
/>
|
||||
|
||||
{selectedItemsList.length ? (
|
||||
<Menu
|
||||
ref={menuRef}
|
||||
animationDuration={200}
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: contextMenuColors.primary.background,
|
||||
marginTop: 35
|
||||
}}
|
||||
onRequestClose={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
name="dots-vertical"
|
||||
color={colors.primary.paragraph}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{[
|
||||
{!selectedItemsList.length
|
||||
? null
|
||||
: [
|
||||
{
|
||||
title: strings.move(),
|
||||
onPress: async () => {
|
||||
@@ -341,20 +275,12 @@ export const SelectionHeader = React.memo(
|
||||
}
|
||||
].map((item) =>
|
||||
!item.visible ? null : (
|
||||
<Button
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderRadius: 0,
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
type="plain"
|
||||
buttonType={{
|
||||
text: contextMenuColors.primary.paragraph
|
||||
}}
|
||||
icon={item.icon}
|
||||
name={item.icon}
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
color={colors.primary.icon}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
@@ -364,9 +290,16 @@ export const SelectionHeader = React.memo(
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Menu>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
onPress={() => {
|
||||
clearSelection();
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
name="close"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Notebook } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
@@ -30,17 +31,16 @@ import {
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { eOnNotebookUpdated } from "../../../utils/events";
|
||||
import { getParentNotebookId } from "../../../utils/notebooks";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { MoveNotes } from "../move-notes/movenote";
|
||||
import { eOnNotebookUpdated } from "../../../utils/events";
|
||||
import { getParentNotebookId } from "../../../utils/notebooks";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const AddNotebookSheet = ({
|
||||
notebook,
|
||||
@@ -97,11 +97,10 @@ export const AddNotebookSheet = ({
|
||||
parentNotebook?.id ||
|
||||
(await getParentNotebookId(notebook?.id || (id as string)));
|
||||
|
||||
eSendEvent(eOnNotebookUpdated, parent);
|
||||
if (notebook) {
|
||||
setImmediate(() => {
|
||||
eSendEvent(eOnNotebookUpdated, notebook.id);
|
||||
});
|
||||
eSendEvent(eOnNotebookUpdated, parent || notebook?.id);
|
||||
|
||||
if (!parent) {
|
||||
useNotebookStore.getState().refresh();
|
||||
}
|
||||
|
||||
if (!notebook && showMoveNotesOnComplete && id) {
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
@@ -45,7 +46,6 @@ import { Pressable } from "../../ui/pressable";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { AddNotebookSheet } from "../add-notebook";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
const useNotebookExpandedStore = create<{
|
||||
expanded: {
|
||||
@@ -119,14 +119,19 @@ export const MoveNotebookSheet = ({
|
||||
},
|
||||
notebook
|
||||
);
|
||||
}
|
||||
await db.relations.add(selectedNotebook, notebook);
|
||||
if (parent) {
|
||||
eSendEvent(eOnNotebookUpdated, parent);
|
||||
}
|
||||
|
||||
await db.relations.add(selectedNotebook, notebook);
|
||||
eSendEvent(eOnNotebookUpdated, selectedNotebook.id);
|
||||
eSendEvent(eOnNotebookUpdated, notebook.id);
|
||||
}
|
||||
|
||||
if (!parent) {
|
||||
useNotebookStore.getState().refresh();
|
||||
} else {
|
||||
eSendEvent(eOnNotebookUpdated, selectedNotebook.id);
|
||||
}
|
||||
|
||||
close?.();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ import { useDBItem, useNoteLocked } from "../../../hooks/use-db-item";
|
||||
import { eSendEvent, presentSheet } from "../../../services/event-manager";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { eOnLoadNote } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import SheetProvider from "../../sheet-provider";
|
||||
import { Button } from "../../ui/button";
|
||||
@@ -423,7 +423,7 @@ export const ReferencesList = ({ item, close }: ReferencesListProps) => {
|
||||
item: note,
|
||||
blockId: blockId
|
||||
});
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
close?.();
|
||||
}}
|
||||
reference={item as Note}
|
||||
|
||||
@@ -17,45 +17,55 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
GroupingKey,
|
||||
GroupOptions,
|
||||
ItemType,
|
||||
SortOptions
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../../common/database";
|
||||
import { eSendEvent } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { RouteName } from "../../../stores/use-navigation-store";
|
||||
import { GROUP, SORT } from "../../../utils/constants";
|
||||
import { eGroupOptionsUpdated, refreshNotesPage } from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import AppIcon from "../../ui/AppIcon";
|
||||
import { Button } from "../../ui/button";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { strings } from "@notesnook/intl";
|
||||
const Sort = ({ type, screen }) => {
|
||||
const isTopicSheet = screen === "TopicSheet";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
const Sort = ({ type, screen }: { type: ItemType; screen: RouteName }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings.getGroupOptions(screen === "Notes" ? "home" : type + "s")
|
||||
db.settings.getGroupOptions(
|
||||
screen === "Notes" ? "home" : ((type + "s") as GroupingKey)
|
||||
)
|
||||
);
|
||||
const updateGroupOptions = async (_groupOptions) => {
|
||||
const groupType = screen === "Notes" ? "home" : type + "s";
|
||||
|
||||
const updateGroupOptions = async (_groupOptions: GroupOptions) => {
|
||||
const groupType =
|
||||
screen === "Notes" ? "home" : ((type + "s") as GroupingKey);
|
||||
console.log("updateGroupOptions for group", groupType, "in", screen);
|
||||
await db.settings.setGroupOptions(groupType, _groupOptions);
|
||||
setGroupOptions(_groupOptions);
|
||||
setTimeout(() => {
|
||||
if (screen !== "TopicSheet") Navigation.queueRoutesForUpdate(screen);
|
||||
Navigation.queueRoutesForUpdate(screen);
|
||||
eSendEvent(eGroupOptionsUpdated, groupType);
|
||||
eSendEvent(refreshNotesPage);
|
||||
}, 1);
|
||||
};
|
||||
|
||||
const setOrderBy = async () => {
|
||||
let _groupOptions = {
|
||||
const _groupOptions: GroupOptions = {
|
||||
...groupOptions,
|
||||
sortDirection: groupOptions?.sortDirection === "asc" ? "desc" : "asc"
|
||||
};
|
||||
if (type === "topics") {
|
||||
_groupOptions.groupBy = "none";
|
||||
}
|
||||
|
||||
await updateGroupOptions(_groupOptions);
|
||||
};
|
||||
|
||||
@@ -64,7 +74,8 @@ const Sort = ({ type, screen }) => {
|
||||
style={{
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
justifyContent: "space-between"
|
||||
justifyContent: "space-between",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -72,11 +83,12 @@ const Sort = ({ type, screen }) => {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
size={SIZE.xl}
|
||||
size={SIZE.md}
|
||||
style={{
|
||||
alignSelf: "center"
|
||||
}}
|
||||
@@ -105,34 +117,22 @@ const Sort = ({ type, screen }) => {
|
||||
? "sort-ascending"
|
||||
: "sort-descending"
|
||||
}
|
||||
height={25}
|
||||
height={30}
|
||||
iconPosition="right"
|
||||
fontSize={SIZE.sm - 1}
|
||||
type="transparent"
|
||||
buttonType={{
|
||||
text: colors.primary.accent
|
||||
}}
|
||||
fontSize={SIZE.sm}
|
||||
type="plain"
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 6
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
onPress={setOrderBy}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Seperator />
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
flexWrap: "wrap",
|
||||
borderBottomWidth: isTopicSheet ? 0 : 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
marginBottom: 12,
|
||||
paddingHorizontal: 12,
|
||||
paddingBottom: 12,
|
||||
alignItems: "center"
|
||||
borderBottomColor: colors.primary.border
|
||||
}}
|
||||
>
|
||||
{groupOptions?.groupBy === "abc" ? (
|
||||
@@ -149,105 +149,118 @@ const Sort = ({ type, screen }) => {
|
||||
) : (
|
||||
Object.keys(SORT).map((item) =>
|
||||
(item === "dueDate" && screen !== "Reminders") ||
|
||||
((screen !== "Tags" || screen !== "Reminders") &&
|
||||
(screen !== "Tags" &&
|
||||
screen !== "Reminders" &&
|
||||
item === "dateModified") ||
|
||||
((screen === "Tags" || screen === "Reminders") &&
|
||||
item === "dateEdited") ? null : (
|
||||
<Button
|
||||
<Pressable
|
||||
key={item}
|
||||
type={groupOptions?.sortBy === item ? "selected" : "plain"}
|
||||
title={strings.sortByStrings[item]()}
|
||||
height={40}
|
||||
iconPosition="left"
|
||||
icon={groupOptions?.sortBy === item ? "check" : null}
|
||||
noborder
|
||||
style={{
|
||||
marginRight: 10,
|
||||
paddingHorizontal: 8
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
height: 40,
|
||||
flexDirection: "row",
|
||||
borderRadius: 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
buttonType={{
|
||||
text:
|
||||
groupOptions?.sortBy === item
|
||||
? colors.primary.accent
|
||||
: colors.secondary.paragraph
|
||||
}}
|
||||
fontSize={SIZE.sm}
|
||||
onPress={async () => {
|
||||
let _groupOptions = {
|
||||
const _groupOptions: GroupOptions = {
|
||||
...groupOptions,
|
||||
sortBy: type === "trash" ? "dateDeleted" : item
|
||||
sortBy:
|
||||
type === "trash"
|
||||
? "dateDeleted"
|
||||
: (item as SortOptions["sortBy"])
|
||||
};
|
||||
if (screen === "TopicSheet") {
|
||||
_groupOptions.groupBy = "none";
|
||||
}
|
||||
await updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
iconSize={SIZE.md}
|
||||
>
|
||||
<Paragraph>
|
||||
{strings.sortByStrings[
|
||||
item as keyof typeof strings.sortByStrings
|
||||
]()}
|
||||
</Paragraph>
|
||||
|
||||
{groupOptions.sortBy === item ? (
|
||||
<AppIcon
|
||||
size={SIZE.lg}
|
||||
name="check"
|
||||
color={colors.selected.accent}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
)
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
|
||||
{isTopicSheet ? null : (
|
||||
<>
|
||||
<Heading
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 12
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
>
|
||||
{strings.groupBy()}
|
||||
</Heading>
|
||||
|
||||
<Seperator />
|
||||
<Heading size={SIZE.md}>{strings.groupBy()}</Heading>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
paddingHorizontal: 12
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{Object.keys(GROUP).map((item) => (
|
||||
<Button
|
||||
<Pressable
|
||||
key={item}
|
||||
testID={"btn-" + item}
|
||||
type={
|
||||
groupOptions?.groupBy === GROUP[item] ? "selected" : "plain"
|
||||
groupOptions?.groupBy === GROUP[item as keyof typeof GROUP]
|
||||
? "selected"
|
||||
: "plain"
|
||||
}
|
||||
buttonType={{
|
||||
text:
|
||||
groupOptions?.groupBy === GROUP[item]
|
||||
? colors.primary.accent
|
||||
: colors.secondary.paragraph
|
||||
noborder
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
height: 40,
|
||||
flexDirection: "row",
|
||||
borderRadius: 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
onPress={async () => {
|
||||
let _groupOptions = {
|
||||
const _groupOptions: GroupOptions = {
|
||||
...groupOptions,
|
||||
groupBy: GROUP[item]
|
||||
sortBy:
|
||||
type === "trash"
|
||||
? "dateDeleted"
|
||||
: (item as SortOptions["sortBy"])
|
||||
};
|
||||
|
||||
if (item === "abc") {
|
||||
_groupOptions.sortBy = "title";
|
||||
_groupOptions.sortDirection = "desc";
|
||||
}
|
||||
|
||||
updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
height={40}
|
||||
icon={groupOptions?.groupBy === GROUP[item] ? "check" : null}
|
||||
title={strings.groupByStrings[item]()}
|
||||
style={{
|
||||
paddingHorizontal: 8,
|
||||
marginBottom: 10,
|
||||
marginRight: 10
|
||||
await updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
{strings.groupByStrings[
|
||||
item as keyof typeof strings.groupByStrings
|
||||
]()}
|
||||
</Paragraph>
|
||||
|
||||
{groupOptions.groupBy === item ? (
|
||||
<AppIcon
|
||||
size={SIZE.lg}
|
||||
name="check"
|
||||
color={colors.selected.accent}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
304
apps/mobile/app/components/sheets/user/index.tsx
Normal file
304
apps/mobile/app/components/sheets/user/index.tsx
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useNetInfo } from "@react-native-community/netinfo";
|
||||
import React from "react";
|
||||
import { ActivityIndicator, Image, View } from "react-native";
|
||||
import { useSheetRef } from "react-native-actions-sheet";
|
||||
import useSyncProgress from "../../../hooks/use-sync-progress";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { SyncStatus, useUserStore } from "../../../stores/use-user-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Card } from "../../list/card";
|
||||
import AppIcon from "../../ui/AppIcon";
|
||||
import { Button } from "../../ui/button";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import { TimeSince } from "../../ui/time-since";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
export const UserSheet = () => {
|
||||
const ref = useSheetRef();
|
||||
const { colors } = useThemeColors();
|
||||
const [userProfile, user, syncing, lastSyncStatus, lastSynced] = useUserStore(
|
||||
(state) => [
|
||||
state.profile,
|
||||
state.user,
|
||||
state.syncing,
|
||||
state.lastSyncStatus,
|
||||
state.lastSynced
|
||||
]
|
||||
);
|
||||
|
||||
const { isInternetReachable } = useNetInfo();
|
||||
const isOffline = !isInternetReachable;
|
||||
const { progress } = useSyncProgress();
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
gap: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{user ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={{
|
||||
uri: userProfile?.profilePicture
|
||||
}}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 10
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<View style={{ marginLeft: 10 }}>
|
||||
<Paragraph size={SIZE.xs}>{userProfile?.fullName}</Paragraph>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
size={SIZE.xxs}
|
||||
color={colors.secondary.heading}
|
||||
>
|
||||
<AppIcon
|
||||
name="checkbox-blank-circle"
|
||||
size={10}
|
||||
allowFontScaling
|
||||
color={
|
||||
!user || lastSyncStatus === SyncStatus.Failed
|
||||
? colors.error.icon
|
||||
: isOffline
|
||||
? colors.static.orange
|
||||
: colors.success.icon
|
||||
}
|
||||
/>{" "}
|
||||
{!user ? (
|
||||
strings.notLoggedIn()
|
||||
) : lastSynced && lastSynced !== "Never" ? (
|
||||
<>
|
||||
{syncing
|
||||
? `${strings.syncing()} ${
|
||||
progress ? `(${progress.current})` : ""
|
||||
}`
|
||||
: lastSyncStatus === SyncStatus.Failed
|
||||
? strings.syncFailed()
|
||||
: strings.synced()}{" "}
|
||||
{!syncing ? (
|
||||
<TimeSince
|
||||
style={{
|
||||
fontSize: SIZE.xxs,
|
||||
color: colors.secondary.paragraph
|
||||
}}
|
||||
updateFrequency={30 * 1000}
|
||||
time={lastSynced as number}
|
||||
/>
|
||||
) : null}
|
||||
{isOffline ? ` (${strings.offline()})` : ""}
|
||||
</>
|
||||
) : (
|
||||
strings.never()
|
||||
)}
|
||||
</Paragraph>
|
||||
</View>
|
||||
{syncing ? (
|
||||
<ActivityIndicator
|
||||
color={colors.primary.accent}
|
||||
size={SIZE.xxl}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Card />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{user ? (
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: DefaultAppStyles.GAP_SMALL,
|
||||
gap: DefaultAppStyles.GAP,
|
||||
borderRadius: 10,
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
gap: DefaultAppStyles.GAP_SMALL,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<Paragraph size={SIZE.xxs}>{strings.storage()}</Paragraph>
|
||||
<Paragraph size={SIZE.xxs}>50/100MB {strings.used()}</Paragraph>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.secondary.background,
|
||||
width: "100%",
|
||||
height: 5,
|
||||
borderRadius: 10
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.static.black,
|
||||
height: 5,
|
||||
width: "50%",
|
||||
borderRadius: 10
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
padding: DefaultAppStyles.GAP_SMALL,
|
||||
borderRadius: 10
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<Paragraph size={SIZE.sm}>{strings.freePlan()}</Paragraph>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xxxs}>
|
||||
{strings.viewAllLimits()}
|
||||
<AppIcon name="information" size={SIZE.xxxs} />
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
title={strings.upgradeNow()}
|
||||
onPress={() => {}}
|
||||
type="accent"
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
height: "auto",
|
||||
paddingVertical: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
borderBottomWidth: 1,
|
||||
height: 1,
|
||||
borderColor: colors.primary.border
|
||||
}}
|
||||
></View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
gap: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{
|
||||
icon: "account-outline",
|
||||
title: strings.editProfile(),
|
||||
onPress: () => {},
|
||||
hidden: !user
|
||||
},
|
||||
{
|
||||
icon: "cog-outline",
|
||||
title: strings.settings(),
|
||||
onPress: () => {
|
||||
ref.current?.hide();
|
||||
Navigation.closeDrawer();
|
||||
Navigation.navigate("Settings");
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: "logout",
|
||||
title: strings.logout(),
|
||||
onPress: () => {
|
||||
ref.current?.hide();
|
||||
},
|
||||
hidden: !user
|
||||
}
|
||||
].map((item) =>
|
||||
item.hidden ? null : (
|
||||
<Pressable
|
||||
key={item.title}
|
||||
style={{
|
||||
paddingVertical: DefaultAppStyles.GAP_SMALL,
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
gap: DefaultAppStyles.GAP_SMALL,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
onPress={() => {
|
||||
item.onPress();
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
color={colors.secondary.icon}
|
||||
name={item.icon}
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
<Paragraph>{item.title}</Paragraph>
|
||||
</Pressable>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
UserSheet.present = () => {
|
||||
presentSheet({
|
||||
component: <UserSheet />
|
||||
});
|
||||
};
|
||||
@@ -18,22 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Color } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { ColoredNotes } from "../../screens/notes/colored";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { SideMenuItem } from "../../utils/menu-items";
|
||||
import ReorderableList from "../list/reorderable-list";
|
||||
import { Properties } from "../properties";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { useSideBarDraggingStore } from "./dragging-store";
|
||||
import { MenuItem } from "./menu-item";
|
||||
|
||||
export const ColorSection = React.memo(
|
||||
function ColorSection() {
|
||||
@@ -54,6 +50,54 @@ export const ColorSection = React.memo(
|
||||
}
|
||||
}, [loading, setColorNotes]);
|
||||
|
||||
const onPress = React.useCallback((item: SideMenuItem) => {
|
||||
ColoredNotes.navigate(item.data as Color, false);
|
||||
setImmediate(() => {
|
||||
Navigation.closeDrawer();
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onLongPress = React.useCallback((item: SideMenuItem) => {
|
||||
if (useSideBarDraggingStore.getState().dragging) return;
|
||||
Properties.present(item.data as Color);
|
||||
}, []);
|
||||
|
||||
const renderIcon = React.useCallback((item: SideMenuItem, size: number) => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: size - 5,
|
||||
height: size - 5,
|
||||
backgroundColor: (item.data as Color).colorCode,
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}, []);
|
||||
|
||||
const menuItems = useMemo(
|
||||
() =>
|
||||
colorNotes.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
icon: "circle",
|
||||
dataType: "color",
|
||||
data: item,
|
||||
onPress: onPress,
|
||||
onLongPress: onLongPress
|
||||
})) as SideMenuItem[],
|
||||
[colorNotes, onPress, onLongPress]
|
||||
);
|
||||
|
||||
return (
|
||||
<ReorderableList
|
||||
onListOrderChanged={(data) => {
|
||||
@@ -66,103 +110,16 @@ export const ColorSection = React.memo(
|
||||
itemOrder={order}
|
||||
hiddenItems={hiddensItems}
|
||||
alwaysBounceVertical={false}
|
||||
data={colorNotes}
|
||||
data={menuItems}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderDraggableItem={({ item }) => {
|
||||
return <ColorItem key={item.id} item={item} />;
|
||||
return <MenuItem item={item} renderIcon={renderIcon} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
|
||||
const ColorItem = React.memo(
|
||||
function ColorItem({ item }: { item: Color }) {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.id
|
||||
);
|
||||
|
||||
const onPress = (item: Color) => {
|
||||
ColoredNotes.navigate(item, false);
|
||||
|
||||
setImmediate(() => {
|
||||
Navigation.closeDrawer();
|
||||
});
|
||||
};
|
||||
|
||||
const onLongPress = () => {
|
||||
if (useSideBarDraggingStore.getState().dragging) return;
|
||||
Properties.present(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
customColor={isFocused ? "rgba(0,0,0,0.04)" : "transparent"}
|
||||
onLongPress={onLongPress}
|
||||
customSelectedColor={item.colorCode}
|
||||
customAlpha={!isDark ? -0.02 : 0.02}
|
||||
customOpacity={0.12}
|
||||
onPress={() => onPress(item)}
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
borderRadius: 5,
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 8,
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
height: normalize(50),
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 30,
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-start"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: SIZE.lg - 2,
|
||||
height: SIZE.lg - 2,
|
||||
backgroundColor: item.colorCode,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
marginRight: 10
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
{isFocused ? (
|
||||
<Heading color={colors.selected.heading} size={SIZE.md}>
|
||||
{item.title}
|
||||
</Heading>
|
||||
) : (
|
||||
<Paragraph color={colors.primary.paragraph} size={SIZE.md}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
},
|
||||
(prev, next) => {
|
||||
if (!next.item) return false;
|
||||
if (prev.item?.title !== next.item.title) return false;
|
||||
if (prev.item?.dateModified !== next.item?.dateModified) return false;
|
||||
if (prev.item?.id !== next.item?.id) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -18,162 +18,351 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback } from "react";
|
||||
import { View } from "react-native";
|
||||
import { DraxProvider, DraxScrollView } from "react-native-drax";
|
||||
import React from "react";
|
||||
import { Image, View } from "react-native";
|
||||
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 useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import { eOpenPremiumDialog } from "../../utils/events";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import ReorderableList from "../list/reorderable-list";
|
||||
import { Button } from "../ui/button";
|
||||
import { ColorSection } from "./color-section";
|
||||
import { useSideBarDraggingStore } from "./dragging-store";
|
||||
import { MenuItem } from "./menu-item";
|
||||
import { PinnedSection } from "./pinned-section";
|
||||
import { UserStatus } from "./user-status";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { deleteItems } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { MoveNotebookSheet } from "../sheets/move-notebook";
|
||||
import { UserSheet } from "../sheets/user";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { SideMenuHome } from "./side-menu-home";
|
||||
import { SideMenuNotebooks } from "./side-menu-notebooks";
|
||||
import { SideMenuTags } from "./side-menu-tags";
|
||||
import {
|
||||
useSideMenuNotebookSelectionStore,
|
||||
useSideMenuTagsSelectionStore
|
||||
} from "./stores";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { rootNavigatorRef } from "../../utils/global-refs";
|
||||
const renderScene = SceneMap({
|
||||
home: SideMenuHome,
|
||||
notebooks: SideMenuNotebooks,
|
||||
tags: SideMenuTags,
|
||||
settings: () => null
|
||||
});
|
||||
|
||||
export const SideMenu = React.memo(
|
||||
function SideMenu() {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const subscriptionType = useUserStore(
|
||||
(state) => state.user?.subscription?.type
|
||||
);
|
||||
const isAppLoading = useSettingStore((state) => state.isAppLoading);
|
||||
const dragging = useSideBarDraggingStore((state) => state.dragging);
|
||||
const [order, hiddensItems] = useMenuStore((state) => [
|
||||
state.order["routes"],
|
||||
state.hiddenItems["routes"]
|
||||
const { colors } = useThemeColors();
|
||||
const [index, setIndex] = React.useState(0);
|
||||
const [routes] = React.useState<Route[]>([
|
||||
{
|
||||
key: "home",
|
||||
title: "Home"
|
||||
},
|
||||
{
|
||||
key: "notebooks",
|
||||
title: "Notebooks"
|
||||
},
|
||||
{
|
||||
key: "tags",
|
||||
title: "Tags"
|
||||
},
|
||||
{
|
||||
key: "settings",
|
||||
title: "Settings"
|
||||
}
|
||||
]);
|
||||
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
|
||||
const pro = {
|
||||
name: "Notesnook Pro",
|
||||
icon: "crown",
|
||||
func: () => {
|
||||
eSendEvent(eOpenPremiumDialog);
|
||||
}
|
||||
};
|
||||
|
||||
const renderItem = useCallback(
|
||||
() => (
|
||||
<>
|
||||
<ReorderableList
|
||||
onListOrderChanged={(data) => {
|
||||
db.settings.setSideBarOrder("routes", data);
|
||||
}}
|
||||
onHiddenItemsChanged={(data) => {
|
||||
db.settings.setSideBarHiddenItems("routes", data);
|
||||
}}
|
||||
itemOrder={order}
|
||||
hiddenItems={hiddensItems}
|
||||
alwaysBounceVertical={false}
|
||||
data={MenuItemsList}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderDraggableItem={({ item, index }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
item={{
|
||||
...item,
|
||||
title:
|
||||
strings.routes[
|
||||
item.name as keyof typeof strings.routes
|
||||
]?.() || item.name
|
||||
}}
|
||||
testID={item.name}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<ColorSection />
|
||||
<PinnedSection />
|
||||
</>
|
||||
),
|
||||
[order, hiddensItems]
|
||||
);
|
||||
|
||||
return !isAppLoading && introCompleted ? (
|
||||
<View
|
||||
<SafeAreaView
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
flex: 1,
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: insets.top
|
||||
}}
|
||||
>
|
||||
<DraxProvider>
|
||||
<DraxScrollView nestedScrollEnabled={false}>
|
||||
{renderItem()}
|
||||
</DraxScrollView>
|
||||
</DraxProvider>
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
{subscriptionType === SUBSCRIPTION_STATUS.TRIAL ||
|
||||
subscriptionType === SUBSCRIPTION_STATUS.BASIC ? (
|
||||
<MenuItem testID={pro.name} key={pro.name} item={pro} index={0} />
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{dragging ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="secondaryAccented"
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
borderRadius: 5,
|
||||
marginBottom: 12,
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
marginTop: 5,
|
||||
paddingVertical: 6,
|
||||
width: "100%"
|
||||
}}
|
||||
icon="close"
|
||||
title={strings.stopReordering()}
|
||||
onPress={() => {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
}}
|
||||
<TabView
|
||||
navigationState={{ index, routes }}
|
||||
renderTabBar={(props) => <TabBar {...props} />}
|
||||
tabBarPosition="bottom"
|
||||
renderScene={renderScene}
|
||||
onIndexChange={setIndex}
|
||||
swipeEnabled={false}
|
||||
animationEnabled={false}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<UserStatus />
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
) : null;
|
||||
</SafeAreaView>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
|
||||
const SettingsIcon = (props: {
|
||||
route: Route;
|
||||
isFocused: boolean;
|
||||
jumpTo: (routeName: string) => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const userProfile = useUserStore((state) => state.profile);
|
||||
const user = useUserStore((state) => state.user);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={props.route.key}
|
||||
onPress={() => {
|
||||
rootNavigatorRef.navigate("Settings");
|
||||
// if (!user) {
|
||||
// rootNavigatorRef.navigate("Settings");
|
||||
// return;
|
||||
// }
|
||||
// UserSheet.present();
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
paddingVertical: 2,
|
||||
width: "25%"
|
||||
}}
|
||||
type={props.isFocused ? "selected" : "plain"}
|
||||
>
|
||||
{userProfile?.profilePicture ? (
|
||||
<Image
|
||||
source={{
|
||||
uri: userProfile?.profilePicture
|
||||
}}
|
||||
style={{
|
||||
width: SIZE.lg,
|
||||
height: SIZE.lg,
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
name="cog-outline"
|
||||
color={props.isFocused ? colors.primary.accent : colors.primary.icon}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Paragraph
|
||||
color={
|
||||
props.isFocused
|
||||
? colors.primary.paragraph
|
||||
: colors.secondary.paragraph
|
||||
}
|
||||
style={{
|
||||
opacity: props.isFocused ? 1 : 0.6
|
||||
}}
|
||||
size={SIZE.xxxs - 1}
|
||||
>
|
||||
{userProfile?.fullName
|
||||
? userProfile.fullName.split(" ")[0]
|
||||
: props.route.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
const TabBar = (
|
||||
props: SceneRendererProps & {
|
||||
navigationState: NavigationState<Route>;
|
||||
options: Record<string, TabDescriptor<Route>> | undefined;
|
||||
}
|
||||
) => {
|
||||
const { colors } = useThemeColors();
|
||||
const notebookSelectionEnabled = useSideMenuNotebookSelectionStore(
|
||||
(state) => state.enabled
|
||||
);
|
||||
const tagSelectionEnabled = useSideMenuTagsSelectionStore(
|
||||
(state) => state.enabled
|
||||
);
|
||||
const isSelectionEnabled = notebookSelectionEnabled || tagSelectionEnabled;
|
||||
|
||||
const getIcon = (key: string) => {
|
||||
switch (key) {
|
||||
case "home":
|
||||
return "home-outline";
|
||||
case "settings":
|
||||
return "cog-outline";
|
||||
case "notebooks":
|
||||
return "book-outline";
|
||||
case "tags":
|
||||
return "pound";
|
||||
default:
|
||||
return "home-outline";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{isSelectionEnabled ? (
|
||||
<>
|
||||
{[
|
||||
{
|
||||
title: "Select all",
|
||||
icon: "check-all"
|
||||
},
|
||||
{
|
||||
title: "Delete",
|
||||
icon: "delete"
|
||||
},
|
||||
{
|
||||
title: "Move",
|
||||
icon: "arrow-right-bold-box-outline",
|
||||
hidden: !notebookSelectionEnabled
|
||||
},
|
||||
{
|
||||
title: "Close",
|
||||
icon: "close"
|
||||
}
|
||||
].map((item) => (
|
||||
<>
|
||||
<Pressable
|
||||
key={item.title}
|
||||
onPress={async () => {
|
||||
switch (item.title) {
|
||||
case "Select all": {
|
||||
if (notebookSelectionEnabled) {
|
||||
useSideMenuNotebookSelectionStore
|
||||
.getState()
|
||||
.selectAll?.();
|
||||
} else {
|
||||
useSideMenuTagsSelectionStore.getState().selectAll?.();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Delete": {
|
||||
if (notebookSelectionEnabled) {
|
||||
const ids = useSideMenuNotebookSelectionStore
|
||||
.getState()
|
||||
.getSelectedItemIds();
|
||||
deleteItems(ids, "notebook");
|
||||
} else {
|
||||
const ids = useSideMenuTagsSelectionStore
|
||||
.getState()
|
||||
.getSelectedItemIds();
|
||||
deleteItems(ids, "tag");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Move": {
|
||||
const ids = useSideMenuNotebookSelectionStore
|
||||
.getState()
|
||||
.getSelectedItemIds();
|
||||
const notebooks = await db.notebooks.all.items(ids);
|
||||
MoveNotebookSheet.present(notebooks);
|
||||
break;
|
||||
}
|
||||
case "Close": {
|
||||
useSideMenuNotebookSelectionStore.setState({
|
||||
enabled: false,
|
||||
selection: {}
|
||||
});
|
||||
useSideMenuTagsSelectionStore.setState({
|
||||
enabled: false,
|
||||
selection: {}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
paddingVertical: 2,
|
||||
width: "25%"
|
||||
}}
|
||||
type="plain"
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
color={colors.primary.icon}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
size={SIZE.xxxs - 1}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{props.navigationState.routes.map((route, index) => {
|
||||
const isFocused = props.navigationState.index === index;
|
||||
|
||||
return route.key === "settings" ? (
|
||||
<SettingsIcon
|
||||
isFocused={isFocused}
|
||||
jumpTo={props.jumpTo}
|
||||
route={route}
|
||||
/>
|
||||
) : (
|
||||
<Pressable
|
||||
key={route.key}
|
||||
onPress={() => {
|
||||
props.jumpTo(route.key);
|
||||
switch (route.key) {
|
||||
case "notebooks":
|
||||
Navigation.routeNeedsUpdate(
|
||||
"Notebooks",
|
||||
Navigation.routeUpdateFunctions.Notebooks
|
||||
);
|
||||
break;
|
||||
case "tags":
|
||||
Navigation.routeNeedsUpdate(
|
||||
"Tags",
|
||||
Navigation.routeUpdateFunctions.Tags
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
opacity: isFocused ? 1 : 0.6,
|
||||
paddingVertical: 2,
|
||||
width: "25%"
|
||||
}}
|
||||
type={isFocused ? "selected" : "plain"}
|
||||
>
|
||||
<Icon
|
||||
name={getIcon(route.key)}
|
||||
color={
|
||||
isFocused ? colors.primary.accent : colors.primary.icon
|
||||
}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
<Paragraph
|
||||
color={
|
||||
isFocused
|
||||
? colors.primary.paragraph
|
||||
: colors.secondary.paragraph
|
||||
}
|
||||
size={SIZE.xxxs - 1}
|
||||
>
|
||||
{route.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,85 +18,146 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import ToggleSwitch from "toggle-switch-react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { useTotalNotes } from "../../hooks/use-db-item";
|
||||
import Navigation from "../../services/navigation";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import { useFavoriteStore } from "../../stores/use-favorite-store";
|
||||
import useNavigationStore, {
|
||||
RouteParams
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { useTrashStore } from "../../stores/use-trash-store";
|
||||
import { SideMenuItem } from "../../utils/menu-items";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
function _MenuItem({
|
||||
item,
|
||||
index,
|
||||
testID,
|
||||
rightBtn
|
||||
renderIcon
|
||||
}: {
|
||||
item: any;
|
||||
index: number;
|
||||
testID: string;
|
||||
rightBtn?: {
|
||||
name: string;
|
||||
icon: string;
|
||||
func: () => void;
|
||||
};
|
||||
item: SideMenuItem;
|
||||
index?: number;
|
||||
testID?: string;
|
||||
renderIcon?: (item: SideMenuItem, size: number) => React.ReactNode;
|
||||
}) {
|
||||
const [itemCount, setItemCount] = useState(0);
|
||||
const { colors } = useThemeColors();
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.name
|
||||
(state) => state.focusedRouteId === item.id
|
||||
);
|
||||
const primaryColors = isFocused ? colors.selected : colors.primary;
|
||||
const totalNotes = useTotalNotes(
|
||||
item.dataType as "notebook" | "tag" | "color"
|
||||
);
|
||||
const getTotalNotesRef = useRef(totalNotes.getTotalNotes);
|
||||
getTotalNotesRef.current = totalNotes.getTotalNotes;
|
||||
|
||||
const menuItemCount = !item.data
|
||||
? itemCount
|
||||
: totalNotes.totalNotes(item.data.id);
|
||||
|
||||
useEffect(() => {
|
||||
let unsub: () => void;
|
||||
if (!item.data) {
|
||||
switch (item.id) {
|
||||
case "Notes":
|
||||
unsub = useNoteStore.subscribe((state) => {
|
||||
setItemCount(
|
||||
useNoteStore.getState().items?.placeholders?.length || 0
|
||||
);
|
||||
});
|
||||
setItemCount(
|
||||
useNoteStore.getState().items?.placeholders?.length || 0
|
||||
);
|
||||
break;
|
||||
case "Favorites":
|
||||
unsub = useFavoriteStore.subscribe((state) => {
|
||||
setItemCount(state.items?.placeholders.length || 0);
|
||||
});
|
||||
setItemCount(
|
||||
useFavoriteStore.getState().items?.placeholders?.length || 0
|
||||
);
|
||||
break;
|
||||
case "Reminders":
|
||||
unsub = useFavoriteStore.subscribe((state) => {
|
||||
setItemCount(state.items?.placeholders.length || 0);
|
||||
});
|
||||
setItemCount(
|
||||
useFavoriteStore.getState().items?.placeholders?.length || 0
|
||||
);
|
||||
break;
|
||||
case "Monographs":
|
||||
db.monographs.all.count().then((count) => {
|
||||
setItemCount(count);
|
||||
});
|
||||
// TODO make it reactive?
|
||||
break;
|
||||
case "Trash":
|
||||
unsub = useTrashStore.subscribe((state) => {
|
||||
setItemCount(state.items?.placeholders.length || 0);
|
||||
});
|
||||
setItemCount(
|
||||
useTrashStore.getState().items?.placeholders?.length || 0
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
getTotalNotesRef.current?.([item.data.id]);
|
||||
}
|
||||
return () => {
|
||||
unsub?.();
|
||||
};
|
||||
}, [item.data, item.id]);
|
||||
|
||||
const _onPress = () => {
|
||||
if (item.func) {
|
||||
item.func();
|
||||
} else {
|
||||
if (useNavigationStore.getState().currentRoute !== item.name) {
|
||||
Navigation.navigate(item.name, {
|
||||
canGoBack: false,
|
||||
beta: item.isBeta
|
||||
if (item.onPress) return item.onPress(item);
|
||||
|
||||
if (useNavigationStore.getState().currentRoute !== item.id) {
|
||||
Navigation.navigate(item.id as keyof RouteParams, {
|
||||
canGoBack: false
|
||||
});
|
||||
}
|
||||
}
|
||||
if (item.close) {
|
||||
setImmediate(() => {
|
||||
Navigation.closeDrawer();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
testID={testID}
|
||||
key={item.name + index}
|
||||
key={item.id}
|
||||
onPress={_onPress}
|
||||
onLongPress={() => item.onLongPress?.(item)}
|
||||
type={isFocused ? "selected" : "plain"}
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
borderRadius: 5,
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 8,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
height: normalize(50),
|
||||
marginBottom: 5
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{renderIcon ? (
|
||||
renderIcon(item, SIZE.md)
|
||||
) : (
|
||||
<Icon
|
||||
style={{
|
||||
width: 30,
|
||||
textAlignVertical: "center",
|
||||
textAlign: "left"
|
||||
}}
|
||||
@@ -106,66 +167,42 @@ function _MenuItem({
|
||||
item.icon === "crown"
|
||||
? colors.static.yellow
|
||||
: isFocused
|
||||
? colors.selected.icon
|
||||
? colors.selected.paragraph
|
||||
: colors.secondary.icon
|
||||
}
|
||||
size={SIZE.lg - 2}
|
||||
size={SIZE.md}
|
||||
/>
|
||||
{isFocused ? (
|
||||
<Heading color={colors.selected.heading} size={SIZE.md}>
|
||||
{item.title || item.name}
|
||||
</Heading>
|
||||
) : (
|
||||
<Paragraph size={SIZE.md}>{item.title || item.name}</Paragraph>
|
||||
)}
|
||||
|
||||
{item.isBeta ? (
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
backgroundColor: primaryColors.accent,
|
||||
paddingHorizontal: 4,
|
||||
marginLeft: 5,
|
||||
paddingVertical: 2
|
||||
}}
|
||||
<Paragraph
|
||||
color={
|
||||
isFocused ? colors.primary.paragraph : colors.secondary.paragraph
|
||||
}
|
||||
size={SIZE.sm}
|
||||
>
|
||||
<Paragraph color={primaryColors.accentForeground} size={SIZE.xxs}>
|
||||
{strings.beta()}
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{item.switch ? (
|
||||
<ToggleSwitch
|
||||
isOn={item.on}
|
||||
onColor={primaryColors.accent}
|
||||
offColor={primaryColors.icon}
|
||||
size="small"
|
||||
animationSpeed={150}
|
||||
onToggle={_onPress}
|
||||
/>
|
||||
) : rightBtn ? (
|
||||
<Button
|
||||
title={rightBtn.name}
|
||||
type="shade"
|
||||
height={30}
|
||||
fontSize={SIZE.xs}
|
||||
iconSize={SIZE.xs}
|
||||
icon={rightBtn.icon}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 16
|
||||
}}
|
||||
onPress={rightBtn.func}
|
||||
/>
|
||||
{menuItemCount > 0 ? (
|
||||
<Paragraph
|
||||
size={SIZE.xxs}
|
||||
color={
|
||||
isFocused ? colors.primary.paragraph : colors.secondary.paragraph
|
||||
}
|
||||
>
|
||||
{menuItemCount}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
export const MenuItem = React.memo(_MenuItem, (prev, next) => {
|
||||
if (prev.item.name !== next.item.name) return false;
|
||||
if (prev.rightBtn?.name !== next.rightBtn?.name) return false;
|
||||
if (
|
||||
prev.item.id !== next.item.id &&
|
||||
prev.item.data?.dateModified !== next.item.data?.dateModified
|
||||
)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -18,30 +18,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Notebook, Tag } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import NotebookScreen from "../../screens/notebook";
|
||||
import { TaggedNotes } from "../../screens/notes/tagged";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { SideMenuItem } from "../../utils/menu-items";
|
||||
import ReorderableList from "../list/reorderable-list";
|
||||
import { Button } from "../ui/button";
|
||||
import { Notice } from "../ui/notice";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Seperator from "../ui/seperator";
|
||||
import SheetWrapper from "../ui/sheet";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { MenuItem } from "./menu-item";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const PinnedSection = React.memo(
|
||||
function PinnedSection() {
|
||||
const { colors } = useThemeColors();
|
||||
const menuPins = useMenuStore((state) => state.menuPins);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
@@ -52,42 +45,55 @@ export const PinnedSection = React.memo(
|
||||
}
|
||||
}, [loading, setMenuPins]);
|
||||
|
||||
const onPress = (item: Notebook | Tag) => {
|
||||
if (item.type === "notebook") {
|
||||
NotebookScreen.navigate(item);
|
||||
} else if (item.type === "tag") {
|
||||
TaggedNotes.navigate(item);
|
||||
const onPress = React.useCallback((item: SideMenuItem) => {
|
||||
const data = item.data as Notebook | Tag;
|
||||
if (data.type === "notebook") {
|
||||
NotebookScreen.navigate(data);
|
||||
} else if (data.type === "tag") {
|
||||
TaggedNotes.navigate(data);
|
||||
}
|
||||
setImmediate(() => {
|
||||
Navigation.closeDrawer();
|
||||
});
|
||||
};
|
||||
const renderItem = ({
|
||||
item,
|
||||
index
|
||||
}: {
|
||||
item: Notebook | Tag;
|
||||
index: number;
|
||||
}) => {
|
||||
return <PinItem item={item} onPress={onPress} />;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const menuItems = useMemo(
|
||||
() =>
|
||||
menuPins.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
icon: item.type === "notebook" ? "notebook-outline" : "pound",
|
||||
dataType: item.type,
|
||||
data: item,
|
||||
onPress: onPress
|
||||
})) as SideMenuItem[],
|
||||
[menuPins, onPress]
|
||||
);
|
||||
|
||||
const renderItem = React.useCallback(({ item }: { item: SideMenuItem }) => {
|
||||
return <MenuItem item={item} />;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexGrow: 1
|
||||
flexGrow: 1,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.primary.border,
|
||||
marginTop: DefaultAppStyles.GAP_SMALL,
|
||||
paddingTop: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<ReorderableList
|
||||
onListOrderChanged={(data) => {
|
||||
db.settings.setSideBarOrder("shortcuts", data);
|
||||
}}
|
||||
onHiddenItemsChanged={(data) => {}}
|
||||
onHiddenItemsChanged={() => {}}
|
||||
canHideItems={false}
|
||||
itemOrder={order}
|
||||
hiddenItems={[]}
|
||||
alwaysBounceVertical={false}
|
||||
data={menuPins}
|
||||
data={menuItems}
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
width: "100%"
|
||||
@@ -97,161 +103,9 @@ export const PinnedSection = React.memo(
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderDraggableItem={renderItem}
|
||||
ListEmptyComponent={
|
||||
<Notice
|
||||
size="small"
|
||||
type="information"
|
||||
text={strings.sideMenuNotice()}
|
||||
style={{
|
||||
marginHorizontal: 12
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
|
||||
export const PinItem = React.memo(
|
||||
function PinItem({
|
||||
item,
|
||||
onPress,
|
||||
isPlaceholder
|
||||
}: {
|
||||
item: Notebook | Tag;
|
||||
onPress: (item: Notebook | Tag) => void;
|
||||
isPlaceholder?: boolean;
|
||||
}) {
|
||||
const { colors } = useThemeColors();
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.id
|
||||
);
|
||||
const primaryColors = isFocused ? colors.selected : colors.primary;
|
||||
const color = isFocused ? colors.selected.accent : colors.primary.icon;
|
||||
const fwdRef = useRef();
|
||||
|
||||
const icons = {
|
||||
topic: "bookmark",
|
||||
notebook: "book-outline",
|
||||
tag: "pound"
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<SheetWrapper
|
||||
onClose={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
gestureEnabled={false}
|
||||
fwdRef={fwdRef}
|
||||
>
|
||||
<Seperator />
|
||||
<Button
|
||||
title={strings.removeShortcut()}
|
||||
type="error"
|
||||
onPress={async () => {
|
||||
await db.shortcuts.remove(item.id);
|
||||
setVisible(false);
|
||||
setMenuPins();
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
width="95%"
|
||||
style={{
|
||||
marginBottom: 30
|
||||
}}
|
||||
/>
|
||||
</SheetWrapper>
|
||||
)}
|
||||
<Pressable
|
||||
type={isFocused ? "selected" : "plain"}
|
||||
onPress={() => onPress(item)}
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
borderRadius: 5,
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 8,
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
height: normalize(50),
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexGrow: 1,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 30,
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
allowFontScaling
|
||||
color={color}
|
||||
size={SIZE.lg - 2}
|
||||
name={icons[item.type]}
|
||||
/>
|
||||
<Icon
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: -6,
|
||||
left: -6
|
||||
}}
|
||||
allowFontScaling
|
||||
color={color}
|
||||
size={SIZE.xs}
|
||||
name="arrow-top-right-thick"
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
alignItems: "flex-start",
|
||||
flexGrow: 1,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
{isFocused ? (
|
||||
<Heading
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
color={primaryColors.heading}
|
||||
size={SIZE.md}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
) : (
|
||||
<Paragraph
|
||||
numberOfLines={1}
|
||||
color={primaryColors.paragraph}
|
||||
size={SIZE.md}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Pressable>
|
||||
</>
|
||||
);
|
||||
},
|
||||
(prev, next) => {
|
||||
if (!next.item) return false;
|
||||
if (prev.item.title !== next.item.title) return false;
|
||||
if (prev.item?.dateModified !== next.item?.dateModified) return false;
|
||||
if (prev.item?.id !== next.item?.id) return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
112
apps/mobile/app/components/side-menu/side-menu-header.tsx
Normal file
112
apps/mobile/app/components/side-menu/side-menu-header.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { IconButton, IconButtonProps } from "../ui/icon-button";
|
||||
import { SvgView } from "../ui/svg";
|
||||
import Heading from "../ui/typography/heading";
|
||||
|
||||
export const SideMenuHeader = (props: { rightButtons?: IconButtonProps[] }) => {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: DefaultAppStyles.GAP_SMALL,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: "black",
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 10
|
||||
}}
|
||||
>
|
||||
<SvgView
|
||||
width={28}
|
||||
height={28}
|
||||
src={`<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_24_33)">
|
||||
<path d="M1024 0H0V1024H1024V0Z" fill="black"/>
|
||||
<path d="M724.985 682.919C707.73 733.33 673.15 775.984 627.397 803.291C581.645 830.598 527.687 840.787 475.128 832.044C422.568 823.301 374.814 796.194 340.365 755.546C305.916 714.898 287.006 663.347 287 610.064V499.814L366.121 532.867V610.019C366.114 630.798 370.555 651.337 379.145 670.256C387.735 689.176 400.276 706.037 415.925 719.707C418.895 722.294 421.978 724.814 425.161 727.166C448.518 744.554 476.563 754.518 505.655 755.763C506.645 755.763 507.601 755.842 508.58 755.864C509.559 755.887 510.83 755.864 511.955 755.864C513.08 755.864 514.205 755.864 515.33 755.864C516.455 755.864 517.265 755.864 518.255 755.763C547.336 754.515 575.371 744.56 598.726 727.188C601.899 724.837 604.981 722.328 607.963 719.741C628.519 701.761 643.619 678.375 651.545 652.241L724.985 682.919Z" fill="white"/>
|
||||
<path d="M737 414V610.065C737 612.596 737 615.139 736.842 617.67L657.879 584.651V414C657.866 376.316 643.272 340.099 617.154 312.934C591.035 285.77 555.419 269.766 517.765 268.274C480.11 266.782 443.339 279.918 415.154 304.931C386.968 329.944 369.554 364.893 366.56 402.457C366.279 406.26 366.121 410.119 366.121 414V462.712L287 429.637V189H512C571.674 189 628.903 212.705 671.099 254.901C713.295 297.097 737 354.326 737 414Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_24_33">
|
||||
<rect width="1024" height="1024" rx="200" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
`}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Heading size={SIZE.lg}>Notesnook</Heading>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{props.rightButtons?.map((button, index) => (
|
||||
<IconButton
|
||||
key={index}
|
||||
{...button}
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
))}
|
||||
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
useThemeStore.getState().setColorScheme();
|
||||
}}
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
name={isDark ? "weather-night" : "weather-sunny"}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
105
apps/mobile/app/components/side-menu/side-menu-home.tsx
Normal file
105
apps/mobile/app/components/side-menu/side-menu-home.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { DraxProvider, DraxScrollView } from "react-native-drax";
|
||||
import { db } from "../../common/database";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import ReorderableList from "../list/reorderable-list";
|
||||
import { ColorSection } from "./color-section";
|
||||
import { MenuItem } from "./menu-item";
|
||||
import { PinnedSection } from "./pinned-section";
|
||||
import { SideMenuHeader } from "./side-menu-header";
|
||||
|
||||
export function SideMenuHome() {
|
||||
const { colors } = useThemeColors();
|
||||
const [isAppLoading, introCompleted] = useSettingStore((state) => [
|
||||
state.isAppLoading,
|
||||
state.settings.introCompleted
|
||||
]);
|
||||
const [order, hiddensItems] = useMenuStore((state) => [
|
||||
state.order["routes"],
|
||||
state.hiddenItems["routes"]
|
||||
]);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
paddingTop: DefaultAppStyles.GAP_SMALL,
|
||||
backgroundColor: colors.primary.background,
|
||||
gap: DefaultAppStyles.GAP,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<SideMenuHeader />
|
||||
|
||||
{!isAppLoading && introCompleted ? (
|
||||
<DraxProvider>
|
||||
<DraxScrollView nestedScrollEnabled={false}>
|
||||
<ReorderableList
|
||||
onListOrderChanged={(data) => {
|
||||
db.settings.setSideBarOrder("routes", data);
|
||||
}}
|
||||
onHiddenItemsChanged={(data) => {
|
||||
db.settings.setSideBarHiddenItems("routes", data);
|
||||
}}
|
||||
itemOrder={order}
|
||||
hiddenItems={hiddensItems}
|
||||
alwaysBounceVertical={false}
|
||||
data={MenuItemsList}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
gap: 2
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderDraggableItem={({ item, index }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={item.title}
|
||||
item={{
|
||||
...item,
|
||||
title:
|
||||
strings.routes[
|
||||
item.title as keyof typeof strings.routes
|
||||
]?.() || item.title
|
||||
}}
|
||||
testID={item.title}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<ColorSection />
|
||||
<PinnedSection />
|
||||
</DraxScrollView>
|
||||
</DraxProvider>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
450
apps/mobile/app/components/side-menu/side-menu-notebooks.tsx
Normal file
450
apps/mobile/app/components/side-menu/side-menu-notebooks.tsx
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Notebook } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import { FlatList, ListRenderItemInfo, View } from "react-native";
|
||||
import { UseBoundStore } from "zustand";
|
||||
import { db } from "../../common/database";
|
||||
import { useTotalNotes } from "../../hooks/use-db-item";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import NotebookScreen from "../../screens/notebook";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { TreeItem } from "../../stores/create-notebook-tree-stores";
|
||||
import { SelectionStore } from "../../stores/item-selection-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotebooks } from "../../stores/use-notebook-store";
|
||||
import { eOnNotebookUpdated } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Properties } from "../properties";
|
||||
import { AddNotebookSheet } from "../sheets/add-notebook";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { SideMenuHeader } from "./side-menu-header";
|
||||
import {
|
||||
useSideMenuNotebookExpandedStore,
|
||||
useSideMenuNotebookSelectionStore,
|
||||
useSideMenuNotebookTreeStore
|
||||
} from "./stores";
|
||||
|
||||
const NotebookItem = ({
|
||||
index,
|
||||
item,
|
||||
expanded,
|
||||
selected,
|
||||
onToggleExpanded,
|
||||
focused,
|
||||
selectionEnabled,
|
||||
selectionStore,
|
||||
onItemUpdate,
|
||||
onPress,
|
||||
onLongPress
|
||||
}: {
|
||||
index: number;
|
||||
item: TreeItem;
|
||||
expanded?: boolean;
|
||||
onToggleExpanded?: () => void;
|
||||
selected?: boolean;
|
||||
focused?: boolean;
|
||||
selectionEnabled?: boolean;
|
||||
selectionStore: UseBoundStore<SelectionStore>;
|
||||
onItemUpdate: (id?: string) => void;
|
||||
onPress?: () => void;
|
||||
onLongPress?: () => void;
|
||||
}) => {
|
||||
const notebook = item.notebook;
|
||||
const [nestedNotebooksSelected, setNestedNotebooksSelected] =
|
||||
React.useState(false);
|
||||
const isFocused = focused;
|
||||
const { totalNotes, getTotalNotes } = useTotalNotes("notebook");
|
||||
const getTotalNotesRef = React.useRef(getTotalNotes);
|
||||
getTotalNotesRef.current = getTotalNotes;
|
||||
const { colors } = useThemeColors("sheet");
|
||||
|
||||
useEffect(() => {
|
||||
getTotalNotesRef.current([item.notebook.id]);
|
||||
}, [item.notebook]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionEnabled) {
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: item.notebook.id
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
selector.ids().then((ids) => {
|
||||
setNestedNotebooksSelected(
|
||||
ids.length === 0
|
||||
? true
|
||||
: ids.every(
|
||||
(id) => selectionStore.getState().selection[id] === "selected"
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}, [selected, item.notebook.id, selectionEnabled, selectionStore]);
|
||||
|
||||
async function selectAll() {
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: item.notebook.id
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
const ids = await selector.ids();
|
||||
selectionStore.setState({
|
||||
selection: {
|
||||
...selectionStore.getState().selection,
|
||||
...ids.reduce((acc: any, id) => {
|
||||
acc[id] = "selected";
|
||||
return acc;
|
||||
}, {})
|
||||
}
|
||||
});
|
||||
setNestedNotebooksSelected(true);
|
||||
}
|
||||
|
||||
async function deselectAll() {
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: item.notebook.id
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
const ids = await selector.ids();
|
||||
useSideMenuNotebookSelectionStore.setState({
|
||||
selection: {
|
||||
...selectionStore.getState().selection,
|
||||
...ids.reduce((acc: any, id) => {
|
||||
acc[id] = "deselected";
|
||||
return acc;
|
||||
}, {})
|
||||
}
|
||||
});
|
||||
setNestedNotebooksSelected(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const unsub = selectionStore.subscribe((state) => {
|
||||
if (state.enabled) {
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: item.notebook.id
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
selector.ids().then((ids) => {
|
||||
if (!ids.length) return;
|
||||
setNestedNotebooksSelected(
|
||||
ids.length === 0
|
||||
? true
|
||||
: ids.every(
|
||||
(id) => selectionStore.getState().selection[id] === "selected"
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsub();
|
||||
};
|
||||
}, [item.notebook.id, selectionStore]);
|
||||
|
||||
useEffect(() => {
|
||||
const onNotebookUpdate = (id?: string) => {
|
||||
if (id && id !== notebook.id) return;
|
||||
onItemUpdate(id);
|
||||
};
|
||||
|
||||
eSubscribeEvent(eOnNotebookUpdated, onNotebookUpdate);
|
||||
return () => {
|
||||
eUnSubscribeEvent(eOnNotebookUpdated, onNotebookUpdate);
|
||||
};
|
||||
}, [notebook.id, onItemUpdate]);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft: item.depth > 0 && item.depth < 6 ? 15 : undefined,
|
||||
width: "100%",
|
||||
marginTop: 2
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
type={isFocused ? "selected" : "transparent"}
|
||||
onLongPress={() => {
|
||||
onLongPress?.();
|
||||
}}
|
||||
testID={`notebook-sheet-item-${item.depth}-${index}`}
|
||||
onPress={async () => {
|
||||
if (selectionEnabled) {
|
||||
if (selected && !nestedNotebooksSelected) {
|
||||
console.log("Select all...");
|
||||
return selectAll();
|
||||
}
|
||||
await deselectAll();
|
||||
selectionStore
|
||||
.getState()
|
||||
.markAs(item.notebook, selected ? "deselected" : "selected");
|
||||
if (selectionStore.getState().getSelectedItemIds().length === 0) {
|
||||
selectionStore.setState({
|
||||
enabled: false
|
||||
});
|
||||
}
|
||||
} else {
|
||||
onPress?.();
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
borderRadius: 5,
|
||||
paddingRight: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={SIZE.md}
|
||||
color={selected ? colors.selected.icon : colors.primary.icon}
|
||||
onPress={() => {
|
||||
onToggleExpanded?.();
|
||||
}}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
right={0}
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 5
|
||||
}}
|
||||
name={expanded ? "chevron-down" : "chevron-right"}
|
||||
/>
|
||||
|
||||
<Paragraph
|
||||
color={
|
||||
isFocused ? colors.selected.paragraph : colors.secondary.paragraph
|
||||
}
|
||||
size={SIZE.xs}
|
||||
>
|
||||
{notebook?.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
{selectionEnabled ? (
|
||||
<View
|
||||
style={{
|
||||
width: 22,
|
||||
height: 22,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name={
|
||||
selected
|
||||
? !nestedNotebooksSelected
|
||||
? "checkbox-intermediate"
|
||||
: "checkbox-outline"
|
||||
: "checkbox-blank-outline"
|
||||
}
|
||||
color={selected ? colors.selected.icon : colors.primary.icon}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{totalNotes(notebook?.id) ? (
|
||||
<Paragraph size={SIZE.xxs} color={colors.secondary.paragraph}>
|
||||
{totalNotes?.(notebook?.id)}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const SideMenuNotebooks = () => {
|
||||
const tree = useSideMenuNotebookTreeStore((state) => state.tree);
|
||||
const [notebooks, loading] = useNotebooks();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
|
||||
const loadRootNotebooks = React.useCallback(async () => {
|
||||
if (!notebooks) return;
|
||||
const _notebooks: Notebook[] = [];
|
||||
for (let i = 0; i < notebooks.placeholders.length; i++) {
|
||||
_notebooks[i] = (await notebooks?.item(i))?.item as Notebook;
|
||||
}
|
||||
useSideMenuNotebookTreeStore.getState().addNotebooks("root", _notebooks, 0);
|
||||
}, [notebooks]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!loading) {
|
||||
loadRootNotebooks();
|
||||
}
|
||||
})();
|
||||
}, [loadRootNotebooks, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
useSideMenuNotebookSelectionStore.setState({
|
||||
selectAll: async () => {
|
||||
const allNotebooks = await db.notebooks.all.items();
|
||||
const allSelected = allNotebooks.every((notebook) => {
|
||||
return (
|
||||
useSideMenuNotebookSelectionStore.getState().selection[
|
||||
notebook.id
|
||||
] === "selected"
|
||||
);
|
||||
});
|
||||
|
||||
if (allSelected) {
|
||||
useSideMenuNotebookSelectionStore.setState({
|
||||
selection: {}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
useSideMenuNotebookSelectionStore.setState({
|
||||
selection: allNotebooks.reduce((acc: any, item) => {
|
||||
acc[item.id] = "selected";
|
||||
return acc;
|
||||
}, {})
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const renderItem = React.useCallback((info: ListRenderItemInfo<TreeItem>) => {
|
||||
return <NotebookItemWrapper index={info.index} item={info.item} />;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingTop: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
data={tree}
|
||||
keyExtractor={(item) => item.notebook.id}
|
||||
windowSize={3}
|
||||
ListHeaderComponent={
|
||||
<SideMenuHeader
|
||||
rightButtons={[
|
||||
{
|
||||
name: "plus",
|
||||
onPress: () => {
|
||||
AddNotebookSheet.present();
|
||||
}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
stickyHeaderIndices={[0]}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const NotebookItemWrapper = ({
|
||||
item,
|
||||
index
|
||||
}: {
|
||||
item: TreeItem;
|
||||
index: number;
|
||||
}) => {
|
||||
const expanded = useSideMenuNotebookExpandedStore(
|
||||
(state) => state.expanded[item.notebook.id]
|
||||
);
|
||||
const selectionEnabled = useSideMenuNotebookSelectionStore(
|
||||
(state) => state.enabled
|
||||
);
|
||||
const selected = useSideMenuNotebookSelectionStore(
|
||||
(state) => state.selection[item.notebook.id] === "selected"
|
||||
);
|
||||
const focused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.notebook.id
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (expanded) {
|
||||
useSideMenuNotebookTreeStore
|
||||
.getState()
|
||||
.fetchAndAdd(item.notebook.id, item.depth + 1);
|
||||
} else {
|
||||
useSideMenuNotebookTreeStore.getState().removeChildren(item.notebook.id);
|
||||
}
|
||||
}, [expanded, item.depth, item.notebook]);
|
||||
|
||||
const onItemUpdate = React.useCallback(async () => {
|
||||
const notebook = await db.notebooks.notebook(item.notebook.id);
|
||||
if (notebook) {
|
||||
useSideMenuNotebookTreeStore
|
||||
.getState()
|
||||
.updateItem(item.notebook.id, notebook);
|
||||
} else {
|
||||
useSideMenuNotebookTreeStore.getState().removeItem(item.notebook.id);
|
||||
}
|
||||
}, [item.notebook.id]);
|
||||
|
||||
return (
|
||||
<NotebookItem
|
||||
item={item}
|
||||
index={index}
|
||||
expanded={expanded}
|
||||
onToggleExpanded={() => {
|
||||
useSideMenuNotebookExpandedStore
|
||||
.getState()
|
||||
.setExpanded(item.notebook.id);
|
||||
}}
|
||||
selected={selected}
|
||||
selectionEnabled={selectionEnabled}
|
||||
selectionStore={useSideMenuNotebookSelectionStore}
|
||||
onItemUpdate={onItemUpdate}
|
||||
focused={focused}
|
||||
onPress={() => {
|
||||
NotebookScreen.navigate(item.notebook, false);
|
||||
}}
|
||||
onLongPress={() => {
|
||||
Properties.present(item.notebook, false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
207
apps/mobile/app/components/side-menu/side-menu-tags.tsx
Normal file
207
apps/mobile/app/components/side-menu/side-menu-tags.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Tag, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import { View } from "react-native";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
import { db } from "../../common/database";
|
||||
import { useDBItem, useTotalNotes } from "../../hooks/use-db-item";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { TaggedNotes } from "../../screens/notes/tagged";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useTags } from "../../stores/use-tag-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Properties } from "../properties";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { SideMenuHeader } from "./side-menu-header";
|
||||
import { useSideMenuTagsSelectionStore } from "./stores";
|
||||
|
||||
const TagItem = (props: {
|
||||
tags: VirtualizedGrouping<Tag>;
|
||||
id: number | string;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(props.id, "tag", props.tags);
|
||||
const isSelected = useSideMenuTagsSelectionStore((state) =>
|
||||
item?.id ? state.selection[item.id] === "selected" : false
|
||||
);
|
||||
const enabled = useSideMenuTagsSelectionStore((state) => state.enabled);
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item?.id
|
||||
);
|
||||
const totalNotes = useTotalNotes("tag");
|
||||
const totalNotesRef = React.useRef(totalNotes);
|
||||
totalNotesRef.current = totalNotes;
|
||||
|
||||
useEffect(() => {
|
||||
if (item?.id) {
|
||||
totalNotesRef.current?.getTotalNotes([item?.id]);
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
return item ? (
|
||||
<Pressable
|
||||
type={isSelected || isFocused ? "selected" : "transparent"}
|
||||
onLongPress={() => {
|
||||
Properties.present(item);
|
||||
}}
|
||||
testID={`tag-item-${props.id}`}
|
||||
onPress={() => {
|
||||
if (enabled) {
|
||||
useSideMenuTagsSelectionStore
|
||||
.getState()
|
||||
.markAs(item, isSelected ? "deselected" : "selected");
|
||||
if (
|
||||
useSideMenuTagsSelectionStore.getState().getSelectedItemIds()
|
||||
.length === 0
|
||||
) {
|
||||
useSideMenuTagsSelectionStore.setState({
|
||||
enabled: false
|
||||
});
|
||||
}
|
||||
} else {
|
||||
TaggedNotes.navigate(item, false);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
borderRadius: 5,
|
||||
paddingRight: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
size={SIZE.md}
|
||||
color={isFocused ? colors.selected.icon : colors.secondary.icon}
|
||||
name="pound"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Paragraph
|
||||
color={
|
||||
isFocused ? colors.selected.paragraph : colors.secondary.paragraph
|
||||
}
|
||||
size={SIZE.xs}
|
||||
>
|
||||
{item?.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
{enabled ? (
|
||||
<View
|
||||
style={{
|
||||
width: 22,
|
||||
height: 22,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name={isSelected ? "checkbox-outline" : "checkbox-blank-outline"}
|
||||
color={isSelected ? colors.selected.icon : colors.primary.icon}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{item?.id && totalNotes.totalNotes?.(item?.id) ? (
|
||||
<Paragraph size={SIZE.xxs} color={colors.secondary.paragraph}>
|
||||
{totalNotes.totalNotes(item?.id)}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Pressable>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export const SideMenuTags = () => {
|
||||
const [tags] = useTags();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
|
||||
useEffect(() => {
|
||||
useSideMenuTagsSelectionStore.setState({
|
||||
selectAll: async () => {
|
||||
const tags = await db.tags.all.items();
|
||||
const allSelected = tags.every((tag) => {
|
||||
return (
|
||||
useSideMenuTagsSelectionStore.getState().selection[tag.id] ===
|
||||
"selected"
|
||||
);
|
||||
});
|
||||
|
||||
if (allSelected) {
|
||||
useSideMenuTagsSelectionStore.getState().setSelection({});
|
||||
} else {
|
||||
useSideMenuTagsSelectionStore.getState().setSelection(
|
||||
tags.reduce((acc: any, tag) => {
|
||||
acc[tag.id] = "selected";
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
(info: { index: number }) => {
|
||||
return <TagItem id={info.index} tags={tags!} />;
|
||||
},
|
||||
[tags]
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingTop: DefaultAppStyles.GAP_SMALL,
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
<SideMenuHeader />
|
||||
<FlashList
|
||||
data={tags?.placeholders}
|
||||
estimatedItemSize={32}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
32
apps/mobile/app/components/side-menu/stores.ts
Normal file
32
apps/mobile/app/components/side-menu/stores.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { createNotebookTreeStores } from "../../stores/create-notebook-tree-stores";
|
||||
import { createItemSelectionStore } from "../../stores/item-selection-store";
|
||||
|
||||
export const {
|
||||
useSideMenuNotebookExpandedStore,
|
||||
useSideMenuNotebookSelectionStore,
|
||||
useSideMenuNotebookTreeStore
|
||||
} = createNotebookTreeStores(true, false);
|
||||
|
||||
export const useSideMenuTagsSelectionStore = createItemSelectionStore(
|
||||
true,
|
||||
false
|
||||
);
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useNetInfo } from "@react-native-community/netinfo";
|
||||
import React from "react";
|
||||
@@ -30,13 +31,12 @@ import Sync from "../../services/sync";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SyncStatus, useUserStore } from "../../stores/use-user-store";
|
||||
import { eOpenLoginDialog } from "../../utils/events";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { TimeSince } from "../ui/time-since";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const UserStatus = () => {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
@@ -71,7 +71,7 @@ export const UserStatus = () => {
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
Navigation.navigate("Settings");
|
||||
tabBarRef.current.closeDrawer();
|
||||
fluidTabsRef.current.closeDrawer();
|
||||
}}
|
||||
type="plain"
|
||||
style={{
|
||||
@@ -206,7 +206,7 @@ export const UserStatus = () => {
|
||||
if (user) {
|
||||
Sync.run();
|
||||
} else {
|
||||
tabBarRef.current?.closeDrawer();
|
||||
fluidTabsRef.current?.closeDrawer();
|
||||
eSendEvent(eOpenLoginDialog);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -25,7 +25,7 @@ import { RGB_Linear_Shade, hexToRGBA } from "../../../utils/colors";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
import { Pressable, PressableProps } from "../pressable";
|
||||
interface IconButtonProps extends PressableProps {
|
||||
export interface IconButtonProps extends PressableProps {
|
||||
name: string;
|
||||
color?: ColorValue;
|
||||
size?: number;
|
||||
|
||||
@@ -273,13 +273,12 @@ export const Pressable = ({
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 0,
|
||||
borderColor:
|
||||
pressed && !disabled
|
||||
borderColor: pressed
|
||||
? customSelectedColor
|
||||
? getColorLinearShade(customSelectedColor, 0.3, false)
|
||||
: borderSelectedColor || borderColor
|
||||
: borderColor || "transparent",
|
||||
borderWidth: borderWidth
|
||||
borderWidth: noborder ? 0 : borderWidth
|
||||
},
|
||||
style,
|
||||
{
|
||||
|
||||
@@ -18,20 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* eslint-disable no-inner-declarations */
|
||||
import {
|
||||
Color,
|
||||
createInternalLink,
|
||||
Item,
|
||||
ItemReference,
|
||||
Note,
|
||||
Notebook,
|
||||
Reminder,
|
||||
Tag,
|
||||
TrashItem,
|
||||
VAULT_ERRORS
|
||||
VAULT_ERRORS,
|
||||
createInternalLink
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { DisplayedNotification } from "@notifee/react-native";
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { InteractionManager, Platform } from "react-native";
|
||||
import Share from "react-native-share";
|
||||
import { DatabaseLogger, db } from "../common/database";
|
||||
@@ -48,6 +45,11 @@ import { ReferencesList } from "../components/sheets/references";
|
||||
import { RelationsList } from "../components/sheets/relations-list/index";
|
||||
import ReminderSheet from "../components/sheets/reminder";
|
||||
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
|
||||
import {
|
||||
useSideMenuNotebookSelectionStore,
|
||||
useSideMenuTagsSelectionStore
|
||||
} from "../components/side-menu/stores";
|
||||
import { ButtonProps } from "../components/ui/button";
|
||||
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
|
||||
import {
|
||||
eSendEvent,
|
||||
@@ -69,13 +71,83 @@ import { deleteItems } from "../utils/functions";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { sleep } from "../utils/time";
|
||||
|
||||
export type ActionId =
|
||||
| "select"
|
||||
| "restore"
|
||||
| "delete"
|
||||
| "reorder"
|
||||
| "rename-tag"
|
||||
| "rename-color"
|
||||
| "pin"
|
||||
| "add-shortcut"
|
||||
| "rename-notebook"
|
||||
| "add-notebook"
|
||||
| "edit-notebook"
|
||||
| "default-notebook"
|
||||
| "move-notes"
|
||||
| "move-notebook"
|
||||
| "disable-reminder"
|
||||
| "edit-reminder"
|
||||
| "delete-reminder"
|
||||
| "delete"
|
||||
| "delete-trash"
|
||||
| "add-reminder"
|
||||
| "copy"
|
||||
| "share"
|
||||
| "read-only"
|
||||
| "local-only"
|
||||
| "duplicate"
|
||||
| "add-note"
|
||||
| "attachments"
|
||||
| "history"
|
||||
| "copy-link"
|
||||
| "reminders"
|
||||
| "lock-unlock"
|
||||
| "publish"
|
||||
| "export"
|
||||
| "notebooks"
|
||||
| "add-tag"
|
||||
| "references"
|
||||
| "pin-to-notifications"
|
||||
| "favorite"
|
||||
| "remove-from-notebook"
|
||||
| "trash";
|
||||
|
||||
export type Action = {
|
||||
id: ActionId;
|
||||
title: string;
|
||||
icon: string;
|
||||
onPress: () => void;
|
||||
isToggle?: boolean;
|
||||
checked?: boolean;
|
||||
pro?: boolean;
|
||||
hidden?: boolean;
|
||||
activeColor?: string;
|
||||
type?: ButtonProps["type"];
|
||||
};
|
||||
|
||||
function isNotePinnedInNotifications(item: Item) {
|
||||
const pinned = Notifications.getPinnedNotes();
|
||||
if (!pinned || pinned.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const index = pinned.findIndex((notif) => notif.id === item.id);
|
||||
if (index !== -1) {
|
||||
return pinned[index];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const useActions = ({
|
||||
close,
|
||||
item
|
||||
item,
|
||||
customActionHandlers
|
||||
}: {
|
||||
item: Note | Notebook | Reminder | Tag | Color | TrashItem;
|
||||
item: Item;
|
||||
close: () => void;
|
||||
customActionHandlers?: Record<ActionId, () => void>;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
const [isPinnedToMenu, setIsPinnedToMenu] = useState(
|
||||
db.shortcuts.exists(item.id)
|
||||
@@ -83,15 +155,14 @@ export const useActions = ({
|
||||
const processingId = useRef<"shareNote" | "copyContent">();
|
||||
const user = useUserStore((state) => state.user);
|
||||
const [notifPinned, setNotifPinned] = useState<DisplayedNotification>();
|
||||
|
||||
const [defaultNotebook, setDefaultNotebook] = useState(
|
||||
db.settings.getDefaultNotebook()
|
||||
);
|
||||
const [noteInCurrentNotebook, setNoteInCurrentNotebook] = useState(false);
|
||||
const [locked, setLocked] = useState(false);
|
||||
|
||||
const isPublished =
|
||||
item.type === "note" && db.monographs.isPublished(item.id);
|
||||
const [locked, setLocked] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (item.type === "note") {
|
||||
@@ -99,46 +170,41 @@ export const useActions = ({
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
const checkNotifPinned = useCallback(() => {
|
||||
const pinned = Notifications.getPinnedNotes();
|
||||
|
||||
if (!pinned || pinned.length === 0) {
|
||||
setNotifPinned(undefined);
|
||||
return;
|
||||
}
|
||||
const index = pinned.findIndex((notif) => notif.id === item.id);
|
||||
if (index !== -1) {
|
||||
setNotifPinned(pinned[index]);
|
||||
} else {
|
||||
setNotifPinned(undefined);
|
||||
}
|
||||
}, [item.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (item.type !== "note") return;
|
||||
checkNotifPinned();
|
||||
setNotifPinned(isNotePinnedInNotifications(item));
|
||||
setIsPinnedToMenu(db.shortcuts.exists(item.id));
|
||||
}, [checkNotifPinned, item]);
|
||||
}, [item]);
|
||||
|
||||
const onUpdate = useCallback(
|
||||
useEffect(() => {
|
||||
const { currentRoute, focusedRouteId } = useNavigationStore.getState();
|
||||
if (item.type !== "note" || currentRoute !== "Notebook" || !focusedRouteId)
|
||||
return;
|
||||
|
||||
!!db.relations
|
||||
.to(item, "notebook")
|
||||
.selector.find((v) => v("id", "==", focusedRouteId))
|
||||
.then((notebook) => {
|
||||
setNoteInCurrentNotebook(!!notebook);
|
||||
});
|
||||
}, [item]);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = eSubscribeEvent(
|
||||
Notifications.Events.onUpdate,
|
||||
async (type: string) => {
|
||||
if (type === "unpin") {
|
||||
await Notifications.get();
|
||||
checkNotifPinned();
|
||||
setNotifPinned(isNotePinnedInNotifications(item));
|
||||
}
|
||||
}
|
||||
},
|
||||
[checkNotifPinned]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = eSubscribeEvent(Notifications.Events.onUpdate, onUpdate);
|
||||
return () => {
|
||||
sub?.unsubscribe();
|
||||
};
|
||||
}, [item, onUpdate]);
|
||||
}, [item]);
|
||||
|
||||
async function restoreTrashItem() {
|
||||
if (!checkItemSynced()) return;
|
||||
close();
|
||||
if ((await db.trash.restore(item.id)) === false) return;
|
||||
Navigation.queueRoutesForUpdate();
|
||||
@@ -154,20 +220,16 @@ export const useActions = ({
|
||||
|
||||
async function pinItem() {
|
||||
if (!item.id) return;
|
||||
close();
|
||||
if (item.type === "note") {
|
||||
await db.notes.pin(!item?.pinned, item.id);
|
||||
} else if (item.type === "notebook") {
|
||||
await db.notebooks.pin(!item?.pinned, item.id);
|
||||
}
|
||||
|
||||
close();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
|
||||
const checkItemSynced = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
async function createMenuShortcut() {
|
||||
if (item.type !== "notebook" && item.type !== "tag") return;
|
||||
|
||||
@@ -299,7 +361,6 @@ export const useActions = ({
|
||||
|
||||
async function deleteTrashItem() {
|
||||
if (item.type !== "trash") return;
|
||||
if (!checkItemSynced()) return;
|
||||
close();
|
||||
await sleep(300);
|
||||
presentDialog({
|
||||
@@ -323,42 +384,14 @@ export const useActions = ({
|
||||
});
|
||||
}
|
||||
|
||||
const actions: {
|
||||
id: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
func: () => void;
|
||||
close?: boolean;
|
||||
check?: boolean;
|
||||
on?: boolean;
|
||||
pro?: boolean;
|
||||
switch?: boolean;
|
||||
hidden?: boolean;
|
||||
type?: string;
|
||||
color?: string;
|
||||
}[] = [
|
||||
// {
|
||||
// id: "ReferencedIn",
|
||||
// title: "References",
|
||||
// icon: "link",
|
||||
// func: async () => {
|
||||
// close();
|
||||
// RelationsList.present({
|
||||
// reference: item,
|
||||
// referenceType: "note",
|
||||
// title: "Referenced in",
|
||||
// relationType: "to",
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
];
|
||||
const actions: Action[] = [];
|
||||
|
||||
if (item.type === "tag") {
|
||||
actions.push({
|
||||
id: "rename-tag",
|
||||
title: strings.rename(),
|
||||
icon: "square-edit-outline",
|
||||
func: renameTag
|
||||
onPress: renameTag
|
||||
});
|
||||
}
|
||||
|
||||
@@ -367,14 +400,14 @@ export const useActions = ({
|
||||
id: "rename-color",
|
||||
title: strings.rename(),
|
||||
icon: "square-edit-outline",
|
||||
func: renameColor
|
||||
onPress: renameColor
|
||||
});
|
||||
|
||||
actions.push({
|
||||
id: "reorder",
|
||||
title: strings.reorder(),
|
||||
icon: "sort-ascending",
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: true
|
||||
});
|
||||
@@ -391,7 +424,7 @@ export const useActions = ({
|
||||
? strings.turnOffReminder()
|
||||
: strings.turnOnReminder(),
|
||||
icon: !item.disabled ? "bell-off-outline" : "bell",
|
||||
func: async () => {
|
||||
onPress: async () => {
|
||||
close();
|
||||
await db.reminders.add({
|
||||
...item,
|
||||
@@ -406,10 +439,9 @@ export const useActions = ({
|
||||
id: "edit-reminder",
|
||||
title: strings.editReminder(),
|
||||
icon: "pencil",
|
||||
func: async () => {
|
||||
onPress: async () => {
|
||||
ReminderSheet.present(item);
|
||||
},
|
||||
close: false
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -420,13 +452,13 @@ export const useActions = ({
|
||||
id: "restore",
|
||||
title: strings.restore(),
|
||||
icon: "delete-restore",
|
||||
func: restoreTrashItem
|
||||
onPress: restoreTrashItem
|
||||
},
|
||||
{
|
||||
id: "delete",
|
||||
title: strings.delete(),
|
||||
icon: "delete",
|
||||
func: deleteTrashItem
|
||||
onPress: deleteTrashItem
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -436,11 +468,27 @@ export const useActions = ({
|
||||
id: "add-shortcut",
|
||||
title: isPinnedToMenu ? strings.removeShortcut() : strings.addShortcut(),
|
||||
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
|
||||
func: createMenuShortcut,
|
||||
close: false,
|
||||
check: true,
|
||||
on: isPinnedToMenu,
|
||||
pro: true
|
||||
onPress: createMenuShortcut,
|
||||
isToggle: true,
|
||||
checked: isPinnedToMenu,
|
||||
activeColor: colors.error.paragraph
|
||||
});
|
||||
actions.push({
|
||||
id: "select",
|
||||
title: strings.select() + " " + strings.dataTypes[item.type](),
|
||||
icon: "checkbox-outline",
|
||||
onPress: () => {
|
||||
const store =
|
||||
item.type === "tag"
|
||||
? useSideMenuTagsSelectionStore
|
||||
: useSideMenuNotebookSelectionStore;
|
||||
store.setState({
|
||||
enabled: true,
|
||||
selection: {}
|
||||
});
|
||||
store.getState().markAs(item, "selected");
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -450,7 +498,7 @@ export const useActions = ({
|
||||
id: "add-notebook",
|
||||
title: strings.addNotebook(),
|
||||
icon: "plus",
|
||||
func: async () => {
|
||||
onPress: async () => {
|
||||
AddNotebookSheet.present(undefined, item);
|
||||
}
|
||||
},
|
||||
@@ -458,7 +506,7 @@ export const useActions = ({
|
||||
id: "edit-notebook",
|
||||
title: strings.editNotebook(),
|
||||
icon: "square-edit-outline",
|
||||
func: async () => {
|
||||
onPress: async () => {
|
||||
AddNotebookSheet.present(item);
|
||||
}
|
||||
},
|
||||
@@ -470,7 +518,7 @@ export const useActions = ({
|
||||
: strings.setAsDefault(),
|
||||
hidden: item.type !== "notebook",
|
||||
icon: "notebook",
|
||||
func: async () => {
|
||||
onPress: async () => {
|
||||
if (defaultNotebook === item.id) {
|
||||
await db.settings.setDefaultNotebook(undefined);
|
||||
setDefaultNotebook(undefined);
|
||||
@@ -483,14 +531,14 @@ export const useActions = ({
|
||||
}
|
||||
close();
|
||||
},
|
||||
on: defaultNotebook === item.id
|
||||
checked: defaultNotebook === item.id
|
||||
},
|
||||
{
|
||||
id: "move-notes",
|
||||
title: strings.moveNotes(),
|
||||
hidden: item.type !== "notebook",
|
||||
icon: "text",
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
MoveNotes.present(item);
|
||||
}
|
||||
},
|
||||
@@ -498,7 +546,7 @@ export const useActions = ({
|
||||
id: "move-notebook",
|
||||
title: strings.moveNotebookFix(),
|
||||
icon: "arrow-right-bold-box-outline",
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
MoveNotebookSheet.present([item]);
|
||||
}
|
||||
}
|
||||
@@ -510,10 +558,9 @@ export const useActions = ({
|
||||
id: "pin",
|
||||
title: item.pinned ? strings.unpin() : strings.pin(),
|
||||
icon: item.pinned ? "pin-off-outline" : "pin-outline",
|
||||
func: pinItem,
|
||||
close: false,
|
||||
check: true,
|
||||
on: item.pinned,
|
||||
onPress: pinItem,
|
||||
isToggle: true,
|
||||
checked: item.pinned,
|
||||
pro: true
|
||||
});
|
||||
}
|
||||
@@ -535,7 +582,7 @@ export const useActions = ({
|
||||
}
|
||||
|
||||
async function toggleLocalOnly() {
|
||||
if (!checkItemSynced() || !user) return;
|
||||
if (!user) return;
|
||||
await db.notes.localOnly(!(item as Note).localOnly, item?.id);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
@@ -556,7 +603,6 @@ export const useActions = ({
|
||||
};
|
||||
|
||||
const duplicateNote = async () => {
|
||||
if (!checkItemSynced()) return;
|
||||
await db.notes.duplicate(item.id);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
@@ -577,19 +623,16 @@ export const useActions = ({
|
||||
|
||||
async function addToFavorites() {
|
||||
if (!item.id || item.type !== "note") return;
|
||||
close();
|
||||
await db.notes.favorite(!item.favorite, item.id);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
}
|
||||
|
||||
async function pinToNotifications() {
|
||||
if (!checkItemSynced()) return;
|
||||
if (Platform.OS === "ios") return;
|
||||
|
||||
if (notifPinned) {
|
||||
Notifications.remove(item.id);
|
||||
await Notifications.get();
|
||||
checkNotifPinned();
|
||||
setNotifPinned(isNotePinnedInNotifications(item));
|
||||
return;
|
||||
}
|
||||
if (locked) {
|
||||
@@ -603,7 +646,7 @@ export const useActions = ({
|
||||
const text = await convertNoteToText(item as Note, true);
|
||||
const html = (text || "").replace(/\n/g, "<br />");
|
||||
await Notifications.displayNotification({
|
||||
title: item.title,
|
||||
title: (item as Note).title,
|
||||
message: (item as Note).headline || text || "",
|
||||
subtitle: "",
|
||||
bigText: html,
|
||||
@@ -612,11 +655,10 @@ export const useActions = ({
|
||||
id: item.id
|
||||
});
|
||||
await Notifications.get();
|
||||
checkNotifPinned();
|
||||
setNotifPinned(isNotePinnedInNotifications(item));
|
||||
}
|
||||
|
||||
async function publishNote() {
|
||||
if (!checkItemSynced()) return;
|
||||
if (!user) {
|
||||
ToastManager.show({
|
||||
heading: strings.loginRequired(),
|
||||
@@ -657,7 +699,6 @@ export const useActions = ({
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!checkItemSynced()) return;
|
||||
if (locked) {
|
||||
close();
|
||||
await sleep(300);
|
||||
@@ -687,8 +728,6 @@ export const useActions = ({
|
||||
|
||||
async function addToVault() {
|
||||
if (item.type !== "note") return;
|
||||
|
||||
if (!checkItemSynced()) return;
|
||||
if (locked) {
|
||||
close();
|
||||
await sleep(300);
|
||||
@@ -709,10 +748,10 @@ export const useActions = ({
|
||||
Navigation.queueRoutesForUpdate();
|
||||
eSendEvent(eUpdateNoteInEditor, item, true);
|
||||
}
|
||||
} catch (e: any) {
|
||||
} catch (e: unknown) {
|
||||
close();
|
||||
await sleep(300);
|
||||
switch (e.message) {
|
||||
switch ((e as Error).message) {
|
||||
case VAULT_ERRORS.noVault:
|
||||
openVault({
|
||||
item: item,
|
||||
@@ -775,37 +814,36 @@ export const useActions = ({
|
||||
id: "favorite",
|
||||
title: !item.favorite ? strings.favorite() : strings.unfavorite(),
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
check: true,
|
||||
on: item.favorite,
|
||||
onPress: addToFavorites,
|
||||
isToggle: true,
|
||||
checked: item.favorite,
|
||||
pro: true,
|
||||
color: "orange"
|
||||
activeColor: "orange"
|
||||
},
|
||||
{
|
||||
id: "remove-from-notebook",
|
||||
title: strings.removeFromNotebook(),
|
||||
hidden: noteInCurrentNotebook,
|
||||
icon: "minus-circle-outline",
|
||||
func: removeNoteFromNotebook
|
||||
onPress: removeNoteFromNotebook
|
||||
},
|
||||
{
|
||||
id: "attachments",
|
||||
title: strings.attachments(),
|
||||
icon: "attachment",
|
||||
func: showAttachments
|
||||
onPress: showAttachments
|
||||
},
|
||||
{
|
||||
id: "history",
|
||||
title: strings.history(),
|
||||
icon: "history",
|
||||
func: openHistory
|
||||
onPress: openHistory
|
||||
},
|
||||
{
|
||||
id: "copy-link",
|
||||
title: strings.copyLink(),
|
||||
icon: "link",
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
Clipboard.setString(createInternalLink("note", item.id));
|
||||
ToastManager.show({
|
||||
heading: strings.linkCopied(),
|
||||
@@ -819,7 +857,7 @@ export const useActions = ({
|
||||
id: "reminders",
|
||||
title: strings.dataTypesPluralCamelCase.reminder(),
|
||||
icon: "clock-outline",
|
||||
func: async () => {
|
||||
onPress: async () => {
|
||||
RelationsList.present({
|
||||
reference: item,
|
||||
referenceType: "reminder",
|
||||
@@ -833,121 +871,108 @@ export const useActions = ({
|
||||
icon: "plus"
|
||||
}
|
||||
});
|
||||
},
|
||||
close: false
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
id: "copy",
|
||||
title: strings.copy(),
|
||||
icon: "content-copy",
|
||||
func: copyContent
|
||||
onPress: copyContent
|
||||
},
|
||||
{
|
||||
id: "share",
|
||||
title: strings.share(),
|
||||
icon: "share-variant",
|
||||
func: shareNote
|
||||
onPress: shareNote
|
||||
},
|
||||
{
|
||||
id: "read-only",
|
||||
title: strings.readOnly(),
|
||||
icon: "pencil-lock",
|
||||
func: toggleReadyOnlyMode,
|
||||
on: item.readonly
|
||||
onPress: toggleReadyOnlyMode,
|
||||
checked: item.readonly
|
||||
},
|
||||
{
|
||||
id: "local-only",
|
||||
title: strings.syncOff(),
|
||||
icon: "sync-off",
|
||||
func: toggleLocalOnly,
|
||||
on: item.localOnly
|
||||
onPress: toggleLocalOnly,
|
||||
checked: item.localOnly
|
||||
},
|
||||
{
|
||||
id: "duplicate",
|
||||
title: strings.duplicate(),
|
||||
icon: "content-duplicate",
|
||||
func: duplicateNote
|
||||
onPress: duplicateNote
|
||||
},
|
||||
|
||||
{
|
||||
id: "add-reminder",
|
||||
title: strings.remindMe(),
|
||||
icon: "clock-plus-outline",
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
ReminderSheet.present(undefined, { id: item.id, type: "note" });
|
||||
},
|
||||
close: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "lock-unlock",
|
||||
title: locked ? strings.unlock() : strings.lock(),
|
||||
icon: locked ? "lock-open-outline" : "key-outline",
|
||||
func: addToVault,
|
||||
on: locked
|
||||
onPress: addToVault,
|
||||
checked: locked
|
||||
},
|
||||
{
|
||||
id: "publish",
|
||||
title: isPublished ? strings.published() : strings.publish(),
|
||||
icon: "cloud-upload-outline",
|
||||
on: isPublished,
|
||||
func: publishNote
|
||||
checked: isPublished,
|
||||
onPress: publishNote
|
||||
},
|
||||
|
||||
{
|
||||
id: "export",
|
||||
title: strings.export(),
|
||||
icon: "export",
|
||||
func: exportNote
|
||||
},
|
||||
|
||||
{
|
||||
id: "pin-to-notifications",
|
||||
title: notifPinned
|
||||
? strings.unpinFromNotifications()
|
||||
: strings.pinToNotifications(),
|
||||
icon: "message-badge-outline",
|
||||
on: !!notifPinned,
|
||||
func: pinToNotifications
|
||||
onPress: exportNote
|
||||
},
|
||||
|
||||
{
|
||||
id: "notebooks",
|
||||
title: strings.linkNotebooks(),
|
||||
icon: "book-outline",
|
||||
func: addTo
|
||||
onPress: addTo
|
||||
},
|
||||
{
|
||||
id: "add-tag",
|
||||
title: strings.addTags(),
|
||||
icon: "pound",
|
||||
func: addTo
|
||||
onPress: addTo
|
||||
},
|
||||
{
|
||||
id: "references",
|
||||
title: strings.references(),
|
||||
icon: "vector-link",
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
ReferencesList.present({
|
||||
reference: item as ItemReference
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { currentRoute, focusedRouteId } = useNavigationStore.getState();
|
||||
if (item.type !== "note" || currentRoute !== "Notebook" || !focusedRouteId)
|
||||
return;
|
||||
|
||||
!!db.relations
|
||||
.to(item, "notebook")
|
||||
.selector.find((v) => v("id", "==", focusedRouteId))
|
||||
.then((notebook) => {
|
||||
setNoteInCurrentNotebook(!!notebook);
|
||||
if (Platform.OS === "android") {
|
||||
actions.push({
|
||||
id: "pin-to-notifications",
|
||||
title: notifPinned
|
||||
? strings.unpinFromNotifications()
|
||||
: strings.pinToNotifications(),
|
||||
icon: "message-badge-outline",
|
||||
checked: !!notifPinned,
|
||||
onPress: pinToNotifications
|
||||
});
|
||||
}, [item]);
|
||||
}
|
||||
}
|
||||
|
||||
actions.push({
|
||||
id: "trash",
|
||||
@@ -960,7 +985,7 @@ export const useActions = ({
|
||||
: strings.moveToTrash(),
|
||||
icon: "delete-outline",
|
||||
type: "error",
|
||||
func: deleteItem
|
||||
onPress: deleteItem
|
||||
});
|
||||
|
||||
return actions;
|
||||
|
||||
@@ -59,8 +59,7 @@ import {
|
||||
import {
|
||||
clearAppState,
|
||||
editorController,
|
||||
editorState,
|
||||
setAppState
|
||||
editorState
|
||||
} from "../screens/editor/tiptap/utils";
|
||||
import { useDragState } from "../screens/settings/editor/state";
|
||||
import BackupService from "../services/backup";
|
||||
@@ -103,7 +102,7 @@ import {
|
||||
refreshNotesPage
|
||||
} from "../utils/events";
|
||||
import { getGithubVersion } from "../utils/github-version";
|
||||
import { tabBarRef } from "../utils/global-refs";
|
||||
import { fluidTabsRef } from "../utils/global-refs";
|
||||
import { NotesnookModule } from "../utils/notesnook-module";
|
||||
import { sleep } from "../utils/time";
|
||||
|
||||
@@ -168,7 +167,7 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
|
||||
clearAppState();
|
||||
editorState().movedAway = false;
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
fluidTabsRef.current?.goToPage(1, false);
|
||||
return;
|
||||
} else if (url.startsWith("https://notesnook.com/open_note")) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
@@ -178,7 +177,7 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
fluidTabsRef.current?.goToPage(1, false);
|
||||
}
|
||||
}
|
||||
} else if (url.startsWith("https://notesnook.com/open_reminder")) {
|
||||
|
||||
@@ -19,13 +19,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import {
|
||||
Attachment,
|
||||
Color,
|
||||
HistorySession,
|
||||
Note,
|
||||
Notebook,
|
||||
Reminder,
|
||||
Shortcut,
|
||||
Tag,
|
||||
VirtualizedGrouping,
|
||||
HistorySession
|
||||
VirtualizedGrouping
|
||||
} from "@notesnook/core";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
@@ -34,8 +34,8 @@ import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../services/event-manager";
|
||||
import { eDBItemUpdate } from "../utils/events";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { eDBItemUpdate } from "../utils/events";
|
||||
|
||||
type ItemTypeKey = {
|
||||
note: Note;
|
||||
@@ -55,7 +55,8 @@ function isValidIdOrIndex(idOrIndex?: string | number) {
|
||||
export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
idOrIndex?: string | number,
|
||||
type?: T,
|
||||
items?: VirtualizedGrouping<ItemTypeKey[T]>
|
||||
items?: VirtualizedGrouping<ItemTypeKey[T]>,
|
||||
onItemUpdated?: (item?: ItemTypeKey[T]) => void
|
||||
): [ItemTypeKey[T] | undefined, () => void] => {
|
||||
const [item, setItem] = useState<ItemTypeKey[T]>();
|
||||
const itemIdRef = useRef<string>();
|
||||
@@ -67,15 +68,15 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const onUpdateItem = (itemId?: string) => {
|
||||
const onUpdateItem = async (itemId?: string) => {
|
||||
if (typeof itemId === "string" && itemId !== itemIdRef.current) return;
|
||||
if (!isValidIdOrIndex(idOrIndex)) return;
|
||||
|
||||
if (items && typeof idOrIndex === "number") {
|
||||
items.item(idOrIndex).then((item) => {
|
||||
setItem(item.item);
|
||||
itemIdRef.current = item.item?.id;
|
||||
});
|
||||
const item = (await items.item(idOrIndex))?.item;
|
||||
setItem(item);
|
||||
itemIdRef.current = item?.id;
|
||||
onItemUpdated?.(item);
|
||||
} else {
|
||||
if (!(db as any)[type + "s"][type]) {
|
||||
console.warn(
|
||||
@@ -83,12 +84,12 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
`db.${type}s.${type}(id: string)`
|
||||
);
|
||||
} else {
|
||||
(db as any)[type + "s"]
|
||||
?.[type]?.(idOrIndex as string)
|
||||
.then((item: ItemTypeKey[T]) => {
|
||||
const item = await (db as any)[type + "s"]?.[type]?.(
|
||||
idOrIndex as string
|
||||
);
|
||||
setItem(item);
|
||||
itemIdRef.current = item.id;
|
||||
});
|
||||
onItemUpdated?.(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -110,7 +111,7 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
return () => {
|
||||
eUnSubscribeEvent(eDBItemUpdate, onUpdateItem);
|
||||
};
|
||||
}, [idOrIndex, type, items]);
|
||||
}, [idOrIndex, type, items, onItemUpdated]);
|
||||
|
||||
return [
|
||||
isValidIdOrIndex(idOrIndex) ? (item as ItemTypeKey[T]) : undefined,
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
|
||||
import { eGroupOptionsUpdated, eOnNotebookUpdated } from "../utils/events";
|
||||
@@ -26,17 +26,20 @@ import { useDBItem, useTotalNotes } from "./use-db-item";
|
||||
export const useNotebook = (
|
||||
id?: string | number,
|
||||
items?: VirtualizedGrouping<Notebook>,
|
||||
nestedNotebooks?: boolean
|
||||
nestedNotebooks?: boolean,
|
||||
countNotes?: boolean
|
||||
) => {
|
||||
const [item, refresh] = useDBItem(id, "notebook", items);
|
||||
const groupOptions = db.settings.getGroupOptions("notebooks");
|
||||
const [notebooks, setNotebooks] = useState<VirtualizedGrouping<Notebook>>();
|
||||
const { totalNotes: nestedNotebookNotesCount, getTotalNotes } =
|
||||
useTotalNotes("notebook");
|
||||
const getTotalNotesRef = useRef(getTotalNotes);
|
||||
getTotalNotesRef.current = getTotalNotes;
|
||||
const onItemUpdated = React.useCallback(
|
||||
(item?: Notebook) => {
|
||||
if (!item) return;
|
||||
|
||||
const onRequestUpdate = React.useCallback(() => {
|
||||
if (!item?.id) return;
|
||||
|
||||
if (nestedNotebooks) {
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
@@ -44,9 +47,8 @@ export const useNotebook = (
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
|
||||
selector.ids().then((notebookIds) => {
|
||||
getTotalNotes(notebookIds);
|
||||
getTotalNotesRef.current(notebookIds);
|
||||
});
|
||||
|
||||
selector
|
||||
@@ -54,28 +56,31 @@ export const useNotebook = (
|
||||
.then((notebooks) => {
|
||||
setNotebooks(notebooks);
|
||||
});
|
||||
}, [getTotalNotes, item?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (nestedNotebooks) {
|
||||
onRequestUpdate();
|
||||
}
|
||||
}, [item?.id, onRequestUpdate, nestedNotebooks]);
|
||||
|
||||
if (countNotes) {
|
||||
getTotalNotesRef.current([item?.id]);
|
||||
}
|
||||
},
|
||||
[countNotes, nestedNotebooks]
|
||||
);
|
||||
|
||||
const [item, refresh] = useDBItem(id, "notebook", items, onItemUpdated);
|
||||
|
||||
const itemRef = useRef(item);
|
||||
itemRef.current = item;
|
||||
const refreshRef = useRef(refresh);
|
||||
refreshRef.current = refresh;
|
||||
|
||||
useEffect(() => {
|
||||
const onNotebookUpdate = (id?: string) => {
|
||||
if (typeof id === "string" && id !== id) return;
|
||||
setImmediate(() => {
|
||||
if (nestedNotebooks) {
|
||||
onRequestUpdate();
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
refreshRef.current();
|
||||
};
|
||||
|
||||
const onUpdate = (type: string) => {
|
||||
if (type !== "notebooks") return;
|
||||
onRequestUpdate();
|
||||
refreshRef.current();
|
||||
};
|
||||
|
||||
eSubscribeEvent(eGroupOptionsUpdated, onUpdate);
|
||||
@@ -84,13 +89,14 @@ export const useNotebook = (
|
||||
eUnSubscribeEvent(eGroupOptionsUpdated, onUpdate);
|
||||
eUnSubscribeEvent(eOnNotebookUpdated, onNotebookUpdate);
|
||||
};
|
||||
}, [onRequestUpdate, item?.id, refresh, nestedNotebooks]);
|
||||
}, [nestedNotebooks]);
|
||||
|
||||
return {
|
||||
notebook: item,
|
||||
nestedNotebookNotesCount,
|
||||
nestedNotebooks: item ? notebooks : undefined,
|
||||
onUpdate: onRequestUpdate,
|
||||
groupOptions
|
||||
onUpdate: () => refresh(),
|
||||
groupOptions,
|
||||
notesCount: !item ? 0 : nestedNotebookNotesCount(item?.id)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ export const useShortcutManager = ({
|
||||
shortcuts = defaultShortcuts
|
||||
}: {
|
||||
onShortcutPressed: (shortcut: ShortcutItem | null) => void;
|
||||
shortcuts: ShortcutItem[];
|
||||
shortcuts?: ShortcutItem[];
|
||||
}) => {
|
||||
const initialShortcutRecieved = useRef(false);
|
||||
|
||||
|
||||
559
apps/mobile/app/navigation/fluid-panels-view.tsx
Normal file
559
apps/mobile/app/navigation/fluid-panels-view.tsx
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
|
||||
import {
|
||||
activateKeepAwake,
|
||||
deactivateKeepAwake
|
||||
} from "@sayem314/react-native-keep-awake";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { Dimensions, LayoutChangeEvent, Platform, View } from "react-native";
|
||||
import {
|
||||
addOrientationListener,
|
||||
addSpecificOrientationListener,
|
||||
getInitialOrientation,
|
||||
getSpecificOrientation,
|
||||
removeOrientationListener,
|
||||
removeSpecificOrientationListener
|
||||
} from "react-native-orientation";
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming
|
||||
} from "react-native-reanimated";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { notesnook } from "../../e2e/test.ids";
|
||||
import { db } from "../common/database";
|
||||
import { FluidPanels } from "../components/fluid-panels";
|
||||
import { SideMenu } from "../components/side-menu";
|
||||
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
|
||||
import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets";
|
||||
import { useShortcutManager } from "../hooks/use-shortcut-manager";
|
||||
import { hideAllTooltips } from "../hooks/use-tooltip";
|
||||
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
|
||||
import {
|
||||
clearAppState,
|
||||
editorController,
|
||||
editorState,
|
||||
getAppState
|
||||
} from "../screens/editor/tiptap/utils";
|
||||
import { EditorWrapper } from "../screens/editor/wrapper";
|
||||
import { DDS } from "../services/device-detection";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../services/event-manager";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import {
|
||||
eCloseFullscreenEditor,
|
||||
eOnEnterEditor,
|
||||
eOnExitEditor,
|
||||
eOnLoadNote,
|
||||
eOpenFullscreenEditor,
|
||||
eUnlockNote
|
||||
} from "../utils/events";
|
||||
import { editorRef, fluidTabsRef } from "../utils/global-refs";
|
||||
import { AppNavigationStack } from "./navigation-stack";
|
||||
|
||||
const MOBILE_SIDEBAR_SIZE = 0.85;
|
||||
|
||||
const valueLimiter = (value: number, min: number, max: number) => {
|
||||
return value < min ? min : value > max ? max : value;
|
||||
};
|
||||
|
||||
export const FluidPanelsView = React.memo(
|
||||
() => {
|
||||
const { colors } = useThemeColors();
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const setFullscreen = useSettingStore((state) => state.setFullscreen);
|
||||
const fullscreen = useSettingStore((state) => state.fullscreen);
|
||||
const setDeviceModeState = useSettingStore((state) => state.setDeviceMode);
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const setDimensions = useSettingStore((state) => state.setDimensions);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const animatedOpacity = useSharedValue(0);
|
||||
const animatedTranslateY = useSharedValue(-9999);
|
||||
const overlayRef = useRef<Animated.View>(null);
|
||||
const [orientation, setOrientation] = useState(getInitialOrientation());
|
||||
|
||||
useShortcutManager({
|
||||
onShortcutPressed: async (item) => {
|
||||
if (!item && getAppState()) {
|
||||
editorState().movedAway = false;
|
||||
fluidTabsRef.current?.goToPage(1, false);
|
||||
return;
|
||||
}
|
||||
if (item?.type === "notesnook.action.newnote") {
|
||||
clearAppState();
|
||||
if (!fluidTabsRef.current) {
|
||||
setTimeout(() => {
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
editorState().movedAway = false;
|
||||
fluidTabsRef.current?.goToPage(1, false);
|
||||
}, 3000);
|
||||
return;
|
||||
}
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
editorState().movedAway = false;
|
||||
fluidTabsRef.current?.goToPage(1, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const onOrientationChange = (o: string, o2: string) => {
|
||||
setOrientation(o || o2);
|
||||
};
|
||||
|
||||
if (Platform.OS === "ios") {
|
||||
addSpecificOrientationListener(onOrientationChange);
|
||||
getSpecificOrientation && getSpecificOrientation(onOrientationChange);
|
||||
} else {
|
||||
addOrientationListener(onOrientationChange);
|
||||
}
|
||||
|
||||
return () => {
|
||||
removeSpecificOrientationListener(onOrientationChange);
|
||||
removeOrientationListener(onOrientationChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const showFullScreenEditor = useCallback(() => {
|
||||
setFullscreen(true);
|
||||
if (deviceMode === "smallTablet") {
|
||||
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]);
|
||||
|
||||
const closeFullScreenEditor = useCallback(
|
||||
(current: string) => {
|
||||
const _deviceMode = current || deviceMode;
|
||||
if (_deviceMode === "smallTablet") {
|
||||
fluidTabsRef.current?.closeDrawer(false);
|
||||
}
|
||||
setFullscreen(false);
|
||||
editorController.current?.commands.updateSettings({
|
||||
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") {
|
||||
fluidTabsRef.current?.goToIndex(1, false);
|
||||
}
|
||||
if (_deviceMode === "mobile") {
|
||||
fluidTabsRef.current?.goToIndex(2, false);
|
||||
}
|
||||
},
|
||||
[deviceMode, dimensions.width, setFullscreen]
|
||||
);
|
||||
|
||||
const toggleView = useCallback(
|
||||
(show: boolean) => {
|
||||
animatedTranslateY.value = show ? 0 : -9999;
|
||||
},
|
||||
[animatedTranslateY]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fluidTabsRef.current?.isDrawerOpen()) {
|
||||
toggleView(false);
|
||||
}
|
||||
eSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor);
|
||||
eSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor);
|
||||
|
||||
return () => {
|
||||
eUnSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor);
|
||||
eUnSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor);
|
||||
};
|
||||
}, [
|
||||
deviceMode,
|
||||
dimensions,
|
||||
colors,
|
||||
showFullScreenEditor,
|
||||
closeFullScreenEditor,
|
||||
toggleView
|
||||
]);
|
||||
|
||||
const setDeviceMode = React.useCallback(
|
||||
(current: string | null, size: { width: number; height: number }) => {
|
||||
setDeviceModeState(current);
|
||||
|
||||
const needsLayout = current !== deviceMode;
|
||||
|
||||
if (fullscreen && current !== "mobile") {
|
||||
// Runs after size is set via state.
|
||||
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();
|
||||
switch (current) {
|
||||
case "tablet":
|
||||
fluidTabsRef.current?.goToIndex(0, false);
|
||||
break;
|
||||
case "smallTablet":
|
||||
if (!fullscreen) {
|
||||
fluidTabsRef.current?.closeDrawer(false);
|
||||
} else {
|
||||
fluidTabsRef.current?.openDrawer(false);
|
||||
}
|
||||
break;
|
||||
case "mobile":
|
||||
if (
|
||||
state &&
|
||||
editorState().movedAway === false &&
|
||||
useTabStore.getState().getCurrentNoteId()
|
||||
) {
|
||||
fluidTabsRef.current?.goToIndex(2, false);
|
||||
} else {
|
||||
fluidTabsRef.current?.goToIndex(1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
[deviceMode, fullscreen, setDeviceModeState]
|
||||
);
|
||||
|
||||
const checkDeviceType = React.useCallback(
|
||||
(size: { width: number; height: number }) => {
|
||||
setDimensions({
|
||||
width: size.width,
|
||||
height: size.height
|
||||
});
|
||||
DDS.setSize(size, orientation);
|
||||
const nextDeviceMode = DDS.isLargeTablet()
|
||||
? "tablet"
|
||||
: DDS.isSmallTab
|
||||
? "smallTablet"
|
||||
: "mobile";
|
||||
|
||||
setDeviceMode(nextDeviceMode, size);
|
||||
},
|
||||
[orientation, setDeviceMode, setDimensions]
|
||||
);
|
||||
|
||||
const _onLayout = React.useCallback(
|
||||
(event: LayoutChangeEvent) => {
|
||||
const size = event?.nativeEvent?.layout;
|
||||
if (!size || (size.width === dimensions.width && deviceMode !== null)) {
|
||||
DDS.setSize(size, orientation);
|
||||
setDeviceMode(deviceMode, size);
|
||||
checkDeviceType(size);
|
||||
return;
|
||||
}
|
||||
|
||||
checkDeviceType(size);
|
||||
},
|
||||
[
|
||||
checkDeviceType,
|
||||
deviceMode,
|
||||
dimensions.width,
|
||||
orientation,
|
||||
setDeviceMode
|
||||
]
|
||||
);
|
||||
|
||||
if (!deviceMode) {
|
||||
const size = Dimensions.get("window");
|
||||
checkDeviceType(size);
|
||||
}
|
||||
|
||||
const PANE_OFFSET = useMemo(
|
||||
() => ({
|
||||
mobile: {
|
||||
sidebar: dimensions.width * MOBILE_SIDEBAR_SIZE,
|
||||
list: dimensions.width + dimensions.width * MOBILE_SIDEBAR_SIZE,
|
||||
editor: dimensions.width * 2 + dimensions.width * MOBILE_SIDEBAR_SIZE
|
||||
},
|
||||
smallTablet: {
|
||||
sidebar: fullscreen
|
||||
? 0
|
||||
: valueLimiter(dimensions.width * 0.3, 300, 350),
|
||||
list: fullscreen
|
||||
? 0
|
||||
: dimensions.width + valueLimiter(dimensions.width * 0.3, 300, 350),
|
||||
editor: fullscreen
|
||||
? 0
|
||||
: dimensions.width + valueLimiter(dimensions.width * 0.3, 300, 350)
|
||||
},
|
||||
tablet: {
|
||||
sidebar: 0,
|
||||
list: 0,
|
||||
editor: 0
|
||||
}
|
||||
}),
|
||||
[dimensions.width, fullscreen]
|
||||
);
|
||||
|
||||
const PANE_WIDTHS = useMemo(
|
||||
() => ({
|
||||
mobile: {
|
||||
sidebar: dimensions.width * MOBILE_SIDEBAR_SIZE,
|
||||
list: dimensions.width,
|
||||
editor: dimensions.width
|
||||
},
|
||||
smallTablet: {
|
||||
sidebar: valueLimiter(dimensions.width * 0.3, 300, 350),
|
||||
list: valueLimiter(dimensions.width * 0.4, 300, 450),
|
||||
editor:
|
||||
dimensions.width - valueLimiter(dimensions.width * 0.4, 300, 450)
|
||||
},
|
||||
tablet: {
|
||||
sidebar: dimensions.width * 0.22,
|
||||
list: dimensions.width * 0.3,
|
||||
editor: dimensions.width * 0.48
|
||||
}
|
||||
}),
|
||||
[dimensions.width]
|
||||
);
|
||||
|
||||
const onScroll = React.useCallback(
|
||||
(scrollOffset: number) => {
|
||||
if (!deviceMode) return;
|
||||
hideAllTooltips();
|
||||
if (
|
||||
scrollOffset >
|
||||
PANE_OFFSET[deviceMode as keyof typeof PANE_OFFSET].sidebar - 10
|
||||
) {
|
||||
animatedOpacity.value = 0;
|
||||
toggleView(false);
|
||||
} else {
|
||||
const o = scrollOffset / 300;
|
||||
const opacity = o < 0 ? 1 : 1 - o;
|
||||
animatedOpacity.value = opacity;
|
||||
toggleView(opacity < 0.1 ? false : true);
|
||||
}
|
||||
},
|
||||
[PANE_OFFSET, animatedOpacity, deviceMode, toggleView]
|
||||
);
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
opacity: animatedOpacity.value,
|
||||
transform: [
|
||||
{
|
||||
translateY: animatedTranslateY.value
|
||||
}
|
||||
]
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
onLayout={_onLayout}
|
||||
testID={notesnook.ids.default.root}
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingBottom: Platform.OS === "android" ? insets?.bottom : 0,
|
||||
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] ? (
|
||||
<FluidPanels
|
||||
ref={fluidTabsRef}
|
||||
dimensions={dimensions}
|
||||
widths={PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS]}
|
||||
enabled={deviceMode !== "tablet" && !fullscreen}
|
||||
onScroll={onScroll}
|
||||
onChangeTab={onChangeTab}
|
||||
onDrawerStateChange={(state) => {
|
||||
if (!state) {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View
|
||||
key="1"
|
||||
style={{
|
||||
height: "100%",
|
||||
width: fullscreen
|
||||
? 0
|
||||
: PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS]?.sidebar
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider value="navigationMenu">
|
||||
<SideMenu />
|
||||
</ScopedThemeProvider>
|
||||
</View>
|
||||
|
||||
<View
|
||||
key="2"
|
||||
style={{
|
||||
height: "100%",
|
||||
width: fullscreen
|
||||
? 0
|
||||
: PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS]?.list
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider value="list">
|
||||
{deviceMode === "mobile" ? (
|
||||
<Animated.View
|
||||
onTouchEnd={() => {
|
||||
if (useSideBarDraggingStore.getState().dragging) {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
fluidTabsRef.current?.closeDrawer();
|
||||
animatedOpacity.value = withTiming(0);
|
||||
animatedTranslateY.value = withTiming(-9999);
|
||||
}}
|
||||
style={[
|
||||
{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: 999,
|
||||
backgroundColor: colors.primary.backdrop
|
||||
},
|
||||
animatedStyle
|
||||
]}
|
||||
ref={overlayRef}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<SafeAreaView
|
||||
style={{
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<AppNavigationStack />
|
||||
</SafeAreaView>
|
||||
</ScopedThemeProvider>
|
||||
</View>
|
||||
|
||||
<ScopedThemeProvider value="editor">
|
||||
<EditorWrapper key="3" widths={PANE_WIDTHS} />
|
||||
</ScopedThemeProvider>
|
||||
</FluidPanels>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
FluidPanelsView.displayName = "FluidPanelsView";
|
||||
|
||||
const onChangeTab = async (event: { i: number; from: number }) => {
|
||||
if (event.i === 2) {
|
||||
editorState().movedAway = false;
|
||||
editorState().isFocused = true;
|
||||
activateKeepAwake();
|
||||
eSendEvent(eOnEnterEditor);
|
||||
|
||||
if (!useTabStore.getState().getCurrentNoteId()) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
newNote: true
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
useTabStore.getState().getTab(useTabStore.getState().currentTab)
|
||||
?.session?.locked
|
||||
) {
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (event.from === 2) {
|
||||
deactivateKeepAwake();
|
||||
editorState().movedAway = true;
|
||||
editorState().isFocused = false;
|
||||
eSendEvent(eOnExitEditor);
|
||||
|
||||
// Lock all tabs with locked notes...
|
||||
for (const tab of useTabStore.getState().tabs) {
|
||||
const noteId = useTabStore.getState().getTab(tab.id)?.session?.noteId;
|
||||
if (!noteId) continue;
|
||||
const note = await db.notes.note(noteId);
|
||||
const locked = note && (await db.vaults.itemExists(note));
|
||||
if (locked) {
|
||||
useTabStore.getState().updateTab(tab.id, {
|
||||
session: {
|
||||
locked: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import * as React from "react";
|
||||
import Container from "../components/container";
|
||||
import Intro from "../components/intro";
|
||||
import { NotebookSheet } from "../components/sheets/notebook-sheet";
|
||||
import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets";
|
||||
import { hideAllTooltips } from "../hooks/use-tooltip";
|
||||
import Favorites from "../screens/favorites";
|
||||
import Home from "../screens/home";
|
||||
import NotebookScreen from "../screens/notebook";
|
||||
import Notebooks from "../screens/notebooks";
|
||||
import { ColoredNotes } from "../screens/notes/colored";
|
||||
import { Monographs } from "../screens/notes/monographs";
|
||||
import { TaggedNotes } from "../screens/notes/tagged";
|
||||
import Reminders from "../screens/reminders";
|
||||
import { Search } from "../screens/search";
|
||||
import Settings from "../screens/settings";
|
||||
import Tags from "../screens/tags";
|
||||
import Trash from "../screens/trash";
|
||||
import { eSendEvent } from "../services/event-manager";
|
||||
import SettingsService from "../services/settings";
|
||||
import useNavigationStore from "../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../stores/use-selection-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { rootNavigatorRef } from "../utils/global-refs";
|
||||
|
||||
const NativeStack = createNativeStackNavigator();
|
||||
const IntroStack = createNativeStackNavigator();
|
||||
|
||||
const IntroStackNavigator = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const height = useSettingStore((state) => state.dimensions.height);
|
||||
return (
|
||||
<IntroStack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
lazy: false,
|
||||
animation: "none",
|
||||
contentStyle: {
|
||||
backgroundColor: colors.primary.background,
|
||||
minHeight: height
|
||||
}
|
||||
}}
|
||||
initialRouteName={"Intro"}
|
||||
>
|
||||
<NativeStack.Screen name="Intro" component={Intro} />
|
||||
</IntroStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const _Tabs = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const homepage = SettingsService.get().homepage;
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
|
||||
const height = useSettingStore((state) => state.dimensions.height);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const screenHeight = height - (50 + insets.top + insets.bottom);
|
||||
React.useEffect(() => {
|
||||
setTimeout(async () => {
|
||||
useNavigationStore.getState().update(homepage);
|
||||
}, 1000);
|
||||
}, [homepage]);
|
||||
|
||||
return (
|
||||
<NativeStack.Navigator
|
||||
tabBar={() => null}
|
||||
initialRouteName={!introCompleted ? "Welcome" : homepage}
|
||||
backBehavior="history"
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
lazy: false,
|
||||
animation: "none",
|
||||
contentStyle: {
|
||||
backgroundColor: colors.primary.background,
|
||||
minHeight: !introCompleted ? undefined : screenHeight
|
||||
}
|
||||
}}
|
||||
>
|
||||
<NativeStack.Screen name="Welcome" component={IntroStackNavigator} />
|
||||
<NativeStack.Screen name="Notes" component={Home} />
|
||||
<NativeStack.Screen name="Notebooks" component={Notebooks} />
|
||||
<NativeStack.Screen name="Favorites" component={Favorites} />
|
||||
<NativeStack.Screen name="Trash" component={Trash} />
|
||||
<NativeStack.Screen name="Tags" component={Tags} />
|
||||
<NativeStack.Screen name="Settings" component={Settings} />
|
||||
<NativeStack.Screen name="TaggedNotes" component={TaggedNotes} />
|
||||
<NativeStack.Screen name="ColoredNotes" component={ColoredNotes} />
|
||||
<NativeStack.Screen name="Reminders" component={Reminders} />
|
||||
<NativeStack.Screen
|
||||
name="Monographs"
|
||||
initialParams={{
|
||||
item: { type: "monograph" },
|
||||
canGoBack: false,
|
||||
title: strings.monographs()
|
||||
}}
|
||||
component={Monographs}
|
||||
/>
|
||||
<NativeStack.Screen name="Notebook" component={NotebookScreen} />
|
||||
<NativeStack.Screen name="Search" component={Search} />
|
||||
</NativeStack.Navigator>
|
||||
);
|
||||
};
|
||||
const Tabs = React.memo(_Tabs, () => true);
|
||||
|
||||
const _NavigationStack = () => {
|
||||
const clearSelection = useSelectionStore((state) => state.clearSelection);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const onStateChange = React.useCallback(() => {
|
||||
if (useSelectionStore.getState().selectionMode) {
|
||||
clearSelection(true);
|
||||
}
|
||||
hideAllTooltips();
|
||||
eSendEvent("navigate");
|
||||
}, [clearSelection]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<NavigationContainer onStateChange={onStateChange} ref={rootNavigatorRef}>
|
||||
<Tabs />
|
||||
</NavigationContainer>
|
||||
{loading ? null : <NotebookSheet />}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
export const NavigationStack = React.memo(_NavigationStack, () => true);
|
||||
158
apps/mobile/app/navigation/navigation-stack.tsx
Normal file
158
apps/mobile/app/navigation/navigation-stack.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import * as React from "react";
|
||||
import AppLockedScreen from "../components/app-lock-overlay";
|
||||
import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets";
|
||||
import { hideAllTooltips } from "../hooks/use-tooltip";
|
||||
import Favorites from "../screens/favorites";
|
||||
import Home from "../screens/home";
|
||||
import NotebookScreen from "../screens/notebook";
|
||||
import Notebooks from "../screens/notebooks";
|
||||
import { ColoredNotes } from "../screens/notes/colored";
|
||||
import { Monographs } from "../screens/notes/monographs";
|
||||
import { TaggedNotes } from "../screens/notes/tagged";
|
||||
import Reminders from "../screens/reminders";
|
||||
import { Search } from "../screens/search";
|
||||
import Settings from "../screens/settings";
|
||||
import Tags from "../screens/tags";
|
||||
import Trash from "../screens/trash";
|
||||
import SettingsService from "../services/settings";
|
||||
import useNavigationStore, {
|
||||
RouteParams
|
||||
} from "../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../stores/use-selection-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import {
|
||||
appNavigatorRef,
|
||||
fluidTabsRef,
|
||||
rootNavigatorRef
|
||||
} from "../utils/global-refs";
|
||||
import { FluidPanelsView } from "./fluid-panels-view";
|
||||
|
||||
const RootStack = createNativeStackNavigator();
|
||||
const AppStack = createNativeStackNavigator();
|
||||
|
||||
const AppNavigation = React.memo(
|
||||
() => {
|
||||
const { colors } = useThemeColors();
|
||||
const homepage = SettingsService.get().homepage;
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const height = useSettingStore((state) => state.dimensions.height);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const screenHeight = height - (50 + insets.top + insets.bottom);
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
useNavigationStore.getState().update(homepage as keyof RouteParams);
|
||||
}, 1000);
|
||||
}, [homepage]);
|
||||
|
||||
return (
|
||||
<AppStack.Navigator
|
||||
initialRouteName={!introCompleted ? "Welcome" : homepage}
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
animation: "none",
|
||||
contentStyle: {
|
||||
backgroundColor: colors.primary.background,
|
||||
minHeight: !introCompleted ? undefined : screenHeight
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AppStack.Screen name="Notes" component={Home} />
|
||||
<AppStack.Screen name="Notebooks" component={Notebooks} />
|
||||
<AppStack.Screen name="Favorites" component={Favorites} />
|
||||
<AppStack.Screen name="Trash" component={Trash} />
|
||||
<AppStack.Screen name="Tags" component={Tags} />
|
||||
<AppStack.Screen name="TaggedNotes" component={TaggedNotes} />
|
||||
<AppStack.Screen name="ColoredNotes" component={ColoredNotes} />
|
||||
<AppStack.Screen name="Reminders" component={Reminders} />
|
||||
<AppStack.Screen
|
||||
name="Monographs"
|
||||
initialParams={{
|
||||
item: { type: "monograph" },
|
||||
canGoBack: false,
|
||||
title: strings.monographs()
|
||||
}}
|
||||
component={Monographs}
|
||||
/>
|
||||
<AppStack.Screen name="Notebook" component={NotebookScreen} />
|
||||
<AppStack.Screen name="Search" component={Search} />
|
||||
</AppStack.Navigator>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
AppNavigation.displayName = "AppNavigation";
|
||||
|
||||
export const RootNavigation = () => {
|
||||
return (
|
||||
<NavigationContainer ref={rootNavigatorRef} independent>
|
||||
<RootStack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false
|
||||
}}
|
||||
screenListeners={{
|
||||
focus: (props) => {
|
||||
if (props.target?.startsWith("FluidPanelsView")) {
|
||||
fluidTabsRef.current?.unlock();
|
||||
} else {
|
||||
fluidTabsRef.current?.lock();
|
||||
}
|
||||
}
|
||||
}}
|
||||
initialRouteName="FluidPanelsView"
|
||||
>
|
||||
<RootStack.Screen name="FluidPanelsView" component={FluidPanelsView} />
|
||||
<RootStack.Screen name="AppLock" component={AppLockedScreen} />
|
||||
<AppStack.Screen name="Settings" component={Settings} />
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const AppNavigationStack = React.memo(
|
||||
() => {
|
||||
const clearSelection = useSelectionStore((state) => state.clearSelection);
|
||||
const onStateChange = React.useCallback(() => {
|
||||
if (useSelectionStore.getState().selectionMode) {
|
||||
clearSelection();
|
||||
}
|
||||
hideAllTooltips();
|
||||
}, [clearSelection]);
|
||||
|
||||
return (
|
||||
<NavigationContainer
|
||||
independent
|
||||
onStateChange={onStateChange}
|
||||
ref={appNavigatorRef}
|
||||
>
|
||||
<AppNavigation />
|
||||
</NavigationContainer>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
AppNavigationStack.displayName = "AppNavigationStack";
|
||||
@@ -1,541 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
|
||||
import {
|
||||
activateKeepAwake,
|
||||
deactivateKeepAwake
|
||||
} from "@sayem314/react-native-keep-awake";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Dimensions, Platform, StatusBar, View } from "react-native";
|
||||
import {
|
||||
addOrientationListener,
|
||||
addSpecificOrientationListener,
|
||||
getInitialOrientation,
|
||||
getSpecificOrientation,
|
||||
removeOrientationListener,
|
||||
removeSpecificOrientationListener
|
||||
} from "react-native-orientation";
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming
|
||||
} from "react-native-reanimated";
|
||||
import { notesnook } from "../../e2e/test.ids";
|
||||
import { db } from "../common/database";
|
||||
import { SideMenu } from "../components/side-menu";
|
||||
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
|
||||
import { FluidTabs } from "../components/tabs";
|
||||
import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets";
|
||||
import { useShortcutManager } from "../hooks/use-shortcut-manager";
|
||||
import { hideAllTooltips } from "../hooks/use-tooltip";
|
||||
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
|
||||
import {
|
||||
clearAppState,
|
||||
editorController,
|
||||
editorState,
|
||||
getAppState
|
||||
} from "../screens/editor/tiptap/utils";
|
||||
import { EditorWrapper } from "../screens/editor/wrapper";
|
||||
import { DDS } from "../services/device-detection";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../services/event-manager";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import {
|
||||
eCloseFullscreenEditor,
|
||||
eOnEnterEditor,
|
||||
eOnExitEditor,
|
||||
eOnLoadNote,
|
||||
eOpenFullscreenEditor,
|
||||
eUnlockNote
|
||||
} from "../utils/events";
|
||||
import { editorRef, tabBarRef } from "../utils/global-refs";
|
||||
import { sleep } from "../utils/time";
|
||||
import { NavigationStack } from "./navigation-stack";
|
||||
|
||||
const _TabsHolder = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const setFullscreen = useSettingStore((state) => state.setFullscreen);
|
||||
const fullscreen = useSettingStore((state) => state.fullscreen);
|
||||
const setDeviceModeState = useSettingStore((state) => state.setDeviceMode);
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const setDimensions = useSettingStore((state) => state.setDimensions);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const animatedOpacity = useSharedValue(0);
|
||||
const animatedTranslateY = useSharedValue(-9999);
|
||||
const overlayRef = useRef();
|
||||
const [orientation, setOrientation] = useState(getInitialOrientation());
|
||||
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
|
||||
useShortcutManager({
|
||||
onShortcutPressed: async (item) => {
|
||||
if (!item && getAppState()) {
|
||||
editorState().movedAway = false;
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
return;
|
||||
}
|
||||
if (item && item.type === "notesnook.action.newnote") {
|
||||
clearAppState();
|
||||
if (!tabBarRef.current) {
|
||||
await sleep(3000);
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
editorState().movedAway = false;
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
return;
|
||||
}
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
editorState().movedAway = false;
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const onOrientationChange = (o, o2) => {
|
||||
setOrientation(o || o2);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (Platform.OS === "ios") {
|
||||
addSpecificOrientationListener(onOrientationChange);
|
||||
getSpecificOrientation && getSpecificOrientation(onOrientationChange);
|
||||
} else {
|
||||
addOrientationListener(onOrientationChange);
|
||||
}
|
||||
|
||||
return () => {
|
||||
removeSpecificOrientationListener(onOrientationChange);
|
||||
removeOrientationListener(onOrientationChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const showFullScreenEditor = useCallback(() => {
|
||||
setFullscreen(true);
|
||||
if (deviceMode === "smallTablet") {
|
||||
tabBarRef.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]);
|
||||
|
||||
const closeFullScreenEditor = useCallback(
|
||||
(current) => {
|
||||
const _deviceMode = current || deviceMode;
|
||||
if (_deviceMode === "smallTablet") {
|
||||
tabBarRef.current?.closeDrawer(false);
|
||||
}
|
||||
setFullscreen(false);
|
||||
editorController.current?.commands.updateSettings({
|
||||
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") {
|
||||
tabBarRef.current?.goToIndex(1, false);
|
||||
}
|
||||
if (_deviceMode === "mobile") {
|
||||
tabBarRef.current?.goToIndex(2, false);
|
||||
}
|
||||
},
|
||||
[deviceMode, dimensions.width, setFullscreen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!tabBarRef.current?.isDrawerOpen()) {
|
||||
toggleView(false);
|
||||
}
|
||||
eSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor);
|
||||
eSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor);
|
||||
|
||||
return () => {
|
||||
eUnSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor);
|
||||
eUnSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor);
|
||||
};
|
||||
}, [
|
||||
deviceMode,
|
||||
dimensions,
|
||||
colors,
|
||||
showFullScreenEditor,
|
||||
closeFullScreenEditor,
|
||||
toggleView
|
||||
]);
|
||||
|
||||
const _onLayout = (event) => {
|
||||
let size = event?.nativeEvent?.layout;
|
||||
if (!size || (size.width === dimensions.width && deviceMode !== null)) {
|
||||
DDS.setSize(size, orientation);
|
||||
setDeviceMode(deviceMode, size);
|
||||
checkDeviceType(size);
|
||||
return;
|
||||
}
|
||||
|
||||
checkDeviceType(size);
|
||||
};
|
||||
|
||||
if (!deviceMode) {
|
||||
const size = Dimensions.get("window");
|
||||
checkDeviceType(size);
|
||||
}
|
||||
|
||||
function checkDeviceType(size) {
|
||||
setDimensions({
|
||||
width: size.width,
|
||||
height: size.height
|
||||
});
|
||||
DDS.setSize(size, orientation);
|
||||
const nextDeviceMode = DDS.isLargeTablet()
|
||||
? "tablet"
|
||||
: DDS.isSmallTab
|
||||
? "smallTablet"
|
||||
: "mobile";
|
||||
|
||||
setDeviceMode(nextDeviceMode, size);
|
||||
}
|
||||
|
||||
function setDeviceMode(current, size) {
|
||||
setDeviceModeState(current);
|
||||
|
||||
let needsUpdate = current !== deviceMode;
|
||||
|
||||
if (fullscreen && current !== "mobile") {
|
||||
// Runs after size is set via state.
|
||||
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 (!needsUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getAppState();
|
||||
switch (current) {
|
||||
case "tablet":
|
||||
tabBarRef.current?.goToIndex(0, false);
|
||||
break;
|
||||
case "smallTablet":
|
||||
if (!fullscreen) {
|
||||
tabBarRef.current?.closeDrawer(false);
|
||||
} else {
|
||||
tabBarRef.current?.openDrawer(false);
|
||||
}
|
||||
break;
|
||||
case "mobile":
|
||||
if (
|
||||
state &&
|
||||
editorState().movedAway === false &&
|
||||
useTabStore.getState().getCurrentNoteId()
|
||||
) {
|
||||
tabBarRef.current?.goToIndex(2, false);
|
||||
} else {
|
||||
tabBarRef.current?.goToIndex(1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = (scrollOffset) => {
|
||||
hideAllTooltips();
|
||||
if (scrollOffset > offsets[deviceMode].sidebar - 10) {
|
||||
animatedOpacity.value = 0;
|
||||
toggleView(false);
|
||||
} else {
|
||||
let o = scrollOffset / 300;
|
||||
let op = 0;
|
||||
if (o < 0) {
|
||||
op = 1;
|
||||
} else {
|
||||
op = 1 - o;
|
||||
}
|
||||
animatedOpacity.value = op;
|
||||
toggleView(op < 0.1 ? false : true);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleView = useCallback(
|
||||
(show) => {
|
||||
animatedTranslateY.value = show ? 0 : -9999;
|
||||
},
|
||||
[animatedTranslateY]
|
||||
);
|
||||
|
||||
const valueLimiter = (value, min, max) => {
|
||||
if (value < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (value > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const offsets = {
|
||||
mobile: {
|
||||
sidebar: dimensions.width * 0.75,
|
||||
list: dimensions.width + dimensions.width * 0.75,
|
||||
editor: dimensions.width * 2 + dimensions.width * 0.75
|
||||
},
|
||||
smallTablet: {
|
||||
sidebar: fullscreen ? 0 : valueLimiter(dimensions.width * 0.3, 300, 350),
|
||||
list: fullscreen
|
||||
? 0
|
||||
: dimensions.width + valueLimiter(dimensions.width * 0.3, 300, 350),
|
||||
editor: fullscreen
|
||||
? 0
|
||||
: dimensions.width + valueLimiter(dimensions.width * 0.3, 300, 350)
|
||||
},
|
||||
tablet: {
|
||||
sidebar: 0,
|
||||
list: 0,
|
||||
editor: 0
|
||||
}
|
||||
};
|
||||
const widths = {
|
||||
mobile: {
|
||||
sidebar: dimensions.width * 0.75,
|
||||
list: dimensions.width,
|
||||
editor: dimensions.width
|
||||
},
|
||||
smallTablet: {
|
||||
sidebar: valueLimiter(dimensions.width * 0.3, 300, 350),
|
||||
list: valueLimiter(dimensions.width * 0.4, 300, 450),
|
||||
editor: dimensions.width - valueLimiter(dimensions.width * 0.4, 300, 450)
|
||||
},
|
||||
tablet: {
|
||||
sidebar: dimensions.width * 0.22,
|
||||
list: dimensions.width * 0.3,
|
||||
editor: dimensions.width * 0.48
|
||||
}
|
||||
};
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
opacity: animatedOpacity.value,
|
||||
transform: [
|
||||
{
|
||||
translateY: animatedTranslateY.value
|
||||
}
|
||||
]
|
||||
};
|
||||
}, []);
|
||||
|
||||
// useEffect(() => {
|
||||
// changeSystemBarColors();
|
||||
// setTimeout(() => {
|
||||
// changeSystemBarColors();
|
||||
// }, 1000);
|
||||
// }, [colors.primary.background, isDark]);
|
||||
|
||||
return (
|
||||
<View
|
||||
onLayout={_onLayout}
|
||||
testID={notesnook.ids.default.root}
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingBottom: Platform.OS === "android" ? insets?.bottom : 0,
|
||||
marginRight:
|
||||
orientation === "LANDSCAPE-RIGHT" && Platform.OS === "ios"
|
||||
? insets.right
|
||||
: 0,
|
||||
marginLeft:
|
||||
orientation === "LANDSCAPE-LEFT" && Platform.OS === "ios"
|
||||
? insets.left
|
||||
: 0
|
||||
}}
|
||||
>
|
||||
<StatusBar translucent={true} backgroundColor="transparent" />
|
||||
|
||||
{!introCompleted ? (
|
||||
<NavigationStack />
|
||||
) : (
|
||||
<>
|
||||
{deviceMode && widths[deviceMode] ? (
|
||||
<FluidTabs
|
||||
ref={tabBarRef}
|
||||
dimensions={dimensions}
|
||||
widths={widths[deviceMode]}
|
||||
enabled={deviceMode !== "tablet" && !fullscreen}
|
||||
onScroll={onScroll}
|
||||
onChangeTab={onChangeTab}
|
||||
onDrawerStateChange={(state) => {
|
||||
if (!state) {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View
|
||||
key="1"
|
||||
style={{
|
||||
height: "100%",
|
||||
width: fullscreen ? 0 : widths[deviceMode]?.sidebar
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider value="navigationMenu">
|
||||
<SideMenu />
|
||||
</ScopedThemeProvider>
|
||||
</View>
|
||||
|
||||
<View
|
||||
key="2"
|
||||
style={{
|
||||
height: "100%",
|
||||
width: fullscreen ? 0 : widths[deviceMode]?.list
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider value="list">
|
||||
{deviceMode === "mobile" ? (
|
||||
<Animated.View
|
||||
onTouchEnd={() => {
|
||||
if (useSideBarDraggingStore.getState().dragging) {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
tabBarRef.current?.closeDrawer();
|
||||
animatedOpacity.value = withTiming(0);
|
||||
animatedTranslateY.value = withTiming(-9999);
|
||||
}}
|
||||
style={[
|
||||
{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: 999,
|
||||
backgroundColor: colors.primary.backdrop
|
||||
},
|
||||
animatedStyle
|
||||
]}
|
||||
ref={overlayRef}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<NavigationStack />
|
||||
</ScopedThemeProvider>
|
||||
</View>
|
||||
|
||||
<ScopedThemeProvider value="editor">
|
||||
<EditorWrapper
|
||||
key="3"
|
||||
widths={widths}
|
||||
dimensions={dimensions}
|
||||
/>
|
||||
</ScopedThemeProvider>
|
||||
</FluidTabs>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export const TabHolder = React.memo(_TabsHolder, () => true);
|
||||
|
||||
const onChangeTab = async (event) => {
|
||||
if (event.i === 2) {
|
||||
editorState().movedAway = false;
|
||||
editorState().isFocused = true;
|
||||
activateKeepAwake();
|
||||
eSendEvent(eOnEnterEditor);
|
||||
|
||||
if (!useTabStore.getState().getCurrentNoteId()) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
newNote: true
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
useTabStore.getState().getTab(useTabStore.getState().currentTab).session
|
||||
?.locked
|
||||
) {
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (event.from === 2) {
|
||||
deactivateKeepAwake();
|
||||
editorState().movedAway = true;
|
||||
editorState().isFocused = false;
|
||||
eSendEvent(eOnExitEditor);
|
||||
|
||||
// Lock all tabs with locked notes...
|
||||
for (const tab of useTabStore.getState().tabs) {
|
||||
const noteId = useTabStore.getState().getTab(tab.id)?.session?.noteId;
|
||||
if (!noteId) continue;
|
||||
const note = await db.notes.note(noteId);
|
||||
const locked = note && (await db.vaults.itemExists(note));
|
||||
if (locked) {
|
||||
useTabStore.getState().updateTab(tab.id, {
|
||||
session: {
|
||||
locked: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
import { i18n } from "@lingui/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
@@ -48,7 +46,6 @@ import {
|
||||
eUnlockWithPassword
|
||||
} from "../../utils/events";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import EditorOverlay from "./loading";
|
||||
import { EDITOR_URI } from "./source";
|
||||
import { EditorProps, useEditorType } from "./tiptap/types";
|
||||
@@ -61,6 +58,9 @@ import {
|
||||
openInternalLink,
|
||||
randId
|
||||
} from "./tiptap/utils";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { i18n } from "@lingui/core";
|
||||
|
||||
const style: ViewStyle = {
|
||||
height: "100%",
|
||||
@@ -348,7 +348,7 @@ const useLockedNoteHandler = () => {
|
||||
}),
|
||||
eSubscribeEvent(eUnlockWithPassword, onSubmit)
|
||||
];
|
||||
if (tabRef.current?.session?.locked && tabBarRef.current?.page() === 2) {
|
||||
if (tabRef.current?.session?.locked && fluidTabsRef.current?.page() === 2) {
|
||||
unlock();
|
||||
}
|
||||
return () => {
|
||||
|
||||
@@ -73,7 +73,7 @@ import {
|
||||
eUnlockWithPassword
|
||||
} from "../../../utils/events";
|
||||
import { openLinkInBrowser } from "../../../utils/functions";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
import { useDragState } from "../../settings/editor/state";
|
||||
import { EditorMessage, EditorProps, useEditorType } from "./types";
|
||||
import { useTabStore } from "./use-tab-store";
|
||||
@@ -259,7 +259,7 @@ export const useEditorEvents = (
|
||||
|
||||
if (deviceMode === "mobile") {
|
||||
editorState().movedAway = true;
|
||||
tabBarRef.current?.goToPage(0);
|
||||
fluidTabsRef.current?.goToPage(0);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -269,7 +269,7 @@ export const useEditorEvents = (
|
||||
}, [editor, deviceMode, fullscreen]);
|
||||
|
||||
const onHardwareBackPress = useCallback(() => {
|
||||
if (tabBarRef.current?.page() === 2) {
|
||||
if (fluidTabsRef.current?.page() === 2) {
|
||||
onBackPress();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ import {
|
||||
eShowMergeDialog,
|
||||
eUpdateNoteInEditor
|
||||
} from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import { unlockVault } from "../../../utils/unlock-vault";
|
||||
import { onNoteCreated } from "../../notes/common";
|
||||
@@ -995,14 +995,14 @@ export const useEditor = (
|
||||
if (!appState) return;
|
||||
state.current.isRestoringState = true;
|
||||
state.current.currentlyEditing = true;
|
||||
if (tabBarRef.current?.page() === 2) {
|
||||
if (fluidTabsRef.current?.page() === 2) {
|
||||
state.current.movedAway = false;
|
||||
}
|
||||
|
||||
if (!state.current.editorStateRestored) {
|
||||
state.current.isRestoringState = true;
|
||||
if (!DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
fluidTabsRef.current?.goToPage(1, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1081,12 +1081,12 @@ export const useEditor = (
|
||||
item: note
|
||||
});
|
||||
}
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
} else {
|
||||
noteId = useTabStore.getState().getCurrentNoteId() || null;
|
||||
if (!noteId) {
|
||||
loadNote({ newNote: true });
|
||||
if (tabBarRef.current?.page() === 1) {
|
||||
if (fluidTabsRef.current?.page() === 1) {
|
||||
state.current.currentlyEditing = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
66
apps/mobile/app/screens/home/filter-bar.tsx
Normal file
66
apps/mobile/app/screens/home/filter-bar.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
|
||||
export const FilterBar = () => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
marginTop: DefaultAppStyles.GAP,
|
||||
width: "93%",
|
||||
alignSelf: "center"
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{
|
||||
name: "All"
|
||||
},
|
||||
{
|
||||
name: "Favorites"
|
||||
}
|
||||
].map((item) => (
|
||||
<Button
|
||||
key={item.name}
|
||||
title={item.name}
|
||||
type="secondary"
|
||||
style={{
|
||||
height: 25
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
key="add"
|
||||
icon="plus"
|
||||
type="secondary"
|
||||
iconSize={SIZE.md}
|
||||
style={{
|
||||
height: 25
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import React from "react";
|
||||
import { FloatingButton } from "../../components/container/floating-button";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
@@ -29,7 +30,7 @@ import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotes } from "../../stores/use-notes-store";
|
||||
import { openEditor } from "../notes/common";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { FilterBar } from "./filter-bar";
|
||||
|
||||
export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
const [notes, loading] = useNotes();
|
||||
@@ -66,6 +67,9 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
id={route.name}
|
||||
onPressDefaultRightButton={openEditor}
|
||||
/>
|
||||
|
||||
<FilterBar />
|
||||
|
||||
<DelayLayout wait={loading}>
|
||||
<List
|
||||
data={notes}
|
||||
|
||||
@@ -17,15 +17,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { resolveItems } from "@notesnook/common";
|
||||
import { VirtualizedGrouping } from "@notesnook/core";
|
||||
import { Note, Notebook } from "@notesnook/core";
|
||||
import { Note, Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { FloatingButton } from "../../components/container/floating-button";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import { NotebookHeader } from "../../components/list-items/headers/notebook-header";
|
||||
import { Properties } from "../../components/properties";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
@@ -40,8 +42,8 @@ import useNavigationStore, {
|
||||
import { eUpdateNotebookRoute } from "../../utils/events";
|
||||
import { findRootNotebookId } from "../../utils/notebooks";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { openEditor, setOnFirstSave } from "../notes/common";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
@@ -125,6 +127,8 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
setNotes(notes);
|
||||
await notes.item(0, resolveItems);
|
||||
syncWithNavigation();
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
setLoading(false);
|
||||
} catch (e) {
|
||||
@@ -160,13 +164,18 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
renderedInRoute={route.name}
|
||||
title={params.current.item?.title}
|
||||
canGoBack={params?.current?.canGoBack}
|
||||
rightButton={{
|
||||
name: "dots-vertical",
|
||||
onPress: () => {
|
||||
Properties.present(params.current.item);
|
||||
}
|
||||
}}
|
||||
hasSearch={true}
|
||||
onSearch={() => {
|
||||
const selector = db.relations.from(
|
||||
params.current.item,
|
||||
"note"
|
||||
).selector;
|
||||
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(params.current.item?.title),
|
||||
type: "note",
|
||||
@@ -175,19 +184,18 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
items: selector
|
||||
});
|
||||
}}
|
||||
titleHiddenOnRender
|
||||
id={params.current.item?.id}
|
||||
onPressDefaultRightButton={openEditor}
|
||||
/>
|
||||
|
||||
{breadcrumbs && breadcrumbs.length > 0 ? (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap"
|
||||
flexWrap: "wrap",
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
@@ -260,6 +268,7 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
}}
|
||||
/>
|
||||
</DelayLayout>
|
||||
<FloatingButton onPress={openEditor} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { useTagStore } from "../../stores/use-tag-store";
|
||||
import { eOnLoadNote, eOnNotebookUpdated } from "../../utils/events";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { editorState } from "../editor/tiptap/utils";
|
||||
|
||||
export const PLACEHOLDER_DATA = {
|
||||
@@ -58,7 +58,7 @@ export function openEditor() {
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
editorState().currentlyEditing = true;
|
||||
editorState().movedAway = false;
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
} else {
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
}
|
||||
|
||||
@@ -17,18 +17,18 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { TextInput } from "react-native-gesture-handler";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import Navigation from "../../services/navigation";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
export const SearchBar = ({
|
||||
onChangeText,
|
||||
loading
|
||||
@@ -50,13 +50,21 @@ export const SearchBar = ({
|
||||
return selectionMode && isFocused ? null : (
|
||||
<View
|
||||
style={{
|
||||
height: Platform.OS === "android" ? 50 + insets.top : 50,
|
||||
paddingTop: Platform.OS === "ios" ? 0 : insets.top,
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingTop: Platform.OS === "ios" ? 0 : insets.top + 5
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1,
|
||||
width: "100%",
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
borderRadius: 10,
|
||||
borderColor: colors.primary.border,
|
||||
borderWidth: 1
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
@@ -69,22 +77,17 @@ export const SearchBar = ({
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
type="plain"
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
marginLeft: -5,
|
||||
marginRight: DDS.isLargeTablet() ? 10 : 7
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
testID="search-input"
|
||||
style={{
|
||||
fontSize: SIZE.md + 1,
|
||||
fontSize: SIZE.sm,
|
||||
fontFamily: "OpenSans-Regular",
|
||||
flexGrow: 1,
|
||||
height: "100%",
|
||||
color: colors.primary.paragraph
|
||||
color: colors.primary.paragraph,
|
||||
height: 40
|
||||
}}
|
||||
autoFocus
|
||||
onChangeText={_onChangeText}
|
||||
@@ -97,5 +100,6 @@ export const SearchBar = ({
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { NativeStackScreenProps } from "@react-navigation/native-stack";
|
||||
import React, { useEffect } from "react";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { KeyboardAwareFlatList } from "react-native-keyboard-aware-scroll-view";
|
||||
import Animated, { FadeInDown } from "react-native-reanimated";
|
||||
@@ -26,7 +26,6 @@ import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { components } from "./components";
|
||||
import { SectionItem } from "./section-item";
|
||||
import { RouteParams, SettingSection } from "./types";
|
||||
@@ -42,16 +41,10 @@ const Group = ({
|
||||
}: NativeStackScreenProps<RouteParams, "SettingsGroup">) => {
|
||||
useNavigationFocus(navigation, {
|
||||
onFocus: () => {
|
||||
tabBarRef.current?.lock();
|
||||
useNavigationStore.getState().setFocusedRouteId("Settings");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
tabBarRef.current?.unlock();
|
||||
};
|
||||
}, []);
|
||||
const renderItem = ({ item }: { item: SettingSection; index: number }) => (
|
||||
<SectionItem item={item} />
|
||||
);
|
||||
|
||||
@@ -56,7 +56,8 @@ const Home = ({
|
||||
<Header
|
||||
renderedInRoute="Settings"
|
||||
title={strings.routes.Settings()}
|
||||
canGoBack={false}
|
||||
canGoBack={true}
|
||||
hasSearch={false}
|
||||
id="Settings"
|
||||
/>
|
||||
<DelayLayout delay={300} type="settings">
|
||||
|
||||
@@ -24,11 +24,18 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import Group from "./group";
|
||||
import Home from "./home";
|
||||
import { RouteParams } from "./types";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
const SettingsStack = createNativeStackNavigator<RouteParams>();
|
||||
|
||||
export const Settings = () => {
|
||||
const { colors } = useThemeColors();
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
<SettingsStack.Navigator
|
||||
initialRouteName="SettingsHome"
|
||||
screenListeners={{
|
||||
@@ -49,6 +56,7 @@ export const Settings = () => {
|
||||
<SettingsStack.Screen name="SettingsHome" component={Home} />
|
||||
<SettingsStack.Screen name="SettingsGroup" component={Group} />
|
||||
</SettingsStack.Navigator>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import { useReminderStore } from "../stores/use-reminder-store";
|
||||
import { useTagStore } from "../stores/use-tag-store";
|
||||
import { useTrashStore } from "../stores/use-trash-store";
|
||||
import { eOnRefreshSearch, eUpdateNotebookRoute } from "../utils/events";
|
||||
import { rootNavigatorRef, tabBarRef } from "../utils/global-refs";
|
||||
import { appNavigatorRef, fluidTabsRef } from "../utils/global-refs";
|
||||
import { eSendEvent } from "./event-manager";
|
||||
|
||||
/**
|
||||
@@ -122,30 +122,36 @@ function queueRoutesForUpdate(...routesToUpdate: RouteName[]) {
|
||||
}
|
||||
|
||||
function navigate<T extends RouteName>(screen: T, params?: RouteParams[T]) {
|
||||
rootNavigatorRef.current?.navigate(screen as any, params);
|
||||
console.log(`Navigation.navigate ${screen} route`);
|
||||
appNavigatorRef.current?.navigate(screen as any, params);
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
rootNavigatorRef.current?.goBack();
|
||||
appNavigatorRef.current?.goBack();
|
||||
}
|
||||
|
||||
function push<T extends RouteName>(screen: T, params: RouteParams[T]) {
|
||||
rootNavigatorRef.current?.dispatch(StackActions.push(screen as any, params));
|
||||
console.log(`Navigation.push ${screen} route`);
|
||||
appNavigatorRef.current?.dispatch(StackActions.push(screen as any, params));
|
||||
}
|
||||
|
||||
function replace<T extends RouteName>(screen: T, params: RouteParams[T]) {
|
||||
rootNavigatorRef.current?.dispatch(StackActions.replace(screen, params));
|
||||
console.log(`Navigation.replace ${screen} route`);
|
||||
appNavigatorRef.current?.dispatch(
|
||||
StackActions.replace(screen as string, params)
|
||||
);
|
||||
}
|
||||
|
||||
function popToTop() {
|
||||
rootNavigatorRef.current?.dispatch(StackActions.popToTop());
|
||||
console.log(`Navigation.popToTop`);
|
||||
appNavigatorRef.current?.dispatch(StackActions.popToTop());
|
||||
}
|
||||
|
||||
function openDrawer() {
|
||||
tabBarRef.current?.openDrawer();
|
||||
fluidTabsRef.current?.openDrawer();
|
||||
}
|
||||
function closeDrawer() {
|
||||
tabBarRef.current?.closeDrawer();
|
||||
fluidTabsRef.current?.closeDrawer();
|
||||
}
|
||||
|
||||
const Navigation = {
|
||||
|
||||
@@ -44,7 +44,7 @@ import { useReminderStore } from "../stores/use-reminder-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { useUserStore } from "../stores/use-user-store";
|
||||
import { eOnLoadNote } from "../utils/events";
|
||||
import { tabBarRef } from "../utils/global-refs";
|
||||
import { fluidTabsRef } from "../utils/global-refs";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { NotesnookModule } from "../utils/notesnook-module";
|
||||
import { sleep } from "../utils/time";
|
||||
@@ -440,7 +440,7 @@ async function loadNote(id: string, jump: boolean) {
|
||||
const note = await db.notes.note(id);
|
||||
if (!note) return;
|
||||
if (!DDS.isTab && jump) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
fluidTabsRef.current?.goToPage(1);
|
||||
}
|
||||
NotesnookModule.setAppState(
|
||||
JSON.stringify({
|
||||
|
||||
165
apps/mobile/app/stores/create-notebook-tree-stores.ts
Normal file
165
apps/mobile/app/stores/create-notebook-tree-stores.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Notebook } from "@notesnook/core";
|
||||
import create from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { createItemSelectionStore } from "./item-selection-store";
|
||||
|
||||
export type TreeItem = {
|
||||
parentId: string;
|
||||
notebook: Notebook;
|
||||
depth: number;
|
||||
};
|
||||
|
||||
function removeTreeItem(tree: TreeItem[], id: string) {
|
||||
const children: TreeItem[] = [];
|
||||
let newTree = tree.filter((item) => {
|
||||
if (item.parentId === id) children.push(item);
|
||||
return item.notebook.id !== id;
|
||||
});
|
||||
for (const child of children) {
|
||||
newTree = removeTreeItem(newTree, child.notebook.id);
|
||||
}
|
||||
return newTree;
|
||||
}
|
||||
|
||||
export function createNotebookTreeStores(
|
||||
multiSelect: boolean,
|
||||
selectionEnabled: boolean
|
||||
) {
|
||||
const useSideMenuNotebookTreeStore = create<{
|
||||
tree: TreeItem[];
|
||||
setTree: (tree: TreeItem[]) => void;
|
||||
removeItem: (id: string) => void;
|
||||
addNotebooks: (
|
||||
parentId: string,
|
||||
notebooks: Notebook[],
|
||||
depth: number
|
||||
) => void;
|
||||
updateItem: (id: string, notebook: Notebook) => void;
|
||||
fetchAndAdd: (parentId: string, depth: number) => Promise<void>;
|
||||
removeChildren: (id: string) => void;
|
||||
}>((set, get) => ({
|
||||
tree: [],
|
||||
setTree(tree) {
|
||||
set({ tree });
|
||||
},
|
||||
updateItem: (id, notebook) => {
|
||||
const newTree = [...get().tree];
|
||||
const index = newTree.findIndex((item) => item.notebook.id === id);
|
||||
newTree[index] = {
|
||||
...newTree[index],
|
||||
notebook
|
||||
};
|
||||
set({
|
||||
tree: newTree
|
||||
});
|
||||
},
|
||||
addNotebooks: (parentId: string, notebooks: Notebook[], depth: number) => {
|
||||
const parentIndex = get().tree.findIndex(
|
||||
(item) => item.notebook.id === parentId
|
||||
);
|
||||
|
||||
let newTree = get().tree.slice();
|
||||
for (const item of newTree) {
|
||||
if (item.parentId === parentId) {
|
||||
newTree = removeTreeItem(newTree, item.notebook.id);
|
||||
}
|
||||
}
|
||||
newTree.splice(
|
||||
parentIndex + 1,
|
||||
0,
|
||||
...notebooks.map((notebook) => {
|
||||
return {
|
||||
parentId,
|
||||
notebook,
|
||||
depth: depth
|
||||
};
|
||||
})
|
||||
);
|
||||
set({
|
||||
tree: newTree
|
||||
});
|
||||
},
|
||||
|
||||
removeItem(id) {
|
||||
set({
|
||||
tree: removeTreeItem(get().tree, id).slice()
|
||||
});
|
||||
},
|
||||
fetchAndAdd: async (parentId: string, depth: number) => {
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: parentId
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
const grouped = await selector.sorted(
|
||||
db.settings.getGroupOptions("notebooks")
|
||||
);
|
||||
|
||||
const notebooks: Notebook[] = [];
|
||||
for (let index = 0; index < grouped.placeholders.length; index++) {
|
||||
const notebook = (await grouped.item(index)).item;
|
||||
if (notebook) notebooks.push(notebook);
|
||||
}
|
||||
get().addNotebooks(parentId, notebooks, depth);
|
||||
},
|
||||
removeChildren(id: string) {
|
||||
let newTree = get().tree.slice();
|
||||
for (const item of newTree) {
|
||||
if (item.parentId === id) {
|
||||
newTree = removeTreeItem(newTree, item.notebook.id);
|
||||
}
|
||||
}
|
||||
set({
|
||||
tree: newTree
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
const useSideMenuNotebookSelectionStore = createItemSelectionStore(
|
||||
multiSelect,
|
||||
selectionEnabled
|
||||
);
|
||||
|
||||
const useSideMenuNotebookExpandedStore = create<{
|
||||
expanded: {
|
||||
[id: string]: boolean;
|
||||
};
|
||||
setExpanded: (id: string) => void;
|
||||
}>((set, get) => ({
|
||||
expanded: {},
|
||||
setExpanded(id: string) {
|
||||
set({
|
||||
expanded: {
|
||||
...get().expanded,
|
||||
[id]: !get().expanded[id]
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
useSideMenuNotebookTreeStore,
|
||||
useSideMenuNotebookSelectionStore,
|
||||
useSideMenuNotebookExpandedStore
|
||||
};
|
||||
}
|
||||
@@ -33,6 +33,7 @@ export interface SelectionStore extends State {
|
||||
enabled?: boolean;
|
||||
getSelectedItemIds: () => string[];
|
||||
getDeselectedItemIds: () => string[];
|
||||
selectAll?: () => void;
|
||||
}
|
||||
|
||||
export function createItemSelectionStore(
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
TrashItem
|
||||
} from "@notesnook/core";
|
||||
import create, { State } from "zustand";
|
||||
import { ParamListBase } from "@react-navigation/core";
|
||||
|
||||
export type GenericRouteParam = undefined;
|
||||
|
||||
@@ -55,7 +56,7 @@ export type AuthParams = {
|
||||
canGoBack?: boolean;
|
||||
};
|
||||
|
||||
export type RouteParams = {
|
||||
export interface RouteParams extends ParamListBase {
|
||||
Notes: GenericRouteParam;
|
||||
Notebooks: {
|
||||
canGoBack?: boolean;
|
||||
@@ -80,7 +81,7 @@ export type RouteParams = {
|
||||
AppLock: AppLockRouteParams;
|
||||
Reminders: GenericRouteParam;
|
||||
SettingsGroup: GenericRouteParam;
|
||||
};
|
||||
}
|
||||
|
||||
export type RouteName = keyof RouteParams;
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ export interface SettingStore extends State {
|
||||
dimensions: DimensionsType;
|
||||
setSettings: (settings: Settings) => void;
|
||||
setFullscreen: (fullscreen: boolean) => void;
|
||||
setDeviceMode: (mode: string) => void;
|
||||
setDeviceMode: (mode: string | null) => void;
|
||||
setDimensions: (dimensions: DimensionsType) => void;
|
||||
isAppLoading: boolean;
|
||||
setAppLoading: (isAppLoading: boolean) => void;
|
||||
|
||||
@@ -25,6 +25,7 @@ import { presentDialog } from "../components/dialog/functions";
|
||||
import { eSendEvent, ToastManager } from "../services/event-manager";
|
||||
import Navigation from "../services/navigation";
|
||||
import { useMenuStore } from "../stores/use-menu-store";
|
||||
import { useNotebookStore } from "../stores/use-notebook-store";
|
||||
import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useTagStore } from "../stores/use-tag-store";
|
||||
import { eOnNotebookUpdated, eUpdateNoteInEditor } from "./events";
|
||||
@@ -72,8 +73,10 @@ async function deleteNotebook(id: string, deleteNotes: boolean) {
|
||||
}
|
||||
}
|
||||
await db.notebooks.moveToTrash(id);
|
||||
if (parentId) {
|
||||
|
||||
eSendEvent(eOnNotebookUpdated, parentId);
|
||||
if (!parentId) {
|
||||
useNotebookStore.getState().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +116,6 @@ export const deleteItems = async (
|
||||
if (!result.delete) return;
|
||||
for (const id of itemIds) {
|
||||
await deleteNotebook(id, result.deleteNotes);
|
||||
eSendEvent(eOnNotebookUpdated, await getParentNotebookId(id));
|
||||
}
|
||||
} else if (type === "tag") {
|
||||
presentDialog({
|
||||
|
||||
@@ -20,10 +20,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { createNavigationContainerRef } from "@react-navigation/native";
|
||||
import { createRef } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { TabsRef } from "../components/tabs";
|
||||
import { TabsRef } from "../components/fluid-panels";
|
||||
import { RouteParams } from "../stores/use-navigation-store";
|
||||
|
||||
export const inputRef = createRef<TextInput>();
|
||||
export const rootNavigatorRef = createNavigationContainerRef<RouteParams>();
|
||||
export const tabBarRef = createRef<TabsRef>();
|
||||
export const rootNavigatorRef = createNavigationContainerRef<{
|
||||
FluidPanelsView: undefined;
|
||||
AppLock: undefined;
|
||||
Settings: undefined;
|
||||
}>();
|
||||
export const appNavigatorRef = createNavigationContainerRef<RouteParams>();
|
||||
export const fluidTabsRef = createRef<TabsRef>();
|
||||
export const editorRef = createRef<View>();
|
||||
|
||||
@@ -17,51 +17,69 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Item, ItemType } from "@notesnook/core";
|
||||
import { Monographs } from "../screens/notes/monographs";
|
||||
export const MenuItemsList = [
|
||||
|
||||
export type SideMenuItem = {
|
||||
dataType?: ItemType | "monograph";
|
||||
data?: Item;
|
||||
title: string;
|
||||
id: string;
|
||||
icon: string;
|
||||
onPress?: (item: SideMenuItem) => void;
|
||||
onLongPress?: (item: SideMenuItem) => void;
|
||||
};
|
||||
|
||||
export const MenuItemsList: SideMenuItem[] = [
|
||||
{
|
||||
id: "notes",
|
||||
name: "Notes",
|
||||
icon: "home-variant-outline",
|
||||
close: true
|
||||
dataType: "note",
|
||||
id: "Notes",
|
||||
title: "Notes",
|
||||
icon: "note-outline"
|
||||
},
|
||||
// {
|
||||
// dataType: "notebook",
|
||||
// id: "Notebooks",
|
||||
// title: "Notebooks",
|
||||
// icon: "book-outline"
|
||||
// },
|
||||
{
|
||||
dataType: "note",
|
||||
id: "Favorites",
|
||||
title: "Favorites",
|
||||
icon: "star-outline"
|
||||
},
|
||||
// {
|
||||
// dataType: "tag",
|
||||
// id: "Tags",
|
||||
// title: "Tags",
|
||||
// icon: "pound"
|
||||
// },
|
||||
{
|
||||
dataType: "reminder",
|
||||
id: "Reminders",
|
||||
title: "Reminders",
|
||||
icon: "bell"
|
||||
},
|
||||
{
|
||||
id: "notebooks",
|
||||
name: "Notebooks",
|
||||
icon: "book-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
id: "favorites",
|
||||
name: "Favorites",
|
||||
icon: "star-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
id: "tags",
|
||||
name: "Tags",
|
||||
icon: "pound",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
id: "reminders",
|
||||
name: "Reminders",
|
||||
icon: "bell",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
id: "monographs",
|
||||
name: "Monographs",
|
||||
dataType: "monograph",
|
||||
id: "Monographs",
|
||||
title: "Monographs",
|
||||
icon: "text-box-multiple-outline",
|
||||
close: true,
|
||||
func: () => {
|
||||
onPress: () => {
|
||||
Monographs.navigate();
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "trash",
|
||||
name: "Trash",
|
||||
icon: "delete-outline",
|
||||
close: true
|
||||
dataType: "note",
|
||||
id: "Archive",
|
||||
title: "Archive",
|
||||
icon: "archive"
|
||||
},
|
||||
{
|
||||
dataType: "note",
|
||||
id: "Trash",
|
||||
title: "Trash",
|
||||
icon: "delete-outline"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { db } from "../common/database";
|
||||
import { eSendEvent } from "../services/event-manager";
|
||||
import { useNotebookStore } from "../stores/use-notebook-store";
|
||||
import { eOnNotebookUpdated } from "./events";
|
||||
|
||||
export async function findRootNotebookId(id: string) {
|
||||
@@ -51,9 +52,14 @@ export async function getParentNotebookId(id: string) {
|
||||
return relation?.[0]?.fromId;
|
||||
}
|
||||
|
||||
export async function updateNotebook(id?: string) {
|
||||
export async function updateNotebook(id?: string, updateParent?: boolean) {
|
||||
eSendEvent(eOnNotebookUpdated, id);
|
||||
if (id) {
|
||||
eSendEvent(eOnNotebookUpdated, await getParentNotebookId(id));
|
||||
if (updateParent && id) {
|
||||
const parent = await getParentNotebookId(id);
|
||||
if (parent) {
|
||||
eSendEvent(eOnNotebookUpdated, parent);
|
||||
} else {
|
||||
useNotebookStore.getState().refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,31 +81,33 @@ export const normalize = (size) => {
|
||||
return correction(size, 1);
|
||||
}
|
||||
};
|
||||
export const SIZE = {
|
||||
xxs: normalize(11) * scale.fontScale,
|
||||
xs: normalize(12.5) * scale.fontScale,
|
||||
|
||||
function getSize() {
|
||||
return {
|
||||
xxxs: normalize(11) * scale.fontScale,
|
||||
xxs: normalize(12.5) * scale.fontScale,
|
||||
xs: normalize(14) * scale.fontScale,
|
||||
sm: normalize(15) * scale.fontScale,
|
||||
md: normalize(16.5) * scale.fontScale,
|
||||
lg: normalize(22) * scale.fontScale,
|
||||
xl: normalize(24) * scale.fontScale,
|
||||
xxl: normalize(28) * scale.fontScale,
|
||||
xxxl: normalize(32) * scale.fontScale
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const SIZE = getSize();
|
||||
|
||||
export function updateSize() {
|
||||
SIZE.xxs = normalize(11) * scale.fontScale;
|
||||
SIZE.xs = normalize(12.5) * scale.fontScale;
|
||||
SIZE.sm = normalize(15) * scale.fontScale;
|
||||
SIZE.md = normalize(16.5) * scale.fontScale;
|
||||
SIZE.lg = normalize(22) * scale.fontScale;
|
||||
SIZE.xl = normalize(24) * scale.fontScale;
|
||||
SIZE.xxl = normalize(28) * scale.fontScale;
|
||||
SIZE.xxxl = normalize(32) * scale.fontScale;
|
||||
const newSize = getSize();
|
||||
for (const key in SIZE) {
|
||||
SIZE[key] = newSize[key];
|
||||
}
|
||||
ph = normalize(10) * scale.fontScale;
|
||||
pv = normalize(10) * scale.fontScale;
|
||||
}
|
||||
|
||||
export const br = 5; // border radius
|
||||
export const br = 8; // border radius
|
||||
export var ph = normalize(10); // padding horizontal
|
||||
export var pv = normalize(10); // padding vertical
|
||||
export const opacity = 0.5; // active opacity
|
||||
|
||||
@@ -16,21 +16,9 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import DialogProvider from "../components/dialog-provider";
|
||||
import { Toast } from "../components/toast";
|
||||
import { TabHolder } from "./tabs-holder";
|
||||
import { ScopedThemeProvider } from "@notesnook/theme";
|
||||
const _ApplicationHolder = () => {
|
||||
return (
|
||||
<>
|
||||
<TabHolder />
|
||||
<ScopedThemeProvider value="dialog">
|
||||
<Toast />
|
||||
</ScopedThemeProvider>
|
||||
<DialogProvider />
|
||||
</>
|
||||
);
|
||||
export const DefaultAppStyles = {
|
||||
GAP: 16,
|
||||
GAP_SMALL: 8,
|
||||
GAP_VERTICAL: 6,
|
||||
GAP_VERTICAL_SMALL: 4
|
||||
};
|
||||
export const ApplicationHolder = React.memo(_ApplicationHolder, () => true);
|
||||
Binary file not shown.
@@ -24,7 +24,7 @@ import {
|
||||
} from "@notesnook/intl/dist/locales/$pseudo-LOCALE.json";
|
||||
|
||||
i18n.load({
|
||||
en: __DEV__ ? $pseudo : $en
|
||||
en: !__DEV__ ? $pseudo : $en
|
||||
});
|
||||
setI18nGlobal(i18n);
|
||||
i18n.activate("en");
|
||||
|
||||
@@ -73,7 +73,9 @@
|
||||
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",
|
||||
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
|
||||
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
|
||||
"@ammarahmed/react-native-share-extension": "^2.9.0"
|
||||
"@ammarahmed/react-native-share-extension": "^2.9.0",
|
||||
"react-native-pager-view": "^6.5.1",
|
||||
"react-native-tab-view": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"detox": "^20.27.6",
|
||||
@@ -88,6 +90,7 @@
|
||||
"@tsconfig/react-native": "^3.0.2",
|
||||
"@types/html-to-text": "^8.0.1",
|
||||
"@types/metro-config": "^0.76.3",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-native": "^0.69.1",
|
||||
"@types/react-native-vector-icons": "^6.4.10",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
|
||||
190
apps/mobile/package-lock.json
generated
190
apps/mobile/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.31",
|
||||
"version": "3.0.32",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.31",
|
||||
"version": "3.0.32",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -14,7 +14,6 @@
|
||||
"app/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ammarahmed/react-native-share-extension": "^2.9.0",
|
||||
"@notesnook/common": "file:../../packages/common",
|
||||
"@notesnook/core": "file:../../packages/core",
|
||||
"@notesnook/crypto": "file:../../packages/crypto",
|
||||
@@ -3728,6 +3727,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook-importer/core": "^2.1.1",
|
||||
"@notesnook/common": "file:../common",
|
||||
"@notesnook/intl": "file:../intl",
|
||||
"@notesnook/theme": "file:../theme",
|
||||
@@ -7743,7 +7743,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/prop-types": {
|
||||
"version": "15.7.11",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/q": {
|
||||
@@ -7763,7 +7763,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/react": {
|
||||
"version": "18.2.39",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
@@ -7794,7 +7794,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/scheduler": {
|
||||
"version": "0.16.8",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/semver": {
|
||||
@@ -12619,7 +12619,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/immer": {
|
||||
"version": "9.0.21",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -23091,6 +23091,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"../../packages/editor/node_modules/jsesc": {
|
||||
@@ -23141,6 +23142,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
@@ -23652,6 +23654,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@@ -23670,6 +23673,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
@@ -23808,6 +23812,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@@ -28315,6 +28320,7 @@
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
|
||||
"@ammarahmed/react-native-share-extension": "^2.9.0",
|
||||
"@ammarahmed/react-native-sodium": "^1.6.1",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
"@callstack/repack": "^4.1.1",
|
||||
@@ -28357,6 +28363,7 @@
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-orientation": "github:yamill/react-native-orientation",
|
||||
"react-native-pager-view": "^6.5.1",
|
||||
"react-native-pdf": "6.6.2",
|
||||
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
|
||||
"react-native-quick-sqlite": "^8.0.6",
|
||||
@@ -28369,6 +28376,7 @@
|
||||
"react-native-share": "^12.0.3",
|
||||
"react-native-svg": "^12.3.0",
|
||||
"react-native-swiper-flatlist": "3.2.2",
|
||||
"react-native-tab-view": "^4.0.2",
|
||||
"react-native-theme-switch-animation": "^0.6.0",
|
||||
"react-native-tooltips": "^1.0.3",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
@@ -28390,6 +28398,7 @@
|
||||
"@types/html-to-text": "^8.0.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/metro-config": "^0.76.3",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-native": "^0.69.1",
|
||||
"@types/react-native-vector-icons": "^6.4.10",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
@@ -28590,6 +28599,7 @@
|
||||
},
|
||||
"node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -28709,6 +28719,7 @@
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -28968,6 +28979,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
|
||||
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
@@ -28982,6 +28994,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz",
|
||||
"integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
|
||||
@@ -29130,6 +29143,7 @@
|
||||
"version": "7.21.0-placeholder-for-preset-env.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
|
||||
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
@@ -29142,6 +29156,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
|
||||
"integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
|
||||
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
@@ -29176,6 +29191,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-class-properties": {
|
||||
"version": "7.12.13",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.12.13"
|
||||
@@ -29188,6 +29204,7 @@
|
||||
"version": "7.14.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
|
||||
"integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.14.5"
|
||||
},
|
||||
@@ -29225,6 +29242,7 @@
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
|
||||
"integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
},
|
||||
@@ -29249,6 +29267,7 @@
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz",
|
||||
"integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -29263,6 +29282,7 @@
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz",
|
||||
"integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -29277,6 +29297,7 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
|
||||
"integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.10.4"
|
||||
},
|
||||
@@ -29288,6 +29309,7 @@
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
|
||||
"integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.8.0"
|
||||
},
|
||||
@@ -29387,6 +29409,7 @@
|
||||
"version": "7.14.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
|
||||
"integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.14.5"
|
||||
},
|
||||
@@ -29414,6 +29437,7 @@
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
|
||||
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
@@ -29442,6 +29466,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz",
|
||||
"integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -29472,6 +29497,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-block-scoped-functions": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29500,6 +29526,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz",
|
||||
"integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29515,6 +29542,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz",
|
||||
"integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -29579,6 +29607,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz",
|
||||
"integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29594,6 +29623,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz",
|
||||
"integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -29608,6 +29638,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
|
||||
"integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
@@ -29620,6 +29651,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-exponentiation-operator": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5",
|
||||
@@ -29636,6 +29668,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz",
|
||||
"integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-export-namespace-from": "^7.8.3"
|
||||
@@ -29663,6 +29696,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-for-of": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29693,6 +29727,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz",
|
||||
"integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-json-strings": "^7.8.3"
|
||||
@@ -29721,6 +29756,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz",
|
||||
"integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
|
||||
@@ -29734,6 +29770,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-member-expression-literals": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29749,6 +29786,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz",
|
||||
"integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29779,6 +29817,7 @@
|
||||
"version": "7.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz",
|
||||
"integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-module-transforms": "^7.22.5",
|
||||
@@ -29796,6 +29835,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz",
|
||||
"integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -29825,6 +29865,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz",
|
||||
"integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -29854,6 +29895,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz",
|
||||
"integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-numeric-separator": "^7.10.4"
|
||||
@@ -29869,6 +29911,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz",
|
||||
"integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.5",
|
||||
"@babel/helper-compilation-targets": "^7.22.5",
|
||||
@@ -29885,6 +29928,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-object-super": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -29901,6 +29945,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz",
|
||||
"integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
|
||||
@@ -29974,6 +30019,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-property-literals": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30043,6 +30089,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -30059,6 +30106,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz",
|
||||
"integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30151,6 +30199,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz",
|
||||
"integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30181,6 +30230,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz",
|
||||
"integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30195,6 +30245,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz",
|
||||
"integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30224,6 +30275,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz",
|
||||
"integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30239,6 +30291,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz",
|
||||
"integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.5",
|
||||
"@babel/helper-compilation-targets": "^7.22.5",
|
||||
@@ -30332,6 +30385,7 @@
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz",
|
||||
"integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
|
||||
@@ -30347,6 +30401,7 @@
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -33650,7 +33705,8 @@
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/graceful-fs": {
|
||||
"version": "4.1.6",
|
||||
@@ -33756,14 +33812,14 @@
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.5",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
|
||||
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
@@ -34119,6 +34175,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
|
||||
"integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
|
||||
@@ -34127,22 +34184,26 @@
|
||||
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
|
||||
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="
|
||||
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
|
||||
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
@@ -34152,12 +34213,14 @@
|
||||
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
|
||||
"integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
@@ -34169,6 +34232,7 @@
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
|
||||
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
@@ -34177,6 +34241,7 @@
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
|
||||
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
@@ -34184,12 +34249,14 @@
|
||||
"node_modules/@webassemblyjs/utf8": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
|
||||
"integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
@@ -34205,6 +34272,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
|
||||
"integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
@@ -34217,6 +34285,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
|
||||
"integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
@@ -34228,6 +34297,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
|
||||
"integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
@@ -34241,6 +34311,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
|
||||
"integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@xtuc/long": "4.2.2"
|
||||
@@ -34298,12 +34369,14 @@
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@yarnpkg/lockfile": {
|
||||
"version": "1.1.0",
|
||||
@@ -35302,6 +35375,7 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
|
||||
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
@@ -35732,7 +35806,7 @@
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
@@ -36332,6 +36406,7 @@
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
|
||||
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
@@ -36451,7 +36526,8 @@
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.0.0",
|
||||
@@ -36796,6 +36872,7 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^4.1.1"
|
||||
@@ -36808,6 +36885,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
@@ -36971,6 +37049,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"estraverse": "^5.2.0"
|
||||
},
|
||||
@@ -36982,6 +37061,7 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
@@ -36990,6 +37070,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -37749,7 +37830,8 @@
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/global": {
|
||||
"version": "4.4.0",
|
||||
@@ -39838,7 +39920,8 @@
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-schema-ref-resolver": {
|
||||
"version": "1.0.1",
|
||||
@@ -40314,6 +40397,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.11.5"
|
||||
}
|
||||
@@ -42543,6 +42627,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
@@ -42564,30 +42649,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom/node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-freeze": {
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
@@ -43023,6 +43084,15 @@
|
||||
"react-native": ">=0.40"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-pager-view": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.5.1.tgz",
|
||||
"integrity": "sha512-YdX7bP+rPYvATMU7HzlMq9JaG3ui/+cVRbFZFGW+QshDULANFg9ECR1BA7H7JTIcO/ZgWCwF+1aVmYG5yBA9Og==",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-pdf": {
|
||||
"version": "6.6.2",
|
||||
"license": "MIT",
|
||||
@@ -43187,6 +43257,27 @@
|
||||
"react-native": ">=0.59.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-tab-view": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-4.0.2.tgz",
|
||||
"integrity": "sha512-u70Et9onvAGhcBRda8NO+JgM/10G3kg/jWsbDY7QQSOAri7F7+VVS/4uupxjbCDSkL1BJdUOqhUToK/M2AH34Q==",
|
||||
"dependencies": {
|
||||
"use-latest-callback": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-pager-view": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-tab-view/node_modules/use-latest-callback": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz",
|
||||
"integrity": "sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-theme-switch-animation": {
|
||||
"version": "0.6.0",
|
||||
"license": "MIT",
|
||||
@@ -43507,6 +43598,7 @@
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
"version": "0.15.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.4"
|
||||
@@ -43859,6 +43951,7 @@
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.1",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
@@ -44516,6 +44609,7 @@
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -44593,6 +44687,7 @@
|
||||
"version": "5.3.10",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
|
||||
"integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jest-worker": "^27.4.5",
|
||||
@@ -44626,6 +44721,7 @@
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
|
||||
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"merge-stream": "^2.0.0",
|
||||
@@ -44639,6 +44735,7 @@
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
@@ -45215,6 +45312,7 @@
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
@@ -45238,6 +45336,7 @@
|
||||
"version": "5.94.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
||||
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.0",
|
||||
@@ -45353,6 +45452,7 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
|
||||
@@ -83,7 +83,16 @@ const EXTRA_ICON_NAMES = [
|
||||
"arrow-up-bold",
|
||||
"login",
|
||||
"gift",
|
||||
"share"
|
||||
"share",
|
||||
"note-outline",
|
||||
"weather-sunny",
|
||||
"weather-night",
|
||||
"notebook-outline",
|
||||
"archive",
|
||||
"home-outline",
|
||||
"checkbox-blank-outline",
|
||||
"checkbox-outline",
|
||||
"checkbox-intermediate"
|
||||
];
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
|
||||
1
packages/editor-mobile/package-lock.json
generated
1
packages/editor-mobile/package-lock.json
generated
@@ -63,6 +63,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook-importer/core": "^2.1.1",
|
||||
"@notesnook/common": "file:../common",
|
||||
"@notesnook/intl": "file:../intl",
|
||||
"@notesnook/theme": "file:../theme",
|
||||
|
||||
@@ -2443,5 +2443,13 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
fontLigatures: () => t`Font ligatures`,
|
||||
fontLigaturesDesc: () =>
|
||||
t`Enable ligatures for common symbols like →, ←, etc`,
|
||||
expandSidebar: () => t`Expand sidebar`
|
||||
expandSidebar: () => t`Expand sidebar`,
|
||||
viewAllLimits: () => `View all limits`,
|
||||
freePlan: () => t`Free plan`,
|
||||
proPlan: () => t`Pro plan`,
|
||||
essentialPlan: () => t`Essential plan`,
|
||||
believerPlan: () => t`Believer plan`,
|
||||
storage: () => t`Storage`,
|
||||
used: () => t`used`,
|
||||
editProfile: () => t`Edit profile`
|
||||
};
|
||||
|
||||
6
packages/theme/package-lock.json
generated
6
packages/theme/package-lock.json
generated
@@ -417,9 +417,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@theme-ui/css": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/css/-/css-0.16.1.tgz",
|
||||
"integrity": "sha512-8TO2DbiqPrRyTlGRIElDak/p0M4ykyd8LkeavyOF/sTE9s93AwyFcle6KYYMEULrJP49SyYiEvTif7J7Z50DhA==",
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/css/-/css-0.16.2.tgz",
|
||||
"integrity": "sha512-fNe+FhwKC5+7jQfxCRnm3oqYNhMFuiWiLA9OoLBEkt3b4egot29UK1+fqemwiNVjl206e2fPT5Z7uXRdb6LC2A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
|
||||
Reference in New Issue
Block a user