diff --git a/apps/mobile/app/components/dialog/dialog-buttons.js b/apps/mobile/app/components/dialog/dialog-buttons.js index 084f13abf..49acbab79 100644 --- a/apps/mobile/app/components/dialog/dialog-buttons.js +++ b/apps/mobile/app/components/dialog/dialog-buttons.js @@ -56,12 +56,7 @@ const DialogButtons = ({ } ]} > - {loading ? ( - - ) : doneText ? ( + {doneText ? ( { const { colors } = useThemeColors(); const [visible, setVisible] = useState(false); const [checked, setChecked] = useState(false); + const [loading, setLoading] = useState(false); const values = useRef({ inputValue: undefined }); @@ -73,14 +74,23 @@ export const Dialog = ({ context = "global" }) => { const onPressPositive = async () => { if (dialogInfo.positivePress) { inputRef.current?.blur(); - let result = await dialogInfo.positivePress( - values.current.inputValue || dialogInfo.defaultValue, - checked - ); + setLoading(true); + let result; + try { + result = await dialogInfo.positivePress( + values.current.inputValue || dialogInfo.defaultValue, + checked + ); + } catch (e) { + /** Empty */ + } + setLoading(false); + if (result === false) { return; } } + setChecked(false); values.current.inputValue = undefined; setVisible(false); @@ -226,6 +236,7 @@ export const Dialog = ({ context = "global" }) => { state.focusedRouteId); - + console.log(focusedRouteId, "focusedRouteId"); useEffect(() => { if (selectionMode) { fluidTabsRef.current?.lock(); @@ -169,116 +170,148 @@ export const SelectionHeader = React.memo( color={allSelected ? colors.primary.accent : colors.primary.icon} name="select-all" /> - {!selectedItemsList.length ? null - : [ - { - title: strings.move(), - onPress: async () => { - const ids = selectedItemsList; - const notebooks = await db.notebooks.all.items(ids); - MoveNotebookSheet.present(notebooks); - }, - visible: renderedInRoute === "Notebooks", - icon: "arrow-right-bold-box-outline" - }, - { - title: strings.manageTags(), - onPress: async () => { - await sleep(100); - ManageTagsSheet.present(selectedItemsList); - }, - visible: type === "note", - icon: "pound" - }, - { - title: strings.export(), - onPress: async () => { - await sleep(100); - ExportNotesSheet.present(selectedItemsList); - }, - visible: type === "note", - icon: "export" - }, - { - title: strings.linkNotebook(), - onPress: async () => { - await sleep(100); - MoveNoteSheet.present(); - }, - visible: type === "note", - icon: "plus" - }, - { - title: strings.unlinkNotebook(), - onPress: async () => { - if (!id) return; - await db.notes.removeFromNotebook(id, ...selectedItemsList); - updateNotebook(id); - Navigation.queueRoutesForUpdate(); - clearSelection(); - }, - visible: renderedInRoute === "Notebook", - icon: "minus" - }, - { - title: strings.unfavorite(), - onPress: addToFavorite, - visible: focusedRouteId === "Favorites", - icon: "star-off" - }, - { - title: strings.moveToTrash(), - onPress: async () => { - const selection = useSelectionStore.getState(); - if (!selection.selectionMode) return; - await deleteItems( - selection.selectionMode as ItemType, - selection.selectedItemsList - ); - selection.clearSelection(); - selection.setSelectionMode(undefined); - }, - visible: type === "note" || type === "notebook", - icon: "delete" - }, - { - title: strings.doActions.delete.unknown( - type!, - selectedItemsList.length - ), - onPress: async () => { - const selection = useSelectionStore.getState(); - if (!selection.selectionMode) return; - await deleteItems( - selection.selectionMode as ItemType, - selection.selectedItemsList - ); - selection.clearSelection(); - selection.setSelectionMode(undefined); - }, - visible: - type !== "trash" && type !== "note" && type !== "notebook", - icon: "delete" - }, - { - title: strings.restore(), - onPress: restoreItem, - visible: type === "trash", - icon: "delete-restore" - }, - { - title: strings.delete(), - onPress: deleteItem, - visible: type === "trash", - icon: "delete" - } - ].map((item) => + : (focusedRouteId === "Monographs" + ? [ + { + title: strings.unpublish(), + onPress: () => { + presentDialog({ + title: strings.doActions.unpublish.note( + selectedItemsList.length + ), + positiveText: strings.unpublish(), + negativeText: strings.cancel(), + positivePress: async () => { + for (const id of selectedItemsList) { + await db.monographs.unpublish(id); + } + Navigation.queueRoutesForUpdate(); + clearSelection(); + }, + positiveType: "errorShade" + }); + }, + visible: true, + icon: "delete" + } + ] + : [ + { + title: strings.move(), + onPress: async () => { + const ids = selectedItemsList; + const notebooks = await db.notebooks.all.items(ids); + MoveNotebookSheet.present(notebooks); + }, + visible: renderedInRoute === "Notebooks", + icon: "arrow-right-bold-box-outline" + }, + { + title: strings.manageTags(), + onPress: async () => { + await sleep(100); + ManageTagsSheet.present(selectedItemsList); + }, + visible: type === "note", + icon: "pound" + }, + { + title: strings.export(), + onPress: async () => { + await sleep(100); + ExportNotesSheet.present(selectedItemsList); + }, + visible: type === "note", + icon: "export" + }, + { + title: strings.linkNotebook(), + onPress: async () => { + await sleep(100); + MoveNoteSheet.present(); + }, + visible: type === "note", + icon: "plus" + }, + { + title: strings.unlinkNotebook(), + onPress: async () => { + if (!id) return; + await db.notes.removeFromNotebook( + id, + ...selectedItemsList + ); + updateNotebook(id); + Navigation.queueRoutesForUpdate(); + clearSelection(); + }, + visible: renderedInRoute === "Notebook", + icon: "minus" + }, + { + title: strings.unfavorite(), + onPress: addToFavorite, + visible: focusedRouteId === "Favorites", + icon: "star-off" + }, + { + title: strings.moveToTrash(), + onPress: async () => { + const selection = useSelectionStore.getState(); + if (!selection.selectionMode) return; + await deleteItems( + selection.selectionMode as ItemType, + selection.selectedItemsList + ); + selection.clearSelection(); + selection.setSelectionMode(undefined); + }, + visible: type === "note" || type === "notebook", + icon: "delete" + }, + { + title: strings.doActions.delete.unknown( + type!, + selectedItemsList.length + ), + onPress: async () => { + const selection = useSelectionStore.getState(); + if (!selection.selectionMode) return; + await deleteItems( + selection.selectionMode as ItemType, + selection.selectedItemsList + ); + selection.clearSelection(); + selection.setSelectionMode(undefined); + }, + visible: + type !== "trash" && + type !== "note" && + type !== "notebook", + icon: "delete" + }, + { + title: strings.restore(), + onPress: restoreItem, + visible: type === "trash", + icon: "delete-restore" + }, + { + title: strings.delete(), + onPress: deleteItem, + visible: type === "trash", + icon: "delete" + } + ] + ).map((item) => !item.visible ? null : ( . */ import { useThemeColors } from "@notesnook/theme"; -import React from "react"; +import React, { useRef } from "react"; import { ColorValue, GestureResponderEvent, TextStyle } from "react-native"; import Icon from "react-native-vector-icons/MaterialCommunityIcons"; -import { RGB_Linear_Shade, hexToRGBA } from "../../../utils/colors"; +import { hexToRGBA, RGB_Linear_Shade } from "../../../utils/colors"; import { AppFontSize } from "../../../utils/size"; import NativeTooltip from "../../../utils/tooltip"; import { Pressable, PressableProps } from "../pressable"; @@ -58,6 +58,7 @@ export const IconButton = ({ ...restProps }: IconButtonProps) => { const { colors } = useThemeColors(); + const localRef = useRef(null); const _onLongPress = (event: GestureResponderEvent) => { if (onLongPress) { @@ -65,14 +66,20 @@ export const IconButton = ({ return; } if (tooltipText) { - NativeTooltip.show(event, tooltipText, tooltipPosition); + NativeTooltip.show( + { + target: fwdRef?.current || localRef.current + }, + tooltipText, + tooltipPosition + ); } }; return ( { if ( + params.current.item.id && useNavigationStore.getState().focusedRouteId !== params.current.item.id && !data @@ -187,7 +188,7 @@ const NotesPage = ({ return ( <> diff --git a/apps/mobile/app/utils/tooltip.ts b/apps/mobile/app/utils/tooltip.ts index ad75c9b47..c5b6f184b 100644 --- a/apps/mobile/app/utils/tooltip.ts +++ b/apps/mobile/app/utils/tooltip.ts @@ -16,7 +16,126 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -import { Platform } from "react-native"; +import { findNodeHandle, NativeModules, Platform } from "react-native"; +import { fluidTabsRef } from "./global-refs"; +import { AppFontSize } from "./size"; + +const { RNTooltips } = NativeModules; + +type TooltipShowOptions = { + text: string; + position: number; + align: number; + autoHide: boolean; + duration: number; + clickToHide: boolean; + corner: number; + tintColor: string; + textColor: string; + textSize: number; + gravity: number; + arrow: boolean; + shadow: boolean; + onHide: () => void; +}; + +class Tooltips { + static POSITION: { + LEFT: 1; + RIGHT: 2; + TOP: 3; + BOTTOM: 4; + }; + + static ALIGN: { + START: 1; + CENTER: 2; + END: 3; + }; + + static GRAVITY: { + START: 1; + CENTER: 2; + END: 3; + }; + + static defaultProps = { + text: "", + position: 4, + align: 2, + autoHide: true, + duration: Platform.OS === "android" ? 4000 : 4, + clickToHide: false, + corner: Platform.OS === "android" ? 30 : 0, + tintColor: "#1F7C82", + textColor: "#FFFFFF", + textSize: 12, + gravity: 2, + arrow: true, + shadow: true + }; + + static Show(target: any, parent: any, props: Partial) { + if (typeof target !== "number") { + target = findNodeHandle(target); + } + if (typeof parent !== "number") { + parent = findNodeHandle(parent); + } + + if (props.text === undefined) { + props.text = Tooltips.defaultProps.text; + } + if (props.position === undefined) { + props.position = Tooltips.defaultProps.position; + } + if (props.align === undefined) { + props.align = Tooltips.defaultProps.align; + } + if (props.autoHide === undefined) { + props.autoHide = Tooltips.defaultProps.autoHide; + } + if (props.duration === undefined) { + props.duration = Tooltips.defaultProps.duration; + } + if (props.clickToHide === undefined) { + props.clickToHide = Tooltips.defaultProps.clickToHide; + } + if (props.corner === undefined) { + props.corner = Tooltips.defaultProps.corner; + } + if (props.tintColor === undefined) { + props.tintColor = Tooltips.defaultProps.tintColor; + } + if (props.textColor === undefined) { + props.textColor = Tooltips.defaultProps.textColor; + } + if (props.textSize === undefined) { + props.textSize = Tooltips.defaultProps.textSize; + } + if (props.gravity === undefined) { + props.gravity = Tooltips.defaultProps.gravity; + } + if (props.shadow === undefined) { + props.shadow = Tooltips.defaultProps.shadow; + } + if (props.arrow === undefined) { + props.arrow = Tooltips.defaultProps.arrow; + } + + RNTooltips.Show(target, parent, props, () => { + props.onHide && props.onHide(); + }); + } + + static Dismiss(target: any) { + if (typeof target !== "number") { + target = findNodeHandle(target); + } + + RNTooltips.Dismiss(target); + } +} export const POSITIONS = { LEFT: 1, @@ -24,26 +143,23 @@ export const POSITIONS = { TOP: 3, BOTTOM: 4 }; -let RNTooltips: any; let prevTarget: any = null; function show(event: any, text: string, position = 2) { - const fluidTabsRef = require("./global-refs").fluidTabsRef; - if (!RNTooltips) { - RNTooltips = require("react-native-tooltips").default; - } - if (!event._targetInst?.ref?.current) return; - prevTarget && RNTooltips.Dismiss(prevTarget); + if (!event.target) return; + prevTarget && Tooltips.Dismiss(prevTarget); prevTarget = null; - prevTarget = event._targetInst.ref.current; - RNTooltips.Show(prevTarget, fluidTabsRef.current?.node?.current, { + prevTarget = event.target; + + Tooltips.Show(prevTarget, fluidTabsRef.current?.node.current, { text: text, tintColor: "#000000", corner: Platform.OS === "ios" ? 5 : 40, - textSize: 14, + textSize: AppFontSize.md, position: position, - duration: 1000, + duration: 2000, autoHide: true, - clickToHide: true + clickToHide: true, + shadow: true }); }