Skip to content

Commit

Permalink
Feat: Kiln widget (#3962)
Browse files Browse the repository at this point in the history
* feat: experiment with kiln widget

* feat: add stake concent warning

* fix: wrong widget url

* feat: add theme support for staking widget

* fix: use https address

* Decoding + ifram tweaks

* OrderConfirmationView

* Switch the widget to testnet

* Format values

* Fix types

* TokenInfoPair

* Fix hydration errors with legal disclaimer

* New widget URL

* Pass value to getConfirmationView

* Fix double request for confirmation view

* Update Stake icon

* Token info from the API

* Hide method call for staking orders

* Update confirmation view types

* Display rewards in fiat

* Custom icon for deposits

* Tx details

* Fix type

* Fix tests

* Extract formatSeconds

* Status chips

* Reuse StakingOrderConfirmationView

* Flat style in tx details

* Geoblocking

* Feature toggles and geoblocking

* Move twap module warning to swap order view

* Hide queue bar

* New label in sidebar

* Capitalize status labels

* Fix unit test

* Reuse widget disclaimer component

* Adjust type

* Unify status chips

* Keep queue bar on the stake page

* TokenInfoPair -> ConfirmationOrderHeader

* Fix status chip style

* Rm sidebar tooltip

* feat: add decoding for withdraw request

.

* fix: failing tests

---------

Co-authored-by: katspaugh <katspaugh@gmail.com>
Co-authored-by: katspaugh <381895+katspaugh@users.noreply.github.com>
  • Loading branch information
3 people authored Sep 12, 2024
1 parent d6539e1 commit 2fcd8fc
Show file tree
Hide file tree
Showing 60 changed files with 938 additions and 356 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@safe-global/protocol-kit": "^4.1.0",
"@safe-global/safe-apps-sdk": "^9.1.0",
"@safe-global/safe-deployments": "^1.37.3",
"@safe-global/safe-gateway-typescript-sdk": "3.22.4-beta.1",
"@safe-global/safe-gateway-typescript-sdk": "3.22.3-beta.12",
"@safe-global/safe-modules-deployments": "^1.2.0",
"@sentry/react": "^7.91.0",
"@spindl-xyz/attribution-lite": "^1.4.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@ import { Typography } from '@mui/material'

import css from './styles.module.css'

const LegalDisclaimerContent = () => (
const linkSx = {
textDecoration: 'none',
}

const WidgetDisclaimer = ({ widgetName }: { widgetName: string }) => (
<div className={css.disclaimerContainer}>
<div className={css.disclaimerInner}>
<Typography mb={4} mt={4}>
You are now accessing a third party widget.
</Typography>

<Typography mb={4}>
Please note that we do not own, control, maintain or audit the CoW Swap Widget. Use of the widget is subject to
Please note that we do not own, control, maintain or audit the {widgetName}. Use of the widget is subject to
third party terms & conditions. We are not liable for any loss you may suffer in connection with interacting
with the widget, which is at your own risk.
</Typography>

<Typography mb={4}>
Our{' '}
<ExternalLink href={AppRoutes.terms} sx={{ textDecoration: 'none' }}>
<ExternalLink href={AppRoutes.terms} sx={linkSx}>
terms
</ExternalLink>{' '}
contain more detailed provisions binding on you relating to such third party content.
</Typography>
<Typography>
By clicking &quot;continue&quot; you re-confirm to have read and understood our{' '}
<ExternalLink href={AppRoutes.terms} sx={{ textDecoration: 'none' }}>
<ExternalLink href={AppRoutes.terms} sx={linkSx}>
terms
</ExternalLink>{' '}
and this message, and agree to them.
Expand All @@ -35,4 +39,4 @@ const LegalDisclaimerContent = () => (
</div>
)

export default LegalDisclaimerContent
export default WidgetDisclaimer
2 changes: 1 addition & 1 deletion src/components/dashboard/Assets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const NoAssets = () => (
</Paper>
)

const AssetRow = ({ item, showSwap }: { item: SafeBalanceResponse['items'][number]; showSwap: boolean }) => (
const AssetRow = ({ item, showSwap }: { item: SafeBalanceResponse['items'][number]; showSwap?: boolean }) => (
<Box className={css.container} key={item.tokenInfo.address}>
<Box flex={1}>
<TokenAmount
Expand Down
26 changes: 15 additions & 11 deletions src/components/safe-apps/AppFrame/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ import css from './styles.module.css'
import SafeAppIframe from './SafeAppIframe'
import { useCustomAppCommunicator } from '@/hooks/safe-apps/useCustomAppCommunicator'

const UNKNOWN_APP_NAME = 'Unknown Safe App'

type AppFrameProps = {
appUrl: string
allowedFeaturesList: string
safeAppFromManifest: SafeAppDataWithPermissions
isNativeEmbed?: boolean
}

const AppFrame = ({ appUrl, allowedFeaturesList, safeAppFromManifest }: AppFrameProps): ReactElement => {
const AppFrame = ({ appUrl, allowedFeaturesList, safeAppFromManifest, isNativeEmbed }: AppFrameProps): ReactElement => {
const { safe, safeLoaded } = useSafeInfo()
const addressBook = useAddressBook()
const chainId = useChainId()
Expand Down Expand Up @@ -98,29 +97,34 @@ const AppFrame = ({ appUrl, allowedFeaturesList, safeAppFromManifest }: AppFrame
}

setAppIsLoading(false)
gtmTrackPageview(`${router.pathname}?appUrl=${router.query.appUrl}`, router.asPath)
}, [appUrl, iframeRef, setAppIsLoading, router])

if (!isNativeEmbed) {
gtmTrackPageview(`${router.pathname}?appUrl=${router.query.appUrl}`, router.asPath)
}
}, [appUrl, iframeRef, setAppIsLoading, router, isNativeEmbed])

useEffect(() => {
if (!appIsLoading && !isBackendAppsLoading) {
if (!isNativeEmbed && !appIsLoading && !isBackendAppsLoading) {
trackSafeAppEvent(
{
...SAFE_APPS_EVENTS.OPEN_APP,
},
appName,
)
}
}, [appIsLoading, isBackendAppsLoading, appName])
}, [appIsLoading, isBackendAppsLoading, appName, isNativeEmbed])

if (!safeLoaded) {
return <div />
}

return (
<>
<Head>
<title>{`Safe Apps - Viewer - ${remoteApp ? remoteApp.name : UNKNOWN_APP_NAME}`}</title>
</Head>
{!isNativeEmbed && (
<Head>
<title>{`Safe{Wallet} - Safe Apps${remoteApp ? ' - ' + remoteApp.name : ''}`}</title>
</Head>
)}

<div className={css.wrapper}>
{thirdPartyCookiesDisabled && <ThirdPartyCookiesWarning onClose={() => setThirdPartyCookiesDisabled(false)} />}
Expand Down Expand Up @@ -160,7 +164,7 @@ const AppFrame = ({ appUrl, allowedFeaturesList, safeAppFromManifest }: AppFrame
transactions={transactions}
/>

{permissionsRequest && (
{!isNativeEmbed && permissionsRequest && (
<PermissionsPrompt
isOpen
origin={permissionsRequest.origin}
Expand Down
5 changes: 1 addition & 4 deletions src/components/sidebar/SidebarNavigation/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export type NavItem = {
href: string
tag?: ReactElement
disabled?: boolean
tooltip?: string
}

export const navItems: NavItem[] = [
Expand All @@ -41,9 +40,7 @@ export const navItems: NavItem[] = [
label: 'Stake',
icon: <SvgIcon component={StakeIcon} inheritViewBox />,
href: AppRoutes.stake,
tag: <Chip label="Soon" />,
disabled: true,
tooltip: 'Native staking is coming soon, stay tuned!',
tag: <Chip label="New" />,
},
{
label: 'Transactions',
Expand Down
48 changes: 26 additions & 22 deletions src/components/sidebar/SidebarNavigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,28 @@ import { isRouteEnabled } from '@/utils/chains'
import { trackEvent } from '@/services/analytics'
import { SWAP_EVENTS, SWAP_LABELS } from '@/services/analytics/events/swaps'
import { GeoblockingContext } from '@/components/common/GeoblockingProvider'
import { Tooltip } from '@mui/material'

const getSubdirectory = (pathname: string): string => {
return pathname.split('/')[1]
}

const geoBlockedRoutes = [AppRoutes.swap, AppRoutes.stake]

const Navigation = (): ReactElement => {
const chain = useCurrentChain()
const router = useRouter()
const { safe } = useSafeInfo()
const currentSubdirectory = getSubdirectory(router.pathname)
const queueSize = useQueuedTxsLength()
const isBlockedCountry = useContext(GeoblockingContext)

const enabledNavItems = useMemo(() => {
return navItems.filter((item) => {
const enabled = isRouteEnabled(item.href, chain)

if (item.href === AppRoutes.swap && isBlockedCountry) {
if (isBlockedCountry && geoBlockedRoutes.includes(item.href)) {
return false
}
return enabled

return isRouteEnabled(item.href, chain)
})
}, [chain, isBlockedCountry])

Expand Down Expand Up @@ -76,23 +77,26 @@ const Navigation = (): ReactElement => {
}

return (
<Tooltip title={item.tooltip} placement="right" key={item.href} arrow>
<ListItem disablePadding selected={isSelected} onClick={() => handleNavigationClick(item.href)}>
<SidebarListItemButton
selected={isSelected}
href={item.href && { pathname: getRoute(item.href), query: { safe: router.query.safe } }}
disabled={item.disabled}
>
{item.icon && <SidebarListItemIcon badge={getBadge(item)}>{item.icon}</SidebarListItemIcon>}

<SidebarListItemText data-testid="sidebar-list-item" bold>
{item.label}

{ItemTag}
</SidebarListItemText>
</SidebarListItemButton>
</ListItem>
</Tooltip>
<ListItem
disablePadding
selected={isSelected}
onClick={() => handleNavigationClick(item.href)}
key={item.href}
>
<SidebarListItemButton
selected={isSelected}
href={item.href && { pathname: getRoute(item.href), query: { safe: router.query.safe } }}
disabled={item.disabled}
>
{item.icon && <SidebarListItemIcon badge={getBadge(item)}>{item.icon}</SidebarListItemIcon>}

<SidebarListItemText data-testid="sidebar-list-item" bold>
{item.label}

{ItemTag}
</SidebarListItemText>
</SidebarListItemButton>
</ListItem>
)
})}
</SidebarList>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const _TrustedToggleButton = ({
}: {
onlyTrusted: boolean
setOnlyTrusted: (on: boolean) => void
hasDefaultTokenlist: boolean
hasDefaultTokenlist?: boolean
}): ReactElement | null => {
const onClick = () => {
setOnlyTrusted(!onlyTrusted)
Expand Down
11 changes: 10 additions & 1 deletion src/components/transactions/TxDetails/Summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import type { ReactElement } from 'react'
import React, { useState } from 'react'
import { Link, Box } from '@mui/material'
import { generateDataRowValue, TxDataRow } from '@/components/transactions/TxDetails/Summary/TxDataRow'
import { isMultisigDetailedExecutionInfo } from '@/utils/transaction-guards'
import { isCustomTxInfo, isMultisigDetailedExecutionInfo } from '@/utils/transaction-guards'
import type { TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
import { Operation } from '@safe-global/safe-gateway-typescript-sdk'
import { dateString } from '@/utils/formatters'
import css from './styles.module.css'
import type { SafeTransaction } from '@safe-global/safe-core-sdk-types'
import SafeTxGasForm from '../SafeTxGasForm'
import DecodedData from '../TxData/DecodedData'

interface Props {
txDetails: TransactionDetails
Expand All @@ -30,6 +31,8 @@ const Summary = ({ txDetails, defaultExpanded = false }: Props): ReactElement =>
refundReceiver = detailedExecutionInfo.refundReceiver?.value
}

const isCustom = isCustomTxInfo(txDetails.txInfo)

return (
<>
{txHash && (
Expand Down Expand Up @@ -67,6 +70,12 @@ const Summary = ({ txDetails, defaultExpanded = false }: Props): ReactElement =>

{expanded && (
<Box mt={1}>
{!isCustom && (
<Box borderBottom="1px solid" borderColor="border.light" p={2} mt={1} mb={2} mx={-2}>
<DecodedData txData={txDetails.txData} toInfo={txDetails.txData?.to} />
</Box>
)}

<TxDataRow datatestid="tx-operation" title="Operation:">
{`${txData.operation} (${Operation[txData.operation].toLowerCase()})`}
</TxDataRow>
Expand Down
18 changes: 18 additions & 0 deletions src/components/transactions/TxDetails/TxData/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import SettingsChangeTxInfo from '@/components/transactions/TxDetails/TxData/SettingsChange'
import type { SpendingLimitMethods } from '@/utils/transaction-guards'
import { isStakingTxExitInfo } from '@/utils/transaction-guards'
import {
isCancellationTxInfo,
isCustomTxInfo,
isMultisigDetailedExecutionInfo,
isOrderTxInfo,
isSettingsChangeTxInfo,
isSpendingLimitMethod,
isStakingTxDepositInfo,
isSupportedSpendingLimitAddress,
isTransferTxInfo,
} from '@/utils/transaction-guards'
Expand All @@ -16,6 +19,9 @@ import RejectionTxInfo from '@/components/transactions/TxDetails/TxData/Rejectio
import DecodedData from '@/components/transactions/TxDetails/TxData/DecodedData'
import TransferTxInfo from '@/components/transactions/TxDetails/TxData/Transfer'
import useChainId from '@/hooks/useChainId'
import SwapOrder from '@/features/swap/components/SwapOrder'
import StakingTxDepositDetails from '@/features/stake/components/StakingTxDepositDetails'
import StakingTxExitDetails from '@/features/stake/components/StakingTxExitDetails'

const TxData = ({
txDetails,
Expand All @@ -30,6 +36,18 @@ const TxData = ({
const txInfo = txDetails.txInfo
const toInfo = isCustomTxInfo(txDetails.txInfo) ? txDetails.txInfo.to : undefined

if (isOrderTxInfo(txDetails.txInfo)) {
return <SwapOrder txData={txDetails.txData} txInfo={txDetails.txInfo} />
}

if (isStakingTxDepositInfo(txDetails.txInfo)) {
return <StakingTxDepositDetails txData={txDetails.txData} info={txDetails.txInfo} />
}

if (isStakingTxExitInfo(txDetails.txInfo)) {
return <StakingTxExitDetails txData={txDetails.txData} info={txDetails.txInfo} />
}

if (isTransferTxInfo(txInfo)) {
return <TransferTxInfo txInfo={txInfo} txStatus={txDetails.txStatus} trusted={trusted} imitation={imitation} />
}
Expand Down
9 changes: 0 additions & 9 deletions src/components/transactions/TxDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import useIsPending from '@/hooks/useIsPending'
import { isImitation, isTrustedTx } from '@/utils/transactions'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
import { SwapOrder } from '@/features/swap/components/SwapOrder'
import { useGetTransactionDetailsQuery } from '@/store/gateway'
import { asError } from '@/services/exceptions/utils'
import { POLLING_INTERVAL } from '@/config/constants'
Expand Down Expand Up @@ -77,14 +76,6 @@ const TxDetailsBlock = ({ txSummary, txDetails }: TxDetailsProps): ReactElement
<>
{/* /Details */}
<div className={`${css.details} ${isUnsigned ? css.noSigners : ''}`}>
{isOrderTxInfo(txDetails.txInfo) && (
<div className={css.swapOrder}>
<ErrorBoundary fallback={<div>Error parsing data</div>}>
<SwapOrder txData={txDetails.txData} txInfo={txDetails.txInfo} />
</ErrorBoundary>
</div>
)}

<div className={css.shareLink}>
<TxShareLink id={txSummary.id} />
</div>
Expand Down
18 changes: 3 additions & 15 deletions src/components/transactions/TxDetails/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,11 @@
.txData,
.txSummary,
.advancedDetails,
.txModule,
.swapOrder {
.txModule {
padding: var(--space-2);
}

.swapOrderTransfer {
border-top: 1px solid var(--color-border-light);
margin-top: var(--space-2);
margin-left: calc(var(--space-2) * -1);
margin-right: calc(var(--space-2) * -1);
padding: var(--space-2);
padding-top: var(--space-3);
}

.txData,
.swapOrder {
.txData {
border-bottom: 1px solid var(--color-border-light);
}

Expand All @@ -59,8 +48,7 @@
padding: 0 var(--space-1);
}

.multiSend,
.swapOrder {
.multiSend {
border-bottom: 1px solid var(--color-border-light);
}

Expand Down
Loading

0 comments on commit 2fcd8fc

Please sign in to comment.