Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/itchy-waves-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@relayprotocol/relay-kit-ui': patch
---

Add onHapticEvent callback to RelayKitProvider
18 changes: 17 additions & 1 deletion demo/components/providers/RelayKitProviderWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import {
RelayChain
} from '@relayprotocol/relay-sdk'
import { RelayKitProvider } from '@relayprotocol/relay-kit-ui'
import type { HapticEventType } from '@relayprotocol/relay-kit-ui'
import { useTheme } from 'next-themes'
import { useRouter } from 'next/router'
import { FC, ReactNode, useMemo } from 'react'
import { FC, ReactNode, useCallback, useMemo } from 'react'
import { useCustomize } from 'context/customizeContext'
import { useWebHaptics } from 'web-haptics/react'

const DEFAULT_APP_FEES = [
{
Expand All @@ -30,6 +32,19 @@ export const RelayKitProviderWrapper: FC<{
const router = useRouter()
const { themeOverrides, websocketsEnabled } = useCustomize()
const appFeesEnabled = router.query.appFees === 'true'
const { trigger } = useWebHaptics({
// enables audio feedback for testing on desktop
// debug: true
})

// web-haptics presets match our HapticEventType names exactly
const onHapticEvent = useCallback(
(type: HapticEventType) => {
console.log(`[haptic] ${type}`)
trigger(type)
},
[trigger]
)

const mergedTheme = useMemo(
() => ({
Expand Down Expand Up @@ -62,6 +77,7 @@ export const RelayKitProviderWrapper: FC<{
},
secureBaseUrl: process.env.NEXT_PUBLIC_RELAY_SECURE_API_URL,
appFees: appFeesEnabled ? DEFAULT_APP_FEES : undefined,
onHapticEvent,
logger: (message, level) => {
window.dispatchEvent(
new CustomEvent('relay-kit-logger', {
Expand Down
3 changes: 2 additions & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"tronweb": "^6.0.4",
"usehooks-ts": "^3.1.0",
"viem": ">=2.26.0",
"wagmi": "^2.15.6"
"wagmi": "^2.15.6",
"web-haptics": "^0.0.6"
},
"devDependencies": {
"@dynamic-labs/types": "4.10.4",
Expand Down
8 changes: 7 additions & 1 deletion packages/ui/src/components/common/CustomAddressModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import type { AdaptedWallet, RelayChain } from '@relayprotocol/relay-sdk'
import type { LinkedWallet } from '../../types/index.js'
import { truncateAddress } from '../../utils/truncate.js'
import { isValidAddress } from '../../utils/address.js'
import { ProviderOptionsContext } from '../../providers/RelayKitProvider.js'
import {
ProviderOptionsContext,
useHapticEvent
} from '../../providers/RelayKitProvider.js'
import {
addCustomAddress,
getCustomAddresses
Expand Down Expand Up @@ -56,6 +59,7 @@ export const CustomAddressModal: FC<Props> = ({
onConfirmed,
onClear
}) => {
const haptic = useHapticEvent()
const connectedAddress = useWalletAddress(wallet, linkedWallets)
const [address, setAddress] = useState('')
const [input, setInput] = useState('')
Expand Down Expand Up @@ -316,6 +320,7 @@ export const CustomAddressModal: FC<Props> = ({
radius="squared"
className="relay:flex relay:items-center relay:gap-[6px] relay:cursor-pointer relay:px-2"
onClick={() => {
haptic('light')
onConfirmed(address)
onOpenChange(false)
onAnalyticEvent?.(EventNames.ADDRESS_MODAL_CONFIRMED, {
Expand Down Expand Up @@ -354,6 +359,7 @@ export const CustomAddressModal: FC<Props> = ({
setRecentCustomAddresses(getCustomAddresses())
}

haptic('light')
onConfirmed(address)
onAnalyticEvent?.(EventNames.ADDRESS_MODAL_CONFIRMED, {
address: address,
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/components/common/PercentageButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export const PercentageButtons: FC<PercentageButtonsProps> = ({
isMobile ? 'relay:rounded-[6px]' : 'relay:rounded-[12px]',
isMobile ? 'relay:flex-1' : '',
'relay:justify-center',
'relay:hover:bg-[var(--relay-colors-widget-selector-hover-background)]'
'relay:hover:bg-[var(--relay-colors-widget-selector-hover-background)]',
'relay:active:bg-[var(--relay-colors-gray5)]'
)

const buttonFontSize = isMobile ? '14px' : '12px'
Expand Down
9 changes: 6 additions & 3 deletions packages/ui/src/components/common/SlippageToleranceConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const tokenToColor = (token: string | undefined): string | undefined => {
return `var(--relay-colors-${token})`
}
import { EventNames } from '../../constants/events.js'
import { useHapticEvent } from '../../providers/RelayKitProvider.js'
import { useDebounceValue, useMediaQuery } from 'usehooks-ts'
import useFallbackState from '../../hooks/useFallbackState.js'
import { Modal } from './Modal.js'
Expand Down Expand Up @@ -73,11 +74,13 @@ const SlippageTabs: FC<SlippageTabsProps> = ({
slippageRatingColor,
inputRef
}) => {
const haptic = useHapticEvent()
const isMobile = useMediaQuery('(max-width: 520px)')
return (
<TabsRoot
value={mode}
onValueChange={(value) => {
haptic('selection')
setMode(value as SlippageToleranceMode)
if (value === 'Auto') {
setDisplayValue(undefined)
Expand Down Expand Up @@ -115,23 +118,23 @@ const SlippageTabs: FC<SlippageTabsProps> = ({
<Button
color="grey"
size="none"
className="relay:flex-1 relay:min-h-0 relay:h-[28px] relay:font-medium relay:text-sm relay:rounded-[6px] relay:justify-center"
className="relay:flex-1 relay:min-h-0 relay:h-[28px] relay:font-medium relay:text-sm relay:rounded-[6px] relay:justify-center relay:active:bg-[var(--relay-colors-gray5)]"
onClick={() => handleInputChange('1')}
>
1%
</Button>
<Button
color="grey"
size="none"
className="relay:flex-1 relay:min-h-0 relay:h-[28px] relay:font-medium relay:text-sm relay:rounded-[6px] relay:justify-center"
className="relay:flex-1 relay:min-h-0 relay:h-[28px] relay:font-medium relay:text-sm relay:rounded-[6px] relay:justify-center relay:active:bg-[var(--relay-colors-gray5)]"
onClick={() => handleInputChange('2')}
>
2%
</Button>
<Button
color="grey"
size="none"
className="relay:flex-1 relay:min-h-0 relay:h-[28px] relay:font-medium relay:text-sm relay:rounded-[6px] relay:justify-center"
className="relay:flex-1 relay:min-h-0 relay:h-[28px] relay:font-medium relay:text-sm relay:rounded-[6px] relay:justify-center relay:active:bg-[var(--relay-colors-gray5)]"
onClick={() => handleInputChange('5')}
>
5%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from '../../../utils/localStorage.js'
import Tooltip from '../../../components/primitives/Tooltip.js'
import { EventNames } from '../../../constants/events.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'
import { ChainSearchInput } from './ChainFilterRow.js'
import { cn } from '../../../utils/cn.js'

Expand Down Expand Up @@ -279,6 +280,7 @@ const ChainFilterRow: FC<ChainFilterRowProps> = ({
showStar = true,
onAnalyticEvent
}) => {
const haptic = useHapticEvent()
const [dropdownOpen, setDropdownOpen] = useState(false)
const [longPressTimer, setLongPressTimer] = useState<number | null>(null)
const dropdownRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -325,6 +327,7 @@ const ChainFilterRow: FC<ChainFilterRowProps> = ({
if (chain.id) {
const previouslyStarred = isStarred
toggleStarredChain(chain.id)
haptic('light')
const eventName = previouslyStarred
? EventNames.CHAIN_UNSTARRED
: EventNames.CHAIN_STARRED
Expand All @@ -341,10 +344,6 @@ const ChainFilterRow: FC<ChainFilterRowProps> = ({
const handleTouchStart = (_e: React.TouchEvent) => {
if (!chain.id) return
const timer = setTimeout(() => {
// Provide haptic feedback on long press
if ('vibrate' in navigator) {
navigator.vibrate(50) // Short 50ms vibration
}
setDropdownOpen(true)
}, 500) // 500ms long press
setLongPressTimer(timer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../../../utils/localStorage.js'
import { EventNames } from '../../../constants/events.js'
import { cn } from '../../../utils/cn.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'

export type ChainFilterRowProps = {
chain: ChainFilterValue
Expand All @@ -38,6 +39,7 @@ export const ChainFilterRow: FC<ChainFilterRowProps> = ({
onAnalyticEvent,
children
}) => {
const haptic = useHapticEvent()
const [dropdownOpen, setDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const isStarred = chain.id ? isChainStarred(chain.id) : false
Expand Down Expand Up @@ -75,6 +77,7 @@ export const ChainFilterRow: FC<ChainFilterRowProps> = ({
if (chain.id) {
const previouslyStarred = isStarred
toggleStarredChain(chain.id)
haptic('light')
const eventName = previouslyStarred
? EventNames.CHAIN_UNSTARRED
: EventNames.CHAIN_STARRED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { faStar } from '@fortawesome/free-solid-svg-icons/faStar'
import Fuse from 'fuse.js'
import type { ChainFilterValue } from './ChainFilter.js'
import { EventNames } from '../../../constants/events.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'
import type { RelayChain } from '@relayprotocol/relay-sdk'
import AllChainsLogo from '../../../img/AllChainsLogo.js'
import { TagPill } from './TagPill.js'
Expand Down Expand Up @@ -69,6 +70,7 @@ export const ChainFilterSidebar: FC<ChainFilterSidebarProps> = ({
onChainStarToggle,
starredChainIds
}) => {
const haptic = useHapticEvent()
const [chainSearchInput, setChainSearchInput] = useState('')
const chainFuse = new Fuse(options, fuseSearchOptions)
const activeChainRef = useRef<HTMLButtonElement | null>(null)
Expand Down Expand Up @@ -129,6 +131,7 @@ export const ChainFilterSidebar: FC<ChainFilterSidebarProps> = ({
(chain) => chain.id?.toString() === selectedValue
)
if (chain) {
haptic('selection')
onSelect(chain)
const fromStarredList =
!isSameChainSelection &&
Expand Down Expand Up @@ -340,6 +343,7 @@ const ChainFilterRow: FC<ChainFilterRowProps> = ({
showStar = true,
onAnalyticEvent
}) => {
const haptic = useHapticEvent()
const [dropdownOpen, setDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const isStarred = chain.id ? isChainStarred(chain.id) : false
Expand Down Expand Up @@ -382,6 +386,7 @@ const ChainFilterRow: FC<ChainFilterRowProps> = ({
if (chain.id) {
const previouslyStarred = isStarred
toggleStarredChain(chain.id)
haptic('light')
const eventName = previouslyStarred
? EventNames.CHAIN_UNSTARRED
: EventNames.CHAIN_STARRED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { faXmark } from '@fortawesome/free-solid-svg-icons/faXmark'
import Fuse from 'fuse.js'
import type { ChainFilterValue } from './ChainFilter.js'
import { EventNames } from '../../../constants/events.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'
import type { RelayChain } from '@relayprotocol/relay-sdk'
import AllChainsLogo from '../../../img/AllChainsLogo.js'
import { TagPill } from './TagPill.js'
Expand Down Expand Up @@ -70,6 +71,7 @@ export const MobileChainSelector: FC<MobileChainSelectorProps> = ({
onChainStarToggle,
starredChainIds
}) => {
const haptic = useHapticEvent()
const [chainSearchInput, setChainSearchInput] = useState('')
const chainFuse = new Fuse(options, fuseSearchOptions)

Expand Down Expand Up @@ -164,6 +166,7 @@ export const MobileChainSelector: FC<MobileChainSelectorProps> = ({
(chain) => chain.id?.toString() === selectedValue
)
if (chain) {
haptic('selection')
const fromStarredList =
!isSameChainSelection &&
chain.id !== undefined &&
Expand Down Expand Up @@ -305,6 +308,7 @@ const MobileChainRow: FC<MobileChainRowProps> = ({
showStar = true,
onAnalyticEvent
}) => {
const haptic = useHapticEvent()
const [dropdownOpen, setDropdownOpen] = useState(false)
const [longPressTimer, setLongPressTimer] = useState<number | null>(null)
const dropdownRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -351,6 +355,7 @@ const MobileChainRow: FC<MobileChainRowProps> = ({
if (chain.id) {
const previouslyStarred = isStarred
toggleStarredChain(chain.id)
haptic('light')
const eventName = previouslyStarred
? EventNames.CHAIN_UNSTARRED
: EventNames.CHAIN_STARRED
Expand All @@ -368,10 +373,6 @@ const MobileChainRow: FC<MobileChainRowProps> = ({
(e: React.TouchEvent) => {
if (!chain.id) return
const timer = setTimeout(() => {
// Provide haptic feedback on long press
if ('vibrate' in navigator) {
navigator.vibrate(50) // Short 50ms vibration
}
setDropdownOpen(true)
}, 500) // 500ms long press
setLongPressTimer(timer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useMultiWalletBalances } from '../../../hooks/useMultiWalletBalances.js
import { useMediaQuery } from 'usehooks-ts'
import { useTokenList } from '@relayprotocol/relay-kit-hooks'
import { EventNames } from '../../../constants/events.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'
import { UnverifiedTokenModal } from '../UnverifiedTokenModal.js'
import { useEnhancedTokensList } from '../../../hooks/useEnhancedTokensList.js'
import { TokenList } from './TokenList.js'
Expand Down Expand Up @@ -81,6 +82,7 @@ const PaymentMethod: FC<PaymentMethodProps> = ({
onAnalyticEvent,
onPaymentMethodOpenChange
}) => {
const haptic = useHapticEvent()
const relayClient = useRelayClient()
const { chains: allRelayChains } = useInternalRelayChains()

Expand Down Expand Up @@ -416,6 +418,7 @@ const PaymentMethod: FC<PaymentMethodProps> = ({

const handleTokenSelection = useCallback(
(selectedToken: Token) => {
haptic('light')
const isVerified = selectedToken.verified
const direction = context === 'from' ? 'input' : 'output'
let position = undefined
Expand Down Expand Up @@ -466,6 +469,7 @@ const PaymentMethod: FC<PaymentMethodProps> = ({
onOpenChange(false)
},
[
haptic,
setToken,
onOpenChange,
resetState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
useTrendingCurrencies
} from '@relayprotocol/relay-kit-hooks'
import { EventNames } from '../../../constants/events.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'
import { UnverifiedTokenModal } from '../UnverifiedTokenModal.js'
import { useEnhancedTokensList } from '../../../hooks/useEnhancedTokensList.js'
import { TokenList } from './TokenList.js'
Expand Down Expand Up @@ -90,6 +91,7 @@ const TokenSelector: FC<TokenSelectorProps> = ({
setToken,
onAnalyticEvent
}) => {
const haptic = useHapticEvent()
const relayClient = useRelayClient()
const { chains: allRelayChains } = useInternalRelayChains()

Expand Down Expand Up @@ -489,6 +491,7 @@ const TokenSelector: FC<TokenSelectorProps> = ({

const handleTokenSelection = useCallback(
(selectedToken: Token) => {
haptic('light')
const isVerified = selectedToken.verified
const direction = context === 'from' ? 'input' : 'output'
let position = undefined
Expand Down Expand Up @@ -539,6 +542,7 @@ const TokenSelector: FC<TokenSelectorProps> = ({
onOpenChange(false)
},
[
haptic,
setToken,
onOpenChange,
resetState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Modal } from '../Modal.js'
import { Flex, Text } from '../../primitives/index.js'
import { ErrorStep } from './steps/ErrorStep.js'
import { EventNames } from '../../../constants/events.js'
import { useHapticEvent } from '../../../providers/RelayKitProvider.js'
import { type Token } from '../../../types/index.js'
import { SwapSuccessStep } from './steps/SwapSuccessStep.js'
import { formatBN } from '../../../utils/numbers.js'
Expand Down Expand Up @@ -51,6 +52,7 @@ export const DepositAddressModal: FC<DepositAddressModalProps> = (
onAnalyticEvent,
onSuccess
} = depositAddressModalProps
const haptic = useHapticEvent()

useEffect(() => {
onOpenChange(open)
Expand Down Expand Up @@ -90,6 +92,7 @@ export const DepositAddressModal: FC<DepositAddressModalProps> = (
const quoteId = quote
? extractQuoteId(quote?.steps as Execute['steps'])
: undefined
haptic('success')
onAnalyticEvent?.(EventNames.SWAP_SUCCESS, {
...extraData,
chain_id_in: fromToken?.chainId,
Expand Down
Loading