diff --git a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx index e2bafa7ad1e..b09ecd910b9 100644 --- a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx +++ b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx @@ -238,7 +238,6 @@ export function CopyButton() { return ( <> - {/* @ts-expect-error JavaScript component */} {lang.t('wallet.copy')} diff --git a/src/components/floating-emojis/CopyFloatingEmojis.js b/src/components/floating-emojis/CopyFloatingEmojis.tsx similarity index 51% rename from src/components/floating-emojis/CopyFloatingEmojis.js rename to src/components/floating-emojis/CopyFloatingEmojis.tsx index 58a786502cd..d9a11bb66fa 100644 --- a/src/components/floating-emojis/CopyFloatingEmojis.js +++ b/src/components/floating-emojis/CopyFloatingEmojis.tsx @@ -1,14 +1,30 @@ -import React from 'react'; +import React, { FC, ReactNode } from 'react'; import { ButtonPressAnimation } from '../animations'; import FloatingEmojis from './FloatingEmojis'; import { useClipboard } from '@/hooks'; import { magicMemo } from '@/utils'; -const CopyFloatingEmojis = ({ children, disabled, onPress, textToCopy, ...props }) => { +interface CopyFloatingEmojisProps { + /** Child elements or nodes to render inside this component */ + children?: ReactNode; + /** Whether the floating emojis and copy functionality is disabled */ + disabled?: boolean; + /** + * Callback to run on press. + * Receives `textToCopy` (if provided) as an argument. + */ + onPress?: (textToCopy?: string) => void; + /** The text that should be copied to the clipboard */ + textToCopy?: string; + /** Any additional props you want to forward to FloatingEmojis */ + [key: string]: unknown; +} + +const CopyFloatingEmojis: FC = ({ children, disabled = false, onPress, textToCopy, ...props }) => { const { setClipboard } = useClipboard(); return ( - + {({ onNewEmoji }) => ( = ({ centerVertically, disableHorizontalMovement, disableVerticalMovement, @@ -35,7 +52,7 @@ const FloatingEmoji = ({ const opacity = interpolate( progress, - [0, distance * (opacityThreshold ?? 0.5), distance - size], + [0, distance * (opacityThreshold ?? 0.5), distance - Number(size)], [1, fadeOut ? 0.89 : 1, fadeOut ? 0 : 1] ); @@ -45,29 +62,29 @@ const FloatingEmoji = ({ const everyThirdEmojiMultiplier = index % 3 === 0 ? 3 : 2; const everySecondEmojiMultiplier = index % 2 === 0 ? -1 : 1; - const translateXComponentA = animation.value * size * everySecondEmojiMultiplier * everyThirdEmojiMultiplier; - /* - We don't really know why these concrete numbers are used there. - Original Author of these numbers: Mike Demarais - */ + // Horizontal movement + const translateXComponentA = animation.value * Number(size) * everySecondEmojiMultiplier * everyThirdEmojiMultiplier; + + // "Wiggle" calculations const wiggleMultiplierA = Math.sin(progress * (distance / 23.3)); const wiggleMultiplierB = interpolate( progress, [0, distance / 10, distance], - [10 * wiggleFactor, 6.9 * wiggleFactor, 4.2137 * wiggleFactor] + [10 * (wiggleFactor ?? 1), 6.9 * (wiggleFactor ?? 1), 4.2137 * (wiggleFactor ?? 1)] ); const translateXComponentB = wiggleMultiplierA * wiggleMultiplierB; const translateX = disableHorizontalMovement ? 0 : translateXComponentA + translateXComponentB; + // Vertical movement const translateY = disableVerticalMovement ? 0 : -progress; return { opacity, transform: [{ rotate }, { scale }, { translateX }, { translateY }], }; - }, []); + }); return ( - + ); }; -FloatingEmoji.propTypes = { - centerVertically: PropTypes.bool, - disableHorizontalMovement: PropTypes.bool, - disableVerticalMovement: PropTypes.bool, - distance: PropTypes.number.isRequired, - duration: PropTypes.number.isRequired, - emoji: PropTypes.string.isRequired, - fadeOut: PropTypes.bool, - left: PropTypes.string.isRequired, - marginTop: PropTypes.number, - opacityThreshold: PropTypes.number, - scaleTo: PropTypes.number.isRequired, - size: PropTypes.string.isRequired, - top: PropTypes.number, - wiggleFactor: PropTypes.number, -}; -const neverRerender = () => true; +const neverRerender = (): boolean => true; + export default React.memo(FloatingEmoji, neverRerender); diff --git a/src/components/floating-emojis/FloatingEmojis.tsx b/src/components/floating-emojis/FloatingEmojis.tsx index 7eccc8b69de..fe9563c100c 100644 --- a/src/components/floating-emojis/FloatingEmojis.tsx +++ b/src/components/floating-emojis/FloatingEmojis.tsx @@ -4,9 +4,6 @@ import FloatingEmoji from './FloatingEmoji'; import GravityEmoji from './GravityEmoji'; import { useTimeout } from '@/hooks'; import { position } from '@/styles'; -import { DebugLayout } from '@/design-system'; -import { DEVICE_HEIGHT, DEVICE_WIDTH } from '@/utils/deviceUtils'; -import { AbsolutePortal } from '../AbsolutePortal'; interface Emoji { emojiToRender: string; @@ -101,49 +98,47 @@ const FloatingEmojis: React.FC = ({ return ( {typeof children === 'function' ? children({ onNewEmoji }) : children} - - - {gravityEnabled - ? floatingEmojis.map(({ emojiToRender, x, y }, index) => ( - - )) - : floatingEmojis.map(({ emojiToRender, x, y }, index) => ( - - ))} - - + + {gravityEnabled + ? floatingEmojis.map(({ emojiToRender, x, y }, index) => ( + + )) + : floatingEmojis.map(({ emojiToRender, x, y }, index) => ( + + ))} + ); }; diff --git a/src/components/floating-emojis/GravityEmoji.tsx b/src/components/floating-emojis/GravityEmoji.tsx index 0b1de95b47c..1bfcd005019 100644 --- a/src/components/floating-emojis/GravityEmoji.tsx +++ b/src/components/floating-emojis/GravityEmoji.tsx @@ -8,7 +8,7 @@ interface GravityEmojiProps { emoji: string; index: number; left: number; - size: number; + size: string; top: number; } @@ -74,16 +74,12 @@ const GravityEmoji = ({ distance, emoji, left, size, top }: GravityEmojiProps) = { left, position: 'absolute', - top: top || size * -0.5, + top: top || Number(size) * -0.5, }, animatedStyle, ]} > - + ); }; diff --git a/src/components/text/Emoji.js b/src/components/text/Emoji.js deleted file mode 100644 index 865f4e8da24..00000000000 --- a/src/components/text/Emoji.js +++ /dev/null @@ -1,28 +0,0 @@ -import { isString } from 'lodash'; -import React from 'react'; -import Text from './Text'; -import { emojis } from '@/references'; - -const emojiData = Object.entries(emojis).map(([emoji, { name }]) => [name, emoji]); - -export const emoji = new Map(emojiData); - -function normalizeName(name) { - if (/:.+:/.test(name)) { - name = name.slice(1, -1); - } - - return name; -} - -function getEmoji(name) { - return isString(name) ? emoji.get(normalizeName(name)) : null; -} - -export default function Emoji({ children = undefined, letterSpacing = 'zero', lineHeight = 'none', name, size = 'h4', ...props }) { - return ( - - {children || getEmoji(name)} - - ); -} diff --git a/src/components/text/Emoji.tsx b/src/components/text/Emoji.tsx new file mode 100644 index 00000000000..b13b08304ec --- /dev/null +++ b/src/components/text/Emoji.tsx @@ -0,0 +1,54 @@ +import { isString } from 'lodash'; +import React, { ReactNode } from 'react'; +import Text from './Text'; +import { emojis } from '@/references'; +import { fonts } from '@/styles'; + +const emojiData = Object.entries(emojis).map(([emojiChar, { name }]) => { + return [name, emojiChar]; +}) as [string, string][]; + +export const emoji = new Map(emojiData); + +function normalizeName(name: string): string { + if (/:.+:/.test(name)) { + name = name.slice(1, -1); + } + return name; +} + +function getEmoji(name: unknown): string | null { + if (!isString(name)) return null; + const result = emoji.get(normalizeName(name)); + return result ?? null; +} + +export interface TextProps { + isEmoji?: boolean; + letterSpacing?: string; + lineHeight?: string; + size?: keyof typeof fonts.size; + [key: string]: unknown; +} + +export interface EmojiProps extends Omit { + children?: ReactNode; + letterSpacing?: string; + lineHeight?: string; + name?: string; +} + +export default function Emoji({ + children = undefined, + letterSpacing = 'zero', + lineHeight = 'none', + name, + size = 'h4', + ...props +}: EmojiProps) { + return ( + + {children || getEmoji(name)} + + ); +} diff --git a/src/helpers/RainbowContext.tsx b/src/helpers/RainbowContext.tsx index 3a0f90586fa..4a700978aa6 100644 --- a/src/helpers/RainbowContext.tsx +++ b/src/helpers/RainbowContext.tsx @@ -97,7 +97,6 @@ export default function RainbowContextWrapper({ children }: PropsWithChildren) { )} {showSwitchModeButton && __DEV__ && ( setTheme(isDarkMode ? 'light' : 'dark')}> - {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'name' is missing in type... Remove this comment to see the full error message */} {isDarkMode ? '🌞' : '🌚'} )} diff --git a/src/screens/ExternalLinkWarningSheet.tsx b/src/screens/ExternalLinkWarningSheet.tsx index 96d3b48fb67..2728d6f2331 100644 --- a/src/screens/ExternalLinkWarningSheet.tsx +++ b/src/screens/ExternalLinkWarningSheet.tsx @@ -14,7 +14,7 @@ import { useTheme } from '@/theme'; import { formatURLForDisplay } from '@/utils'; import { IS_ANDROID } from '@/env'; -export const ExternalLinkWarningSheetHeight = 380 + (android ? 20 : 0); +export const ExternalLinkWarningSheetHeight = 380 + (IS_ANDROID ? 20 : 0); const Container = styled(Centered).attrs({ direction: 'column' })(({ deviceHeight, height }) => ({ ...position.coverAsObject, @@ -52,12 +52,7 @@ const ExternalLinkWarningSheet = () => { width: '100%', }} > - + 🧭