From be21c4735fcdea598f54ce6675a60c70603e309c Mon Sep 17 00:00:00 2001 From: ammarahm-ed Date: Tue, 30 Aug 2022 18:27:09 +0500 Subject: [PATCH] mobile: fix all eslint warnings --- .../app/components/announcements/index.js | 7 +- .../app/components/attachments/actions.js | 7 +- .../components/attachments/attachment-item.js | 6 +- apps/mobile/app/components/auth/two-factor.js | 7 +- .../components/container/floating-button.js | 28 +- apps/mobile/app/components/dialog/index.js | 18 +- .../dialogs/jump-to-section/index.js | 20 +- apps/mobile/app/components/header/index.js | 24 +- apps/mobile/app/components/header/title.js | 37 +-- apps/mobile/app/components/launcher/index.js | 23 +- .../list-items/headers/section-header.js | 7 +- .../selection-wrapper/action-strip.js | 2 +- .../list-items/selection-wrapper/selection.js | 9 +- apps/mobile/app/components/list/index.js | 2 +- .../app/components/note-history/index.js | 9 +- .../app/components/premium/premium-toast.js | 40 +-- .../app/components/premium/pricing-plans.js | 7 +- .../app/components/selection-header/index.js | 18 +- .../app/components/sheet-provider/index.js | 53 ++-- .../app/components/sheets/add-to/index.js | 7 +- .../components/sheets/manage-tags/index.js | 24 +- .../components/sheets/restore-data/index.js | 8 +- .../app/components/sheets/update/index.js | 2 +- .../app/components/side-menu/color-section.js | 30 +- apps/mobile/app/components/side-menu/index.js | 2 +- .../app/components/side-menu/menu-item.js | 28 +- .../components/side-menu/pinned-section.js | 34 ++- apps/mobile/app/components/tabs/index.tsx | 22 +- apps/mobile/app/components/toast/index.js | 75 ++--- .../app/components/ui/pressable/index.tsx | 2 +- apps/mobile/app/components/ui/sheet/index.js | 2 +- .../ui/transitions/bouncing-view.js | 2 +- apps/mobile/app/hooks/use-actions.js | 26 +- apps/mobile/app/hooks/use-app-events.js | 259 ++++++++++-------- .../app/hooks/use-attachment-progress.js | 2 +- apps/mobile/app/hooks/use-editor-tags.ts | 15 +- apps/mobile/app/hooks/use-immediate.ts | 4 +- .../app/hooks/use-is-floating-keyboard.ts | 12 +- apps/mobile/app/hooks/use-navigation-focus.ts | 6 +- apps/mobile/app/hooks/use-rotator.ts | 3 +- apps/mobile/app/hooks/use-sync-progress.ts | 2 +- apps/mobile/app/hooks/use-tooltip.ts | 2 +- apps/mobile/app/hooks/use-vault-status.js | 2 +- apps/mobile/app/hooks/use-vault-status.ts | 2 +- .../mobile/app/navigation/navigation-stack.js | 4 +- apps/mobile/app/navigation/tabs-holder.js | 27 +- apps/mobile/app/screens/editor/index.tsx | 49 ++-- apps/mobile/app/screens/editor/loading.js | 58 ++-- .../editor/tiptap/use-editor-events.ts | 255 ++++++++--------- .../app/screens/editor/tiptap/use-editor.ts | 154 ++++++----- apps/mobile/app/screens/notebook/index.tsx | 56 ++-- apps/mobile/app/screens/notes/colored.tsx | 6 +- apps/mobile/app/screens/notes/common.ts | 6 +- apps/mobile/app/screens/notes/index.tsx | 88 +++--- apps/mobile/app/screens/notes/monographs.tsx | 6 +- apps/mobile/app/screens/notes/tagged.tsx | 6 +- apps/mobile/app/screens/notes/topic-notes.tsx | 6 +- apps/mobile/app/screens/search/index.js | 2 +- apps/mobile/app/screens/settings/2fa.tsx | 2 +- apps/mobile/app/screens/settings/debug.tsx | 107 ++++---- .../screens/settings/editor/tool-sheet.tsx | 95 +++---- .../app/screens/settings/editor/tool.tsx | 117 ++++---- apps/mobile/app/screens/settings/index.tsx | 15 +- apps/mobile/app/services/tip-manager.ts | 2 +- apps/mobile/app/utils/types.ts | 4 + 65 files changed, 1075 insertions(+), 887 deletions(-) diff --git a/apps/mobile/app/components/announcements/index.js b/apps/mobile/app/components/announcements/index.js index 45b959cc1..8742bb8cb 100644 --- a/apps/mobile/app/components/announcements/index.js +++ b/apps/mobile/app/components/announcements/index.js @@ -31,6 +31,7 @@ import { } from "../../utils/events"; import BaseDialog from "../dialog/base-dialog"; import { allowedOnPlatform, renderItem } from "./functions"; +import { useCallback } from "react"; export const AnnouncementDialog = () => { const colors = useThemeStore((state) => state.colors); @@ -45,20 +46,20 @@ export const AnnouncementDialog = () => { eUnSubscribeEvent(eOpenAnnouncementDialog, open); eUnSubscribeEvent(eCloseAnnouncementDialog, close); }; - }, [visible]); + }, [close, visible]); const open = (data) => { setInfo(data); setVisible(true); }; - const close = () => { + const close = useCallback(() => { if (visible) { remove(info.id); setInfo(null); setVisible(false); } - }; + }, [info.id, remove, visible]); return ( { const colors = useThemeStore((state) => state.colors); @@ -154,7 +155,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => { } ]; - const getNotes = () => { + const getNotes = useCallback(() => { let allNotes = db.notes.all; let attachmentNotes = attachment.noteIds?.map((id) => { let index = allNotes?.findIndex((note) => id === note.id); @@ -169,11 +170,11 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => { } }); return attachmentNotes; - }; + }, [attachment.noteIds]); useEffect(() => { setNotes(getNotes()); - }, [attachment]); + }, [attachment, getNotes]); return ( { attachment, encryption ); - const encryptionProgress = encryption - ? useAttachmentStore((state) => state.encryptionProgress) - : null; + const encryptionProgress = useAttachmentStore( + (state) => state.encryptionProgress + ); const onPress = () => { Actions.present(attachment, setAttachments, attachment.metadata.hash); diff --git a/apps/mobile/app/components/auth/two-factor.js b/apps/mobile/app/components/auth/two-factor.js index 14703f199..fc6b02b8b 100644 --- a/apps/mobile/app/components/auth/two-factor.js +++ b/apps/mobile/app/components/auth/two-factor.js @@ -35,6 +35,7 @@ import { PressableButton } from "../ui/pressable"; import Seperator from "../ui/seperator"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; +import { useCallback } from "react"; const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => { const colors = useThemeStore((state) => state.colors); @@ -128,9 +129,9 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => { if (currentMethod.method === "sms" || currentMethod.method === "email") { onSendCode(); } - }, [currentMethod.method]); + }, [currentMethod.method, onSendCode]); - const onSendCode = async () => { + const onSendCode = useCallback(async () => { if (seconds || sending) return; // TODO setSending(true); @@ -143,7 +144,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => { setSending(false); ToastEvent.error(e, "Error sending 2FA Code", "local"); } - }; + }, [currentMethod.method, mfaInfo.token, seconds, sending, start]); return ( diff --git a/apps/mobile/app/components/container/floating-button.js b/apps/mobile/app/components/container/floating-button.js index 7bfd08ed8..f8b6b4e63 100644 --- a/apps/mobile/app/components/container/floating-button.js +++ b/apps/mobile/app/components/container/floating-button.js @@ -32,6 +32,7 @@ import { useSettingStore } from "../../stores/use-setting-store"; import { getElevation, showTooltip, TOOLTIP_POSITIONS } from "../../utils"; import { normalize, SIZE } from "../../utils/size"; import { PressableButton } from "../ui/pressable"; +import { useCallback } from "react"; export const FloatingButton = ({ title, @@ -58,26 +59,29 @@ export const FloatingButton = ({ useEffect(() => { animate(selectionMode ? 150 : 0); - }, [selectionMode]); + }, [animate, selectionMode]); - function animate(toValue) { - translate.value = withTiming(toValue, { - duration: 250, - easing: Easing.elastic(1) - }); - } + const animate = useCallback( + (toValue) => { + translate.value = withTiming(toValue, { + duration: 250, + easing: Easing.elastic(1) + }); + }, + [translate] + ); - const onKeyboardHide = async () => { + const onKeyboardHide = useCallback(async () => { editorState().keyboardState = false; if (deviceMode !== "mobile") return; animate(0); - }; + }, [animate, deviceMode]); - const onKeyboardShow = async () => { + const onKeyboardShow = useCallback(async () => { editorState().keyboardState = true; if (deviceMode !== "mobile") return; animate(150); - }; + }, [animate, deviceMode]); useEffect(() => { let sub1 = Keyboard.addListener("keyboardDidShow", onKeyboardShow); @@ -86,7 +90,7 @@ export const FloatingButton = ({ sub1?.remove(); sub2?.remove(); }; - }, [deviceMode]); + }, [deviceMode, onKeyboardHide, onKeyboardShow]); const paddings = { ios: 20, android: 20, diff --git a/apps/mobile/app/components/dialog/index.js b/apps/mobile/app/components/dialog/index.js index e74abd619..21c9e9e44 100644 --- a/apps/mobile/app/components/dialog/index.js +++ b/apps/mobile/app/components/dialog/index.js @@ -33,6 +33,7 @@ import Seperator from "../ui/seperator"; import BaseDialog from "./base-dialog"; import DialogButtons from "./dialog-buttons"; import DialogHeader from "./dialog-header"; +import { useCallback } from "react"; export const Dialog = ({ context = "global" }) => { const colors = useThemeStore((state) => state.colors); @@ -63,7 +64,7 @@ export const Dialog = ({ context = "global" }) => { eUnSubscribeEvent(eOpenSimpleDialog, show); eUnSubscribeEvent(eCloseSimpleDialog, hide); }; - }, []); + }, [show]); const onPressPositive = async () => { if (dialogInfo.positivePress) { @@ -79,12 +80,15 @@ export const Dialog = ({ context = "global" }) => { hide(); }; - const show = (data) => { - if (!data.context) data.context = "global"; - if (data.context !== context) return; - setDialogInfo(data); - setVisible(true); - }; + const show = useCallback( + (data) => { + if (!data.context) data.context = "global"; + if (data.context !== context) return; + setDialogInfo(data); + setVisible(true); + }, + [context] + ); const hide = () => { setInputValue(null); diff --git a/apps/mobile/app/components/dialogs/jump-to-section/index.js b/apps/mobile/app/components/dialogs/jump-to-section/index.js index 906692a37..1cab970c0 100644 --- a/apps/mobile/app/components/dialogs/jump-to-section/index.js +++ b/apps/mobile/app/components/dialogs/jump-to-section/index.js @@ -35,6 +35,7 @@ import { SIZE } from "../../../utils/size"; import BaseDialog from "../../dialog/base-dialog"; import { PressableButton } from "../../ui/pressable"; import Paragraph from "../../ui/typography/paragraph"; +import { useCallback } from "react"; const offsets = []; let timeout = null; @@ -66,7 +67,7 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => { eUnSubscribeEvent(eCloseJumpToDialog, close); eUnSubscribeEvent(eScrollEvent, onScroll); }; - }, []); + }, [open]); const onScroll = (data) => { let y = data.y; @@ -80,10 +81,13 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => { }, 200); }; - const open = (_type) => { - if (_type !== type) return; - setVisible(true); - }; + const open = useCallback( + (_type) => { + if (_type !== type) return; + setVisible(true); + }, + [type] + ); const close = () => { setVisible(false); @@ -91,9 +95,9 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => { useEffect(() => { loadOffsets(); - }, [notes]); + }, [loadOffsets, notes]); - const loadOffsets = () => { + const loadOffsets = useCallback(() => { notes .filter((i) => i.type === "header") .map((item, index) => { @@ -108,7 +112,7 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => { offset = offset + ind * 100 + msgOffset; offsets.push(offset); }); - }; + }, [notes]); return !visible ? null : ( { const colors = useThemeStore((state) => state.colors); @@ -41,22 +42,25 @@ const _Header = () => { (state) => state.currentScreen?.name ); - const onScroll = (data) => { - if (data.y > 150) { - if (!hide) return; - setHide(false); - } else { - if (hide) return; - setHide(true); - } - }; + const onScroll = useCallback( + (data) => { + if (data.y > 150) { + if (!hide) return; + setHide(false); + } else { + if (hide) return; + setHide(true); + } + }, + [hide] + ); useEffect(() => { eSubscribeEvent(eScrollEvent, onScroll); return () => { eUnSubscribeEvent(eScrollEvent, onScroll); }; - }, [hide]); + }, [hide, onScroll]); return selectionMode ? null : ( <> diff --git a/apps/mobile/app/components/header/title.js b/apps/mobile/app/components/header/title.js index 2e79a62b6..d3a6f6e1a 100644 --- a/apps/mobile/app/components/header/title.js +++ b/apps/mobile/app/components/header/title.js @@ -30,6 +30,7 @@ import { eScrollEvent } from "../../utils/events"; import { SIZE } from "../../utils/size"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; +import { useCallback } from "react"; const titleState = {}; @@ -46,7 +47,6 @@ export const Title = () => { : false ); const isHidden = titleState[currentScreen.id]; - console.log(currentScreen, "header"); const notebook = isTopic && currentScreen.notebookId ? db.notebooks?.notebook(currentScreen.notebookId)?.data @@ -54,19 +54,22 @@ export const Title = () => { const title = currentScreen.title; const isTag = currentScreen?.name === "TaggedNotes"; - const onScroll = (data) => { - if (currentScreen.name !== "Notebook") { - setHide(false); - return; - } - if (data.y > 150) { - if (!hide) return; - setHide(false); - } else { - if (hide) return; - setHide(true); - } - }; + const onScroll = useCallback( + (data) => { + if (currentScreen.name !== "Notebook") { + setHide(false); + return; + } + if (data.y > 150) { + if (!hide) return; + setHide(false); + } else { + if (hide) return; + setHide(true); + } + }, + [currentScreen.name, hide] + ); useEffect(() => { if (currentScreen.name === "Notebook") { @@ -78,18 +81,18 @@ export const Title = () => { } else { setHide(titleState[currentScreen.id]); } - }, [currentScreen.id]); + }, [currentScreen.id, currentScreen.name]); useEffect(() => { titleState[currentScreen.id] = hide; - }, [hide]); + }, [currentScreen.id, hide]); useEffect(() => { eSubscribeEvent(eScrollEvent, onScroll); return () => { eUnSubscribeEvent(eScrollEvent, onScroll); }; - }, [hide]); + }, [hide, onScroll]); function navigateToNotebook() { if (!isTopic) return; diff --git a/apps/mobile/app/components/launcher/index.js b/apps/mobile/app/components/launcher/index.js index d69e441f7..e80fcbea8 100644 --- a/apps/mobile/app/components/launcher/index.js +++ b/apps/mobile/app/components/launcher/index.js @@ -58,6 +58,7 @@ import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; import { Walkthrough } from "../walkthroughs"; import { useAppState } from "../../hooks/use-app-state"; +import { useCallback } from "react"; const Launcher = React.memo( function Launcher() { @@ -75,7 +76,7 @@ const Launcher = React.memo( (state) => state.settings.introCompleted ); const dbInitCompleted = useRef(false); - const loadNotes = async () => { + const loadNotes = useCallback(async () => { if (verifyUser) { return; } @@ -94,9 +95,9 @@ const Launcher = React.memo( }); }); }); - }; + }, [doAppLoadActions, setLoading, verifyUser]); - const init = async () => { + const init = useCallback(async () => { if (!dbInitCompleted.current) { await RNBootSplash.hide({ fade: true }); await loadDatabase(); @@ -121,7 +122,7 @@ const Launcher = React.memo( } else { useUserStore.getState().setUser(await db.user?.getUser()); } - }; + }, [loadNotes, verifyUser]); // useEffect(() => { // hideSplashScreen(); @@ -134,9 +135,9 @@ const Launcher = React.memo( return () => { dbInitCompleted.current = false; }; - }, [loading]); + }, [doAppLoadActions, loading]); - const doAppLoadActions = async () => { + const doAppLoadActions = useCallback(async () => { await sleep(500); if (SettingsService.get().sessionExpired) { eSendEvent("session_expired"); @@ -170,7 +171,7 @@ const Launcher = React.memo( } }); } - }; + }, [introCompleted]); const checkAppUpdateAvailable = async () => { if (__DEV__) return; @@ -237,7 +238,7 @@ const Launcher = React.memo( // return false; }; - const onUnlockBiometrics = async () => { + const onUnlockBiometrics = useCallback(async () => { if (!(await BiometricService.isBiometryAvailable())) { ToastEvent.show({ heading: "Biometrics unavailable", @@ -254,17 +255,17 @@ const Launcher = React.memo( enabled(false); password.current = null; } - }; + }, [setVerifyUser]); useEffect(() => { init(); - }, [verifyUser]); + }, [init, verifyUser]); useEffect(() => { if (verifyUser && appState === "active") { onUnlockBiometrics(); } - }, [appState]); + }, [appState, onUnlockBiometrics, verifyUser]); const onSubmit = async () => { if (!password.current) return; diff --git a/apps/mobile/app/components/list-items/headers/section-header.js b/apps/mobile/app/components/list-items/headers/section-header.js index 08cdde531..eff2114c0 100644 --- a/apps/mobile/app/components/list-items/headers/section-header.js +++ b/apps/mobile/app/components/list-items/headers/section-header.js @@ -36,6 +36,7 @@ import { IconButton } from "../../ui/icon-button"; import { Button } from "../../ui/button"; import Sort from "../../sheets/sort"; import Heading from "../../ui/typography/heading"; +import { useCallback } from "react"; export const SectionHeader = React.memo( function SectionHeader({ item, index, type, color, screen }) { @@ -63,16 +64,16 @@ export const SectionHeader = React.memo( ? "Default" : groupBy.slice(0, 1).toUpperCase() + groupBy.slice(1, groupBy.length); - const onUpdate = () => { + const onUpdate = useCallback(() => { setGroupOptions({ ...db.settings?.getGroupOptions(type) }); - }; + }, [type]); useEffect(() => { eSubscribeEvent("groupOptionsUpdate", onUpdate); return () => { eUnSubscribeEvent("groupOptionsUpdate", onUpdate); }; - }, []); + }, [onUpdate]); return ( { useEffect(() => { if (note.type === "note") return; setIsPinnedToMenu(db.settings.isPinned(note.id)); - }, []); + }, [note.id, note.type]); const updateNotes = () => { Navigation.queueRoutesForUpdate( diff --git a/apps/mobile/app/components/list-items/selection-wrapper/selection.js b/apps/mobile/app/components/list-items/selection-wrapper/selection.js index 4cf44c8ef..0391fb3c6 100644 --- a/apps/mobile/app/components/list-items/selection-wrapper/selection.js +++ b/apps/mobile/app/components/list-items/selection-wrapper/selection.js @@ -50,7 +50,14 @@ export const SelectionIcon = ({ setActionStrip, item, compactMode }) => { } } } - }, [selectedItemsList, item.id]); + }, [ + selectedItemsList, + item.id, + selectionMode, + setActionStrip, + item.dateCreated, + selected + ]); const onPress = () => { setSelectedItem(item); diff --git a/apps/mobile/app/components/list/index.js b/apps/mobile/app/components/list/index.js index 871ca78a9..f20107447 100644 --- a/apps/mobile/app/components/list/index.js +++ b/apps/mobile/app/components/list/index.js @@ -105,7 +105,7 @@ const List = ({ screen={screen} /> ), - [] + [headerProps.color, headerProps.heading, screen, type] ); const _onRefresh = async () => { diff --git a/apps/mobile/app/components/note-history/index.js b/apps/mobile/app/components/note-history/index.js index ed77e4d7d..e014195e7 100644 --- a/apps/mobile/app/components/note-history/index.js +++ b/apps/mobile/app/components/note-history/index.js @@ -43,11 +43,10 @@ export default function NoteHistory({ note, fwdRef }) { setHistory([...(await db.noteHistory.get(note.id))]); setLoading(false); })(); - }, []); + }, [note.id]); - async function preview(item) { + const preview = useCallback(async (item) => { let content = await db.noteHistory.content(item.id); - presentSheet({ component: ( { let _start = timeConverter(start); @@ -94,7 +93,7 @@ export default function NoteHistory({ note, fwdRef }) { ), - [] + [colors.icon, preview] ); return ( diff --git a/apps/mobile/app/components/premium/premium-toast.js b/apps/mobile/app/components/premium/premium-toast.js index cda1a249f..7aa7e43b5 100644 --- a/apps/mobile/app/components/premium/premium-toast.js +++ b/apps/mobile/app/components/premium/premium-toast.js @@ -38,31 +38,35 @@ import { sleep } from "../../utils/time"; import { Button } from "../ui/button"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; +import { useCallback } from "react"; export const PremiumToast = ({ context = "global", offset = 0 }) => { const colors = useThemeStore((state) => state.colors); const [msg, setMsg] = useState(null); const timer = useRef(); - const open = (event) => { - if (!event) { - clearTimeout(timer); - timer.current = null; - setMsg(null); - return; - } - - if (event.context === context && msg?.desc !== event.desc) { - if (timer.current !== null) { - clearTimeout(timer.current); + const open = useCallback( + (event) => { + if (!event) { + clearTimeout(timer); timer.current = null; - } - setMsg(event); - timer.current = setTimeout(async () => { setMsg(null); - }, 3000); - } - }; + return; + } + + if (event.context === context && msg?.desc !== event.desc) { + if (timer.current !== null) { + clearTimeout(timer.current); + timer.current = null; + } + setMsg(event); + timer.current = setTimeout(async () => { + setMsg(null); + }, 3000); + } + }, + [context, msg?.desc] + ); useEffect(() => { eSubscribeEvent(eShowGetPremium, open); @@ -70,7 +74,7 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => { clearTimeout(timer.current); eUnSubscribeEvent(eShowGetPremium, open); }; - }, []); + }, [open]); const onPress = async () => { open(null); diff --git a/apps/mobile/app/components/premium/pricing-plans.js b/apps/mobile/app/components/premium/pricing-plans.js index 0738078b6..22512015b 100644 --- a/apps/mobile/app/components/premium/pricing-plans.js +++ b/apps/mobile/app/components/premium/pricing-plans.js @@ -47,6 +47,7 @@ import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; import { Walkthrough } from "../walkthroughs"; import { PricingItem } from "./pricing-item"; +import { useCallback } from "react"; const promoCyclesMonthly = { 1: "first month", @@ -77,7 +78,7 @@ export const PricingPlans = ({ const yearlyPlan = usePricing("yearly"); const monthlyPlan = usePricing("monthly"); - const getSkus = async () => { + const getSkus = useCallback(async () => { try { setLoading(true); if (promo?.promoCode) { @@ -88,7 +89,7 @@ export const PricingPlans = ({ setLoading(false); console.log("error getting sku", e); } - }; + }, [promo?.promoCode]); const getPromo = async (code) => { try { @@ -129,7 +130,7 @@ export const PricingPlans = ({ useEffect(() => { getSkus(); - }, []); + }, [getSkus]); const buySubscription = async (product) => { if (buying) return; diff --git a/apps/mobile/app/components/selection-header/index.js b/apps/mobile/app/components/selection-header/index.js index 7a5cd11cf..7f10aada4 100644 --- a/apps/mobile/app/components/selection-header/index.js +++ b/apps/mobile/app/components/selection-header/index.js @@ -16,24 +16,23 @@ * along with this program. If not, see . */ -import React, { useEffect } from "react"; +import React, { useCallback, useEffect } from "react"; import { BackHandler, Platform, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useThemeStore } from "../../stores/use-theme-store"; -import { useSelectionStore } from "../../stores/use-selection-store"; +import { db } from "../../common/database"; import { eSendEvent, ToastEvent } from "../../services/event-manager"; import Navigation from "../../services/navigation"; -import { db } from "../../common/database"; +import useNavigationStore from "../../stores/use-navigation-store"; +import { useSelectionStore } from "../../stores/use-selection-store"; +import { useThemeStore } from "../../stores/use-theme-store"; import { eOpenMoveNoteDialog } from "../../utils/events"; import { deleteItems } from "../../utils/functions"; import { tabBarRef } from "../../utils/global-refs"; -import layoutmanager from "../../utils/layout-manager"; import { SIZE } from "../../utils/size"; import { sleep } from "../../utils/time"; import { presentDialog } from "../dialog/functions"; import { IconButton } from "../ui/icon-button"; import Heading from "../ui/typography/heading"; -import useNavigationStore from "../../stores/use-navigation-store"; export const SelectionHeader = React.memo(() => { const colors = useThemeStore((state) => state.colors); @@ -131,11 +130,10 @@ export const SelectionHeader = React.memo(() => { }); }; - const onBackPress = () => { - layoutmanager.withSpringAnimation(500); + const onBackPress = useCallback(() => { clearSelection(); return true; - }; + }, [clearSelection]); useEffect(() => { if (selectionMode) { @@ -143,7 +141,7 @@ export const SelectionHeader = React.memo(() => { } else { BackHandler.removeEventListener("hardwareBackPress", onBackPress); } - }, [selectionMode]); + }, [onBackPress, selectionMode]); return !selectionMode ? null : ( { const colors = useThemeStore((state) => state.colors); const [visible, setVisible] = useState(false); @@ -48,26 +49,29 @@ const SheetProvider = ({ context = "global" }) => { eUnSubscribeEvent(eOpenProgressDialog, open); eUnSubscribeEvent(eCloseProgressDialog, close); }; - }, [visible]); + }, [close, open, visible]); - const open = async (data) => { - if (!data.context) data.context = "global"; - if (data.context !== context) return; - if (visible || dialogData) { - setDialogData(null); - setVisible(false); - await sleep(500); - } - setDialogData(data); - setVisible(true); - if (data.editor) { - editor.current.refocus = false; - if (editorState().keyboardState) { - // tiny.call(EditorWebView, tiny.cacheRange + tiny.blur); - editor.current.refocus = true; + const open = useCallback( + async (data) => { + if (!data.context) data.context = "global"; + if (data.context !== context) return; + if (visible || dialogData) { + setDialogData(null); + setVisible(false); + await sleep(500); } - } - }; + setDialogData(data); + setVisible(true); + if (data.editor) { + editor.current.refocus = false; + if (editorState().keyboardState) { + // tiny.call(EditorWebView, tiny.cacheRange + tiny.blur); + editor.current.refocus = true; + } + } + }, + [context, dialogData, visible] + ); useEffect(() => { (async () => { @@ -85,11 +89,14 @@ const SheetProvider = ({ context = "global" }) => { })(); }, [visible, dialogData]); - const close = (ctx) => { - if (!ctx) ctx = "global"; - if (ctx !== context) return; - actionSheetRef.current?.setModalVisible(false); - }; + const close = useCallback( + (ctx) => { + if (!ctx) ctx = "global"; + if (ctx !== context) return; + actionSheetRef.current?.setModalVisible(false); + }, + [context] + ); return !visible || !dialogData ? null : ( { useEffect(() => { updateNoteExists(); - }, []); + }, [updateNoteExists]); - const updateNoteExists = () => { + const updateNoteExists = useCallback(() => { if (!note?.id && selectedItemsList?.length === 0) return; let notes = @@ -208,7 +209,7 @@ const MoveNoteComponent = ({ note }) => { } console.log("ids: ", ids); setNoteExists(ids); - }; + }, [note?.id, selectedItemsList]); const openAddTopicDialog = (item) => { presentDialog({ diff --git a/apps/mobile/app/components/sheets/manage-tags/index.js b/apps/mobile/app/components/sheets/manage-tags/index.js index 8139a0f38..0009a67b2 100644 --- a/apps/mobile/app/components/sheets/manage-tags/index.js +++ b/apps/mobile/app/components/sheets/manage-tags/index.js @@ -36,6 +36,7 @@ import { PressableButton } from "../../ui/pressable"; import SheetWrapper from "../../ui/sheet"; import Heading from "../../ui/typography/heading"; import Paragraph from "../../ui/typography/paragraph"; +import { useCallback } from "react"; const ManageTagsSheet = () => { const colors = useThemeStore((state) => state.colors); const [visible, setVisible] = useState(false); @@ -53,16 +54,16 @@ const ManageTagsSheet = () => { eUnSubscribeEvent(eOpenTagsDialog, open); eUnSubscribeEvent(eCloseTagsDialog, close); }; - }, []); + }, [open]); useEffect(() => { if (visible) { console.log("sorting tags"); sortTags(); } - }, [allTags, note, query, visible]); + }, [allTags, note, query, sortTags, visible]); - const sortTags = () => { + const sortTags = useCallback(() => { let _tags = [...allTags]; _tags = _tags.filter((t) => t.type === "tag"); _tags = _tags.sort((a, b) => a.title.localeCompare(b.title)); @@ -85,14 +86,17 @@ const ManageTagsSheet = () => { noteTags = noteTags.sort((a, b) => a.title.localeCompare(b.title)); let combinedTags = [...noteTags, ..._tags]; setTags(combinedTags); - }; + }, [allTags, note, query]); - const open = (item) => { - setNote(item); - useTagStore.getState().setTags(); - sortTags(); - setVisible(true); - }; + const open = useCallback( + (item) => { + setNote(item); + useTagStore.getState().setTags(); + sortTags(); + setVisible(true); + }, + [sortTags] + ); useEffect(() => { if (visible) { diff --git a/apps/mobile/app/components/sheets/restore-data/index.js b/apps/mobile/app/components/sheets/restore-data/index.js index acb6d71f6..9b1723e03 100644 --- a/apps/mobile/app/components/sheets/restore-data/index.js +++ b/apps/mobile/app/components/sheets/restore-data/index.js @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import React, { createRef, useEffect, useState } from "react"; +import React, { createRef, useCallback, useEffect, useState } from "react"; import { ActivityIndicator, Platform, View } from "react-native"; import DocumentPicker from "react-native-document-picker"; import { FlatList } from "react-native-gesture-handler"; @@ -54,7 +54,7 @@ const RestoreDataSheet = () => { eUnSubscribeEvent(eOpenRestoreDialog, open); eUnSubscribeEvent(eCloseRestoreDialog, close); }; - }, []); + }, [close]); const open = async () => { setVisible(true); @@ -62,7 +62,7 @@ const RestoreDataSheet = () => { actionSheetRef.current?.setModalVisible(true); }; - const close = () => { + const close = useCallback(() => { if (restoring) { showIsWorking(); return; @@ -71,7 +71,7 @@ const RestoreDataSheet = () => { setTimeout(() => { setVisible(false); }, 300); - }; + }, [restoring]); const showIsWorking = () => { ToastEvent.show({ diff --git a/apps/mobile/app/components/sheets/update/index.js b/apps/mobile/app/components/sheets/update/index.js index a7744d0de..e3165e204 100644 --- a/apps/mobile/app/components/sheets/update/index.js +++ b/apps/mobile/app/components/sheets/update/index.js @@ -76,7 +76,7 @@ export const Update = ({ version: appVersion, fwdRef }) => { } })(); } - }, []); + }, [version]); return ( { let alias = db.colors.alias(item.id); @@ -61,25 +62,28 @@ const ColorItem = React.memo( const [headerTextState, setHeaderTextState] = useState(null); alias = db.colors.alias(item.id) || ""; - const onHeaderStateChange = (state) => { - setTimeout(() => { - let id = state.currentScreen?.id; - if (id === item.id) { - setHeaderTextState({ id: state.currentScreen.id }); - } else { - if (headerTextState !== null) { - setHeaderTextState(null); + const onHeaderStateChange = useCallback( + (state) => { + setTimeout(() => { + let id = state.currentScreen?.id; + if (id === item.id) { + setHeaderTextState({ id: state.currentScreen.id }); + } else { + if (headerTextState !== null) { + setHeaderTextState(null); + } } - } - }, 300); - }; + }, 300); + }, + [headerTextState, item.id] + ); useEffect(() => { let unsub = useNavigationStore.subscribe(onHeaderStateChange); return () => { unsub(); }; - }, [headerTextState]); + }, [headerTextState, onHeaderStateChange]); const onPress = (item) => { ColoredNotes.navigate(item, false); diff --git a/apps/mobile/app/components/side-menu/index.js b/apps/mobile/app/components/side-menu/index.js index 2d531ceed..bd722eaf7 100644 --- a/apps/mobile/app/components/side-menu/index.js +++ b/apps/mobile/app/components/side-menu/index.js @@ -96,7 +96,7 @@ export const SideMenu = React.memo( ), - [] + [noTextMode] ); return !loading && introCompleted ? ( diff --git a/apps/mobile/app/components/side-menu/menu-item.js b/apps/mobile/app/components/side-menu/menu-item.js index e129f15e7..8eb34e9c6 100644 --- a/apps/mobile/app/components/side-menu/menu-item.js +++ b/apps/mobile/app/components/side-menu/menu-item.js @@ -28,6 +28,7 @@ import { Button } from "../ui/button"; import { PressableButton } from "../ui/pressable"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; +import { useCallback } from "react"; export const MenuItem = React.memo( function MenuItem({ item, index, testID, rightBtn }) { @@ -51,25 +52,28 @@ export const MenuItem = React.memo( } }; - const onHeaderStateChange = (state) => { - setTimeout(() => { - let id = state.currentScreen?.id; - if (id === screenId) { - setHeaderTextState({ id: state.currentScreen.id }); - } else { - if (headerTextState !== null) { - setHeaderTextState(null); + const onHeaderStateChange = useCallback( + (state) => { + setTimeout(() => { + let id = state.currentScreen?.id; + if (id === screenId) { + setHeaderTextState({ id: state.currentScreen.id }); + } else { + if (headerTextState !== null) { + setHeaderTextState(null); + } } - } - }, 300); - }; + }, 300); + }, + [headerTextState, screenId] + ); useEffect(() => { let unsub = useNavigationStore.subscribe(onHeaderStateChange); return () => { unsub(); }; - }, [headerTextState]); + }, [headerTextState, onHeaderStateChange]); return ( { if (item.type === "notebook") { @@ -109,27 +110,30 @@ export const PinItem = React.memo( const color = headerTextState?.id === item.id ? colors.accent : colors.pri; const fwdRef = useRef(); - const onHeaderStateChange = (state) => { - setTimeout(() => { - let id = state.currentScreen?.id; - if (id === item.id) { - setHeaderTextState({ - id: state.currentScreen.id - }); - } else { - if (headerTextState !== null) { - setHeaderTextState(null); + const onHeaderStateChange = useCallback( + (state) => { + setTimeout(() => { + let id = state.currentScreen?.id; + if (id === item.id) { + setHeaderTextState({ + id: state.currentScreen.id + }); + } else { + if (headerTextState !== null) { + setHeaderTextState(null); + } } - } - }, 300); - }; + }, 300); + }, + [headerTextState, item.id] + ); useEffect(() => { let unsub = useNavigationStore.subscribe(onHeaderStateChange); return () => { unsub(); }; - }, [headerTextState]); + }, [headerTextState, onHeaderStateChange]); const icons = { topic: "bookmark", diff --git a/apps/mobile/app/components/tabs/index.tsx b/apps/mobile/app/components/tabs/index.tsx index 111b2121f..a3da1569c 100644 --- a/apps/mobile/app/components/tabs/index.tsx +++ b/apps/mobile/app/components/tabs/index.tsx @@ -126,7 +126,16 @@ export const FluidTabs = forwardRef(function FluidTabs( return () => { sub && sub.remove(); }; - }, [introCompleted, deviceMode, widths]); + }, [ + introCompleted, + deviceMode, + widths, + fullscreen, + translateX, + isDrawerOpen, + homePosition, + onDrawerStateChange + ]); useImperativeHandle( ref, @@ -193,7 +202,16 @@ export const FluidTabs = forwardRef(function FluidTabs( setScrollEnabled: () => true, node: node }), - [deviceMode, homePosition, editorPosition] + [ + currentTab, + deviceMode, + translateX, + onDrawerStateChange, + homePosition, + editorPosition, + forcedLock, + isDrawerOpen + ] ); useAnimatedReaction( diff --git a/apps/mobile/app/components/toast/index.js b/apps/mobile/app/components/toast/index.js index 2aa2a2352..4b9b5dac1 100644 --- a/apps/mobile/app/components/toast/index.js +++ b/apps/mobile/app/components/toast/index.js @@ -34,6 +34,7 @@ import { SIZE } from "../../utils/size"; import { Button } from "../ui/button"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; +import { useCallback } from "react"; let toastMessages = []; export const Toast = ({ context = "global" }) => { const colors = useThemeStore((state) => state.colors); @@ -43,42 +44,46 @@ export const Toast = ({ context = "global" }) => { const hideTimeout = useRef(); const [visible, setVisible] = useState(false); - const showToastFunc = async (data) => { - console.log("toast show", data.message, toastMessages.length); - if (!data) return; - if (data.context !== context) return; - if (toastMessages.findIndex((m) => m.message === data.message) >= 0) { - console.log("returning from here"); - return; - } - toastMessages.push(data); - if (toastMessages?.length > 1) return; - setData(data); + const showToastFunc = useCallback( + async (data) => { + if (!data) return; + if (data.context !== context) return; + if (toastMessages.findIndex((m) => m.message === data.message) >= 0) { + return; + } + toastMessages.push(data); + if (toastMessages?.length > 1) return; + setData(data); - setVisible(true); - if (hideTimeout.current) { - clearTimeout(hideTimeout.current); - } - hideTimeout.current = setTimeout(() => { - hideToastFunc(); - }, data.duration); - }; + setVisible(true); + if (hideTimeout.current) { + clearTimeout(hideTimeout.current); + } + hideTimeout.current = setTimeout(() => { + hideToastFunc(); + }, data.duration); + }, + [context, hideToastFunc] + ); - const showNext = (data) => { - if (!data) { - hideToastFunc(); - return; - } - setData(data); - if (hideTimeout.current) { - clearTimeout(hideTimeout.current); - } - hideTimeout.current = setTimeout(() => { - hideToastFunc(); - }, data?.duration); - }; + const showNext = useCallback( + (data) => { + if (!data) { + hideToastFunc(); + return; + } + setData(data); + if (hideTimeout.current) { + clearTimeout(hideTimeout.current); + } + hideTimeout.current = setTimeout(() => { + hideToastFunc(); + }, data?.duration); + }, + [hideToastFunc] + ); - const hideToastFunc = () => { + const hideToastFunc = useCallback(() => { if (hideTimeout.current) { clearTimeout(hideTimeout.current); } @@ -100,7 +105,7 @@ export const Toast = ({ context = "global" }) => { } }, 100); } - }; + }, [showNext]); const _onKeyboardShow = () => { setKeyboard(true); @@ -127,7 +132,7 @@ export const Toast = ({ context = "global" }) => { eUnSubscribeEvent(eShowToast, showToastFunc); eUnSubscribeEvent(eHideToast, hideToastFunc); }; - }, [keyboard]); + }, [hideToastFunc, keyboard, showToastFunc]); return ( visible && ( diff --git a/apps/mobile/app/components/ui/pressable/index.tsx b/apps/mobile/app/components/ui/pressable/index.tsx index ead5cecbe..1d0269713 100644 --- a/apps/mobile/app/components/ui/pressable/index.tsx +++ b/apps/mobile/app/components/ui/pressable/index.tsx @@ -105,7 +105,7 @@ export const PressableButton = ({ }, customStyle ], - [customStyle, noborder, type, colors] + [alpha, selectedColor, opacity, primaryColor, noborder, customStyle] ); return ( diff --git a/apps/mobile/app/components/ui/sheet/index.js b/apps/mobile/app/components/ui/sheet/index.js index 7d08a5fed..c83ef6ad8 100644 --- a/apps/mobile/app/components/ui/sheet/index.js +++ b/apps/mobile/app/components/ui/sheet/index.js @@ -65,7 +65,7 @@ const SheetWrapper = ({ borderBottomRightRadius: 0, borderBottomLeftRadius: 0 }; - }, [colors.bg, gestureEnabled]); + }, [colors.bg, largeTablet, smallTablet, width]); const _onOpen = () => { onOpen && onOpen(); diff --git a/apps/mobile/app/components/ui/transitions/bouncing-view.js b/apps/mobile/app/components/ui/transitions/bouncing-view.js index f9dcf1456..e90e8cad8 100644 --- a/apps/mobile/app/components/ui/transitions/bouncing-view.js +++ b/apps/mobile/app/components/ui/transitions/bouncing-view.js @@ -49,7 +49,7 @@ export const BouncingView = ({ duration: duration, easing: Easing.elastic(1) }); - }, []); + }, [animated, duration, initialScale, scale]); return ( {children} diff --git a/apps/mobile/app/hooks/use-actions.js b/apps/mobile/app/hooks/use-actions.js index 8169c90bb..aa5b1fcaa 100644 --- a/apps/mobile/app/hooks/use-actions.js +++ b/apps/mobile/app/hooks/use-actions.js @@ -55,6 +55,7 @@ import { } from "../utils/events"; import { deleteItems } from "../utils/functions"; import { sleep } from "../utils/time"; +import { useCallback } from "react"; export const useActions = ({ close = () => null, item }) => { const colors = useThemeStore((state) => state.colors); @@ -78,9 +79,9 @@ export const useActions = ({ close = () => null, item }) => { if (item.type !== "note") { setIsPinnedToMenu(db.settings.isPinned(item.id)); } - }, [item]); + }, [checkNotifPinned, item]); - function checkNotifPinned() { + const checkNotifPinned = useCallback(() => { let pinned = Notifications.getPinnedNotes(); if (!pinned) { setNotifPinned(null); @@ -93,7 +94,7 @@ export const useActions = ({ close = () => null, item }) => { } else { setNotifPinned(null); } - } + }, [item.id]); const isNoteInTopic = () => { const currentScreen = useNavigationStore.getState().currentScreen; @@ -104,13 +105,16 @@ export const useActions = ({ close = () => null, item }) => { .has(item.id); }; - const onUpdate = async (type) => { - if (type === "unpin") { - await sleep(1000); - await Notifications.get(); - checkNotifPinned(); - } - }; + const onUpdate = useCallback( + async (type) => { + if (type === "unpin") { + await sleep(1000); + await Notifications.get(); + checkNotifPinned(); + } + }, + [checkNotifPinned] + ); useEffect(() => { eSubscribeEvent("onUpdate", onUpdate); @@ -118,7 +122,7 @@ export const useActions = ({ close = () => null, item }) => { return () => { eUnSubscribeEvent("onUpdate", onUpdate); }; - }, [item]); + }, [item, onUpdate]); function switchTheme() { toggleDarkMode(); diff --git a/apps/mobile/app/hooks/use-app-events.js b/apps/mobile/app/hooks/use-app-events.js index 53c7afdf9..392312348 100644 --- a/apps/mobile/app/hooks/use-app-events.js +++ b/apps/mobile/app/hooks/use-app-events.js @@ -62,6 +62,7 @@ import { } from "../services/event-manager"; import { useEditorStore } from "../stores/use-editor-store"; import { useDragState } from "../screens/settings/editor/state"; +import { useCallback } from "react"; const SodiumEventEmitter = new NativeEventEmitter(NativeModules.Sodium); export const useAppEvents = () => { @@ -121,7 +122,7 @@ export const useAppEvents = () => { onRequestPartialSync ); }; - }, [loading]); + }, [loading, onSyncComplete]); useEffect(() => { let subs = [ @@ -156,10 +157,10 @@ export const useAppEvents = () => { subs.forEach((sub) => sub?.remove()); }; - }, []); + }, [onEmailVerified, onSyncComplete, onUrlRecieved, onUserUpdated]); const onSessionExpired = async () => { - await SettingsService.set({ + SettingsService.set({ sessionExpired: true }); eSendEvent("session_expired"); @@ -188,18 +189,26 @@ export const useAppEvents = () => { } return () => { refValues.current?.removeInternetStateListener && + // eslint-disable-next-line react-hooks/exhaustive-deps refValues.current?.removeInternetStateListener(); sub?.remove(); unsubIAP(); }; - }, [loading, verify]); + }, [ + loading, + onAppStateChanged, + onEmailVerified, + onInternetStateChanged, + onUserUpdated, + verify + ]); - const onInternetStateChanged = async (state) => { + const onInternetStateChanged = useCallback(async (state) => { if (!syncedOnLaunch.current) return; reconnectSSE(state); - }; + }, []); - const onSyncComplete = async () => { + const onSyncComplete = useCallback(async () => { console.log("sync complete"); initAfterSync(); setLastSynced(await db.lastSynced()); @@ -209,22 +218,25 @@ export const useAppEvents = () => { if (note) { //await updateNoteInEditor(); } - }; + }, [setLastSynced]); - const onUrlRecieved = async (res) => { - let url = res ? res.url : ""; - try { - if (url.startsWith("https://app.notesnook.com/account/verified")) { - await onEmailVerified(); - } else { - return; + const onUrlRecieved = useCallback( + async (res) => { + let url = res ? res.url : ""; + try { + if (url.startsWith("https://app.notesnook.com/account/verified")) { + await onEmailVerified(); + } else { + return; + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); - } - }; + }, + [onEmailVerified] + ); - const onEmailVerified = async () => { + const onEmailVerified = useCallback(async () => { let user = await db.user.getUser(); setUser(user); if (!user) return; @@ -236,9 +248,9 @@ export const useAppEvents = () => { if (user?.isEmailConfirmed) { clearMessage(); } - }; + }, [setUser]); - const attachIAPListeners = async () => { + const attachIAPListeners = useCallback(async () => { await RNIap.initConnection() .catch(() => null) .then(async () => { @@ -247,7 +259,7 @@ export const useAppEvents = () => { refValues.current.subsriptionErrorListener = RNIap.purchaseErrorListener(onSubscriptionError); }); - }; + }, []); const onAccountStatusChange = async (userStatus) => { if (!PremiumService.get() && userStatus.type === 5) { @@ -281,59 +293,62 @@ export const useAppEvents = () => { } }; - const onUserUpdated = async (login) => { - console.log(`onUserUpdated: ${login}`); - let user; - try { - user = await db.user.getUser(); - await PremiumService.setPremiumStatus(); - setLastSynced(await db.lastSynced()); - await useDragState.getState().init(); - if (!user) { - return setLoginMessage(); - } + const onUserUpdated = useCallback( + async (login) => { + console.log(`onUserUpdated: ${login}`); + let user; + try { + user = await db.user.getUser(); + await PremiumService.setPremiumStatus(); + setLastSynced(await db.lastSynced()); + await useDragState.getState().init(); + if (!user) { + return setLoginMessage(); + } - let userEmailConfirmed = SettingsService.get().userEmailConfirmed; - setUser(user); - if (SettingsService.get().sessionExpired) { - syncedOnLaunch.current = true; - return; - } - - clearMessage(); - attachIAPListeners(); - - if (!login) { - user = await db.user.fetchUser(); + let userEmailConfirmed = SettingsService.get().userEmailConfirmed; setUser(user); + if (SettingsService.get().sessionExpired) { + syncedOnLaunch.current = true; + return; + } + + clearMessage(); + attachIAPListeners(); + + if (!login) { + user = await db.user.fetchUser(); + setUser(user); + } + + await PremiumService.setPremiumStatus(); + if (user?.isEmailConfirmed && !userEmailConfirmed) { + setTimeout(() => { + onEmailVerified(); + }, 1000); + SettingsService.set({ + userEmailConfirmed: true + }); + } + } catch (e) { + ToastEvent.error(e, "An error occured", "global"); } - await PremiumService.setPremiumStatus(); - if (user?.isEmailConfirmed && !userEmailConfirmed) { - setTimeout(() => { - onEmailVerified(); - }, 1000); - SettingsService.set({ - userEmailConfirmed: true - }); + user = await db.user.getUser(); + if ( + user?.isEmailConfirmed && + !SettingsService.get().recoveryKeySaved && + !useMessageStore.getState().message?.visible + ) { + setRecoveryKeyMessage(); } - } catch (e) { - ToastEvent.error(e, "An error occured", "global"); - } + if (!user?.isEmailConfirmed) setEmailVerifyMessage(); + refValues.current.isUserReady = true; - user = await db.user.getUser(); - if ( - user?.isEmailConfirmed && - !SettingsService.get().recoveryKeySaved && - !useMessageStore.getState().message?.visible - ) { - setRecoveryKeyMessage(); - } - if (!user?.isEmailConfirmed) setEmailVerifyMessage(); - refValues.current.isUserReady = true; - - syncedOnLaunch.current = true; - }; + syncedOnLaunch.current = true; + }, + [attachIAPListeners, onEmailVerified, setLastSynced, setUser] + ); const onSuccessfulSubscription = async (subscription) => { await PremiumService.subscriptions.set(subscription); @@ -349,59 +364,65 @@ export const useAppEvents = () => { }); }; - const onAppStateChanged = async (state) => { - console.log("onAppStateChanged"); - if (state === "active") { - updateStatusBarColor(); - if ( - SettingsService.get().appLockMode !== "background" && - !SettingsService.get().privacyScreen - ) { - enabled(false); - } - if (SettingsService.get().appLockMode === "background") { - if (useSettingStore.getState().requestBiometrics) { - useSettingStore.getState().setRequestBiometrics(false); - return; + const onAppStateChanged = useCallback( + async (state) => { + console.log("onAppStateChanged"); + if (state === "active") { + updateStatusBarColor(); + if ( + SettingsService.get().appLockMode !== "background" && + !SettingsService.get().privacyScreen + ) { + enabled(false); } - } - - await reconnectSSE(); - await checkIntentState(); - MMKV.removeItem("appState"); - let user = await db.user.getUser(); - if (user && !user?.isEmailConfirmed) { - try { - let user = await db.user.fetchUser(); - if (user?.isEmailConfirmed) { - onEmailVerified(); + if (SettingsService.get().appLockMode === "background") { + if (useSettingStore.getState().requestBiometrics) { + useSettingStore.getState().setRequestBiometrics(false); + return; } - } catch (e) { - console.error(e); + } + + await reconnectSSE(); + await checkIntentState(); + MMKV.removeItem("appState"); + let user = await db.user.getUser(); + if (user && !user?.isEmailConfirmed) { + try { + let user = await db.user.fetchUser(); + if (user?.isEmailConfirmed) { + onEmailVerified(); + } + } catch (e) { + console.error(e); + } + } + } else { + let id = useEditorStore.getState().currentEditingNote; + let note = id && db.notes.note(id).data; + if ( + note?.locked && + SettingsService.get().appLockMode === "background" + ) { + eSendEvent(eClearEditor); + } + await storeAppState(); + if ( + SettingsService.get().appLockMode === "background" && + !useSettingStore.getState().requestBiometrics && + !useUserStore.getState().verifyUser + ) { + useUserStore.getState().setVerifyUser(true); + } + if ( + SettingsService.get().privacyScreen || + SettingsService.get().appLockMode === "background" + ) { + !useSettingStore.getState().requestBiometrics ? enabled(true) : null; } } - } else { - let id = useEditorStore.getState().currentEditingNote; - let note = id && db.notes.note(id).data; - if (note?.locked && SettingsService.get().appLockMode === "background") { - eSendEvent(eClearEditor); - } - await storeAppState(); - if ( - SettingsService.get().appLockMode === "background" && - !useSettingStore.getState().requestBiometrics && - !useUserStore.getState().verifyUser - ) { - useUserStore.getState().setVerifyUser(true); - } - if ( - SettingsService.get().privacyScreen || - SettingsService.get().appLockMode === "background" - ) { - !useSettingStore.getState().requestBiometrics ? enabled(true) : null; - } - } - }; + }, + [onEmailVerified] + ); async function reconnectSSE(connection) { if (refValues.current?.isReconnecting) return; diff --git a/apps/mobile/app/hooks/use-attachment-progress.js b/apps/mobile/app/hooks/use-attachment-progress.js index e1c1dc88a..8ce3da222 100644 --- a/apps/mobile/app/hooks/use-attachment-progress.js +++ b/apps/mobile/app/hooks/use-attachment-progress.js @@ -46,7 +46,7 @@ export const useAttachmentProgress = (attachment, encryption) => { } else { setCurrentProgress(null); } - }, [progress]); + }, [attachment.metadata.hash, progress]); return [currentProgress, setCurrentProgress]; }; diff --git a/apps/mobile/app/hooks/use-editor-tags.ts b/apps/mobile/app/hooks/use-editor-tags.ts index acd058ee3..44549f08c 100644 --- a/apps/mobile/app/hooks/use-editor-tags.ts +++ b/apps/mobile/app/hooks/use-editor-tags.ts @@ -22,6 +22,7 @@ import { useEditorStore } from "../stores/use-editor-store"; import { useTagStore } from "../stores/use-tag-store"; import { db } from "../common/database"; import { NoteType } from "app/utils/types"; +import { useCallback } from "react"; /** * A hook that injects/removes tags from tags bar in editor @@ -34,7 +35,7 @@ const useEditorTags = () => { const [note, setNote] = useState(null); const [noteTags, setNoteTags] = useState([]); - async function refreshNote() { + const refreshNote = useCallback(() => { const current = useEditorStore.getState().currentEditingNote; if (!current) { setNote(null); @@ -44,23 +45,23 @@ const useEditorTags = () => { const note = db.notes?.note(current)?.data as NoteType; setNote(note ? { ...note } : null); getTags(note); - } + }, []); useEffect(() => { refreshNote(); - }, [currentEditingNote, tags]); + }, [currentEditingNote, refreshNote, tags]); - function load() { + const load = useCallback(() => { if (!note) return; // tiny.call(EditorWebView, renderTags(noteTags)); - } + }, [note]); useEffect(() => { eSubscribeEvent("updateTags", load); return () => { eUnSubscribeEvent("updateTags", load); }; - }, [noteTags]); + }, [load, noteTags]); function getTags(note: NoteType) { if (!note || !note.tags) return []; @@ -72,7 +73,7 @@ const useEditorTags = () => { useEffect(() => { load(); - }, [noteTags]); + }, [load, noteTags]); return []; }; diff --git a/apps/mobile/app/hooks/use-immediate.ts b/apps/mobile/app/hooks/use-immediate.ts index 54f5cb4cd..0f7fb5c5a 100644 --- a/apps/mobile/app/hooks/use-immediate.ts +++ b/apps/mobile/app/hooks/use-immediate.ts @@ -25,7 +25,9 @@ const useImmediateEffect = (callback: EffectCallback, deps: DependencyList) => { cleanup = callback(); }); return cleanup; - }, deps); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [callback, ...deps]); }; export default useImmediateEffect; diff --git a/apps/mobile/app/hooks/use-is-floating-keyboard.ts b/apps/mobile/app/hooks/use-is-floating-keyboard.ts index a4d7f9bf3..54dff1137 100644 --- a/apps/mobile/app/hooks/use-is-floating-keyboard.ts +++ b/apps/mobile/app/hooks/use-is-floating-keyboard.ts @@ -18,6 +18,7 @@ import { useEffect, useState } from "react"; import { Keyboard, KeyboardEvent, useWindowDimensions } from "react-native"; +import { useCallback } from "react"; /** * A hook that detects floating keyboard on iPad @@ -28,9 +29,12 @@ const useIsFloatingKeyboard = () => { const [floating, setFloating] = useState(false); - const onKeyboardWillChangeFrame = (event: KeyboardEvent) => { - setFloating(event.endCoordinates.width !== width); - }; + const onKeyboardWillChangeFrame = useCallback( + (event: KeyboardEvent) => { + setFloating(event.endCoordinates.width !== width); + }, + [width] + ); useEffect(() => { const sub1 = Keyboard.addListener( @@ -40,7 +44,7 @@ const useIsFloatingKeyboard = () => { return () => { sub1?.remove(); }; - }, [width]); + }, [onKeyboardWillChangeFrame, width]); return floating; }; diff --git a/apps/mobile/app/hooks/use-navigation-focus.ts b/apps/mobile/app/hooks/use-navigation-focus.ts index ed9cbe010..e88635e1f 100644 --- a/apps/mobile/app/hooks/use-navigation-focus.ts +++ b/apps/mobile/app/hooks/use-navigation-focus.ts @@ -47,7 +47,7 @@ export const useNavigationFocus = ( }, isBlurred.current ? 0 : delay || 300 ); - }, [onFocus, prev]); + }, [delay, onFocus]); const _onBlur = useCallback(() => { isBlurred.current = true; @@ -58,7 +58,7 @@ export const useNavigationFocus = ( setFocused(false); } }, delay || 300); - }, [onBlur, prev]); + }, [delay, onBlur]); useEffect(() => { if (!navigation) return; @@ -69,7 +69,7 @@ export const useNavigationFocus = ( return () => { subs.forEach((sub) => sub()); }; - }, [navigation]); + }, [_onBlur, _onFocus, navigation]); return isFocused; }; diff --git a/apps/mobile/app/hooks/use-rotator.ts b/apps/mobile/app/hooks/use-rotator.ts index 1dbd49a2b..88e52f6d5 100644 --- a/apps/mobile/app/hooks/use-rotator.ts +++ b/apps/mobile/app/hooks/use-rotator.ts @@ -23,7 +23,6 @@ import { useEffect, useRef, useState } from "react"; * It will return random item in an array after given interval */ function useRotator(data: T[], interval = 3000): T | null { - if (!Array.isArray(data)) return null; //@ts-ignore Added sample() method to Array.prototype to get random value. const [current, setCurrent] = useState(data.sample()); const intervalRef = useRef(); @@ -39,7 +38,7 @@ function useRotator(data: T[], interval = 3000): T | null { clearInterval(intervalRef.current); } }; - }); + }, [data, interval]); return current; } diff --git a/apps/mobile/app/hooks/use-sync-progress.ts b/apps/mobile/app/hooks/use-sync-progress.ts index 53f89955c..0afef9f38 100644 --- a/apps/mobile/app/hooks/use-sync-progress.ts +++ b/apps/mobile/app/hooks/use-sync-progress.ts @@ -47,7 +47,7 @@ const useSyncProgress = () => { EV?.unsubscribe(EVENTS.syncProgress, onProgress); EV?.unsubscribe(EVENTS.syncCompleted, onSyncComplete); }; - }, []); + }, [EV, onProgress]); return { progress diff --git a/apps/mobile/app/hooks/use-tooltip.ts b/apps/mobile/app/hooks/use-tooltip.ts index 515a2f79d..5f7b9c7b7 100644 --- a/apps/mobile/app/hooks/use-tooltip.ts +++ b/apps/mobile/app/hooks/use-tooltip.ts @@ -113,7 +113,7 @@ export const useTooltipHandler = ( return () => { eUnSubscribeEvent(id, callback); }; - }, []); + }, [callback, id]); return null; }; diff --git a/apps/mobile/app/hooks/use-vault-status.js b/apps/mobile/app/hooks/use-vault-status.js index 7d4673eae..d88edc351 100644 --- a/apps/mobile/app/hooks/use-vault-status.js +++ b/apps/mobile/app/hooks/use-vault-status.js @@ -54,7 +54,7 @@ export const useVaultStatus = () => { return () => { eUnSubscribeEvent("vaultUpdated", () => checkVaultStatus()); }; - }, []); + }, [checkVaultStatus]); return vaultStatus; }; diff --git a/apps/mobile/app/hooks/use-vault-status.ts b/apps/mobile/app/hooks/use-vault-status.ts index 39385563f..57daf8eb1 100644 --- a/apps/mobile/app/hooks/use-vault-status.ts +++ b/apps/mobile/app/hooks/use-vault-status.ts @@ -60,7 +60,7 @@ export const useVaultStatus = () => { return () => { eUnSubscribeEvent("vaultUpdated", () => checkVaultStatus()); }; - }, []); + }, [checkVaultStatus]); return vaultStatus; }; diff --git a/apps/mobile/app/navigation/navigation-stack.js b/apps/mobile/app/navigation/navigation-stack.js index a19169be3..697646d64 100644 --- a/apps/mobile/app/navigation/navigation-stack.js +++ b/apps/mobile/app/navigation/navigation-stack.js @@ -89,7 +89,7 @@ const _Tabs = () => { setTimeout(() => { useNavigationStore.getState().update({ name: homepage }); }, 1000); - }, []); + }, [homepage]); return ( { } hideAllTooltips(); eSendEvent("navigate"); - }); + }, [clearSelection]); return ( diff --git a/apps/mobile/app/navigation/tabs-holder.js b/apps/mobile/app/navigation/tabs-holder.js index 288e9f55f..1caac960e 100644 --- a/apps/mobile/app/navigation/tabs-holder.js +++ b/apps/mobile/app/navigation/tabs-holder.js @@ -59,6 +59,7 @@ import { import { editorRef, tabBarRef } from "../utils/global-refs"; import { hideAllTooltips } from "../hooks/use-tooltip"; import { NavigationStack } from "./navigation-stack"; +import { useCallback } from "react"; const _TabsHolder = () => { const colors = useThemeStore((state) => state.colors); @@ -92,7 +93,7 @@ const _TabsHolder = () => { }; }, []); - const showFullScreenEditor = () => { + const showFullScreenEditor = useCallback(() => { setFullscreen(true); if (deviceMode === "smallTablet") { tabBarRef.current?.openDrawer(); @@ -107,9 +108,9 @@ const _TabsHolder = () => { : dimensions.width * 0.15 } }); - }; + }, [deviceMode, dimensions.width, setFullscreen]); - const closeFullScreenEditor = () => { + const closeFullScreenEditor = useCallback(() => { if (deviceMode === "smallTablet") { tabBarRef.current?.closeDrawer(); } @@ -132,7 +133,7 @@ const _TabsHolder = () => { tabBarRef.current?.goToIndex(1); }, 100); } - }; + }, [deviceMode, dimensions.width, setFullscreen]); useEffect(() => { if (!tabBarRef.current?.isDrawerOpen()) { @@ -145,7 +146,14 @@ const _TabsHolder = () => { eUnSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor); eUnSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor); }; - }, [deviceMode, dimensions, colors]); + }, [ + deviceMode, + dimensions, + colors, + showFullScreenEditor, + closeFullScreenEditor, + toggleView + ]); const _onLayout = async (event) => { console.log("layout called here"); @@ -259,9 +267,12 @@ const _TabsHolder = () => { } }; - const toggleView = (show) => { - animatedTranslateY.value = show ? 0 : -9999; - }; + const toggleView = useCallback( + (show) => { + animatedTranslateY.value = show ? 0 : -9999; + }, + [animatedTranslateY] + ); const valueLimiter = (value, min, max) => { if (value < min) { diff --git a/apps/mobile/app/screens/editor/index.tsx b/apps/mobile/app/screens/editor/index.tsx index 1942d051d..dbaf6d19d 100755 --- a/apps/mobile/app/screens/editor/index.tsx +++ b/apps/mobile/app/screens/editor/index.tsx @@ -42,6 +42,7 @@ import { EditorProps, useEditorType } from "./tiptap/types"; import { useEditor } from "./tiptap/use-editor"; import { useEditorEvents } from "./tiptap/use-editor-events"; import { editorController } from "./tiptap/utils"; +import { useCallback } from "react"; const style: ViewStyle = { height: "100%", @@ -91,23 +92,32 @@ const Editor = React.memo( get: () => editor })); - const onMediaDownloaded = ({ - hash, - groupId, - src - }: { - hash: string; - groupId: string; - src: string; - }) => { - console.log("onMediaDownoaded", groupId); + const onMediaDownloaded = useCallback( + ({ + hash, + groupId, + src + }: { + hash: string; + groupId: string; + src: string; + }) => { + console.log("onMediaDownoaded", groupId); - if (groupId !== editor.note.current?.id) return; - editor.commands.updateImage({ - hash: hash, - src: src - }); - }; + if (groupId !== editor.note.current?.id) return; + editor.commands.updateImage({ + hash: hash, + src: src + }); + }, + [editor.commands, editor.note] + ); + + const onError = useCallback(() => { + console.log("RENDER PROCESS GONE!!!"); + editor.setLoading(true); + setTimeout(() => editor.setLoading(false), 10); + }, [editor]); useEffect(() => { onLoad && onLoad(); @@ -117,17 +127,12 @@ const Editor = React.memo( eUnSubscribeEvent("webview_reset", onError); EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded); }; - }, []); + }, [onError, onLoad, onMediaDownloaded]); if (withController) { editorController.current = editor; } - const onError = () => { - console.log("RENDER PROCESS GONE!!!"); - editor.setLoading(true); - setTimeout(() => editor.setLoading(false), 10); - }; console.log(editor.loading, "loading editor"); return editor.loading ? null : ( <> diff --git a/apps/mobile/app/screens/editor/loading.js b/apps/mobile/app/screens/editor/loading.js index 077183f6a..dd3f5c258 100644 --- a/apps/mobile/app/screens/editor/loading.js +++ b/apps/mobile/app/screens/editor/loading.js @@ -36,6 +36,7 @@ import { useThemeStore } from "../../stores/use-theme-store"; import { eClearEditor, eOnLoadNote } from "../../utils/events"; import { SIZE } from "../../utils/size"; import { editorState } from "./tiptap/utils"; +import { useCallback } from "react"; const EditorOverlay = ({ editorId = "", editor }) => { const colors = useThemeStore((state) => state.colors); const [error, setError] = useState(false); @@ -55,34 +56,37 @@ const EditorOverlay = ({ editorId = "", editor }) => { clearTimeout(timers.current.closing); }; - const load = async (_loading) => { - editorState().overlay = true; - clearTimers(); - if (_loading) { - opacity.value = 1; - translateValue.value = 0; - timers.current.error = setTimeout(() => { - if (_loading) { - let note = _loading; - note.forced = true; - eSendEvent(eOnLoadNote + editorId, note); - } - setError(true); - }, 4000); - } else { + const load = useCallback( + async (_loading) => { + editorState().overlay = true; clearTimers(); - setTimeout(() => { - setError(false); - editorState().overlay = false; - opacity.value = withTiming(0, { - duration: 500 - }); + if (_loading) { + opacity.value = 1; + translateValue.value = 0; + timers.current.error = setTimeout(() => { + if (_loading) { + let note = _loading; + note.forced = true; + eSendEvent(eOnLoadNote + editorId, note); + } + setError(true); + }, 4000); + } else { + clearTimers(); setTimeout(() => { - translateValue.value = 6000; - }, 500); - }, 100); - } - }; + setError(false); + editorState().overlay = false; + opacity.value = withTiming(0, { + duration: 500 + }); + setTimeout(() => { + translateValue.value = 6000; + }, 500); + }, 100); + } + }, + [editorId, opacity, translateValue] + ); useEffect(() => { eSubscribeEvent("loadingNote" + editorId, load); @@ -90,7 +94,7 @@ const EditorOverlay = ({ editorId = "", editor }) => { clearTimers(); eUnSubscribeEvent("loadingNote" + editorId, load); }; - }, [editorId]); + }, [editorId, load]); const animatedStyle = useAnimatedStyle(() => { return { diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts index f2efd0057..35d828522 100644 --- a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts +++ b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts @@ -151,10 +151,8 @@ export const useEditorEvents = ( ); const tools = useDragState((state) => state.data); const { keyboardShown } = useKeyboard(); - if (!editor) return null; useEffect(() => { - console.log("keyboardShown", keyboardShown); editor.commands.setSettings({ deviceMode: deviceMode || "mobile", fullscreen: fullscreen || false, @@ -176,7 +174,10 @@ export const useEditorEvents = ( tools, editor.commands, keyboardShown, - doubleSpacedLines + doubleSpacedLines, + editorPropReadonly, + noHeader, + noToolbar ]); const onBackPress = useCallback(async () => { @@ -211,14 +212,14 @@ export const useEditorEvents = ( editorState().currentlyEditing = false; editor.reset(); }, 1); - }, [fullscreen, deviceMode]); + }, [editor, deviceMode, fullscreen]); const onHardwareBackPress = useCallback(() => { if (editorState().currentlyEditing) { onBackPress(); return true; } - }, []); + }, [onBackPress]); const onLoadNote = useCallback(async () => { InteractionManager.runAfterInteractions(() => { @@ -229,29 +230,32 @@ export const useEditorEvents = ( ); } }); - }, []); - const onCallClear = useCallback(async (value: string) => { - if (value === "removeHandler") { - if (handleBack.current) { - handleBack.current.remove(); - } - return; - } - if (value === "addHandler") { - if (handleBack.current) { - handleBack.current.remove(); + }, [onHardwareBackPress]); + const onCallClear = useCallback( + async (value: string) => { + if (value === "removeHandler") { + if (handleBack.current) { + handleBack.current.remove(); + } + return; } + if (value === "addHandler") { + if (handleBack.current) { + handleBack.current.remove(); + } - handleBack.current = BackHandler.addEventListener( - "hardwareBackPress", - onHardwareBackPress - ); - return; - } - if (editorState().currentlyEditing) { - await onBackPress(); - } - }, []); + handleBack.current = BackHandler.addEventListener( + "hardwareBackPress", + onHardwareBackPress + ); + return; + } + if (editorState().currentlyEditing) { + await onBackPress(); + } + }, + [onBackPress, onHardwareBackPress] + ); useEffect(() => { if (fullscreen && DDS.isTab) { @@ -266,7 +270,7 @@ export const useEditorEvents = ( handleBack.current.remove(); } }; - }, [fullscreen]); + }, [fullscreen, onHardwareBackPress]); useEffect(() => { eSubscribeEvent(eOnLoadNote + editor.editorId, onLoadNote); @@ -275,107 +279,110 @@ export const useEditorEvents = ( eUnSubscribeEvent(eClearEditor, onCallClear); eUnSubscribeEvent(eOnLoadNote, onLoadNote); }; - }, []); + }, [editor.editorId, onCallClear, onLoadNote]); - const onMessage = (event: WebViewMessageEvent) => { - const data = event.nativeEvent.data; - const editorMessage = JSON.parse(data) as EditorMessage; + const onMessage = useCallback( + (event: WebViewMessageEvent) => { + const data = event.nativeEvent.data; + const editorMessage = JSON.parse(data) as EditorMessage; - logger.info("editor", editorMessage.type); - if ( - editorMessage.sessionId !== editor.sessionId && - editorMessage.type !== EditorEvents.status - ) { - logger.error( - "editor", - "invalid session", - editorMessage.type, - editor.sessionId, - editorMessage.sessionId - ); - - return; - } - switch (editorMessage.type) { - case EventTypes.logger: - logger.info("[WEBVIEW LOG]", editorMessage.value); - break; - case EventTypes.content: - editor.saveContent({ - type: editorMessage.type, - content: editorMessage.value as string - }); - break; - case EventTypes.selection: - break; - case EventTypes.title: - editor.saveContent({ - type: editorMessage.type, - title: editorMessage.value as string - }); - break; - case EventTypes.newtag: - if (!editor.note.current) return; - eSendEvent(eOpenTagsDialog, editor.note.current); - break; - case EventTypes.tag: - if (editorMessage.value) { - if (!editor.note.current) return; - db.notes - ?.note(editor.note.current?.id) - .untag(editorMessage.value) - .then(async () => { - useTagStore.getState().setTags(); - await editor.commands.setTags(editor.note.current); - Navigation.queueRoutesForUpdate( - "ColoredNotes", - "Notes", - "TaggedNotes", - "TopicNotes", - "Tags" - ); - }); - } - break; - case EventTypes.filepicker: - const { pick } = require("./picker.js").default; - pick({ type: editorMessage.value }); - break; - case EventTypes.download: - console.log("download attachment request", editorMessage.value); - const attachmentInfo = editorMessage.value as Attachment; - filesystem.downloadAttachment(attachmentInfo?.hash, true); - break; - case EventTypes.pro: - if (editor.state.current?.isFocused) { - editor.state.current.isFocused = true; - } - umami.pageView("/pro-screen", "/editor"); - eSendEvent(eOpenPremiumDialog); - break; - case EventTypes.monograph: - publishNote(editor); - break; - case EventTypes.properties: - showActionsheet(editor); - break; - case EventTypes.fullscreen: - editorState().isFullscreen = true; - eSendEvent(eOpenFullscreenEditor); - break; - case EventTypes.back: - onBackPress(); - break; - default: - console.log( - "unhandled event recieved from editor: ", + logger.info("editor", editorMessage.type); + if ( + editorMessage.sessionId !== editor.sessionId && + editorMessage.type !== EditorEvents.status + ) { + logger.error( + "editor", + "invalid session", editorMessage.type, - editorMessage.value + editor.sessionId, + editorMessage.sessionId ); - break; - } - eSendEvent(editorMessage.type, editorMessage); - }; + + return; + } + switch (editorMessage.type) { + case EventTypes.logger: + logger.info("[WEBVIEW LOG]", editorMessage.value); + break; + case EventTypes.content: + editor.saveContent({ + type: editorMessage.type, + content: editorMessage.value as string + }); + break; + case EventTypes.selection: + break; + case EventTypes.title: + editor.saveContent({ + type: editorMessage.type, + title: editorMessage.value as string + }); + break; + case EventTypes.newtag: + if (!editor.note.current) return; + eSendEvent(eOpenTagsDialog, editor.note.current); + break; + case EventTypes.tag: + if (editorMessage.value) { + if (!editor.note.current) return; + db.notes + ?.note(editor.note.current?.id) + .untag(editorMessage.value) + .then(async () => { + useTagStore.getState().setTags(); + await editor.commands.setTags(editor.note.current); + Navigation.queueRoutesForUpdate( + "ColoredNotes", + "Notes", + "TaggedNotes", + "TopicNotes", + "Tags" + ); + }); + } + break; + case EventTypes.filepicker: + const { pick } = require("./picker.js").default; + pick({ type: editorMessage.value }); + break; + case EventTypes.download: + console.log("download attachment request", editorMessage.value); + const attachmentInfo = editorMessage.value as Attachment; + filesystem.downloadAttachment(attachmentInfo?.hash, true); + break; + case EventTypes.pro: + if (editor.state.current?.isFocused) { + editor.state.current.isFocused = true; + } + umami.pageView("/pro-screen", "/editor"); + eSendEvent(eOpenPremiumDialog); + break; + case EventTypes.monograph: + publishNote(editor); + break; + case EventTypes.properties: + showActionsheet(editor); + break; + case EventTypes.fullscreen: + editorState().isFullscreen = true; + eSendEvent(eOpenFullscreenEditor); + break; + case EventTypes.back: + onBackPress(); + break; + default: + console.log( + "unhandled event recieved from editor: ", + editorMessage.type, + editorMessage.value + ); + break; + } + eSendEvent(editorMessage.type, editorMessage); + }, + [editor, onBackPress] + ); return onMessage; }; diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor.ts b/apps/mobile/app/screens/editor/tiptap/use-editor.ts index 697dba349..3910b99a9 100644 --- a/apps/mobile/app/screens/editor/tiptap/use-editor.ts +++ b/apps/mobile/app/screens/editor/tiptap/use-editor.ts @@ -79,7 +79,7 @@ export const useEditor = ( commands.setInsets( isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 } ); - }, [insets]); + }, [commands, insets, isDefaultEditor]); useEffect(() => { sessionIdRef.current = sessionId; @@ -87,7 +87,7 @@ export const useEditor = ( useEffect(() => { commands.setTags(currentNote.current); - }, [tags]); + }, [commands, tags]); useEffect(() => { if (theme) return; @@ -98,28 +98,17 @@ export const useEditor = ( return () => { unsub(); }; - }, []); + }, [postMessage, theme]); - useEffect(() => { - console.log("sessionId:", sessionId); - async () => { - await commands.setSessionId(sessionIdRef.current); - if (sessionIdRef.current) { - if (!state.current?.ready) return; - await onReady(); - } - }; - return () => { - state.current.saveCount = 0; - }; - }, [sessionId, loading]); - - const overlay = (show: boolean, data = { type: "new" }) => { - eSendEvent( - "loadingNote" + editorId, - show ? currentNote.current || data : false - ); - }; + const overlay = useCallback( + (show: boolean, data = { type: "new" }) => { + eSendEvent( + "loadingNote" + editorId, + show ? currentNote.current || data : false + ); + }, + [editorId] + ); const onReady = useCallback(async () => { if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) { @@ -127,7 +116,19 @@ export const useEditor = ( overlay(true); setLoading(true); } - }, []); + }, [overlay]); + + useEffect(() => { + console.log("sessionId:", sessionId); + state.current.saveCount = 0; + async () => { + await commands.setSessionId(sessionIdRef.current); + if (sessionIdRef.current) { + if (!state.current?.ready) return; + await onReady(); + } + }; + }, [sessionId, loading, commands, onReady]); useEffect(() => { if (loading) { @@ -143,24 +144,27 @@ export const useEditor = ( [] ); - const reset = useCallback(async (resetState = true) => { - currentNote.current?.id && db.fs.cancel(currentNote.current.id); - currentNote.current = null; - currentContent.current = null; - sessionHistoryId.current = undefined; - saveCount.current = 0; - useEditorStore.getState().setReadonly(false); - postMessage(EditorEvents.title, ""); - await commands.clearContent(); - await commands.clearTags(); - console.log("reset state: ", resetState); - if (resetState) { - isDefaultEditor && - useEditorStore.getState().setCurrentlyEditingNote(null); - placeholderTip.current = TipManager.placeholderTip(); - await commands.setPlaceholder(placeholderTip.current); - } - }, []); + const reset = useCallback( + async (resetState = true) => { + currentNote.current?.id && db.fs.cancel(currentNote.current.id); + currentNote.current = null; + currentContent.current = null; + sessionHistoryId.current = undefined; + saveCount.current = 0; + useEditorStore.getState().setReadonly(false); + postMessage(EditorEvents.title, ""); + await commands.clearContent(); + await commands.clearTags(); + console.log("reset state: ", resetState); + if (resetState) { + isDefaultEditor && + useEditorStore.getState().setCurrentlyEditingNote(null); + placeholderTip.current = TipManager.placeholderTip(); + await commands.setPlaceholder(placeholderTip.current); + } + }, + [commands, isDefaultEditor, postMessage] + ); const saveNote = useCallback( async ({ @@ -236,6 +240,7 @@ export const useEditor = ( } } else { noteData.contentId = note?.contentId; + // eslint-disable-next-line @typescript-eslint/no-explicit-any await db.vault?.save(noteData as any); } console.log(id, sessionIdRef.current, currentSessionId); @@ -265,7 +270,7 @@ export const useEditor = ( console.log("error saving: ", e); } }, - [commands, reset] + [commands, isDefaultEditor, postMessage, readonly, reset] ); const loadContent = useCallback(async (note: NoteType) => { @@ -322,7 +327,15 @@ export const useEditor = ( loadImages(); } }, - [setSessionId] + [ + commands, + editorId, + isDefaultEditor, + loadContent, + overlay, + postMessage, + reset + ] ); const loadImages = () => { @@ -352,7 +365,7 @@ export const useEditor = ( return () => { eUnSubscribeEvent(eOnLoadNote + editorId, loadNote); }; - }, [editorId]); + }, [editorId, loadNote]); const saveContent = useCallback( ({ @@ -398,28 +411,10 @@ export const useEditor = ( 500 ); }, - [sessionIdRef, sessionId] + [sessionId, withTimer, onChange, saveNote] ); - const onLoad = useCallback(async () => { - console.log("on editor load"); - state.current.ready = true; - onReady(); - postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors); - commands.setInsets( - isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 } - ); - if (currentNote.current) { - console.log("force reload note"); - loadNote({ ...currentNote.current, forced: true }); - } else { - await commands.setPlaceholder(placeholderTip.current); - isDefaultEditor && restoreEditorState(); - } - commands.setSettings(); - }, [state, currentNote, loadNote]); - - async function restoreEditorState() { + const restoreEditorState = useCallback(async () => { const json = await MMKV.getItem("appState"); if (json) { const appState = JSON.parse(json) as AppState; @@ -451,7 +446,34 @@ export const useEditor = ( return; } state.current.isRestoringState = false; - } + }, [loadNote, overlay]); + + const onLoad = useCallback(async () => { + console.log("on editor load"); + state.current.ready = true; + onReady(); + postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors); + commands.setInsets( + isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 } + ); + if (currentNote.current) { + console.log("force reload note"); + loadNote({ ...currentNote.current, forced: true }); + } else { + await commands.setPlaceholder(placeholderTip.current); + isDefaultEditor && restoreEditorState(); + } + commands.setSettings(); + }, [ + onReady, + postMessage, + theme, + commands, + isDefaultEditor, + insets, + loadNote, + restoreEditorState + ]); return { ref: editorRef, diff --git a/apps/mobile/app/screens/notebook/index.tsx b/apps/mobile/app/screens/notebook/index.tsx index 04dd02bd8..9656a7174 100644 --- a/apps/mobile/app/screens/notebook/index.tsx +++ b/apps/mobile/app/screens/notebook/index.tsx @@ -30,12 +30,11 @@ import { eSubscribeEvent, eUnSubscribeEvent } from "../../services/event-manager"; -import Navigation, { - NavigationProps, - NotebookScreenParams -} from "../../services/navigation"; +import Navigation, { NavigationProps } from "../../services/navigation"; import SearchService from "../../services/search"; -import useNavigationStore from "../../stores/use-navigation-store"; +import useNavigationStore, { + NotebookScreenParams +} from "../../stores/use-navigation-store"; import { eOnNewTopicAdded, eOpenAddNotebookDialog, @@ -60,7 +59,7 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => { onBlur: () => false }); - const syncWithNavigation = () => { + const syncWithNavigation = React.useCallback(() => { useNavigationStore.getState().update( { name: route.name, @@ -71,35 +70,38 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => { params.current?.canGoBack ); SearchService.prepareSearch = prepareSearch; - }; + }, [route.name]); - const onRequestUpdate = (data?: NotebookScreenParams) => { - if (data) params.current = data; - params.current.title = params.current.item.title; - try { - const notebook = db.notebooks?.notebook(params?.current?.item?.id) - ?.data as NotebookType; - if (notebook) { - params.current.item = notebook; - setTopics( - groupArray( - qclone(notebook.topics), - db.settings?.getGroupOptions("topics") - ) - ); - syncWithNavigation(); + const onRequestUpdate = React.useCallback( + (data?: NotebookScreenParams) => { + if (data) params.current = data; + params.current.title = params.current.item.title; + try { + const notebook = db.notebooks?.notebook(params?.current?.item?.id) + ?.data as NotebookType; + if (notebook) { + params.current.item = notebook; + setTopics( + groupArray( + qclone(notebook.topics), + db.settings?.getGroupOptions("topics") + ) + ); + syncWithNavigation(); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); - } - }; + }, + [syncWithNavigation] + ); useEffect(() => { eSubscribeEvent(eOnNewTopicAdded, onRequestUpdate); return () => { eUnSubscribeEvent(eOnNewTopicAdded, onRequestUpdate); }; - }, [topics]); + }, [onRequestUpdate, topics]); const prepareSearch = () => { SearchService.update({ diff --git a/apps/mobile/app/screens/notes/colored.tsx b/apps/mobile/app/screens/notes/colored.tsx index 123f944d3..be2f87a44 100644 --- a/apps/mobile/app/screens/notes/colored.tsx +++ b/apps/mobile/app/screens/notes/colored.tsx @@ -20,10 +20,8 @@ import { groupArray } from "@notesnook/core/utils/grouping"; import React from "react"; import NotesPage, { PLACEHOLDER_DATA } from "."; import { db } from "../../common/database"; -import Navigation, { - NavigationProps, - NotesScreenParams -} from "../../services/navigation"; +import Navigation, { NavigationProps } from "../../services/navigation"; +import { NotesScreenParams } from "../../stores/use-navigation-store"; import { ColorType } from "../../utils/types"; import { getAlias, openEditor, toCamelCase } from "./common"; export const ColoredNotes = ({ diff --git a/apps/mobile/app/screens/notes/common.ts b/apps/mobile/app/screens/notes/common.ts index d2cddfac0..1b14495e0 100644 --- a/apps/mobile/app/screens/notes/common.ts +++ b/apps/mobile/app/screens/notes/common.ts @@ -18,7 +18,7 @@ import { DDS } from "../../services/device-detection"; import { eSendEvent } from "../../services/event-manager"; -import Navigation, { NotesScreenParams } from "../../services/navigation"; +import Navigation from "../../services/navigation"; import { useMenuStore } from "../../stores/use-menu-store"; import { useTagStore } from "../../stores/use-tag-store"; import { db } from "../../common/database"; @@ -26,6 +26,8 @@ import { eOnLoadNote } from "../../utils/events"; import { openLinkInBrowser } from "../../utils/functions"; import { tabBarRef } from "../../utils/global-refs"; import { editorController, editorState } from "../editor/tiptap/utils"; +import { NotesScreenParams } from "../../stores/use-navigation-store"; +import { TopicType } from "../../utils/types"; export function toCamelCase(title: string) { return title.slice(0, 1).toUpperCase() + title.slice(1); @@ -34,7 +36,7 @@ export function toCamelCase(title: string) { export function getAlias(params: Partial) { if (!params) return ""; const { item } = params; - return item?.alias || item?.title; + return (item as TopicType)?.alias || item?.title; } export function openMonographsWebpage() { diff --git a/apps/mobile/app/screens/notes/index.tsx b/apps/mobile/app/screens/notes/index.tsx index 1ba87aa04..a0a634a89 100644 --- a/apps/mobile/app/screens/notes/index.tsx +++ b/apps/mobile/app/screens/notes/index.tsx @@ -25,13 +25,11 @@ import { eSubscribeEvent, eUnSubscribeEvent } from "../../services/event-manager"; -import Navigation, { - NavigationProps, - NotesScreenParams -} from "../../services/navigation"; +import Navigation, { NavigationProps } from "../../services/navigation"; import SearchService from "../../services/search"; import useNavigationStore, { HeaderRightButton, + NotesScreenParams, RouteName } from "../../stores/use-navigation-store"; import { useNoteStore } from "../../stores/use-notes-store"; @@ -117,10 +115,21 @@ const NotesPage = ({ focusOnInit: !focusControl }); - const syncWithNavigation = () => { + const prepareSearch = React.useCallback(() => { + const { item } = params.current; + SearchService.update({ + placeholder: `Search in ${alias}`, + type: "notes", + title: item.type === "tag" ? "#" + alias : toCamelCase(item.title), + get: () => { + return get(params.current, false); + } + }); + }, [alias, get]); + + const syncWithNavigation = React.useCallback(() => { const { item, title } = params.current; const alias = getAlias(params.current); - console.log(alias, title, "syncWithNavigation", params.current); useNavigationStore.getState().update( { name: route.name, @@ -146,35 +155,44 @@ const NotesPage = ({ color: item.title, notebook: (item as TopicType).notebookId }); - }; + }, [ + isMonograph, + onPressFloatingButton, + prepareSearch, + rightButtons, + route.name + ]); - const onRequestUpdate = (data?: NotesScreenParams) => { - const isNew = data && data?.item?.id !== params.current?.item?.id; - if (data) params.current = data; - params.current.title = params.current.title || params.current.item.title; - const { item } = params.current; - try { - if (isNew) setLoadingNotes(true); - const notes = get(params.current, true) as NoteType[]; - if ( - (item.type === "tag" || item.type === "color") && - (!notes || notes.length === 0) - ) { - return Navigation.goBack(); + const onRequestUpdate = React.useCallback( + (data?: NotesScreenParams) => { + const isNew = data && data?.item?.id !== params.current?.item?.id; + if (data) params.current = data; + params.current.title = params.current.title || params.current.item.title; + const { item } = params.current; + try { + if (isNew) setLoadingNotes(true); + const notes = get(params.current, true) as NoteType[]; + if ( + (item.type === "tag" || item.type === "color") && + (!notes || notes.length === 0) + ) { + return Navigation.goBack(); + } + if (item.type === "topic") setWarning(!isSynced(params.current)); + setNotes(notes); + syncWithNavigation(); + } catch (e) { + console.error(e); } - if (item.type === "topic") setWarning(!isSynced(params.current)); - setNotes(notes); - syncWithNavigation(); - } catch (e) { - console.error(e); - } - }; + }, + [get, syncWithNavigation] + ); useEffect(() => { if (loadingNotes) { setTimeout(() => setLoadingNotes(false), 300); } - }, [notes]); + }, [loadingNotes, notes]); useEffect(() => { eSubscribeEvent(route.name, onRequestUpdate); @@ -182,19 +200,7 @@ const NotesPage = ({ setOnFirstSave(null); eUnSubscribeEvent(route.name, onRequestUpdate); }; - }, []); - - const prepareSearch = () => { - const { item } = params.current; - SearchService.update({ - placeholder: `Search in ${alias}`, - type: "notes", - title: item.type === "tag" ? "#" + alias : toCamelCase(item.title), - get: () => { - return get(params.current, false); - } - }); - }; + }, [onRequestUpdate, route.name]); return ( { setSearchResults([]); setSearchStatus(false, null); }; - }, []); + }, [setSearchResults, setSearchStatus]); return ( diff --git a/apps/mobile/app/screens/settings/2fa.tsx b/apps/mobile/app/screens/settings/2fa.tsx index 9b795f88e..261963cde 100644 --- a/apps/mobile/app/screens/settings/2fa.tsx +++ b/apps/mobile/app/screens/settings/2fa.tsx @@ -180,7 +180,7 @@ export const MFASetup = ({ setLoading(false); }); } - }, []); + }, [method?.id]); const codeHelpText = { app: "After putting the above code in authenticator app, the app will display a code that you can enter below.", diff --git a/apps/mobile/app/screens/settings/debug.tsx b/apps/mobile/app/screens/settings/debug.tsx index fcce6cd5b..ad48a0b21 100644 --- a/apps/mobile/app/screens/settings/debug.tsx +++ b/apps/mobile/app/screens/settings/debug.tsx @@ -78,59 +78,62 @@ export default function DebugLogs() { setLogs(logs); } })(); - }, [seconds]); + }, [currentLog, seconds, start]); - const renderItem = ({ item }: { item: LogMessage; index: number }) => { - const background = - item.level === LogLevel.Error || item.level === LogLevel.Fatal - ? hexToRGBA(colors.red, 0.2) - : item.level === LogLevel.Warn - ? hexToRGBA(colors.orange, 0.2) - : "transparent"; + const renderItem = React.useCallback( + ({ item }: { item: LogMessage; index: number }) => { + const background = + item.level === LogLevel.Error || item.level === LogLevel.Fatal + ? hexToRGBA(colors.red, 0.2) + : item.level === LogLevel.Warn + ? hexToRGBA(colors.orange, 0.2) + : "transparent"; - const color = - item.level === LogLevel.Error || item.level === LogLevel.Fatal - ? colors.red - : item.level === LogLevel.Warn - ? colors.orange - : colors.pri; + const color = + item.level === LogLevel.Error || item.level === LogLevel.Fatal + ? colors.red + : item.level === LogLevel.Warn + ? colors.orange + : colors.pri; - return !item ? null : ( - { - Clipboard.setString(format(item)); - ToastEvent.show({ - heading: "Debug log copied!", - context: "global", - type: "success" - }); - }} - style={{ - paddingHorizontal: 12, - paddingVertical: 12, - backgroundColor: background, - flexShrink: 1, - borderBottomWidth: 1, - borderBottomColor: colors.nav - }} - > - { + Clipboard.setString(format(item)); + ToastEvent.show({ + heading: "Debug log copied!", + context: "global", + type: "success" + }); + }} + style={{ + paddingHorizontal: 12, + paddingVertical: 12, + backgroundColor: background, + flexShrink: 1, + borderBottomWidth: 1, + borderBottomColor: colors.nav }} - size={12} - color={color} > - {format(item)} - - - ); - }; + + {format(item)} + + + ); + }, + [colors.nav, colors.orange, colors.pri, colors.red] + ); - const downloadLogs = async () => { + const downloadLogs = React.useCallback(async () => { try { let path = null; const fileName = sanitizeFilename(`notesnook_logs_${Date.now()}`); @@ -165,9 +168,9 @@ export default function DebugLogs() { } catch (e) { console.log(e); } - }; + }, [currentLog?.logs]); - const copyLogs = () => { + const copyLogs = React.useCallback(() => { const data = currentLog?.logs .map((log) => { return !log ? "" : format(log); @@ -180,9 +183,9 @@ export default function DebugLogs() { context: "global", type: "success" }); - }; + }, [currentLog?.logs]); - const clearLogs = () => { + const clearLogs = React.useCallback(() => { if (!currentLog) return; presentDialog({ title: "Clear logs", @@ -203,7 +206,7 @@ export default function DebugLogs() { } } }); - }; + }, [currentLog, logs]); return ( state.data); const ungrouped = getUngroupedTools(data) as ToolId[]; - const renderTool = React.useCallback((item: ToolId) => { - const tool = findToolById(item); - const iconSvgString = tool ? getToolIcon(tool.icon as ToolId) : null; - return ( - { - const _data = useDragState.getState().data.slice(); - if (group.groupIndex !== undefined) { - (_data[group.groupIndex][group.index] as ToolId[]).unshift( - item as ToolId - ); - } else { - _data[group.index].unshift(item); - } - useDragState.getState().setData(_data); - }} - customStyle={{ - marginBottom: 10, - width: "100%", - height: 50, - paddingHorizontal: 12, - paddingRight: 0, - borderRadius: 5, - flexDirection: "row", - alignItems: "center", - justifyContent: "flex-start" - }} - > - { + const tool = findToolById(item); + const iconSvgString = tool ? getToolIcon(tool.icon as ToolId) : null; + return ( + { + const _data = useDragState.getState().data.slice(); + if (group.groupIndex !== undefined) { + (_data[group.groupIndex][group.index] as ToolId[]).unshift( + item as ToolId + ); + } else { + _data[group.index].unshift(item); + } + useDragState.getState().setData(_data); + }} + customStyle={{ + marginBottom: 10, + width: "100%", + height: 50, + paddingHorizontal: 12, + paddingRight: 0, + borderRadius: 5, flexDirection: "row", - alignItems: "center" + alignItems: "center", + justifyContent: "flex-start" }} > - {iconSvgString ? ( - - ) : null} - - {tool?.title} - - - - ); - }, []); + {iconSvgString ? ( + + ) : null} + + {tool?.title} + + + + ); + }, + [colors.pri, group.groupIndex, group.index] + ); return ( { - presentDialog({ - context: "global", - title: "Delete collapsed section?", - positiveText: "Delete", - paragraph: - "All tools in the collapsed section will also be removed.", - positivePress: () => { + const buttons = React.useMemo( + () => + isSubgroup + ? [ + { + name: "minus", + onPress: () => { + presentDialog({ + context: "global", + title: "Delete collapsed section?", + positiveText: "Delete", + paragraph: + "All tools in the collapsed section will also be removed.", + positivePress: () => { + if (typeof groupIndex !== "number") return; + const _data = useDragState.getState().data.slice(); + _data[groupIndex].splice(index, 1); + setData(_data); + } + }); + } + }, + { + name: "plus", + onPress: () => { + ToolSheet.present({ + item, + index, + groupIndex, + parentIndex + }); + } + } + ] + : [ + { + name: "minus", + onPress: () => { if (typeof groupIndex !== "number") return; const _data = useDragState.getState().data.slice(); - _data[groupIndex].splice(index, 1); + if (typeof parentIndex !== "number") { + const index = _data[groupIndex].findIndex( + (tool) => tool === item + ); + _data[groupIndex].splice(index, 1); + } else { + const index = ( + _data[parentIndex][groupIndex] as ToolId[] + ).findIndex((tool: string) => tool === item); + (_data[parentIndex][groupIndex] as ToolId[]).splice(index, 1); + } + console.log(_data[groupIndex]); setData(_data); } - }); - } - }, - { - name: "plus", - onPress: () => { - ToolSheet.present({ - item, - index, - groupIndex, - parentIndex - }); - } - } - ] - : [ - { - name: "minus", - onPress: () => { - if (typeof groupIndex !== "number") return; - const _data = useDragState.getState().data.slice(); - if (typeof parentIndex !== "number") { - const index = _data[groupIndex].findIndex( - (tool) => tool === item - ); - _data[groupIndex].splice(index, 1); - } else { - const index = ( - _data[parentIndex][groupIndex] as ToolId[] - ).findIndex((tool: string) => tool === item); - (_data[parentIndex][groupIndex] as ToolId[]).splice(index, 1); } - console.log(_data[groupIndex]); - setData(_data); - } - } - ]; + ], + [groupIndex, index, isSubgroup, item, parentIndex, setData] + ); if (parentIndex === undefined && !isSubgroup) { buttons.unshift({ @@ -236,7 +240,20 @@ export const Tool = ({ ) : null} ), - [] + [ + buttons, + colors.bg, + colors.icon, + colors.nav, + colors.pri, + groupIndex, + iconSvgString, + index, + isDragged, + isSubgroup, + item, + tool?.title + ] ); const onDrop = (data: DraxDragWithReceiverEventData) => { diff --git a/apps/mobile/app/screens/settings/index.tsx b/apps/mobile/app/screens/settings/index.tsx index b8c6e132a..743efe41c 100644 --- a/apps/mobile/app/screens/settings/index.tsx +++ b/apps/mobile/app/screens/settings/index.tsx @@ -24,13 +24,6 @@ import Group from "./group"; import Home from "./home"; import { RouteParams } from "./types"; const SettingsStack = createNativeStackNavigator(); -const screenListeners = { - beforeRemove: (e: any) => { - if (e.target?.startsWith("SettingsGroup")) { - useNavigationStore.getState().update({ name: "Settings" }, false); - } - } -}; // const Home = React.lazy(() => import(/* webpackChunkName: "settings-home" */ './home')); // const Group = React.lazy(() => import(/* webpackChunkName: "settings-group" */ './group')); @@ -65,7 +58,13 @@ export const Settings = () => { return ( { + if (e.target?.startsWith("SettingsGroup")) { + useNavigationStore.getState().update({ name: "Settings" }, false); + } + } + }} screenOptions={{ animation: "none", headerShown: false, diff --git a/apps/mobile/app/services/tip-manager.ts b/apps/mobile/app/services/tip-manager.ts index a4832e9e1..f7294a6e4 100644 --- a/apps/mobile/app/services/tip-manager.ts +++ b/apps/mobile/app/services/tip-manager.ts @@ -137,7 +137,7 @@ export const useTip = ( return () => { clearInterval(intervalRef.current); }; - }, [context, fallback]); + }, [context, fallback, options.delay, options?.rotate]); return tip; }; diff --git a/apps/mobile/app/utils/types.ts b/apps/mobile/app/utils/types.ts index 09636016c..3487e0561 100644 --- a/apps/mobile/app/utils/types.ts +++ b/apps/mobile/app/utils/types.ts @@ -58,6 +58,10 @@ export type Item = export type MonographType = { type: "monograph"; + id: string; + title: string; + dateModified: number; + dateCreated: number; }; export interface Entity {