mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 23:27:26 +01:00
Merge pull request #6584 from BlueWallet/tooltipreff
REF: ToolTipMenu to TSX
This commit is contained in:
commit
72439f36c1
15 changed files with 348 additions and 355 deletions
|
@ -7,6 +7,7 @@ import loc from '../loc';
|
||||||
import Clipboard from '@react-native-clipboard/clipboard';
|
import Clipboard from '@react-native-clipboard/clipboard';
|
||||||
import { useTheme } from './themes';
|
import { useTheme } from './themes';
|
||||||
import { ActionIcons } from '../typings/ActionIcons';
|
import { ActionIcons } from '../typings/ActionIcons';
|
||||||
|
import { Action } from './types';
|
||||||
|
|
||||||
interface QRCodeComponentProps {
|
interface QRCodeComponentProps {
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -18,22 +19,6 @@ interface QRCodeComponentProps {
|
||||||
onError?: () => void;
|
onError?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActionType {
|
|
||||||
Share: 'share';
|
|
||||||
Copy: 'copy';
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Action {
|
|
||||||
id: string;
|
|
||||||
text: string;
|
|
||||||
icon: ActionIcons;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionKeys: ActionType = {
|
|
||||||
Share: 'share',
|
|
||||||
Copy: 'copy',
|
|
||||||
};
|
|
||||||
|
|
||||||
const actionIcons: { [key: string]: ActionIcons } = {
|
const actionIcons: { [key: string]: ActionIcons } = {
|
||||||
Share: {
|
Share: {
|
||||||
iconType: 'SYSTEM',
|
iconType: 'SYSTEM',
|
||||||
|
@ -45,6 +30,22 @@ const actionIcons: { [key: string]: ActionIcons } = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const actionKeys = {
|
||||||
|
Share: 'share',
|
||||||
|
Copy: 'copy',
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuActions: Action[] =
|
||||||
|
Platform.OS === 'ios' || Platform.OS === 'macos'
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: actionKeys.Copy,
|
||||||
|
text: loc.transactions.details_copy,
|
||||||
|
icon: actionIcons.Copy,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [{ id: actionKeys.Share, text: loc.receive.details_share, icon: actionIcons.Share }];
|
||||||
|
|
||||||
const QRCodeComponent: React.FC<QRCodeComponentProps> = ({
|
const QRCodeComponent: React.FC<QRCodeComponentProps> = ({
|
||||||
value = '',
|
value = '',
|
||||||
isLogoRendered = true,
|
isLogoRendered = true,
|
||||||
|
@ -75,23 +76,6 @@ const QRCodeComponent: React.FC<QRCodeComponentProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuActions = (): Action[] => {
|
|
||||||
const actions: Action[] = [];
|
|
||||||
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
||||||
actions.push({
|
|
||||||
id: actionKeys.Copy,
|
|
||||||
text: loc.transactions.details_copy,
|
|
||||||
icon: actionIcons.Copy,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
actions.push({
|
|
||||||
id: actionKeys.Share,
|
|
||||||
text: loc.receive.details_share,
|
|
||||||
icon: actionIcons.Share,
|
|
||||||
});
|
|
||||||
return actions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderQRCode = (
|
const renderQRCode = (
|
||||||
<QRCode
|
<QRCode
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -115,7 +99,7 @@ const QRCodeComponent: React.FC<QRCodeComponentProps> = ({
|
||||||
accessibilityLabel={loc.receive.qrcode_for_the_address}
|
accessibilityLabel={loc.receive.qrcode_for_the_address}
|
||||||
>
|
>
|
||||||
{isMenuAvailable ? (
|
{isMenuAvailable ? (
|
||||||
<ToolTipMenu actions={menuActions()} onPressMenuItem={onPressMenuItem}>
|
<ToolTipMenu actions={menuActions} onPressMenuItem={onPressMenuItem}>
|
||||||
{renderQRCode}
|
{renderQRCode}
|
||||||
</ToolTipMenu>
|
</ToolTipMenu>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { StyleProp, ViewStyle } from 'react-native';
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
import ToolTipMenu from './TooltipMenu';
|
|
||||||
import loc from '../loc';
|
import loc from '../loc';
|
||||||
import { ActionIcons } from '../typings/ActionIcons';
|
import { ActionIcons } from '../typings/ActionIcons';
|
||||||
import * as fs from '../blue_modules/fs';
|
import * as fs from '../blue_modules/fs';
|
||||||
|
import { Action } from './types';
|
||||||
|
import ToolTipMenu from './TooltipMenu';
|
||||||
|
|
||||||
interface SaveFileButtonProps {
|
interface SaveFileButtonProps {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
@ -11,7 +12,7 @@ interface SaveFileButtonProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
afterOnPress?: () => void;
|
afterOnPress?: () => void;
|
||||||
beforeOnPress?: () => Promise<void>; // Changed this line
|
beforeOnPress?: () => Promise<void>;
|
||||||
onMenuWillHide?: () => void;
|
onMenuWillHide?: () => void;
|
||||||
onMenuWillShow?: () => void;
|
onMenuWillShow?: () => void;
|
||||||
}
|
}
|
||||||
|
@ -26,30 +27,24 @@ const SaveFileButton: React.FC<SaveFileButtonProps> = ({
|
||||||
onMenuWillHide,
|
onMenuWillHide,
|
||||||
onMenuWillShow,
|
onMenuWillShow,
|
||||||
}) => {
|
}) => {
|
||||||
const actions = [
|
|
||||||
{ id: 'save', text: loc._.save, icon: actionIcons.Save },
|
|
||||||
{ id: 'share', text: loc.receive.details_share, icon: actionIcons.Share },
|
|
||||||
];
|
|
||||||
|
|
||||||
const handlePressMenuItem = async (actionId: string) => {
|
const handlePressMenuItem = async (actionId: string) => {
|
||||||
if (beforeOnPress) {
|
if (beforeOnPress) {
|
||||||
await beforeOnPress(); // Now properly awaiting a function that returns a promise
|
await beforeOnPress();
|
||||||
}
|
}
|
||||||
const action = actions.find(a => a.id === actionId);
|
const action = actions.find(a => a.id === actionId);
|
||||||
|
|
||||||
if (action?.id === 'save') {
|
if (action?.id === 'save') {
|
||||||
await fs.writeFileAndExport(fileName, fileContent, false).finally(() => {
|
await fs.writeFileAndExport(fileName, fileContent, false).finally(() => {
|
||||||
afterOnPress?.(); // Safely call afterOnPress if it exists
|
afterOnPress?.();
|
||||||
});
|
});
|
||||||
} else if (action?.id === 'share') {
|
} else if (action?.id === 'share') {
|
||||||
await fs.writeFileAndExport(fileName, fileContent, true).finally(() => {
|
await fs.writeFileAndExport(fileName, fileContent, true).finally(() => {
|
||||||
afterOnPress?.(); // Safely call afterOnPress if it exists
|
afterOnPress?.();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-ignore: Tooltip must be refactored to use TSX}
|
|
||||||
<ToolTipMenu
|
<ToolTipMenu
|
||||||
onMenuWillHide={onMenuWillHide}
|
onMenuWillHide={onMenuWillHide}
|
||||||
onMenuWillShow={onMenuWillShow}
|
onMenuWillShow={onMenuWillShow}
|
||||||
|
@ -57,7 +52,7 @@ const SaveFileButton: React.FC<SaveFileButtonProps> = ({
|
||||||
isMenuPrimaryAction
|
isMenuPrimaryAction
|
||||||
actions={actions}
|
actions={actions}
|
||||||
onPressMenuItem={handlePressMenuItem}
|
onPressMenuItem={handlePressMenuItem}
|
||||||
buttonStyle={style}
|
buttonStyle={style as ViewStyle} // Type assertion to match ViewStyle
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ToolTipMenu>
|
</ToolTipMenu>
|
||||||
|
@ -76,3 +71,7 @@ const actionIcons: { [key: string]: ActionIcons } = {
|
||||||
iconValue: 'square.and.arrow.down',
|
iconValue: 'square.and.arrow.down',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const actions: Action[] = [
|
||||||
|
{ id: 'save', text: loc._.save, icon: actionIcons.Save },
|
||||||
|
{ id: 'share', text: loc.receive.details_share, icon: actionIcons.Share },
|
||||||
|
];
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import React, { useRef, useEffect, forwardRef } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Pressable } from 'react-native';
|
|
||||||
import showPopupMenu from '../blue_modules/showPopupMenu';
|
|
||||||
|
|
||||||
const BaseToolTipMenu = (props, ref) => {
|
|
||||||
const menuRef = useRef();
|
|
||||||
const disabled = props.disabled ?? false;
|
|
||||||
const isMenuPrimaryAction = props.isMenuPrimaryAction ?? false;
|
|
||||||
const enableAndroidRipple = props.enableAndroidRipple ?? true;
|
|
||||||
const buttonStyle = props.buttonStyle ?? {};
|
|
||||||
const handleToolTipSelection = selection => {
|
|
||||||
props.onPressMenuItem(selection.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref && ref.current) {
|
|
||||||
ref.current.dismissMenu = dismissMenu;
|
|
||||||
}
|
|
||||||
}, [ref]);
|
|
||||||
|
|
||||||
const dismissMenu = () => {
|
|
||||||
console.log('dismissMenu Not implemented');
|
|
||||||
};
|
|
||||||
|
|
||||||
const showMenu = () => {
|
|
||||||
const menu = [];
|
|
||||||
for (const actions of props.actions) {
|
|
||||||
if (Array.isArray(actions)) {
|
|
||||||
for (const actionToMap of actions) {
|
|
||||||
menu.push({ id: actionToMap.id, label: actionToMap.text });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
menu.push({ id: actions.id, label: actions.text });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showPopupMenu(menu, handleToolTipSelection, menuRef.current);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Pressable
|
|
||||||
{...(enableAndroidRipple ? { android_ripple: { color: 'lightgrey' } } : {})}
|
|
||||||
ref={menuRef}
|
|
||||||
disabled={disabled}
|
|
||||||
style={buttonStyle}
|
|
||||||
{...(isMenuPrimaryAction ? { onPress: showMenu } : { onPress: props.onPress, onLongPress: showMenu })}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</Pressable>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ToolTipMenu = forwardRef(BaseToolTipMenu);
|
|
||||||
|
|
||||||
export default ToolTipMenu;
|
|
||||||
ToolTipMenu.propTypes = {
|
|
||||||
actions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
|
|
||||||
children: PropTypes.node,
|
|
||||||
onPressMenuItem: PropTypes.func.isRequired,
|
|
||||||
isMenuPrimaryAction: PropTypes.bool,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
};
|
|
76
components/TooltipMenu.android.tsx
Normal file
76
components/TooltipMenu.android.tsx
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import React, { useRef, useEffect, forwardRef, Ref, useMemo, useCallback } from 'react';
|
||||||
|
import { Pressable, View } from 'react-native';
|
||||||
|
import showPopupMenu, { OnPopupMenuItemSelect, PopupMenuItem } from '../blue_modules/showPopupMenu.android';
|
||||||
|
import { ToolTipMenuProps } from './types';
|
||||||
|
|
||||||
|
const dismissMenu = () => {
|
||||||
|
console.log('dismissMenu Not implemented');
|
||||||
|
};
|
||||||
|
const BaseToolTipMenu = (props: ToolTipMenuProps, ref: Ref<{ dismissMenu?: () => void }>) => {
|
||||||
|
const menuRef = useRef<View>(null);
|
||||||
|
const {
|
||||||
|
actions,
|
||||||
|
children,
|
||||||
|
onPressMenuItem,
|
||||||
|
isMenuPrimaryAction = false,
|
||||||
|
buttonStyle = {},
|
||||||
|
enableAndroidRipple = true,
|
||||||
|
disabled = false,
|
||||||
|
onPress,
|
||||||
|
...restProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const handleToolTipSelection = useCallback<OnPopupMenuItemSelect>(
|
||||||
|
(selection: PopupMenuItem) => {
|
||||||
|
if (selection.id) {
|
||||||
|
onPressMenuItem(selection.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onPressMenuItem],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// @ts-ignore: fix later
|
||||||
|
if (ref && ref.current) {
|
||||||
|
// @ts-ignore: fix later
|
||||||
|
ref.current.dismissMenu = dismissMenu;
|
||||||
|
}
|
||||||
|
}, [ref]);
|
||||||
|
|
||||||
|
const menuItems = useMemo(() => {
|
||||||
|
const menu: { id: string; label: string }[] = [];
|
||||||
|
actions.forEach(action => {
|
||||||
|
if (Array.isArray(action)) {
|
||||||
|
action.forEach(actionToMap => {
|
||||||
|
menu.push({ id: actionToMap.id.toString(), label: actionToMap.text });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
menu.push({ id: action.id.toString(), label: action.text });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menu;
|
||||||
|
}, [actions]);
|
||||||
|
|
||||||
|
const showMenu = useCallback(() => {
|
||||||
|
if (menuRef.current) {
|
||||||
|
showPopupMenu(menuItems, handleToolTipSelection, menuRef.current);
|
||||||
|
}
|
||||||
|
}, [menuItems, handleToolTipSelection]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
{...(enableAndroidRipple ? { android_ripple: { color: 'lightgrey' } } : {})}
|
||||||
|
ref={menuRef}
|
||||||
|
disabled={disabled}
|
||||||
|
style={buttonStyle}
|
||||||
|
{...(isMenuPrimaryAction ? { onPress: showMenu } : { onPress, onLongPress: showMenu })}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ToolTipMenu = forwardRef(BaseToolTipMenu);
|
||||||
|
|
||||||
|
export default ToolTipMenu;
|
|
@ -1,140 +0,0 @@
|
||||||
import React, { forwardRef } from 'react';
|
|
||||||
import { ContextMenuView, ContextMenuButton } from 'react-native-ios-context-menu';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { TouchableOpacity } from 'react-native';
|
|
||||||
|
|
||||||
const BaseToolTipMenu = (props, ref) => {
|
|
||||||
const menuItemMapped = ({ action, menuOptions }) => {
|
|
||||||
const item = {
|
|
||||||
actionKey: action.id.toString(),
|
|
||||||
actionTitle: action.text,
|
|
||||||
icon: action.icon,
|
|
||||||
menuOptions,
|
|
||||||
menuTitle: action.menuTitle,
|
|
||||||
};
|
|
||||||
item.menuState = action.menuStateOn ? 'on' : 'off';
|
|
||||||
|
|
||||||
if (action.disabled) {
|
|
||||||
item.menuAttributes = ['disabled'];
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = props.actions.map(action => {
|
|
||||||
if (Array.isArray(action)) {
|
|
||||||
const mapped = [];
|
|
||||||
for (const actionToMap of action) {
|
|
||||||
mapped.push(menuItemMapped({ action: actionToMap }));
|
|
||||||
}
|
|
||||||
const submenu = {
|
|
||||||
menuOptions: ['displayInline'],
|
|
||||||
menuItems: mapped,
|
|
||||||
menuTitle: '',
|
|
||||||
};
|
|
||||||
return submenu;
|
|
||||||
} else {
|
|
||||||
return menuItemMapped({ action });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const menuTitle = props.title ?? '';
|
|
||||||
const isButton = !!props.isButton;
|
|
||||||
const isMenuPrimaryAction = props.isMenuPrimaryAction ? props.isMenuPrimaryAction : false;
|
|
||||||
const renderPreview = props.renderPreview ?? undefined;
|
|
||||||
const disabled = props.disabled ?? false;
|
|
||||||
const onPress = props.onPress ?? undefined;
|
|
||||||
const onMenuWillShow = props.onMenuWillShow ?? undefined;
|
|
||||||
const onMenuWillHide = props.onMenuWillHide ?? undefined;
|
|
||||||
|
|
||||||
const buttonStyle = props.buttonStyle;
|
|
||||||
return isButton ? (
|
|
||||||
<TouchableOpacity onPress={onPress} disabled={disabled} accessibilityRole="button" style={buttonStyle}>
|
|
||||||
<ContextMenuButton
|
|
||||||
ref={ref}
|
|
||||||
onMenuWillShow={onMenuWillShow}
|
|
||||||
onMenuWillHide={onMenuWillHide}
|
|
||||||
useActionSheetFallback={false}
|
|
||||||
onPressMenuItem={({ nativeEvent }) => {
|
|
||||||
props.onPressMenuItem(nativeEvent.actionKey);
|
|
||||||
}}
|
|
||||||
isMenuPrimaryAction={isMenuPrimaryAction}
|
|
||||||
menuConfig={{
|
|
||||||
menuTitle,
|
|
||||||
menuItems,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</ContextMenuButton>
|
|
||||||
</TouchableOpacity>
|
|
||||||
) : props.onPress ? (
|
|
||||||
<ContextMenuView
|
|
||||||
ref={ref}
|
|
||||||
lazyPreview
|
|
||||||
shouldEnableAggressiveCleanup
|
|
||||||
shouldCleanupOnComponentWillUnmountForMenuPreview
|
|
||||||
internalCleanupMode="automatic"
|
|
||||||
onPressMenuItem={({ nativeEvent }) => {
|
|
||||||
props.onPressMenuItem(nativeEvent.actionKey);
|
|
||||||
}}
|
|
||||||
useActionSheetFallback={false}
|
|
||||||
menuConfig={{
|
|
||||||
menuTitle,
|
|
||||||
menuItems,
|
|
||||||
}}
|
|
||||||
{...(renderPreview
|
|
||||||
? {
|
|
||||||
previewConfig: {
|
|
||||||
previewType: 'CUSTOM',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
},
|
|
||||||
renderPreview,
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
>
|
|
||||||
<TouchableOpacity accessibilityRole="button" onPress={props.onPress}>
|
|
||||||
{props.children}
|
|
||||||
</TouchableOpacity>
|
|
||||||
</ContextMenuView>
|
|
||||||
) : (
|
|
||||||
<ContextMenuView
|
|
||||||
ref={ref}
|
|
||||||
internalCleanupMode="viewController"
|
|
||||||
onPressMenuItem={({ nativeEvent }) => {
|
|
||||||
props.onPressMenuItem(nativeEvent.actionKey);
|
|
||||||
}}
|
|
||||||
lazyPreview
|
|
||||||
shouldEnableAggressiveCleanup
|
|
||||||
useActionSheetFallback={false}
|
|
||||||
menuConfig={{
|
|
||||||
menuTitle,
|
|
||||||
menuItems,
|
|
||||||
}}
|
|
||||||
{...(renderPreview
|
|
||||||
? {
|
|
||||||
previewConfig: {
|
|
||||||
previewType: 'CUSTOM',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
},
|
|
||||||
renderPreview,
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</ContextMenuView>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ToolTipMenu = forwardRef(BaseToolTipMenu);
|
|
||||||
|
|
||||||
export default ToolTipMenu;
|
|
||||||
ToolTipMenu.propTypes = {
|
|
||||||
actions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
children: PropTypes.node,
|
|
||||||
onPressMenuItem: PropTypes.func.isRequired,
|
|
||||||
isMenuPrimaryAction: PropTypes.bool,
|
|
||||||
isButton: PropTypes.bool,
|
|
||||||
renderPreview: PropTypes.func,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
previewValue: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
};
|
|
122
components/TooltipMenu.ios.tsx
Normal file
122
components/TooltipMenu.ios.tsx
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import React, { forwardRef, Ref, useMemo, useCallback } from 'react';
|
||||||
|
import { ContextMenuView, ContextMenuButton, RenderItem } from 'react-native-ios-context-menu';
|
||||||
|
import { TouchableOpacity } from 'react-native';
|
||||||
|
import { ToolTipMenuProps, Action } from './types';
|
||||||
|
|
||||||
|
const BaseToolTipMenu = (props: ToolTipMenuProps, ref: Ref<any>) => {
|
||||||
|
const {
|
||||||
|
title = '',
|
||||||
|
isButton = false,
|
||||||
|
isMenuPrimaryAction = false,
|
||||||
|
renderPreview,
|
||||||
|
disabled = false,
|
||||||
|
onPress,
|
||||||
|
onMenuWillShow,
|
||||||
|
onMenuWillHide,
|
||||||
|
buttonStyle,
|
||||||
|
onPressMenuItem,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const menuItemMapped = useCallback(({ action, menuOptions }: { action: Action; menuOptions?: string[] }) => {
|
||||||
|
const item: any = {
|
||||||
|
actionKey: action.id.toString(),
|
||||||
|
actionTitle: action.text,
|
||||||
|
icon: action.icon,
|
||||||
|
menuOptions,
|
||||||
|
menuTitle: action.menuTitle,
|
||||||
|
};
|
||||||
|
item.menuState = action.menuStateOn ? 'on' : 'off';
|
||||||
|
|
||||||
|
if (action.disabled) {
|
||||||
|
item.menuAttributes = ['disabled'];
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const menuItems = useMemo(
|
||||||
|
() =>
|
||||||
|
props.actions.map(action => {
|
||||||
|
if (Array.isArray(action)) {
|
||||||
|
const mapped = action.map(actionToMap => menuItemMapped({ action: actionToMap }));
|
||||||
|
return {
|
||||||
|
menuOptions: ['displayInline'],
|
||||||
|
menuItems: mapped,
|
||||||
|
menuTitle: '',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return menuItemMapped({ action });
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[props.actions, menuItemMapped],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePressMenuItem = useCallback(
|
||||||
|
({ nativeEvent }: { nativeEvent: { actionKey: string } }) => {
|
||||||
|
onPressMenuItem(nativeEvent.actionKey);
|
||||||
|
},
|
||||||
|
[onPressMenuItem],
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderContextMenuButton = () => (
|
||||||
|
<ContextMenuButton
|
||||||
|
ref={ref}
|
||||||
|
onMenuWillShow={onMenuWillShow}
|
||||||
|
onMenuWillHide={onMenuWillHide}
|
||||||
|
useActionSheetFallback={false}
|
||||||
|
onPressMenuItem={handlePressMenuItem}
|
||||||
|
isMenuPrimaryAction={isMenuPrimaryAction}
|
||||||
|
menuConfig={{
|
||||||
|
menuTitle: title,
|
||||||
|
menuItems,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</ContextMenuButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderContextMenuView = () => (
|
||||||
|
<ContextMenuView
|
||||||
|
ref={ref}
|
||||||
|
lazyPreview
|
||||||
|
shouldEnableAggressiveCleanup
|
||||||
|
internalCleanupMode="automatic"
|
||||||
|
onPressMenuItem={handlePressMenuItem}
|
||||||
|
useActionSheetFallback={false}
|
||||||
|
menuConfig={{
|
||||||
|
menuTitle: title,
|
||||||
|
menuItems,
|
||||||
|
}}
|
||||||
|
{...(renderPreview
|
||||||
|
? {
|
||||||
|
previewConfig: {
|
||||||
|
previewType: 'CUSTOM',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
},
|
||||||
|
renderPreview: renderPreview as RenderItem,
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
>
|
||||||
|
{onPress ? (
|
||||||
|
<TouchableOpacity accessibilityRole="button" onPress={onPress}>
|
||||||
|
{props.children}
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : (
|
||||||
|
props.children
|
||||||
|
)}
|
||||||
|
</ContextMenuView>
|
||||||
|
);
|
||||||
|
|
||||||
|
return isMenuPrimaryAction && onPress ? (
|
||||||
|
<TouchableOpacity onPress={onPress} disabled={disabled} accessibilityRole="button" style={buttonStyle}>
|
||||||
|
{renderContextMenuButton()}
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : isButton ? (
|
||||||
|
renderContextMenuButton()
|
||||||
|
) : (
|
||||||
|
renderContextMenuView()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ToolTipMenu = forwardRef(BaseToolTipMenu);
|
||||||
|
|
||||||
|
export default ToolTipMenu;
|
|
@ -1,9 +0,0 @@
|
||||||
import { forwardRef } from 'react';
|
|
||||||
|
|
||||||
const BaseToolTipMenu = (props, _ref) => {
|
|
||||||
return props.children;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ToolTipMenu = forwardRef(BaseToolTipMenu);
|
|
||||||
|
|
||||||
export default ToolTipMenu;
|
|
11
components/TooltipMenu.tsx
Normal file
11
components/TooltipMenu.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { forwardRef, Ref } from 'react';
|
||||||
|
import { ToolTipMenuProps } from './types';
|
||||||
|
|
||||||
|
const BaseToolTipMenu = (props: ToolTipMenuProps, ref: Ref<any>) => {
|
||||||
|
console.debug('ToolTipMenu.tsx ref:', ref);
|
||||||
|
return props.children;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ToolTipMenu = forwardRef(BaseToolTipMenu);
|
||||||
|
|
||||||
|
export default ToolTipMenu;
|
|
@ -20,6 +20,7 @@ import { useTheme } from './themes';
|
||||||
import ListItem from './ListItem';
|
import ListItem from './ListItem';
|
||||||
import { useSettings } from './Context/SettingsContext';
|
import { useSettings } from './Context/SettingsContext';
|
||||||
import { LightningTransaction, Transaction } from '../class/wallets/types';
|
import { LightningTransaction, Transaction } from '../class/wallets/types';
|
||||||
|
import { Action } from './types';
|
||||||
|
|
||||||
interface TransactionListItemProps {
|
interface TransactionListItemProps {
|
||||||
itemPriceUnit: BitcoinUnit;
|
itemPriceUnit: BitcoinUnit;
|
||||||
|
@ -287,9 +288,9 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
||||||
handleOnViewOnBlockExplorer,
|
handleOnViewOnBlockExplorer,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
const toolTipActions = useMemo((): Action[] | Action[][] => {
|
||||||
|
const actions: (Action | Action[])[] = [];
|
||||||
|
|
||||||
const toolTipActions = useMemo(() => {
|
|
||||||
const actions = [];
|
|
||||||
if (rowTitle !== loc.lnd.expired) {
|
if (rowTitle !== loc.lnd.expired) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: actionKeys.CopyAmount,
|
id: actionKeys.CopyAmount,
|
||||||
|
@ -305,6 +306,7 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
||||||
icon: actionIcons.Clipboard,
|
icon: actionIcons.Clipboard,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.hash) {
|
if (item.hash) {
|
||||||
actions.push(
|
actions.push(
|
||||||
{
|
{
|
||||||
|
@ -337,10 +339,9 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions;
|
return actions as Action[] | Action[][];
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [item.hash, subtitle, rowTitle, subtitleNumberOfLines, txMetadata]);
|
}, [item.hash, subtitle, rowTitle, subtitleNumberOfLines, txMetadata]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ToolTipMenu ref={menuRef} actions={toolTipActions} onPressMenuItem={onToolTipPress} onPress={onPress}>
|
<ToolTipMenu ref={menuRef} actions={toolTipActions} onPressMenuItem={onToolTipPress} onPress={onPress}>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React, { useRef } from 'react';
|
import React, { useMemo, useRef } from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { ListItem } from 'react-native-elements';
|
import { ListItem } from 'react-native-elements';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { AddressTypeBadge } from './AddressTypeBadge';
|
import { AddressTypeBadge } from './AddressTypeBadge';
|
||||||
import loc, { formatBalance } from '../../loc';
|
import loc, { formatBalance } from '../../loc';
|
||||||
import TooltipMenu from '../TooltipMenu';
|
import TooltipMenu from '../TooltipMenu';
|
||||||
|
@ -16,6 +15,7 @@ import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/h
|
||||||
import QRCodeComponent from '../QRCodeComponent';
|
import QRCodeComponent from '../QRCodeComponent';
|
||||||
import confirm from '../../helpers/confirm';
|
import confirm from '../../helpers/confirm';
|
||||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||||
|
import { Action } from '../types';
|
||||||
|
|
||||||
interface AddressItemProps {
|
interface AddressItemProps {
|
||||||
// todo: fix `any` after addresses.js is converted to the church of holy typescript
|
// todo: fix `any` after addresses.js is converted to the church of holy typescript
|
||||||
|
@ -80,6 +80,8 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const menuActions = useMemo(() => getAvailableActions({ allowSignVerifyMessage }), [allowSignVerifyMessage]);
|
||||||
|
|
||||||
const balance = formatBalance(item.balance, balanceUnit, true);
|
const balance = formatBalance(item.balance, balanceUnit, true);
|
||||||
|
|
||||||
const handleCopyPress = () => {
|
const handleCopyPress = () => {
|
||||||
|
@ -130,39 +132,6 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAvailableActions = () => {
|
|
||||||
const actions = [
|
|
||||||
{
|
|
||||||
id: AddressItem.actionKeys.CopyToClipboard,
|
|
||||||
text: loc.transactions.details_copy,
|
|
||||||
icon: AddressItem.actionIcons.Clipboard,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: AddressItem.actionKeys.Share,
|
|
||||||
text: loc.receive.details_share,
|
|
||||||
icon: AddressItem.actionIcons.Share,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (allowSignVerifyMessage) {
|
|
||||||
actions.push({
|
|
||||||
id: AddressItem.actionKeys.SignVerify,
|
|
||||||
text: loc.addresses.sign_title,
|
|
||||||
icon: AddressItem.actionIcons.Signature,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowSignVerifyMessage) {
|
|
||||||
actions.push({
|
|
||||||
id: AddressItem.actionKeys.ExportPrivateKey,
|
|
||||||
text: loc.addresses.copy_private_key,
|
|
||||||
icon: AddressItem.actionIcons.ExportPrivateKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderPreview = () => {
|
const renderPreview = () => {
|
||||||
return <QRCodeComponent value={item.address} isMenuAvailable={false} />;
|
return <QRCodeComponent value={item.address} isMenuAvailable={false} />;
|
||||||
};
|
};
|
||||||
|
@ -172,7 +141,7 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||||
<TooltipMenu
|
<TooltipMenu
|
||||||
title={item.address}
|
title={item.address}
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
actions={getAvailableActions()}
|
actions={menuActions}
|
||||||
onPressMenuItem={onToolTipPress}
|
onPressMenuItem={onToolTipPress}
|
||||||
renderPreview={renderPreview}
|
renderPreview={renderPreview}
|
||||||
onPress={navigateToReceive}
|
onPress={navigateToReceive}
|
||||||
|
@ -247,15 +216,37 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
AddressItem.propTypes = {
|
const getAvailableActions = ({ allowSignVerifyMessage }: { allowSignVerifyMessage: boolean }): Action[] | Action[][] => {
|
||||||
item: PropTypes.shape({
|
const actions = [
|
||||||
key: PropTypes.string,
|
{
|
||||||
index: PropTypes.number,
|
id: AddressItem.actionKeys.CopyToClipboard,
|
||||||
address: PropTypes.string,
|
text: loc.transactions.details_copy,
|
||||||
isInternal: PropTypes.bool,
|
icon: AddressItem.actionIcons.Clipboard,
|
||||||
transactions: PropTypes.number,
|
},
|
||||||
balance: PropTypes.number,
|
{
|
||||||
}),
|
id: AddressItem.actionKeys.Share,
|
||||||
balanceUnit: PropTypes.string,
|
text: loc.receive.details_share,
|
||||||
|
icon: AddressItem.actionIcons.Share,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (allowSignVerifyMessage) {
|
||||||
|
actions.push({
|
||||||
|
id: AddressItem.actionKeys.SignVerify,
|
||||||
|
text: loc.addresses.sign_title,
|
||||||
|
icon: AddressItem.actionIcons.Signature,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowSignVerifyMessage) {
|
||||||
|
actions.push({
|
||||||
|
id: AddressItem.actionKeys.ExportPrivateKey,
|
||||||
|
text: loc.addresses.copy_private_key,
|
||||||
|
icon: AddressItem.actionIcons.ExportPrivateKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { AddressItem };
|
export { AddressItem };
|
||||||
|
|
30
components/types.ts
Normal file
30
components/types.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
export interface Action {
|
||||||
|
id: string | number;
|
||||||
|
text: string;
|
||||||
|
icon: {
|
||||||
|
iconType: string;
|
||||||
|
iconValue: string;
|
||||||
|
};
|
||||||
|
menuTitle?: string;
|
||||||
|
menuStateOn?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToolTipMenuProps {
|
||||||
|
actions: Action[] | Action[][];
|
||||||
|
children: React.ReactNode;
|
||||||
|
enableAndroidRipple?: boolean;
|
||||||
|
onPressMenuItem: (id: string) => void;
|
||||||
|
title?: string;
|
||||||
|
isMenuPrimaryAction?: boolean;
|
||||||
|
isButton?: boolean;
|
||||||
|
renderPreview?: () => React.ReactNode;
|
||||||
|
onPress?: () => void;
|
||||||
|
previewValue?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
buttonStyle?: ViewStyle;
|
||||||
|
onMenuWillShow?: () => void;
|
||||||
|
onMenuWillHide?: () => void;
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ export const useExtendedNavigation = (): NavigationProp<ParamListBase> => {
|
||||||
const isAuthenticated = await unlockWithBiometrics();
|
const isAuthenticated = await unlockWithBiometrics();
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
proceedWithNavigation();
|
proceedWithNavigation();
|
||||||
return; // Ensure the function exits if this path is taken
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.error('Biometric authentication failed');
|
console.error('Biometric authentication failed');
|
||||||
// Decide if navigation should proceed or not after failed authentication
|
// Decide if navigation should proceed or not after failed authentication
|
||||||
|
|
|
@ -23,6 +23,22 @@ interface TransactionDetailsProps {
|
||||||
navigation: NativeStackNavigationProp<any>;
|
navigation: NativeStackNavigationProp<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actionKeys = {
|
||||||
|
CopyToClipboard: 'copyToClipboard',
|
||||||
|
GoToWallet: 'goToWallet',
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionIcons = {
|
||||||
|
Clipboard: {
|
||||||
|
iconType: 'SYSTEM',
|
||||||
|
iconValue: 'doc.on.doc',
|
||||||
|
},
|
||||||
|
GoToWallet: {
|
||||||
|
iconType: 'SYSTEM',
|
||||||
|
iconValue: 'wallet.pass',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function onlyUnique(value: any, index: number, self: any[]) {
|
function onlyUnique(value: any, index: number, self: any[]) {
|
||||||
return self.indexOf(value) === index;
|
return self.indexOf(value) === index;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +53,14 @@ function arrDiff(a1: any[], a2: any[]) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toolTipMenuActions = [
|
||||||
|
{
|
||||||
|
id: actionKeys.CopyToClipboard,
|
||||||
|
text: loc.transactions.copy_link,
|
||||||
|
icon: actionIcons.Clipboard,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const TransactionDetails = () => {
|
const TransactionDetails = () => {
|
||||||
const { setOptions, navigate } = useNavigation();
|
const { setOptions, navigate } = useNavigation();
|
||||||
const { hash, walletID } = useRoute<TransactionDetailsProps['route']>().params;
|
const { hash, walletID } = useRoute<TransactionDetailsProps['route']>().params;
|
||||||
|
@ -162,9 +186,7 @@ const TransactionDetails = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopyPress = (stringToCopy: string) => {
|
const handleCopyPress = (stringToCopy: string) => {
|
||||||
Clipboard.setString(
|
Clipboard.setString(stringToCopy !== actionKeys.CopyToClipboard ? stringToCopy : `https://mempool.space/tx/${tx?.hash}`);
|
||||||
stringToCopy !== TransactionDetails.actionKeys.CopyToClipboard ? stringToCopy : `https://mempool.space/tx/${tx?.hash}`,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading || !tx) {
|
if (isLoading || !tx) {
|
||||||
|
@ -189,9 +211,9 @@ const TransactionDetails = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPressMenuItem = (key: string) => {
|
const onPressMenuItem = (key: string) => {
|
||||||
if (key === TransactionDetails.actionKeys.CopyToClipboard) {
|
if (key === actionKeys.CopyToClipboard) {
|
||||||
handleCopyPress(key);
|
handleCopyPress(key);
|
||||||
} else if (key === TransactionDetails.actionKeys.GoToWallet) {
|
} else if (key === actionKeys.GoToWallet) {
|
||||||
const wallet = weOwnAddress(key);
|
const wallet = weOwnAddress(key);
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
navigateToWallet(wallet);
|
navigateToWallet(wallet);
|
||||||
|
@ -205,16 +227,16 @@ const TransactionDetails = () => {
|
||||||
for (const [index, address] of array.entries()) {
|
for (const [index, address] of array.entries()) {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
actions.push({
|
actions.push({
|
||||||
id: TransactionDetails.actionKeys.CopyToClipboard,
|
id: actionKeys.CopyToClipboard,
|
||||||
text: loc.transactions.details_copy,
|
text: loc.transactions.details_copy,
|
||||||
icon: TransactionDetails.actionIcons.Clipboard,
|
icon: actionIcons.Clipboard,
|
||||||
});
|
});
|
||||||
const isWeOwnAddress = weOwnAddress(address);
|
const isWeOwnAddress = weOwnAddress(address);
|
||||||
if (isWeOwnAddress) {
|
if (isWeOwnAddress) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: TransactionDetails.actionKeys.GoToWallet,
|
id: actionKeys.GoToWallet,
|
||||||
text: loc.formatString(loc.transactions.view_wallet, { walletLabel: isWeOwnAddress.getLabel() }),
|
text: loc.formatString(loc.transactions.view_wallet, { walletLabel: isWeOwnAddress.getLabel() }),
|
||||||
icon: TransactionDetails.actionIcons.GoToWallet,
|
icon: actionIcons.GoToWallet,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,16 +342,10 @@ const TransactionDetails = () => {
|
||||||
)}
|
)}
|
||||||
<ToolTipMenu
|
<ToolTipMenu
|
||||||
isButton
|
isButton
|
||||||
actions={[
|
actions={toolTipMenuActions}
|
||||||
{
|
|
||||||
id: TransactionDetails.actionKeys.CopyToClipboard,
|
|
||||||
text: loc.transactions.copy_link,
|
|
||||||
icon: TransactionDetails.actionIcons.Clipboard,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressMenuItem={handleCopyPress}
|
onPressMenuItem={handleCopyPress}
|
||||||
onPress={handleOnOpenTransactionOnBlockExplorerTapped}
|
onPress={handleOnOpenTransactionOnBlockExplorerTapped}
|
||||||
buttonStyle={[styles.greyButton, stylesHooks.greyButton]}
|
buttonStyle={StyleSheet.flatten([styles.greyButton, stylesHooks.greyButton])}
|
||||||
>
|
>
|
||||||
<Text style={[styles.Link, stylesHooks.Link]}>{loc.transactions.details_show_in_block_explorer}</Text>
|
<Text style={[styles.Link, stylesHooks.Link]}>{loc.transactions.details_show_in_block_explorer}</Text>
|
||||||
</ToolTipMenu>
|
</ToolTipMenu>
|
||||||
|
@ -338,22 +354,6 @@ const TransactionDetails = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDetails.actionKeys = {
|
|
||||||
CopyToClipboard: 'copyToClipboard',
|
|
||||||
GoToWallet: 'goToWallet',
|
|
||||||
};
|
|
||||||
|
|
||||||
TransactionDetails.actionIcons = {
|
|
||||||
Clipboard: {
|
|
||||||
iconType: 'SYSTEM',
|
|
||||||
iconValue: 'doc.on.doc',
|
|
||||||
},
|
|
||||||
GoToWallet: {
|
|
||||||
iconType: 'SYSTEM',
|
|
||||||
iconValue: 'wallet.pass',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
scroll: {
|
scroll: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
|
@ -21,25 +21,19 @@ import { satoshiToLocalCurrency } from '../../blue_modules/currency';
|
||||||
import { BlueLoading } from '../../BlueComponents';
|
import { BlueLoading } from '../../BlueComponents';
|
||||||
import { PaymentCodeStackParamList } from '../../navigation/PaymentCodeStack';
|
import { PaymentCodeStackParamList } from '../../navigation/PaymentCodeStack';
|
||||||
import presentAlert from '../../components/Alert';
|
import presentAlert from '../../components/Alert';
|
||||||
|
import { Action } from '../../components/types';
|
||||||
|
|
||||||
interface DataSection {
|
interface DataSection {
|
||||||
title: string;
|
title: string;
|
||||||
data: string[];
|
data: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IActionKey {
|
|
||||||
id: Actions;
|
|
||||||
text: string;
|
|
||||||
icon: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Actions {
|
enum Actions {
|
||||||
pay,
|
pay,
|
||||||
rename,
|
rename,
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionKeys: IActionKey[] = [
|
const actionKeys: Action[] = [
|
||||||
{
|
{
|
||||||
id: Actions.pay,
|
id: Actions.pay,
|
||||||
text: loc.bip47.pay_this_contact,
|
text: loc.bip47.pay_this_contact,
|
||||||
|
@ -97,9 +91,7 @@ export default function PaymentCodesList() {
|
||||||
setData(newData);
|
setData(newData);
|
||||||
}, [walletID, wallets, reload]);
|
}, [walletID, wallets, reload]);
|
||||||
|
|
||||||
const toolTipActions = useMemo(() => {
|
const toolTipActions = useMemo(() => actionKeys, []);
|
||||||
return actionKeys;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const shortenContactName = (name: string): string => {
|
const shortenContactName = (name: string): string => {
|
||||||
if (name.length < 20) return name;
|
if (name.length < 20) return name;
|
||||||
|
|
|
@ -206,6 +206,6 @@ jest.mock('react-native-keychain', () => mockKeychain);
|
||||||
|
|
||||||
jest.mock('react-native-tcp-socket', () => mockKeychain);
|
jest.mock('react-native-tcp-socket', () => mockKeychain);
|
||||||
|
|
||||||
jest.mock('../components/TooltipMenu.ios.js', () => require('../components/TooltipMenu.js'));
|
jest.mock('../components/TooltipMenu.ios.tsx', () => require('../components/TooltipMenu.tsx'));
|
||||||
|
|
||||||
global.alert = () => {};
|
global.alert = () => {};
|
||||||
|
|
Loading…
Add table
Reference in a new issue