0xtrails 0.1.13 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aave.d.ts.map +1 -1
- package/dist/analytics.d.ts +11 -2
- package/dist/analytics.d.ts.map +1 -1
- package/dist/apiClient.d.ts +1 -1
- package/dist/apiClient.d.ts.map +1 -1
- package/dist/{proxyCaller.d.ts → balanceInjector.d.ts} +5 -4
- package/dist/balanceInjector.d.ts.map +1 -0
- package/dist/{ccip-D3gTQONK.js → ccip-D6ToCrWc.js} +12 -12
- package/dist/cctp.d.ts.map +1 -1
- package/dist/cctpqueue.d.ts +3 -3
- package/dist/cctpqueue.d.ts.map +1 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +17 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +5 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts +2 -0
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/customChains.d.ts +24 -0
- package/dist/customChains.d.ts.map +1 -0
- package/dist/{index-CnUM7lKf.js → index-BqgeTLL8.js} +34072 -30146
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -400
- package/dist/intentEntrypoint.d.ts +96 -0
- package/dist/intentEntrypoint.d.ts.map +1 -0
- package/dist/intents.d.ts +5 -3
- package/dist/intents.d.ts.map +1 -1
- package/dist/metaTxnMonitor.d.ts.map +1 -1
- package/dist/morpho.d.ts.map +1 -1
- package/dist/pools.d.ts +3 -1
- package/dist/pools.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +8 -2
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +1 -1
- package/dist/prices.d.ts.map +1 -1
- package/dist/relaySdk.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/toast.d.ts +9 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/tokenBalances.d.ts +6 -2
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/trails.d.ts +6 -5
- package/dist/trails.d.ts.map +1 -1
- package/dist/trailsClient.d.ts +12 -0
- package/dist/trailsClient.d.ts.map +1 -0
- package/dist/transactions.d.ts +8 -0
- package/dist/transactions.d.ts.map +1 -1
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/AccountSettings.d.ts +7 -0
- package/dist/widget/components/AccountSettings.d.ts.map +1 -0
- package/dist/widget/components/ChainList.d.ts +0 -1
- package/dist/widget/components/ChainList.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +46 -0
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -0
- package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
- package/dist/widget/components/ConnectedWallets.d.ts +9 -0
- package/dist/widget/components/ConnectedWallets.d.ts.map +1 -0
- package/dist/widget/components/DebugMenu.d.ts.map +1 -1
- package/dist/widget/components/DebugScreensList.d.ts.map +1 -1
- package/dist/widget/components/DebugToast.d.ts +3 -0
- package/dist/widget/components/DebugToast.d.ts.map +1 -0
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/EarnPools.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts +44 -0
- package/dist/widget/components/Fund.d.ts.map +1 -0
- package/dist/widget/components/Identicon.d.ts +9 -0
- package/dist/widget/components/Identicon.d.ts.map +1 -0
- package/dist/widget/components/Pay.d.ts +46 -0
- package/dist/widget/components/Pay.d.ts.map +1 -0
- package/dist/widget/components/Receive.d.ts.map +1 -1
- package/dist/widget/components/RecentTokens.d.ts.map +1 -1
- package/dist/widget/components/Recipients.d.ts +9 -0
- package/dist/widget/components/Recipients.d.ts.map +1 -0
- package/dist/widget/components/RefundWarning.d.ts +9 -0
- package/dist/widget/components/RefundWarning.d.ts.map +1 -0
- package/dist/widget/components/SimpleSwap.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/SwapSettings.d.ts +1 -5
- package/dist/widget/components/SwapSettings.d.ts.map +1 -1
- package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
- package/dist/widget/components/ThemeSyncer.d.ts +6 -0
- package/dist/widget/components/ThemeSyncer.d.ts.map +1 -0
- package/dist/widget/components/Toast.d.ts +24 -0
- package/dist/widget/components/Toast.d.ts.map +1 -0
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
- package/dist/widget/components/TruncatedAddress.d.ts +2 -0
- package/dist/widget/components/TruncatedAddress.d.ts.map +1 -1
- package/dist/widget/components/UserPreferences.d.ts +7 -0
- package/dist/widget/components/UserPreferences.d.ts.map +1 -0
- package/dist/widget/hooks/useBalanceVisible.d.ts +1 -0
- package/dist/widget/hooks/useBalanceVisible.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts +54 -0
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -0
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/usePayMessage.d.ts +34 -0
- package/dist/widget/hooks/usePayMessage.d.ts.map +1 -0
- package/dist/widget/hooks/useRecipients.d.ts +17 -0
- package/dist/widget/hooks/useRecipients.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts +12 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +2 -0
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useSwapAmount.d.ts +13 -0
- package/dist/widget/hooks/useSwapAmount.d.ts.map +1 -0
- package/dist/widget/hooks/useSwapSettings.d.ts +16 -0
- package/dist/widget/hooks/useSwapSettings.d.ts.map +1 -0
- package/dist/widget/hooks/useTargetAmount.d.ts +5 -0
- package/dist/widget/hooks/useTargetAmount.d.ts.map +1 -0
- package/dist/widget/hooks/useTheme.d.ts +14 -0
- package/dist/widget/hooks/useTheme.d.ts.map +1 -0
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +2 -2
- package/dist/widget/widget.d.ts +9 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +29 -28
- package/src/aave.ts +6 -1
- package/src/analytics.ts +103 -53
- package/src/apiClient.ts +1 -1
- package/src/{proxyCaller.ts → balanceInjector.ts} +22 -17
- package/src/cctp.ts +6 -2
- package/src/cctpqueue.ts +7 -7
- package/src/chains.ts +8 -0
- package/src/config.ts +40 -9
- package/src/constants.ts +11 -8
- package/src/contractUtils.ts +33 -2
- package/src/customChains.ts +24 -0
- package/src/index.ts +11 -1
- package/src/intentEntrypoint.ts +253 -0
- package/src/intents.ts +87 -54
- package/src/metaTxnMonitor.ts +1 -0
- package/src/morpho.ts +13 -2
- package/src/pools.ts +68 -86
- package/src/prepareSend.ts +437 -207
- package/src/prices.ts +51 -7
- package/src/relaySdk.ts +6 -4
- package/src/relayer.ts +2 -0
- package/src/toast.ts +110 -0
- package/src/tokenBalances.ts +112 -20
- package/src/tokens.ts +70 -7
- package/src/trails.ts +80 -77
- package/src/trailsClient.ts +45 -0
- package/src/transactions.ts +27 -35
- package/src/umd.tsx +1 -1
- package/src/wallets.ts +2 -1
- package/src/widget/assets/sequence-logo.svg +15 -0
- package/src/widget/compiled.css +2 -2
- package/src/widget/components/AccountActionsDropdown.tsx +18 -159
- package/src/widget/components/AccountIntentTransactionHistory.tsx +346 -63
- package/src/widget/components/AccountSettings.tsx +96 -0
- package/src/widget/components/ChainFilterDropdown.tsx +1 -1
- package/src/widget/components/ChainList.tsx +10 -20
- package/src/widget/components/ClassicSwap.tsx +923 -0
- package/src/widget/components/ConfigDisplay.tsx +8 -5
- package/src/widget/components/ConnectedWallets.tsx +260 -0
- package/src/widget/components/DebugMenu.tsx +2 -0
- package/src/widget/components/DebugScreensList.tsx +3 -0
- package/src/widget/components/DebugToast.tsx +63 -0
- package/src/widget/components/Earn.tsx +108 -116
- package/src/widget/components/EarnPools.tsx +2 -4
- package/src/widget/components/EarnPoolsFilters.tsx +6 -6
- package/src/widget/components/Fund.tsx +1245 -0
- package/src/widget/components/FundMethods.tsx +1 -1
- package/src/widget/components/FundSendForm.tsx +1 -1
- package/src/widget/components/Identicon.tsx +158 -0
- package/src/widget/components/Pay.tsx +1088 -0
- package/src/widget/components/PaySendForm.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +1 -1
- package/src/widget/components/Receipt.tsx +1 -1
- package/src/widget/components/Receive.tsx +4 -2
- package/src/widget/components/RecentTokens.tsx +2 -1
- package/src/widget/components/Recipients.tsx +448 -0
- package/src/widget/components/RefundWarning.tsx +61 -0
- package/src/widget/components/ScreenHeader.tsx +1 -1
- package/src/widget/components/SimpleSwap.tsx +74 -58
- package/src/widget/components/Swap.tsx +35 -853
- package/src/widget/components/SwapSettings.tsx +5 -11
- package/src/widget/components/ThemeProvider.tsx +32 -0
- package/src/widget/components/ThemeSyncer.tsx +47 -0
- package/src/widget/components/Toast.tsx +315 -0
- package/src/widget/components/TokenList.tsx +2 -34
- package/src/widget/components/TokenSelector.tsx +3 -3
- package/src/widget/components/TransactionDetails.tsx +153 -13
- package/src/widget/components/TruncatedAddress.tsx +5 -1
- package/src/widget/components/UserPreferences.tsx +156 -0
- package/src/widget/components/WalletList.tsx +1 -1
- package/src/widget/hooks/useBalanceVisible.tsx +40 -2
- package/src/widget/hooks/useCheckout.ts +13 -0
- package/src/widget/hooks/useCurrentScreen.tsx +3 -0
- package/src/widget/hooks/useDebugScreens.ts +12 -2
- package/src/widget/hooks/useDefaultTokenSelection.tsx +475 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +212 -0
- package/src/widget/hooks/usePayMessage.tsx +370 -0
- package/src/widget/hooks/useRecipients.ts +168 -0
- package/src/widget/hooks/useSelectedRecipient.tsx +48 -0
- package/src/widget/hooks/useSendForm.ts +179 -26
- package/src/widget/hooks/useSwapAmount.tsx +50 -0
- package/src/widget/hooks/useSwapSettings.tsx +100 -0
- package/src/widget/hooks/useTargetAmount.ts +23 -0
- package/src/widget/hooks/useTheme.tsx +80 -0
- package/src/widget/hooks/useTokenList.ts +20 -11
- package/src/widget/index.css +45 -21
- package/src/widget/widget.tsx +164 -68
- package/dist/address.d.ts +0 -2
- package/dist/address.d.ts.map +0 -1
- package/dist/proxyCaller.d.ts.map +0 -1
- package/src/address.ts +0 -6
|
@@ -3,16 +3,10 @@ import type React from "react"
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react"
|
|
4
4
|
import { SwapDisplayMode } from "./SwapDisplayMode.js"
|
|
5
5
|
import { SlippageToleranceSettings } from "./SlippageToleranceSettings.js"
|
|
6
|
+
import { useSwapSettings } from "../hooks/useSwapSettings.js"
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
onModeChange: (isSimple: boolean) => void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const SwapSettings: React.FC<SwapSettingsProps> = ({
|
|
13
|
-
isSimpleMode,
|
|
14
|
-
onModeChange,
|
|
15
|
-
}) => {
|
|
8
|
+
export const SwapSettings: React.FC = () => {
|
|
9
|
+
const { isSimpleSwapMode, setIsSimpleSwapModeWithStorage } = useSwapSettings()
|
|
16
10
|
const [isSettingsDropdownOpen, setIsSettingsDropdownOpen] = useState(false)
|
|
17
11
|
const settingsDropdownRef = useRef<HTMLDivElement>(null)
|
|
18
12
|
|
|
@@ -34,7 +28,7 @@ export const SwapSettings: React.FC<SwapSettingsProps> = ({
|
|
|
34
28
|
}, [isSettingsDropdownOpen])
|
|
35
29
|
|
|
36
30
|
const handleModeSelect = (isSimple: boolean) => {
|
|
37
|
-
|
|
31
|
+
setIsSimpleSwapModeWithStorage(isSimple)
|
|
38
32
|
setIsSettingsDropdownOpen(false)
|
|
39
33
|
}
|
|
40
34
|
|
|
@@ -54,7 +48,7 @@ export const SwapSettings: React.FC<SwapSettingsProps> = ({
|
|
|
54
48
|
<div className="absolute right-0 top-full mt-2 w-80 trails-bg-card rounded-lg shadow-lg border trails-border-primary z-20">
|
|
55
49
|
<div className="p-4 space-y-4">
|
|
56
50
|
<SwapDisplayMode
|
|
57
|
-
isSimpleMode={
|
|
51
|
+
isSimpleMode={isSimpleSwapMode}
|
|
58
52
|
onModeChange={handleModeSelect}
|
|
59
53
|
/>
|
|
60
54
|
|
|
@@ -2,6 +2,7 @@ import type React from "react"
|
|
|
2
2
|
import { createContext, useContext, useEffect, useState } from "react"
|
|
3
3
|
import type { Theme } from "../../theme.js"
|
|
4
4
|
import { ThemeProvider as DesignSystemThemeProvider } from "@0xsequence/design-system"
|
|
5
|
+
import { logger } from "../../logger.js"
|
|
5
6
|
|
|
6
7
|
interface ThemeContextType {
|
|
7
8
|
theme: Theme
|
|
@@ -60,6 +61,37 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
60
61
|
return () => mediaQuery.removeEventListener("change", handleChange)
|
|
61
62
|
}, [theme])
|
|
62
63
|
|
|
64
|
+
// Listen for theme preference changes from ThemeSyncer
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const handleThemePreferenceChange = (event: CustomEvent) => {
|
|
67
|
+
const { theme: preferredTheme } = event.detail
|
|
68
|
+
logger.console.log(
|
|
69
|
+
"[ThemeProvider] Received theme preference event:",
|
|
70
|
+
preferredTheme,
|
|
71
|
+
)
|
|
72
|
+
if (
|
|
73
|
+
preferredTheme &&
|
|
74
|
+
["auto", "light", "dark"].includes(preferredTheme)
|
|
75
|
+
) {
|
|
76
|
+
logger.console.log("[ThemeProvider] Applying theme:", preferredTheme)
|
|
77
|
+
// Override the current theme with user preference
|
|
78
|
+
setTheme(preferredTheme as Theme)
|
|
79
|
+
setIsDark(getIsDark(preferredTheme as Theme))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
window.addEventListener(
|
|
84
|
+
"applyThemePreference",
|
|
85
|
+
handleThemePreferenceChange as EventListener,
|
|
86
|
+
)
|
|
87
|
+
return () => {
|
|
88
|
+
window.removeEventListener(
|
|
89
|
+
"applyThemePreference",
|
|
90
|
+
handleThemePreferenceChange as EventListener,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}, [])
|
|
94
|
+
|
|
63
95
|
const getActiveTheme = (): "light" | "dark" => {
|
|
64
96
|
return isDark ? "dark" : "light"
|
|
65
97
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useEffect } from "react"
|
|
2
|
+
import { useThemePreference } from "../hooks/useTheme.js"
|
|
3
|
+
import { useWidgetProps } from "../hooks/useWidgetProps.js"
|
|
4
|
+
import { logger } from "../../logger.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Component that syncs user theme preference with the actual theme system
|
|
8
|
+
* This ensures the user's saved theme preference is applied on widget load
|
|
9
|
+
*/
|
|
10
|
+
export const ThemeSyncer: React.FC = () => {
|
|
11
|
+
const { selectedTheme } = useThemePreference()
|
|
12
|
+
const { customCss, theme: widgetTheme } = useWidgetProps()
|
|
13
|
+
|
|
14
|
+
// Sync user preference with actual theme on mount and when preference changes
|
|
15
|
+
// Only sync if customCss is not provided (developer override)
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!customCss) {
|
|
18
|
+
// Use setTimeout to ensure the ThemeProvider has mounted and set up its listener
|
|
19
|
+
const timeoutId = setTimeout(() => {
|
|
20
|
+
// Determine which theme to apply:
|
|
21
|
+
// 1. If user has a preference, use it
|
|
22
|
+
// 2. If no user preference but widget has theme prop, use widget theme
|
|
23
|
+
// 3. If neither, default to "auto"
|
|
24
|
+
const themeToApply = selectedTheme || widgetTheme || "auto"
|
|
25
|
+
|
|
26
|
+
logger.console.log(
|
|
27
|
+
"[ThemeSyncer] Applying theme - User preference:",
|
|
28
|
+
selectedTheme,
|
|
29
|
+
"Widget theme:",
|
|
30
|
+
widgetTheme,
|
|
31
|
+
"Final theme:",
|
|
32
|
+
themeToApply,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const event = new CustomEvent("applyThemePreference", {
|
|
36
|
+
detail: { theme: themeToApply },
|
|
37
|
+
})
|
|
38
|
+
window.dispatchEvent(event)
|
|
39
|
+
}, 0)
|
|
40
|
+
|
|
41
|
+
return () => clearTimeout(timeoutId)
|
|
42
|
+
}
|
|
43
|
+
}, [selectedTheme, customCss, widgetTheme])
|
|
44
|
+
|
|
45
|
+
// This component doesn't render anything
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { AnimatePresence, motion } from "motion/react"
|
|
2
|
+
import { X, CheckCircle, AlertCircle, Info } from "lucide-react"
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useState,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useRef,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
} from "react"
|
|
12
|
+
import { createPortal } from "react-dom"
|
|
13
|
+
import {
|
|
14
|
+
setGlobalShowToast,
|
|
15
|
+
setGlobalUpdatePersistentToast,
|
|
16
|
+
setGlobalRemovePersistentToast,
|
|
17
|
+
setToastEnabled,
|
|
18
|
+
} from "../../toast.js"
|
|
19
|
+
import { useWidgetProps } from "../hooks/useWidgetProps.js"
|
|
20
|
+
import { useTheme } from "./ThemeProvider.js"
|
|
21
|
+
import css from "../compiled.css?inline"
|
|
22
|
+
|
|
23
|
+
const TRAILS_ICON_URL = "https://trails.build/web-app-manifest-192x192.png"
|
|
24
|
+
|
|
25
|
+
// Default durations by toast type (in milliseconds)
|
|
26
|
+
const DEFAULT_DURATIONS: Record<ToastType, number> = {
|
|
27
|
+
success: 3000, // 3 seconds
|
|
28
|
+
error: 5000, // 5 seconds
|
|
29
|
+
info: 5000, // 5 seconds
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ToastType = "success" | "error" | "info"
|
|
33
|
+
|
|
34
|
+
export interface Toast {
|
|
35
|
+
id: string
|
|
36
|
+
title: string
|
|
37
|
+
message: string
|
|
38
|
+
type: ToastType
|
|
39
|
+
duration?: number
|
|
40
|
+
iconUrl?: string
|
|
41
|
+
isPersistent?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface ToastContextValue {
|
|
45
|
+
toasts: Toast[]
|
|
46
|
+
showToast: (
|
|
47
|
+
title: string,
|
|
48
|
+
message: string,
|
|
49
|
+
type?: ToastType,
|
|
50
|
+
duration?: number,
|
|
51
|
+
iconUrl?: string,
|
|
52
|
+
) => void
|
|
53
|
+
updatePersistentToast: (
|
|
54
|
+
title: string,
|
|
55
|
+
message: string,
|
|
56
|
+
type?: ToastType,
|
|
57
|
+
iconUrl?: string,
|
|
58
|
+
) => void
|
|
59
|
+
removePersistentToast: () => void
|
|
60
|
+
removeToast: (id: string) => void
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const ToastContext = createContext<ToastContextValue | null>(null)
|
|
64
|
+
|
|
65
|
+
export function useToast() {
|
|
66
|
+
const context = useContext(ToastContext)
|
|
67
|
+
if (!context) {
|
|
68
|
+
throw new Error("useToast must be used within ToastProvider")
|
|
69
|
+
}
|
|
70
|
+
return context
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const PERSISTENT_TOAST_ID = "persistent-toast"
|
|
74
|
+
|
|
75
|
+
export function ToastProvider({ children }: { children: ReactNode }) {
|
|
76
|
+
const [toasts, setToasts] = useState<Toast[]>([])
|
|
77
|
+
const { toast: toastEnabled = false } = useWidgetProps()
|
|
78
|
+
|
|
79
|
+
// Set global toast enabled state
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
setToastEnabled(toastEnabled)
|
|
82
|
+
}, [toastEnabled])
|
|
83
|
+
|
|
84
|
+
const showToast = useCallback(
|
|
85
|
+
(
|
|
86
|
+
title: string,
|
|
87
|
+
message: string,
|
|
88
|
+
type: ToastType = "info",
|
|
89
|
+
duration?: number,
|
|
90
|
+
iconUrl?: string,
|
|
91
|
+
) => {
|
|
92
|
+
// Check if toasts are enabled before showing
|
|
93
|
+
if (!toastEnabled) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Use default duration based on type if not provided
|
|
98
|
+
const effectiveDuration = duration ?? DEFAULT_DURATIONS[type]
|
|
99
|
+
|
|
100
|
+
// Use Trails icon by default if no custom icon provided
|
|
101
|
+
const effectiveIconUrl = iconUrl ?? TRAILS_ICON_URL
|
|
102
|
+
|
|
103
|
+
const id = `toast-${Date.now()}-${Math.random()}`
|
|
104
|
+
const toast: Toast = {
|
|
105
|
+
id,
|
|
106
|
+
title,
|
|
107
|
+
message,
|
|
108
|
+
type,
|
|
109
|
+
duration: effectiveDuration,
|
|
110
|
+
iconUrl: effectiveIconUrl,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setToasts((prev) => [...prev, toast])
|
|
114
|
+
|
|
115
|
+
// Auto-remove after duration
|
|
116
|
+
if (effectiveDuration > 0) {
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
setToasts((prev) => prev.filter((t) => t.id !== id))
|
|
119
|
+
}, effectiveDuration)
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
[toastEnabled],
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const updatePersistentToast = useCallback(
|
|
126
|
+
(
|
|
127
|
+
title: string,
|
|
128
|
+
message: string,
|
|
129
|
+
type: ToastType = "info",
|
|
130
|
+
iconUrl?: string,
|
|
131
|
+
) => {
|
|
132
|
+
// Check if toasts are enabled before showing
|
|
133
|
+
if (!toastEnabled) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Use Trails icon by default if no custom icon provided
|
|
138
|
+
const effectiveIconUrl = iconUrl ?? TRAILS_ICON_URL
|
|
139
|
+
|
|
140
|
+
const toast: Toast = {
|
|
141
|
+
id: PERSISTENT_TOAST_ID,
|
|
142
|
+
title,
|
|
143
|
+
message,
|
|
144
|
+
type,
|
|
145
|
+
duration: 0, // Never auto-dismiss
|
|
146
|
+
iconUrl: effectiveIconUrl,
|
|
147
|
+
isPersistent: true,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setToasts((prev) => {
|
|
151
|
+
// Remove existing persistent toast if any
|
|
152
|
+
const filtered = prev.filter((t) => t.id !== PERSISTENT_TOAST_ID)
|
|
153
|
+
// Add new/updated persistent toast
|
|
154
|
+
return [...filtered, toast]
|
|
155
|
+
})
|
|
156
|
+
},
|
|
157
|
+
[toastEnabled],
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const removePersistentToast = useCallback(() => {
|
|
161
|
+
setToasts((prev) => prev.filter((t) => t.id !== PERSISTENT_TOAST_ID))
|
|
162
|
+
}, [])
|
|
163
|
+
|
|
164
|
+
const removeToast = useCallback((id: string) => {
|
|
165
|
+
setToasts((prev) => prev.filter((t) => t.id !== id))
|
|
166
|
+
}, [])
|
|
167
|
+
|
|
168
|
+
// Set global toast functions for use outside React components
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
setGlobalShowToast(showToast)
|
|
171
|
+
}, [showToast])
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
setGlobalUpdatePersistentToast(updatePersistentToast)
|
|
175
|
+
}, [updatePersistentToast])
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
setGlobalRemovePersistentToast(removePersistentToast)
|
|
179
|
+
}, [removePersistentToast])
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<ToastContext.Provider
|
|
183
|
+
value={{
|
|
184
|
+
toasts,
|
|
185
|
+
showToast,
|
|
186
|
+
updatePersistentToast,
|
|
187
|
+
removePersistentToast,
|
|
188
|
+
removeToast,
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
{children}
|
|
192
|
+
<ToastContainer toasts={toasts} onRemove={removeToast} />
|
|
193
|
+
</ToastContext.Provider>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
interface ToastContainerProps {
|
|
198
|
+
toasts: Toast[]
|
|
199
|
+
onRemove: (id: string) => void
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function ToastContainer({ toasts, onRemove }: ToastContainerProps) {
|
|
203
|
+
const hostRef = useRef<HTMLDivElement>(null)
|
|
204
|
+
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null)
|
|
205
|
+
const { isDark } = useTheme()
|
|
206
|
+
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (hostRef.current && !hostRef.current.shadowRoot) {
|
|
209
|
+
const shadow = hostRef.current.attachShadow({ mode: "open" })
|
|
210
|
+
setShadowRoot(shadow)
|
|
211
|
+
|
|
212
|
+
// Inject styles into shadow root
|
|
213
|
+
const styleTag = document.createElement("style")
|
|
214
|
+
styleTag.textContent = css
|
|
215
|
+
shadow.appendChild(styleTag)
|
|
216
|
+
}
|
|
217
|
+
}, [])
|
|
218
|
+
|
|
219
|
+
const toastContent = (
|
|
220
|
+
<div
|
|
221
|
+
className={`fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none ${isDark ? "dark" : ""}`}
|
|
222
|
+
>
|
|
223
|
+
<AnimatePresence>
|
|
224
|
+
{toasts.map((toast) => (
|
|
225
|
+
<ToastItem key={toast.id} toast={toast} onRemove={onRemove} />
|
|
226
|
+
))}
|
|
227
|
+
</AnimatePresence>
|
|
228
|
+
</div>
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<div
|
|
233
|
+
ref={hostRef}
|
|
234
|
+
style={{ position: "fixed", top: 0, right: 0, zIndex: 99999 }}
|
|
235
|
+
>
|
|
236
|
+
{shadowRoot ? createPortal(toastContent, shadowRoot) : null}
|
|
237
|
+
</div>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
interface ToastItemProps {
|
|
242
|
+
toast: Toast
|
|
243
|
+
onRemove: (id: string) => void
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function ToastItem({ toast, onRemove }: ToastItemProps) {
|
|
247
|
+
const getIcon = () => {
|
|
248
|
+
switch (toast.type) {
|
|
249
|
+
case "success":
|
|
250
|
+
return (
|
|
251
|
+
<CheckCircle className="w-5 h-5 text-green-600 dark:text-green-400" />
|
|
252
|
+
)
|
|
253
|
+
case "error":
|
|
254
|
+
return (
|
|
255
|
+
<AlertCircle className="w-5 h-5 text-red-600 dark:text-red-400" />
|
|
256
|
+
)
|
|
257
|
+
case "info":
|
|
258
|
+
return <Info className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
|
259
|
+
default:
|
|
260
|
+
return <Info className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const isPersistent = toast.isPersistent
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<motion.div
|
|
268
|
+
key={toast.id}
|
|
269
|
+
initial={{ opacity: 0, y: -20, x: 20 }}
|
|
270
|
+
animate={{ opacity: 1, y: 0, x: 0 }}
|
|
271
|
+
exit={{ opacity: 0, x: 20 }}
|
|
272
|
+
transition={{ duration: 0.3, ease: [0.4, 0, 0.2, 1] }}
|
|
273
|
+
className="pointer-events-auto"
|
|
274
|
+
>
|
|
275
|
+
<div
|
|
276
|
+
className={`bg-white/95 dark:bg-gray-900/95 backdrop-blur-xl [box-shadow:0_10px_25px_-5px_rgba(0,0,0,0.08),0_8px_10px_-6px_rgba(0,0,0,0.08)] p-4 min-w-[340px] max-w-md border border-gray-200/50 dark:border-gray-700/50 [border-radius:var(--trails-widget-border-radius,16px)] ${isPersistent ? "ring-2 ring-blue-500/20 dark:ring-blue-400/20" : ""}`}
|
|
277
|
+
>
|
|
278
|
+
<div className="flex items-start gap-3">
|
|
279
|
+
{toast.iconUrl && (
|
|
280
|
+
<div className="flex-shrink-0">
|
|
281
|
+
<img
|
|
282
|
+
src={toast.iconUrl}
|
|
283
|
+
alt="App Icon"
|
|
284
|
+
className="w-10 h-10 rounded-xl shadow-sm"
|
|
285
|
+
onError={(e) => {
|
|
286
|
+
e.currentTarget.style.display = "none"
|
|
287
|
+
}}
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
<div className="flex-1 min-w-0">
|
|
292
|
+
<div className="flex items-center gap-2 mb-0.5">
|
|
293
|
+
<div className="flex-shrink-0">{getIcon()}</div>
|
|
294
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white leading-snug">
|
|
295
|
+
{toast.title}
|
|
296
|
+
</h4>
|
|
297
|
+
</div>
|
|
298
|
+
<p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
|
|
299
|
+
{toast.message}
|
|
300
|
+
</p>
|
|
301
|
+
</div>
|
|
302
|
+
<button
|
|
303
|
+
type="button"
|
|
304
|
+
onClick={() => onRemove(toast.id)}
|
|
305
|
+
className="flex-shrink-0 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
|
|
306
|
+
>
|
|
307
|
+
<X className="w-4 h-4" />
|
|
308
|
+
</button>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</motion.div>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export default ToastProvider
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState
|
|
1
|
+
import { useState } from "react"
|
|
2
2
|
import type React from "react"
|
|
3
3
|
import type { Token } from "../hooks/useTokenList.js"
|
|
4
4
|
import type { SupportedToken } from "../../tokens.js"
|
|
@@ -8,7 +8,6 @@ import { ChainList } from "./ChainList.js"
|
|
|
8
8
|
import { useTokenList } from "../hooks/useTokenList.js"
|
|
9
9
|
import { useMode } from "../hooks/useMode.js"
|
|
10
10
|
import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
|
|
11
|
-
import { getChainInfo } from "../../chains.js"
|
|
12
11
|
|
|
13
12
|
interface TokenListProps {
|
|
14
13
|
onContinue: (selectedToken: Token) => void
|
|
@@ -55,32 +54,6 @@ export const TokenList: React.FC<TokenListProps> = ({
|
|
|
55
54
|
fundMethod,
|
|
56
55
|
})
|
|
57
56
|
|
|
58
|
-
// Get filtered tokens for chain list
|
|
59
|
-
const { filteredTokensFormatted } = useTokenList({
|
|
60
|
-
onContinue: () => {}, // dummy function for getting tokens
|
|
61
|
-
targetAmountUsd,
|
|
62
|
-
onError: () => {}, // dummy function
|
|
63
|
-
fundMethod,
|
|
64
|
-
allSupportedTokens: false,
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Get unique chains from all tokens
|
|
68
|
-
const uniqueChains = useMemo(() => {
|
|
69
|
-
const chainIds = new Set(
|
|
70
|
-
filteredTokensFormatted.map((token) => token.chainId),
|
|
71
|
-
)
|
|
72
|
-
return Array.from(chainIds)
|
|
73
|
-
.map((chainId) => {
|
|
74
|
-
const chainInfo = getChainInfo(chainId)
|
|
75
|
-
return {
|
|
76
|
-
chainId,
|
|
77
|
-
name: chainInfo?.name || `Chain ${chainId}`,
|
|
78
|
-
imageUrl: "", // We'll use ChainImage component for chain icons
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
82
|
-
}, [filteredTokensFormatted])
|
|
83
|
-
|
|
84
57
|
const handleShowChainList = () => {
|
|
85
58
|
if (onShowChainList) {
|
|
86
59
|
onShowChainList()
|
|
@@ -92,12 +65,7 @@ export const TokenList: React.FC<TokenListProps> = ({
|
|
|
92
65
|
|
|
93
66
|
// If showing chain list internally, render ChainList
|
|
94
67
|
if (showChainListInternal) {
|
|
95
|
-
return (
|
|
96
|
-
<ChainList
|
|
97
|
-
chains={uniqueChains}
|
|
98
|
-
onBack={() => setShowChainListInternal(false)}
|
|
99
|
-
/>
|
|
100
|
-
)
|
|
68
|
+
return <ChainList onBack={() => setShowChainListInternal(false)} />
|
|
101
69
|
}
|
|
102
70
|
|
|
103
71
|
return (
|
|
@@ -12,7 +12,7 @@ import { RecentTokens } from "./RecentTokens.js"
|
|
|
12
12
|
import { getChainInfo } from "../../chains.js"
|
|
13
13
|
import { ChainFilterDropdown } from "./ChainFilterDropdown.js"
|
|
14
14
|
import { SearchInputField } from "./SearchInputField.js"
|
|
15
|
-
import { truncateAddress } from "../../
|
|
15
|
+
import { truncateAddress } from "../../utils.js"
|
|
16
16
|
import { logger } from "../../logger.js"
|
|
17
17
|
|
|
18
18
|
interface TokenSelectorProps {
|
|
@@ -276,8 +276,8 @@ export const TokenSelector: React.FC<TokenSelectorProps> = ({
|
|
|
276
276
|
? "Insufficient balance for this token"
|
|
277
277
|
: `Select ${tokenName} token`
|
|
278
278
|
}
|
|
279
|
-
className={`w-full py-2 px-4 flex items-center space-x-3 transition-all duration-200 trails-
|
|
280
|
-
isTokenSelected(token) ? "trails-
|
|
279
|
+
className={`w-full py-2 px-4 flex items-center space-x-3 transition-all duration-200 trails-list-item trails-border-radius-list-button ${
|
|
280
|
+
isTokenSelected(token) ? "trails-list-item-selected" : ""
|
|
281
281
|
} ${!isSufficientBalance && fundMethod !== "qr-code" && fundMethod !== "exchange" ? "opacity-75 cursor-not-allowed" : "cursor-pointer"}`}
|
|
282
282
|
>
|
|
283
283
|
<div className="relative flex-shrink-0 mr-2">
|