ci(release): publish latest release

parent ea3b63f5
* @uniswap/web-admins
We are back with a new feature:
IPFS hash of the deployment:
- CIDv0: `QmNy6ppw64jmLBEZ6r8D19beUVH3objJPrjMfNxvugqakD`
- CIDv1: `bafybeiajkzyd25iwsu5lax4wtilh5ukji3kmzrt7r76k45pcminbsirsty`
Fiat Offramp: Users can now sell their crypto back into cash and have it deposit to their bank account, centralized exchange account, and more!
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
You can also access the Uniswap Interface from an IPFS gateway.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeiajkzyd25iwsu5lax4wtilh5ukji3kmzrt7r76k45pcminbsirsty.ipfs.dweb.link/
- [ipfs://QmNy6ppw64jmLBEZ6r8D19beUVH3objJPrjMfNxvugqakD/](ipfs://QmNy6ppw64jmLBEZ6r8D19beUVH3objJPrjMfNxvugqakD/)
### 5.68.4 (2025-02-05)
### Bug Fixes
* **web:** use price to create mock pair (#15923) 40968c3
Other changes:
- More robust dapp information on Activity screen
- Reduced onboarding screens for new wallets
- Improved volume-based sorting on our Explore page
- Various bug fixes and performance improvements
\ No newline at end of file
mobile/1.44.1
\ No newline at end of file
web/5.68.4
\ No newline at end of file
......@@ -12,11 +12,11 @@
"@svgr/webpack": "8.0.1",
"@tamagui/core": "1.114.4",
"@types/uuid": "9.0.1",
"@uniswap/analytics-events": "2.40.0",
"@uniswap/analytics-events": "2.41.0",
"@uniswap/uniswapx-sdk": "3.0.0-beta.1",
"@uniswap/universal-router-sdk": "4.10.0",
"@uniswap/v3-sdk": "3.21.0",
"@uniswap/v4-sdk": "1.15.0",
"@uniswap/universal-router-sdk": "4.14.0",
"@uniswap/v3-sdk": "3.24.0",
"@uniswap/v4-sdk": "1.18.0",
"dotenv-webpack": "8.0.1",
"ethers": "5.7.2",
"eventemitter3": "5.0.1",
......
import { memo } from 'react'
import { ScrollView } from 'ui/src'
import { useActivityData } from 'wallet/src/features/activity/useActivityData'
import { useActivityData } from 'wallet/src/features/activity/hooks/useActivityData'
export const ActivityTab = memo(function _ActivityTab({ address }: { address: Address }): JSX.Element {
const { maybeEmptyComponent, renderActivityItem, sectionData } = useActivityData({
......
......@@ -57,7 +57,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 24px; height: 24px;"
style="width: 24px; height: 24px; color: rgb(125, 125, 125);"
viewBox="0 0 16 16"
>
<path
......@@ -155,7 +155,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 14px; height: 14px;"
style="width: 14px; height: 14px; color: rgb(125, 125, 125);"
viewBox="0 0 24 24"
>
<path
......@@ -210,7 +210,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `
<svg
fill="none"
stroke-width="2"
style="color: rgb(125, 125, 125); width: 16px; height: 16px;"
style="width: 16px; height: 16px; color: rgb(125, 125, 125);"
viewBox="0 0 24 24"
>
<path
......@@ -289,7 +289,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 24px; height: 24px;"
style="width: 24px; height: 24px; color: rgb(125, 125, 125);"
viewBox="0 0 16 16"
>
<path
......@@ -387,7 +387,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 14px; height: 14px;"
style="width: 14px; height: 14px; color: rgb(125, 125, 125);"
viewBox="0 0 24 24"
>
<path
......@@ -442,7 +442,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `
<svg
fill="none"
stroke-width="2"
style="color: rgb(125, 125, 125); width: 16px; height: 16px;"
style="width: 16px; height: 16px; color: rgb(125, 125, 125);"
viewBox="0 0 24 24"
>
<path
......
......@@ -30,7 +30,7 @@ exports[`ReceiveScreen renders without error 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 24px; height: 24px;"
style="width: 24px; height: 24px; color: rgb(125, 125, 125);"
viewBox="0 0 16 16"
>
<path
......@@ -99,7 +99,7 @@ exports[`ReceiveScreen renders without error 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 16px; height: 16px;"
style="width: 16px; height: 16px; color: rgb(125, 125, 125);"
viewBox="0 0 24 24"
>
<path
......@@ -6066,7 +6066,7 @@ exports[`ReceiveScreen renders without error 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 24px; height: 24px;"
style="width: 24px; height: 24px; color: rgb(125, 125, 125);"
viewBox="0 0 16 16"
>
<path
......@@ -6135,7 +6135,7 @@ exports[`ReceiveScreen renders without error 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(125, 125, 125); width: 16px; height: 16px;"
style="width: 16px; height: 16px; color: rgb(125, 125, 125);"
viewBox="0 0 24 24"
>
<path
......
......@@ -4,6 +4,7 @@ import { useDispatch } from 'react-redux'
import { Link } from 'react-router-dom'
import { ScreenHeader } from 'src/app/components/layout/ScreenHeader'
import { SCREEN_ITEM_HORIZONTAL_PAD } from 'src/app/constants'
import { useAllDappConnectionsForActiveAccount } from 'src/app/features/dapp/hooks'
import { SettingsItemWithDropdown } from 'src/app/features/settings/SettingsItemWithDropdown'
import { AppRoutes, SettingsRoutes } from 'src/app/navigation/constants'
import { useExtensionNavigation } from 'src/app/navigation/utils'
......@@ -66,6 +67,7 @@ export function SettingsScreen(): JSX.Element {
const { navigateTo, navigateBack } = useExtensionNavigation()
const currentLanguageInfo = useCurrentLanguageInfo()
const appFiatCurrencyInfo = useAppFiatCurrencyInfo()
const dappUrls = useAllDappConnectionsForActiveAccount()
const [isLanguageModalOpen, setIsLanguageModalOpen] = useState(false)
const [isTestnetModalOpen, setIsTestnetModalOpen] = useState(false)
......@@ -185,6 +187,7 @@ export function SettingsScreen(): JSX.Element {
<SettingsItem
Icon={Globe}
title={t('settings.setting.wallet.connections.title')}
count={dappUrls.length}
onPress={(): void => navigateTo(`${AppRoutes.Settings}/${SettingsRoutes.ManageConnections}`)}
/>
<SettingsItem
......@@ -237,6 +240,7 @@ function SettingsItem({
iconProps,
themeProps,
url,
count,
hideChevron = false,
}: {
Icon: GeneratedIcon
......@@ -247,6 +251,7 @@ function SettingsItem({
// TODO: do this with a wrapping Theme, "detrimental" wasn't working
themeProps?: { color?: string; hoverColor?: string }
url?: string
count?: number
}): JSX.Element {
const colors = useSporeColors()
const hoverColor = themeProps?.hoverColor ?? colors.surface2.val
......@@ -257,7 +262,7 @@ function SettingsItem({
borderRadius="$rounded12"
flexDirection="row"
flexGrow={1}
gap="$spacing16"
gap="$spacing12"
hoverStyle={{
backgroundColor: hoverColor as ColorTokens,
}}
......@@ -266,6 +271,7 @@ function SettingsItem({
py="$spacing8"
onPress={onPress}
>
<Flex row justifyContent="space-between" flexGrow={1}>
<Flex row gap="$spacing12">
<Icon
color={themeProps?.color ?? '$neutral2'}
......@@ -276,6 +282,12 @@ function SettingsItem({
{title}
</Text>
</Flex>
{count !== undefined && (
<Text alignSelf="center" color="$neutral2" variant="subheading2">
{count}
</Text>
)}
</Flex>
{!hideChevron && (
<RotatableChevron color="$neutral3" direction="end" height={iconSizes.icon20} width={iconSizes.icon20} />
)}
......
......@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Uniswap Extension",
"description": "The Uniswap Extension is a self-custody crypto wallet that's built for swapping.",
"version": "1.14.0",
"version": "1.15.0",
"minimum_chrome_version": "116",
"icons": {
"16": "assets/icon16.png",
......
......@@ -89,9 +89,9 @@ if (isCI && datadogPropertiesAvailable && !isE2E) {
apply from: "../../../../node_modules/@datadog/mobile-react-native/datadog-sourcemaps.gradle"
}
def devVersionName = "1.44.1"
def betaVersionName = "1.44.1"
def prodVersionName = "1.44.1"
def devVersionName = "1.45"
def betaVersionName = "1.45"
def prodVersionName = "1.45"
android {
ndkVersion rootProject.ext.ndkVersion
......@@ -252,9 +252,6 @@ dependencies {
implementation 'com.onesignal:OneSignal:4.8.9'
implementation 'com.github.statsig-io:android-sdk:4.36.0'
// This is required for the backported AndroidX Photo Picker on versions of Android below 30
implementation("androidx.activity:activity:1.9.+")
// For animated GIF support
implementation 'com.facebook.fresco:animated-gif:3.6.0'
......
......@@ -9,10 +9,6 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
<!-- This permission which may be added by expo modules. Unless it's used app-wide, should not be included in our app per Play Store rules -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="remove" />
<application
android:name=".MainApplication"
android:label="@string/app_name"
......@@ -27,17 +23,6 @@
android:taskAffinity=""
android:excludeFromRecents="true">
<!-- Trigger Google Play services to install the backported photo picker module. -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
android:enabled="false"
android:exported="false"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
</intent-filter>
<meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>
<meta-data
android:name="com.onesignal.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_onesignal_default" />
......
......@@ -2337,9 +2337,7 @@ PODS:
- React
- react-native-get-random-values (1.8.0):
- React-Core
- react-native-image-picker (7.2.3):
- glog
- RCT-Folly (= 2022.05.16.00)
- react-native-image-picker (7.0.1):
- React-Core
- react-native-mmkv (2.10.1):
- MMKV (>= 1.2.13)
......@@ -3083,7 +3081,7 @@ SPEC CHECKSUMS:
react-native-compat: 100540c3cebb076da442cf058e375e8ca895ae28
react-native-context-menu-view: dcec18eb8882e20596dbb75802e7d19cb87dac02
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
react-native-image-picker: b049e0ea9d6b1b58c06262e19f8b66c87ac7b760
react-native-image-picker: 1569cfade34b3a011191ce262423e6ce2f8db5a1
react-native-mmkv: dea675cf9697ad35940f1687e98e133e1358ef9f
react-native-netinfo: 129bd99f607a2dc5bb096168f3e5c150fd1f1c95
react-native-onesignal: ab800900cffeca4d9db70a05244013fc8a36ceb8
......
......@@ -2230,7 +2230,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -2283,7 +2283,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
......@@ -2336,7 +2336,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
......@@ -2389,7 +2389,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
......@@ -2427,7 +2427,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -2463,7 +2463,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
......@@ -2498,7 +2498,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
......@@ -2533,7 +2533,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
......@@ -2580,7 +2580,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -2626,7 +2626,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
......@@ -2672,7 +2672,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets;
......@@ -2718,7 +2718,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
......@@ -2760,7 +2760,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -2803,7 +2803,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
......@@ -2846,7 +2846,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension;
......@@ -2889,7 +2889,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
......@@ -2925,7 +2925,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -2963,7 +2963,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3164,7 +3164,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -3210,7 +3210,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
......@@ -3322,7 +3322,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3394,7 +3394,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
......@@ -3506,7 +3506,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3578,7 +3578,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.44.1;
MARKETING_VERSION = 1.45;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension;
......
......@@ -61,12 +61,6 @@ jest.mock('react-native', () => {
return RN
})
jest.mock('react-native-safe-area-context', () => ({
useSafeAreaInsets: jest.fn().mockImplementation(() => ({})),
useSafeAreaFrame: jest.fn().mockImplementation(() => ({})),
SafeAreaProvider: jest.fn(({ children }) => children),
}))
jest.mock('@react-navigation/elements', () => ({
useHeaderHeight: jest.fn().mockImplementation(() => 200),
}))
......
......@@ -82,10 +82,10 @@
"@sparkfabrik/react-native-idfa-aaid": "1.2.0",
"@tanstack/react-query": "5.51.16",
"@uniswap/analytics": "1.7.0",
"@uniswap/analytics-events": "2.40.0",
"@uniswap/analytics-events": "2.41.0",
"@uniswap/client-explore": "0.0.14",
"@uniswap/ethers-rs-mobile": "0.0.5",
"@uniswap/sdk-core": "7.1.0",
"@uniswap/sdk-core": "7.5.0",
"@walletconnect/core": "2.17.1",
"@walletconnect/react-native-compat": "2.17.1",
"@walletconnect/utils": "2.17.1",
......@@ -124,7 +124,7 @@
"react-native-gesture-handler": "2.19.0",
"react-native-get-random-values": "1.8.0",
"react-native-image-colors": "1.5.2",
"react-native-image-picker": "7.2.3",
"react-native-image-picker": "7.0.1",
"react-native-localize": "2.2.6",
"react-native-markdown-display": "7.0.0-alpha.2",
"react-native-mmkv": "2.10.1",
......
import { useFocusEffect } from '@react-navigation/core'
import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingsStackNavigationProp } from 'src/app/navigation/types'
import { NotificationsBackgroundImage } from 'src/components/notifications/NotificationsBGImage'
import { promptPushPermission } from 'src/features/notifications/Onesignal'
import {
NotificationPermission,
useNotificationOSPermissionsEnabled,
} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled'
import { DeprecatedButton, Flex } from 'ui/src'
import { BellOn } from 'ui/src/components/icons/BellOn'
import { GenericHeader } from 'uniswap/src/components/misc/GenericHeader'
import { Modal } from 'uniswap/src/components/modals/Modal'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { openSettings } from 'wallet/src/utils/linking'
type NotificationsOSSettingsModalProps = {
navigation: SettingsStackNavigationProp
}
/**
* This modal is used to inform the user that they need to enable notifications in the
* OS settings for the app
*/
export function NotificationsOSSettingsModal({ navigation }: NotificationsOSSettingsModalProps): JSX.Element {
const { notificationPermissionsEnabled, checkNotificationPermissions } = useNotificationOSPermissionsEnabled()
const { t } = useTranslation()
const shouldNavigateToSettings = useMemo(() => {
return notificationPermissionsEnabled === NotificationPermission.Enabled
}, [notificationPermissionsEnabled])
const navigateToSettings = useCallback(() => {
navigation.navigate(MobileScreens.SettingsStack, {
screen: MobileScreens.SettingsNotifications,
})
}, [navigation])
useFocusEffect(
useCallback(() => {
if (shouldNavigateToSettings) {
navigation.goBack()
}
}, [shouldNavigateToSettings, navigation]),
)
const onPressEnableNotifications = useCallback(async () => {
const arePushNotificationsEnabled = await promptPushPermission()
if (!arePushNotificationsEnabled) {
await openSettings()
} else {
await checkNotificationPermissions()
}
}, [checkNotificationPermissions])
const onClose = useCallback(() => {
if (shouldNavigateToSettings) {
navigateToSettings()
} else {
navigation.goBack()
}
}, [navigation, shouldNavigateToSettings, navigateToSettings])
return (
<Modal name={ModalName.NotificationsOSSettings} isModalOpen={true} onClose={onClose}>
<Flex animation="fast" gap="$spacing40" pb="$spacing12" px="$spacing24" width="100%">
<GenericHeader
Icon={BellOn}
subtitle={t('onboarding.notification.subtitle')}
title={t('onboarding.notification.title')}
/>
<Flex gap="$spacing40" justifyContent="space-between">
<NotificationsBackgroundImage />
<Trace logPress element={ElementName.Enable}>
<DeprecatedButton testID="turn-on-notifications" onPress={onPressEnableNotifications}>
{t('settings.action.enableInSettings')}
</DeprecatedButton>
</Trace>
</Flex>
</Flex>
</Modal>
)
}
......@@ -87,61 +87,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
"width": 56,
}
}
>
<View
style={
{
"height": 56,
"width": 56,
}
}
>
<View
color="#00C3A01F"
cx={28}
cy={28}
r={28}
/>
<View
transform={
[
{
"translateX": 9.333333333333332,
},
{
"translateY": 9.333333333333332,
},
]
}
>
<View
transform={
[
{
"scale": 0.7777777777777778,
},
]
}
>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M20.9454 0.0175773C22.4936 -0.167833 23.763 1.13917 23.763 2.72944V21.3625C23.763 22.9527 22.4993 24.2419 20.9405 24.2419L2.67549 24.2419C1.11665 24.2419 -0.16452 22.9469 0.0172307 21.3675C1.30306 10.1936 9.99222 1.32931 20.9454 0.0175773Z"
strokeWidth={1}
/>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M27.0546 47.9824C25.5064 48.1678 24.237 46.8608 24.237 45.2706V26.6375C24.237 25.0473 25.5007 23.7581 27.0595 23.7581L45.3245 23.7581C46.8833 23.7581 48.1645 25.0531 47.9828 26.6325C46.6969 37.8064 38.0078 46.6707 27.0546 47.9824Z"
strokeWidth={1}
/>
</View>
</View>
</View>
</View>
</View>
<View
style={
{
......
......@@ -5,12 +5,13 @@ import {
NavigationState,
createNavigationContainerRef,
} from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { TransitionPresets, createStackNavigator } from '@react-navigation/stack'
import { NativeStackNavigationOptions, createNativeStackNavigator } from '@react-navigation/native-stack'
import { StackNavigationOptions, TransitionPresets, createStackNavigator } from '@react-navigation/stack'
import React, { useEffect } from 'react'
import { DevSettings } from 'react-native'
import { useSelector } from 'react-redux'
import StorybookUIRoot from 'src/../.storybook'
import { NotificationsOSSettingsModal } from 'src/app/modals/NotificationsOSSettingsModal'
import { renderHeaderBackButton, renderHeaderBackImage } from 'src/app/navigation/components'
import { navigationRef } from 'src/app/navigation/navigationRef'
import {
......@@ -77,6 +78,7 @@ import { useSporeColors } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { useAppInsets } from 'uniswap/src/hooks/useAppInsets'
import { OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import {
......@@ -103,7 +105,7 @@ function SettingsStackGroup(): JSX.Element {
return (
<SettingsStack.Navigator
screenOptions={{
...navOptions.noHeader,
...navNativeStackOptions.noHeader,
fullScreenGestureEnabled: true,
animation: 'slide_from_right',
}}
......@@ -135,6 +137,9 @@ function SettingsStackGroup(): JSX.Element {
<SettingsStack.Screen component={SettingsAppearanceScreen} name={MobileScreens.SettingsAppearance} />
<SettingsStack.Screen component={SettingsPrivacyScreen} name={MobileScreens.SettingsPrivacy} />
<SettingsStack.Screen component={SettingsNotificationsScreen} name={MobileScreens.SettingsNotifications} />
<SettingsStack.Group screenOptions={navNativeStackOptions.presentationBottomSheet}>
<SettingsStack.Screen component={NotificationsOSSettingsModal} name={ModalName.NotificationsOSSettings} />
</SettingsStack.Group>
</SettingsStack.Navigator>
)
}
......@@ -212,7 +217,7 @@ export function ExploreStackNavigator(): JSX.Element {
<ExploreStack.Navigator
initialRouteName={MobileScreens.Explore}
screenOptions={{
...navOptions.noHeader,
...navNativeStackOptions.noHeader,
fullScreenGestureEnabled: true,
gestureEnabled: true,
animation: 'slide_from_right',
......@@ -247,7 +252,7 @@ export function FiatOnRampStackNavigator(): JSX.Element {
<FiatOnRampStack.Navigator
initialRouteName={FiatOnRampScreens.AmountInput}
screenOptions={{
...navOptions.noHeader,
...navNativeStackOptions.noHeader,
fullScreenGestureEnabled: true,
gestureEnabled: true,
animation: 'slide_from_right',
......@@ -288,13 +293,13 @@ export function OnboardingStackNavigator(): JSX.Element {
<OnboardingStack.Screen
component={AppLoadingScreen}
name={OnboardingScreens.AppLoading}
options={navOptions.noHeader}
options={navNativeStackOptions.noHeader}
/>
)}
<OnboardingStack.Screen
component={LandingScreen}
name={OnboardingScreens.Landing}
options={navOptions.noHeader}
options={navNativeStackOptions.noHeader}
/>
<OnboardingStack.Screen component={ClaimUnitagScreen} name={UnitagScreens.ClaimUnitag} />
<OnboardingStack.Screen
......@@ -323,12 +328,12 @@ export function OnboardingStackNavigator(): JSX.Element {
<OnboardingStack.Screen
component={OnDeviceRecoveryScreen}
name={OnboardingScreens.OnDeviceRecovery}
options={navOptions.noHeader}
options={navNativeStackOptions.noHeader}
/>
<OnboardingStack.Screen
component={OnDeviceRecoveryViewSeedPhraseScreen}
name={OnboardingScreens.OnDeviceRecoveryViewSeedPhrase}
options={navOptions.noHeader}
options={navNativeStackOptions.noHeader}
/>
<OnboardingStack.Screen
component={RestoreCloudBackupLoadingScreen}
......@@ -377,12 +382,12 @@ export function UnitagStackNavigator(): JSX.Element {
<UnitagStack.Screen
component={UnitagConfirmationScreen}
name={UnitagScreens.UnitagConfirmation}
options={{ ...navOptions.noHeader, gestureEnabled: false }}
options={{ ...navStackOptions.noHeader, gestureEnabled: false }}
/>
<UnitagStack.Screen
component={EditUnitagProfileScreen}
name={UnitagScreens.EditProfile}
options={{ ...navOptions.noHeader, gestureEnabled: false }}
options={{ ...navStackOptions.noHeader, gestureEnabled: false }}
/>
</UnitagStack.Group>
</UnitagStack.Navigator>
......@@ -410,7 +415,7 @@ export function AppStackNavigator(): JSX.Element {
return (
<AppStack.Navigator
screenOptions={{
...navOptions.noHeader,
...navNativeStackOptions.noHeader,
fullScreenGestureEnabled: true,
gestureEnabled: true,
animation: 'slide_from_right',
......@@ -433,7 +438,7 @@ export function AppStackNavigator(): JSX.Element {
<AppStack.Screen component={NFTCollectionScreen} name={MobileScreens.NFTCollection} />
<AppStack.Screen component={WebViewScreen} name={MobileScreens.WebView} />
<AppStack.Screen component={SettingsStackGroup} name={MobileScreens.SettingsStack} />
<AppStack.Group screenOptions={navOptions.presentationModal}>
<AppStack.Group screenOptions={navNativeStackOptions.presentationModal}>
<AppStack.Screen component={EducationScreen} name={MobileScreens.Education} />
</AppStack.Group>
{isDevEnv() && <AppStack.Screen component={StorybookUIRoot} name={MobileScreens.Storybook} />}
......@@ -441,7 +446,17 @@ export function AppStackNavigator(): JSX.Element {
)
}
const navOptions = {
const navNativeStackOptions: Record<string, NativeStackNavigationOptions> = {
noHeader: { headerShown: false },
presentationModal: { presentation: 'modal' },
} as const
presentationBottomSheet: {
presentation: 'containedTransparentModal',
animation: 'none',
animationDuration: 0,
contentStyle: { backgroundColor: 'transparent' },
},
}
const navStackOptions: Record<string, StackNavigationOptions> = {
noHeader: { headerShown: false },
}
......@@ -6,6 +6,7 @@ import {
} from '@react-navigation/native'
import { NativeStackNavigationProp, NativeStackScreenProps } from '@react-navigation/native-stack'
import { HomeScreenTabIndex } from 'src/screens/HomeScreen/HomeScreenTabIndex'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import {
FiatOnRampScreens,
......@@ -69,6 +70,7 @@ export type SettingsStackParamList = {
[MobileScreens.SettingsWalletEdit]: { address: Address }
[MobileScreens.SettingsWalletManageConnection]: { address: Address }
[MobileScreens.WebView]: { headerTitle: string; uriLink: string }
[ModalName.NotificationsOSSettings]: undefined
}
export type OnboardingStackBaseParams = {
......
......@@ -18,7 +18,7 @@ import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
export interface SettingsSection {
subTitle: string
subTitle?: string
data: (SettingsSectionItem | SettingsSectionItemComponent)[]
isHidden?: boolean
}
......@@ -43,11 +43,17 @@ export interface SettingsSectionItem {
currentSetting?: string
onToggle?: () => void
isToggleEnabled?: boolean
checkIfCanProceed?: () => boolean
/**
* Number to display in the right of the body. Ex: Used for displaying the number of connections
*/
count?: number
}
interface SettingsRowProps {
page: SettingsSectionItem
navigation: SettingsStackNavigationProp & OnboardingStackNavigationProp
checkIfCanProceed?: SettingsSectionItem['checkIfCanProceed']
}
export function SettingsRow({
......@@ -64,13 +70,19 @@ export function SettingsRow({
currentSetting,
onToggle,
isToggleEnabled,
count,
},
navigation,
checkIfCanProceed,
}: SettingsRowProps): JSX.Element {
const colors = useSporeColors()
const dispatch = useDispatch()
const handleRow = async (): Promise<void> => {
if (checkIfCanProceed && !checkIfCanProceed()) {
return
}
if (onToggle) {
return
} else if (screen) {
......@@ -88,7 +100,7 @@ export function SettingsRow({
return (
<TouchableArea disabled={Boolean(action)} onPress={handleRow}>
<Flex grow row alignItems="center" gap="$spacing16" minHeight={40}>
<Flex grow row alignItems="center" gap="$spacing12" minHeight={40}>
<Flex grow row alignItems={subText ? 'flex-start' : 'center'} flexBasis={0} gap="$spacing12">
<Flex centered height={32} width={32}>
{icon}
......@@ -104,6 +116,11 @@ export function SettingsRow({
)}
</Flex>
</Flex>
{count !== undefined && (
<Text color="$neutral2" variant="body3">
{count}
</Text>
)}
{onToggle && typeof isToggleEnabled === 'boolean' ? (
<Switch checked={isToggleEnabled} variant="branded" disabled={disabled} onCheckedChange={onToggle} />
) : screen || modal ? (
......
......@@ -91,10 +91,32 @@ function _TokenFiatOnRampList({
UNSUPPORTED = 'UNSUPPORTED',
}
const sortedSupportedAssetsWithBalance = list
.filter((c) => {
if (!c.currencyInfo) {
return false
}
const quantity = balancesById?.[c.currencyInfo?.currencyId]?.quantity ?? 0
return quantity > 0
})
.sort((a, b) => {
if (!a.currencyInfo || !b.currencyInfo) {
return 0
}
const aQuantity = balancesById?.[a.currencyInfo.currencyId]?.balanceUSD ?? 0
const bQuantity = balancesById?.[b.currencyInfo.currencyId]?.balanceUSD ?? 0
return bQuantity - aQuantity
})
const supportedAssetsWithoutBalance = list.filter(
(c) => c.currencyInfo && !balancesById?.[c.currencyInfo?.currencyId],
)
const unsupportedAssetsWithBalance = getUnsupportedFORTokensWithBalance(list, balancesById)
const tokenList = isOffRamp
? [
{ title: ListSection.SUPPORTED, data: list },
{ title: ListSection.SUPPORTED, data: [...sortedSupportedAssetsWithBalance, ...supportedAssetsWithoutBalance] },
{ title: ListSection.UNSUPPORTED, data: unsupportedAssetsWithBalance },
]
: [{ title: ListSection.SUPPORTED, data: list }]
......
......@@ -139,62 +139,9 @@ exports[`AccountCardItem renders correctly 1`] = `
"width": 36,
}
}
>
<View
style={
{
"height": 36,
"width": 36,
}
}
>
<View
color="#00C3A01F"
cx={18}
cy={18}
r={18}
/>
<View
transform={
[
{
"translateX": 6,
},
{
"translateY": 6,
},
]
}
>
<View
transform={
[
{
"scale": 0.5,
},
]
}
>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M20.9454 0.0175773C22.4936 -0.167833 23.763 1.13917 23.763 2.72944V21.3625C23.763 22.9527 22.4993 24.2419 20.9405 24.2419L2.67549 24.2419C1.11665 24.2419 -0.16452 22.9469 0.0172307 21.3675C1.30306 10.1936 9.99222 1.32931 20.9454 0.0175773Z"
strokeWidth={1}
/>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M27.0546 47.9824C25.5064 48.1678 24.237 46.8608 24.237 45.2706V26.6375C24.237 25.0473 25.5007 23.7581 27.0595 23.7581L45.3245 23.7581C46.8833 23.7581 48.1645 25.0531 47.9828 26.6325C46.6969 37.8064 38.0078 46.6707 27.0546 47.9824Z"
strokeWidth={1}
/>
</View>
</View>
</View>
</View>
</View>
</View>
<View
style={
{
......
......@@ -115,62 +115,9 @@ exports[`AccountHeader renders correctly 1`] = `
"width": 52,
}
}
>
<View
style={
{
"height": 52,
"width": 52,
}
}
>
<View
color="#00C3A01F"
cx={26}
cy={26}
r={26}
/>
<View
transform={
[
{
"translateX": 8.666666666666668,
},
{
"translateY": 8.666666666666668,
},
]
}
>
<View
transform={
[
{
"scale": 0.7222222222222222,
},
]
}
>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M20.9454 0.0175773C22.4936 -0.167833 23.763 1.13917 23.763 2.72944V21.3625C23.763 22.9527 22.4993 24.2419 20.9405 24.2419L2.67549 24.2419C1.11665 24.2419 -0.16452 22.9469 0.0172307 21.3675C1.30306 10.1936 9.99222 1.32931 20.9454 0.0175773Z"
strokeWidth={1}
/>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M27.0546 47.9824C25.5064 48.1678 24.237 46.8608 24.237 45.2706V26.6375C24.237 25.0473 25.5007 23.7581 27.0595 23.7581L45.3245 23.7581C46.8833 23.7581 48.1645 25.0531 47.9828 26.6325C46.6969 37.8064 38.0078 46.6707 27.0546 47.9824Z"
strokeWidth={1}
/>
</View>
</View>
</View>
</View>
</View>
</View>
<View
style={
{
......
......@@ -252,62 +252,9 @@ exports[`AccountList renders without error 1`] = `
"width": 36,
}
}
>
<View
style={
{
"height": 36,
"width": 36,
}
}
>
<View
color="#00C3A01F"
cx={18}
cy={18}
r={18}
/>
<View
transform={
[
{
"translateX": 6,
},
{
"translateY": 6,
},
]
}
>
<View
transform={
[
{
"scale": 0.5,
},
]
}
>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M20.9454 0.0175773C22.4936 -0.167833 23.763 1.13917 23.763 2.72944V21.3625C23.763 22.9527 22.4993 24.2419 20.9405 24.2419L2.67549 24.2419C1.11665 24.2419 -0.16452 22.9469 0.0172307 21.3675C1.30306 10.1936 9.99222 1.32931 20.9454 0.0175773Z"
strokeWidth={1}
/>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M27.0546 47.9824C25.5064 48.1678 24.237 46.8608 24.237 45.2706V26.6375C24.237 25.0473 25.5007 23.7581 27.0595 23.7581L45.3245 23.7581C46.8833 23.7581 48.1645 25.0531 47.9828 26.6325C46.6969 37.8064 38.0078 46.6707 27.0546 47.9824Z"
strokeWidth={1}
/>
</View>
</View>
</View>
</View>
</View>
</View>
<View
style={
{
......
......@@ -170,61 +170,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
"width": 20,
}
}
>
<View
style={
{
"height": 20,
"width": 20,
}
}
>
<View
color="#00C3A01F"
cx={10}
cy={10}
r={10}
/>
<View
transform={
[
{
"translateX": 3.333333333333333,
},
{
"translateY": 3.333333333333333,
},
]
}
>
<View
transform={
[
{
"scale": 0.2777777777777778,
},
]
}
>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M20.9454 0.0175773C22.4936 -0.167833 23.763 1.13917 23.763 2.72944V21.3625C23.763 22.9527 22.4993 24.2419 20.9405 24.2419L2.67549 24.2419C1.11665 24.2419 -0.16452 22.9469 0.0172307 21.3675C1.30306 10.1936 9.99222 1.32931 20.9454 0.0175773Z"
strokeWidth={1}
/>
<View
clip-rule="evenodd"
color="#00C3A0"
fillType="evenOdd"
path="M27.0546 47.9824C25.5064 48.1678 24.237 46.8608 24.237 45.2706V26.6375C24.237 25.0473 25.5007 23.7581 27.0595 23.7581L45.3245 23.7581C46.8833 23.7581 48.1645 25.0531 47.9828 26.6325C46.6969 37.8064 38.0078 46.6707 27.0546 47.9824Z"
strokeWidth={1}
/>
</View>
</View>
</View>
</View>
</View>
<View
style={
{
......
......@@ -16,7 +16,7 @@ import { DDRumManualTiming } from 'utilities/src/logger/datadogEvents'
import { usePerformanceLogger } from 'utilities/src/logger/usePerformanceLogger'
import { isAndroid } from 'utilities/src/platform'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { useActivityData } from 'wallet/src/features/activity/useActivityData'
import { useActivityData } from 'wallet/src/features/activity/hooks/useActivityData'
export const ACTIVITY_TAB_DATA_DEPENDENCIES = [GQLQueries.TransactionList]
......
import { ForwardedRef, forwardRef, memo, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, RefreshControl } from 'react-native'
import Animated from 'react-native-reanimated'
import { useDispatch, useSelector } from 'react-redux'
import { useAdaptiveFooter } from 'src/components/home/hooks'
import { AnimatedFlatList } from 'src/components/layout/AnimatedFlatList'
import { TAB_BAR_HEIGHT, TabProps } from 'src/components/layout/TabHelpers'
import { Loader } from 'src/components/loading/loaders'
import { openModal } from 'src/features/modals/modalSlice'
import { removePendingSession } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Text, useSporeColors } from 'ui/src'
import { NoTransactions } from 'ui/src/components/icons'
import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard'
import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries'
import { selectWatchedAddressSet } from 'uniswap/src/features/favorites/selectors'
import { useHideSpamTokensSetting } from 'uniswap/src/features/settings/hooks'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { useAppInsets } from 'uniswap/src/hooks/useAppInsets'
import { isAndroid } from 'utilities/src/platform'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { useFormattedTransactionDataForFeed } from 'wallet/src/features/activity/hooks'
import { generateActivityItemRenderer } from 'wallet/src/features/transactions/SummaryCards/utils'
export const FEED_TAB_DATA_DEPENDENCIES = [GQLQueries.FeedTransactionList]
const ESTIMATED_ITEM_SIZE = 92
const SectionTitle = ({ title }: { title: string }): JSX.Element => (
<Flex pb="$spacing12">
<Text color="$neutral2" variant="subheading2">
{title}
</Text>
</Flex>
)
export const FeedTab = memo(
forwardRef<FlatList<unknown>, TabProps>(function _FeedTab(
{ containerProps, scrollHandler, headerHeight, refreshing, onRefresh },
ref,
) {
const { t } = useTranslation()
const dispatch = useDispatch()
const colors = useSporeColors()
const insets = useAppInsets()
const watchedWalletsSet = useSelector(selectWatchedAddressSet)
const watchedWalletsList = useMemo(() => Array.from(watchedWalletsSet), [watchedWalletsSet])
const { onContentSizeChange } = useAdaptiveFooter(containerProps?.contentContainerStyle)
// Hide all spam transactions if active wallet has enabled setting.
const hideSpamTokens = useHideSpamTokensSetting()
const renderActivityItem = useMemo(() => {
return generateActivityItemRenderer(<Loader.Transaction />, SectionTitle, undefined, undefined)
}, [])
const { onRetry, hasData, isLoading, isError, sectionData, keyExtractor } = useFormattedTransactionDataForFeed(
watchedWalletsList,
hideSpamTokens,
)
const onPressReceive = (): void => {
// in case we received a pending session from a previous scan after closing modal
dispatch(removePendingSession())
dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }))
}
const errorCard = (
<Flex grow style={containerProps?.emptyComponentStyle}>
<BaseCard.ErrorState
retryButtonLabel={t('common.button.retry')}
title={t('home.feed.error')}
onRetry={onRetry}
/>
</Flex>
)
const emptyListView = (
<Flex grow style={containerProps?.emptyComponentStyle}>
<BaseCard.EmptyState
description={t('home.feed.empty.description')}
icon={<NoTransactions color="$neutral3" size="$icon.70" />}
title={t('home.feed.empty.title')}
onPress={onPressReceive}
/>
</Flex>
)
let emptyComponent = null
if (!hasData && isError) {
emptyComponent = errorCard
} else if (!isLoading && emptyListView) {
emptyComponent = emptyListView
}
const refreshControl = useMemo(() => {
return (
<RefreshControl
progressViewOffset={insets.top + (isAndroid && headerHeight ? headerHeight + TAB_BAR_HEIGHT : 0)}
refreshing={refreshing ?? false}
tintColor={colors.neutral3.get()}
onRefresh={onRefresh}
/>
)
}, [refreshing, headerHeight, onRefresh, colors.neutral3, insets.top])
if (!hasData && isError) {
return errorCard
}
// We want to display the loading shimmer in the footer only when the data haven't been fetched yet
// (list items use their own loading shimmer so there is no need to display it in the footer)
const isLoadingInitially = isLoading && !sectionData
return (
<Flex grow px="$spacing24">
<AnimatedFlatList
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ref={ref as ForwardedRef<Animated.FlatList<any>>}
ListEmptyComponent={emptyComponent}
// we add a footer to cover any possible space, so user can scroll the top menu all the way to the top
ListFooterComponent={<>{isLoadingInitially && <Loader.Transaction repeat={4} />}</>}
data={sectionData}
estimatedItemSize={ESTIMATED_ITEM_SIZE}
initialNumToRender={20}
keyExtractor={keyExtractor}
maxToRenderPerBatch={20}
refreshControl={refreshControl}
refreshing={refreshing}
renderItem={renderActivityItem}
showsVerticalScrollIndicator={false}
updateCellsBatchingPeriod={10}
onContentSizeChange={onContentSizeChange}
onRefresh={onRefresh}
onScroll={scrollHandler}
{...containerProps}
/>
</Flex>
)
}),
)
......@@ -5,7 +5,7 @@ import { getChainInfo } from 'uniswap/src/features/chains/chainInfo'
import { useBlockExplorerLogo } from 'uniswap/src/features/chains/logos'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
type IconComponentProps = SvgProps & { size?: IconSizeTokens | number }
type IconComponentProps = SvgProps & { size?: IconSizeTokens | number | { width: number; height: number } }
const iconsCache = new Map<UniverseChainId, React.FC<IconComponentProps>>()
......
import { useLayoutEffect, useState } from 'react'
import { Dimensions, Image, Platform } from 'react-native'
import { Flex, useIsDarkMode } from 'ui/src'
import { ONBOARDING_NOTIFICATIONS_DARK, ONBOARDING_NOTIFICATIONS_LIGHT } from 'ui/src/assets'
/**
* Helper component to render the notifications background image based on the current theme
* and platform.
*
* One of the reasons why this is more complicated than it needs to be is because the android
* and ios images are different sizes and not the same aspect ratio.
*/
export const NotificationsBackgroundImage = (): JSX.Element => {
const isDarkMode = useIsDarkMode()
const [imageHeight, setImageHeight] = useState(0)
const [imageWidth, setImageWidth] = useState(0)
const imageSource = isDarkMode
? Platform.select(ONBOARDING_NOTIFICATIONS_DARK)
: Platform.select(ONBOARDING_NOTIFICATIONS_LIGHT)
const imageUri = Image.resolveAssetSource(imageSource).uri
useLayoutEffect(() => {
Image.getSize(imageUri, (width, height) => {
setImageWidth(width)
setImageHeight(height)
})
}, [imageUri])
const screenWidth = Dimensions.get('window').width
// Since this image is dynamically loaded in a BSM, the initial BSM height
// does not account for the image. This variable is so that we can put
// a placeholder view immediately to smooth out the BSM animation.
const containerHeight = imageWidth > 0 && imageHeight > 0 ? (imageHeight * (0.9 * screenWidth)) / imageWidth : 0
return (
<Flex
centered
style={{
height: containerHeight,
}}
>
{imageWidth > 0 && imageHeight > 0 && (
<Image
resizeMode="contain"
source={imageSource}
style={{
aspectRatio: imageWidth / imageHeight,
// Due to drop shadows and padding being hard to align,
// we scale the image down so it's not awkwardly misaligned
// with any full-width components.
width: '90%',
height: undefined,
}}
/>
)}
</Flex>
)
}
......@@ -277,6 +277,8 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
break
}
case DeepLinkAction.FiatOnRampScreen: {
const validUserAddress = yield* call(parseAndValidateUserAddress, deepLinkAction.data.userAddress)
yield* put(setAccountAsActive(validUserAddress))
yield* call(handleGoToFiatOnRampDeepLink)
break
}
......
......@@ -36,6 +36,8 @@ function* _handleOffRampReturnLink(url: URL) {
throw new Error('Missing externalTransactionId or moonpay data in fiat offramp deep link')
}
sendAnalyticsEvent(FiatOffRampEventName.FiatOffRampWidgetCompleted, { externalTransactionId })
let offRampTransferDetails: OffRampTransferDetailsResponse | undefined
try {
......@@ -49,7 +51,6 @@ function* _handleOffRampReturnLink(url: URL) {
} catch (error) {
logger.error(error, {
tags: { file: 'handleOffRampReturnLinkSaga', function: 'handleOffRampReturnLink' },
extra: { url: url.toString() },
})
throw new Error('Failed to fetch offramp transfer details')
}
......@@ -61,16 +62,6 @@ function* _handleOffRampReturnLink(url: URL) {
const { tokenAddress, baseCurrencyCode, baseCurrencyAmount, depositWalletAddress, logos, provider, chainId } =
offRampTransferDetails
const analyticsProperties = {
cryptoCurrency: baseCurrencyCode,
currencyAmount: baseCurrencyAmount,
serviceProvider: provider,
chainId,
externalTransactionId,
}
sendAnalyticsEvent(FiatOffRampEventName.FiatOffRampWidgetCompleted, analyticsProperties)
const currencyTradeableAsset: TradeableAsset = {
address: tokenAddress,
chainId: Number(chainId) as UniverseChainId,
......@@ -81,7 +72,13 @@ function* _handleOffRampReturnLink(url: URL) {
name: provider,
logoUrl: logos.lightLogo,
onSubmitCallback: () => {
sendAnalyticsEvent(FiatOffRampEventName.FiatOffRampFundsSent, analyticsProperties)
sendAnalyticsEvent(FiatOffRampEventName.FiatOffRampFundsSent, {
cryptoCurrency: baseCurrencyCode,
currencyAmount: baseCurrencyAmount,
serviceProvider: provider,
chainId,
externalTransactionId,
})
},
moonpayCurrencyCode: baseCurrencyCode,
meldCurrencyCode: baseCurrencyCode,
......
......@@ -9,7 +9,10 @@ export enum NotificationPermission {
Loading = 'loading',
}
export function useNotificationOSPermissionsEnabled(): NotificationPermission {
export function useNotificationOSPermissionsEnabled(): {
notificationPermissionsEnabled: NotificationPermission
checkNotificationPermissions: () => Promise<void>
} {
const [notificationPermissionsEnabled, setNotificationPermissionsEnabled] = useState<NotificationPermission>(
NotificationPermission.Loading,
)
......@@ -26,5 +29,5 @@ export function useNotificationOSPermissionsEnabled(): NotificationPermission {
useAppStateTrigger('background', 'active', checkNotificationPermissions)
return notificationPermissionsEnabled
return { notificationPermissionsEnabled, checkNotificationPermissions }
}
......@@ -4,7 +4,7 @@ import {
NotificationPermission,
useNotificationOSPermissionsEnabled,
} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled'
import { useNotificationToggle } from 'src/features/notifications/hooks/useNotificationsToggle'
import { useAddressNotificationToggle } from 'src/features/notifications/hooks/useNotificationsToggle'
import { showNotificationSettingsAlert } from 'src/screens/Onboarding/NotificationsSetupScreen'
import { act, renderHook, waitFor } from 'src/test/test-utils'
import { useSelectAccountNotificationSetting } from 'wallet/src/features/wallet/hooks'
......@@ -42,7 +42,7 @@ jest.mock('wallet/src/features/wallet/hooks', () => ({
useSelectAccountNotificationSetting: jest.fn(),
}))
describe('useNotificationToggle', () => {
describe('useAddressNotificationToggle', () => {
const mockAddress = '0xAddress'
const mockDispatch = jest.fn()
const mockUseDispatch = useDispatch as jest.MockedFunction<typeof useDispatch>
......@@ -66,9 +66,12 @@ describe('useNotificationToggle', () => {
firebaseEnabled?: boolean
onPermissionChanged?: (enabled: boolean) => void
} = {}) {
mockUseNotificationOSPermissionsQuery.mockReturnValue(osPermissionStatus)
mockUseNotificationOSPermissionsQuery.mockReturnValue({
notificationPermissionsEnabled: osPermissionStatus,
checkNotificationPermissions: jest.fn(),
})
mockUseSelectAccountNotificationSetting.mockReturnValue(firebaseEnabled)
return renderHook(() => useNotificationToggle({ address: mockAddress, onPermissionChanged }))
return renderHook(() => useAddressNotificationToggle({ address: mockAddress, onToggle: onPermissionChanged }))
}
describe('initial states', () => {
......
......@@ -6,6 +6,7 @@ import {
NotificationPermission,
useNotificationOSPermissionsEnabled,
} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled'
import { NotifSettingType, getNotifSetting, handleNotifSettingToggled } from 'src/features/notifications/settings'
import { showNotificationSettingsAlert } from 'src/screens/Onboarding/NotificationsSetupScreen'
import { EditAccountAction, editAccountActions } from 'wallet/src/features/wallet/accounts/editAccountSaga'
import { useSelectAccountNotificationSetting } from 'wallet/src/features/wallet/hooks'
......@@ -20,6 +21,60 @@ enum NotificationError {
OsPermissionDenied = 'OS_PERMISSION_DENIED',
}
export function useAddressNotificationToggle({
address,
onToggle,
}: {
address: string
onToggle?: (enabled: boolean) => void
}): ReturnType<typeof useBaseNotificationToggle> {
const dispatch = useDispatch()
const isAppPermissionEnabled = useSelectAccountNotificationSetting(address)
const handleToggle = useCallback(
(enabled: boolean) => {
dispatch(
editAccountActions.trigger({
type: EditAccountAction.TogglePushNotification,
enabled,
address,
}),
)
onToggle?.(enabled)
},
[address, dispatch, onToggle],
)
return useBaseNotificationToggle({ isAppPermissionEnabled, onToggle: handleToggle })
}
export function useSettingNotificationToggle({
type,
onToggle,
}: {
type: NotifSettingType
onToggle?: (enabled: boolean) => void
}): ReturnType<typeof useBaseNotificationToggle> {
const [isAppPermissionEnabled, setAppPermissionEnabled] = useState(false)
useEffect(() => {
getNotifSetting(type)
.then(setAppPermissionEnabled)
.catch(() => {})
}, [type])
const handleToggle = useCallback(
(enabled: boolean) => {
handleNotifSettingToggled(type, enabled)
setAppPermissionEnabled(enabled)
onToggle?.(enabled)
},
[onToggle, type],
)
return useBaseNotificationToggle({ isAppPermissionEnabled, onToggle: handleToggle })
}
/**
* useNotificationToggle
*
......@@ -64,20 +119,23 @@ enum NotificationError {
* - Normal OS permission flow resumes
*/
export function useNotificationToggle(props: { address: string; onPermissionChanged?: (enabled: boolean) => void }): {
function useBaseNotificationToggle({
isAppPermissionEnabled,
onToggle,
}: {
isAppPermissionEnabled: boolean
onToggle: (enabled: boolean) => void
}): {
isEnabled: boolean
isPending: boolean
toggle: () => void
} {
const dispatch = useDispatch()
// Get real states from different systems
const osPermissionStatus = useNotificationOSPermissionsEnabled()
const reduxPushNotificationsEnabled = useSelectAccountNotificationSetting(props.address)
const { notificationPermissionsEnabled: osPermissionStatus } = useNotificationOSPermissionsEnabled()
const isOSPermissionEnabled = osPermissionStatus === NotificationPermission.Enabled
// Derive real enabled state - only true if both systems are enabled
const isEnabled = isOSPermissionEnabled && reduxPushNotificationsEnabled
const isEnabled = isOSPermissionEnabled && isAppPermissionEnabled
// Optimistic UI state
const [optimisticEnabled, setOptimisticEnabled] = useState<boolean>(isEnabled)
......@@ -86,23 +144,14 @@ export function useNotificationToggle(props: { address: string; onPermissionChan
const requestOSPermissions = useCallback(async (): Promise<true> => {
const granted = await promptPushPermission()
if (!granted) {
// first let's enable the redux state (firebase)
// this will ensure that when the user goes to settings and enables notifications
// we're not stuck in a state where notifications are disabled
// and the user has to hit the toggle again
dispatch(
editAccountActions.trigger({
type: EditAccountAction.TogglePushNotification,
enabled: true,
address: props.address,
}),
)
// Keep app permissions enabled for when OS permissions are restored
onToggle(true)
// this means the user denied the permission at the system level
// and needs to go to settings to re-enable (boo)
throw new Error(NotificationError.OsPermissionDenied)
}
return true
}, [dispatch, props.address])
}, [onToggle])
// Reset optimistic state if real state changes
useEffect(() => {
......@@ -124,15 +173,7 @@ export function useNotificationToggle(props: { address: string; onPermissionChan
// After this point, we're guaranteed to have requested OS permissions
// If we just obtained permissions, we want to enable notifications
// Otherwise, we're toggling the current redux state
const shouldEnable = isOsEnabled ? !reduxPushNotificationsEnabled : true
dispatch(
editAccountActions.trigger({
type: EditAccountAction.TogglePushNotification,
enabled: shouldEnable,
address: props.address,
}),
)
const shouldEnable = isOsEnabled ? !isAppPermissionEnabled : true
return shouldEnable
},
onError: (error) => {
......@@ -147,7 +188,7 @@ export function useNotificationToggle(props: { address: string; onPermissionChan
// setState will bail if the value is the same as the current state
// so we can safely call it without conditionals
setOptimisticEnabled(enabled)
props.onPermissionChanged?.(enabled)
onToggle(enabled)
},
})
......
import OneSignal from 'react-native-onesignal'
// Enum value represents tag name in OneSignal
export enum NotifSettingType {
GeneralUpdates = 'settings_general_updates_enabled',
PriceAlerts = 'settings_price_alerts_enabled',
}
export function handleNotifSettingToggled(type: NotifSettingType, enabled: boolean): void {
OneSignal.sendTag(type, enabled ? 'true' : 'false')
}
export async function getNotifSetting(type: NotifSettingType): Promise<boolean> {
return new Promise((resolve, _reject) => {
OneSignal.getTags((tags) => resolve(tags?.[type] === 'true'))
})
}
......@@ -40,7 +40,7 @@ function useFinishAutomatedRecovery(navigation: Props['navigation']): {
const dispatch = useDispatch()
const { setRecoveredImportedAccounts, finishOnboarding } = useOnboardingContext()
const notificationOSPermission = useNotificationOSPermissionsEnabled()
const { notificationPermissionsEnabled: notificationOSPermission } = useNotificationOSPermissionsEnabled()
const hasAnyNotificationsEnabled = useSelector(selectAnyAddressHasNotificationsEnabled)
const { deviceSupportsBiometrics } = useBiometricContext()
const { requiredForTransactions: isBiometricAuthEnabled } = useBiometricAppSettings()
......
......@@ -23,7 +23,6 @@ import { AppStackScreenProp } from 'src/app/navigation/types'
import TraceTabView from 'src/components/Trace/TraceTabView'
import { AccountHeader } from 'src/components/accounts/AccountHeader'
import { ACTIVITY_TAB_DATA_DEPENDENCIES, ActivityTab } from 'src/components/home/ActivityTab'
import { FEED_TAB_DATA_DEPENDENCIES, FeedTab } from 'src/components/home/FeedTab'
import { HomeExploreTab } from 'src/components/home/HomeExploreTab'
import { NFTS_TAB_DATA_DEPENDENCIES, NftsTab } from 'src/components/home/NftsTab'
import { TOKENS_TAB_DATA_DEPENDENCIES, TokensTab } from 'src/components/home/TokensTab'
......@@ -105,8 +104,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
const isModalOpen = useSelector(selectSomeModalOpen)
const isHomeScreenBlur = !isFocused || isModalOpen
const showFeedTab = useFeatureFlag(FeatureFlags.FeedTab)
const { showEmptyWalletState, isTabsDataLoaded } = useHomeScreenState()
// opens the wallet restore modal if recovery phrase is missing after the app is opened
......@@ -126,7 +123,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
const tokensTitle = t('home.tokens.title')
const nftsTitle = t('home.nfts.title')
const activityTitle = t('home.activity.title')
const feedTitle = t('home.feed.title')
const exploreTitle = t('home.explore.title')
const routes = useMemo((): HomeRoute[] => {
......@@ -145,12 +141,8 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
{ key: SectionName.HomeActivityTab, title: activityTitle, enableNotificationBadge: true },
]
if (showFeedTab) {
tabs.push({ key: SectionName.HomeFeedTab, title: feedTitle })
}
return tabs
}, [showEmptyWalletState, tokensTitle, nftsTitle, activityTitle, showFeedTab, exploreTitle, feedTitle])
}, [showEmptyWalletState, tokensTitle, nftsTitle, activityTitle, exploreTitle])
useEffect(
function syncTabIndex() {
......@@ -202,12 +194,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
[activityTabScrollValue],
)
const feedTabScrollValue = useSharedValue(0)
const feedTabScrollHandler = useAnimatedScrollHandler(
(event) => (feedTabScrollValue.value = event.contentOffset.y),
[feedTabScrollValue],
)
const exploreTabScrollValue = useSharedValue(0)
const exploreTabScrollHandler = useAnimatedScrollHandler(
(event) => (exploreTabScrollValue.value = event.contentOffset.y),
......@@ -220,8 +206,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const activityTabScrollRef = useAnimatedRef<FlatList<any>>()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const feedTabScrollRef = useAnimatedRef<FlatList<any>>()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const exploreTabScrollRef = useAnimatedRef<FlatList<any>>()
const currentScrollValue = useDerivedValue(() => {
......@@ -234,12 +218,11 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
} else if (tabIndex === HomeScreenTabIndex.Activity) {
return activityTabScrollValue.value
}
return feedTabScrollValue.value
return 0
}, [
activityTabScrollValue.value,
exploreTabScrollValue.value,
showEmptyWalletState,
feedTabScrollValue.value,
nftsTabScrollValue.value,
tabIndex,
tokensTabScrollValue.value,
......@@ -258,12 +241,10 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
nftsTabScrollValue.value = 0
tokensTabScrollValue.value = 0
activityTabScrollValue.value = 0
feedTabScrollValue.value = 0
exploreTabScrollValue.value = 0
nftsTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true })
tokensTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true })
activityTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true })
feedTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true })
exploreTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true })
}, [
activeAccount,
......@@ -275,8 +256,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
nftsTabScrollValue,
tokensTabScrollRef,
tokensTabScrollValue,
feedTabScrollRef,
feedTabScrollValue,
])
// Need to create a derived value for tab index so it can be referenced from a static ref
......@@ -318,13 +297,10 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
{ list: tokensTabScrollRef, position: tokensTabScrollValue, index: 0 },
{ list: nftsTabScrollRef, position: nftsTabScrollValue, index: 1 },
{ list: activityTabScrollRef, position: activityTabScrollValue, index: 2 },
{ list: feedTabScrollRef, position: feedTabScrollValue, index: 3 },
],
[
activityTabScrollRef,
activityTabScrollValue,
feedTabScrollRef,
feedTabScrollValue,
nftsTabScrollRef,
nftsTabScrollValue,
tokensTabScrollRef,
......@@ -567,18 +543,13 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
setRefreshing(true)
await apolloClient.refetchQueries({
include: [
...TOKENS_TAB_DATA_DEPENDENCIES,
...NFTS_TAB_DATA_DEPENDENCIES,
...ACTIVITY_TAB_DATA_DEPENDENCIES,
...(showFeedTab ? FEED_TAB_DATA_DEPENDENCIES : []),
],
include: [...TOKENS_TAB_DATA_DEPENDENCIES, ...NFTS_TAB_DATA_DEPENDENCIES, ...ACTIVITY_TAB_DATA_DEPENDENCIES],
})
// Artificially delay 0.5 second to show the refresh animation
const timeout = setTimeout(() => setRefreshing(false), 500)
return () => clearTimeout(timeout)
}, [apolloClient, showFeedTab])
}, [apolloClient])
const renderTab = useCallback(
({
......@@ -639,18 +610,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
/>
</Freeze>
)
case SectionName.HomeFeedTab:
return (
<FeedTab
ref={feedTabScrollRef}
containerProps={sharedProps}
headerHeight={headerHeight}
owner={activeAccount?.address}
refreshing={refreshing}
scrollHandler={feedTabScrollHandler}
onRefresh={onRefreshHomeData}
/>
)
case SectionName.HomeExploreTab:
return (
<HomeExploreTab
......@@ -681,8 +640,6 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
nftsTabScrollHandler,
activityTabScrollRef,
activityTabScrollHandler,
feedTabScrollRef,
feedTabScrollHandler,
exploreTabScrollRef,
exploreTabScrollHandler,
],
......
......@@ -3,9 +3,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { useNftsTabQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains'
import { usePortfolioBalances } from 'uniswap/src/features/dataApi/balances'
import { getValidAddress } from 'uniswap/src/utils/addresses'
import { logger } from 'utilities/src/logger/logger'
import { useFormattedTransactionDataForActivity } from 'wallet/src/features/activity/hooks'
import { useFormattedTransactionDataForActivity } from 'wallet/src/features/activity/hooks/useFormattedTransactionDataForActivity'
import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks'
import { selectHasBalanceOrActivityForAddress } from 'wallet/src/features/wallet/selectors'
import { setHasBalanceOrActivity } from 'wallet/src/features/wallet/slice'
......@@ -51,27 +49,15 @@ export function useHomeScreenState(): {
const hasNft = !!nftData?.nftBalances?.edges.length
const hasTokenBalance = !!Object.entries(balancesById || {}).length
const hasUsedWalletFromRemote = hasTokenBalance || hasNft || hasActivity
const dataIsLoading = areBalancesLoading || areNFTsLoading || isActivityLoading
// Note: This is to prevent loading the empty wallet state for an active
// wallet loading tabs for the first time.
const isTabsDataLoaded = !(dataIsLoading && hasUsedWalletFromCache)
const hasUsedWalletFromRemote = !!hasTokenBalance || !!hasNft || !!hasActivity
const isTabsDataLoaded =
hasUsedWalletFromRemote || (!areBalancesLoading && !areNFTsLoading && !isActivityLoading) || hasUsedWalletFromCache
const hasUsedWallet = hasUsedWalletFromCache || hasUsedWalletFromRemote
useEffect(() => {
if (hasUsedWallet && !hasUsedWalletFromCache) {
const validAddress = getValidAddress(address)
if (!validAddress) {
// do nothing. This will revert to the old logic to overfetch.
logger.error('Unexpected call to `setHasUsedWallet` with invalid `address`', {
extra: { payload: address },
tags: { file: 'behaviorHistory/slice.ts', function: 'setHasUsedWallet' },
})
return
}
dispatch(setHasBalanceOrActivity({ address: validAddress, hasBalanceOrActivity: true }))
dispatch(setHasBalanceOrActivity({ address, hasBalanceOrActivity: true }))
}
}, [hasUsedWallet, dispatch, address, hasUsedWalletFromCache])
......
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Alert, Image, Platform, StyleSheet } from 'react-native'
import { Alert } from 'react-native'
import { OnboardingStackParamList } from 'src/app/navigation/types'
import { NotificationsBackgroundImage } from 'src/components/notifications/NotificationsBGImage'
import { useBiometricContext } from 'src/features/biometrics/context'
import { useBiometricAppSettings } from 'src/features/biometrics/hooks'
import { promptPushPermission } from 'src/features/notifications/Onesignal'
import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { useCompleteOnboardingCallback } from 'src/features/onboarding/hooks'
import { DeprecatedButton, Flex, useIsDarkMode } from 'ui/src'
import { ONBOARDING_NOTIFICATIONS_DARK, ONBOARDING_NOTIFICATIONS_LIGHT } from 'ui/src/assets'
import { DeprecatedButton, Flex } from 'ui/src'
import { BellOn } from 'ui/src/components/icons'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
......@@ -77,7 +77,7 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop
title={t('onboarding.notification.title')}
onSkip={navigateToNextScreen}
>
<Flex centered shrink>
<Flex fill centered shrink>
<NotificationsBackgroundImage />
</Flex>
<Trace logPress element={ElementName.Enable}>
......@@ -88,23 +88,3 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop
</OnboardingScreen>
)
}
const NotificationsBackgroundImage = (): JSX.Element => {
const isDarkMode = useIsDarkMode()
return (
<Image
resizeMode="contain"
source={
isDarkMode ? Platform.select(ONBOARDING_NOTIFICATIONS_DARK) : Platform.select(ONBOARDING_NOTIFICATIONS_LIGHT)
}
style={styles.image}
/>
)
}
const styles = StyleSheet.create({
image: {
height: '100%',
width: '100%',
},
})
......@@ -2,10 +2,16 @@ import React, { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { BackHeader } from 'src/components/layout/BackHeader'
import { Screen } from 'src/components/layout/Screen'
import { useNotificationToggle } from 'src/features/notifications/hooks/useNotificationsToggle'
import {
useAddressNotificationToggle,
useSettingNotificationToggle,
} from 'src/features/notifications/hooks/useNotificationsToggle'
import { NotifSettingType } from 'src/features/notifications/settings'
import { Flex, Switch, Text } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { AccountType } from 'uniswap/src/features/accounts/types'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { MobileEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
......@@ -16,14 +22,40 @@ export function SettingsNotificationsScreen(): JSX.Element {
const accounts = useAccountsList()
const priceAlertsToggleEnabled = useFeatureFlag(FeatureFlags.NotificationPriceAlerts)
const { isEnabled: updatesNotifEnabled, toggle: toggleUpdatesNotif } = useSettingNotificationToggle({
type: NotifSettingType.GeneralUpdates,
})
const { isEnabled: priceAlertsNotifEnabled, toggle: togglePriceAlertsNotif } = useSettingNotificationToggle({
type: NotifSettingType.PriceAlerts,
})
return (
<Screen>
<BackHeader alignment="center" mx="$spacing16" pt="$spacing16">
<Text variant="body1">{t('settings.setting.notifications.title')}</Text>
</BackHeader>
<Flex py="$spacing20" px="$spacing24">
<Flex pb="$spacing12">
<Flex py="$spacing20" px="$spacing24" gap="$spacing24">
<NotificationSettingRow
title={t('settings.setting.notifications.row.updates.title')}
description={t('settings.setting.notifications.row.updates.description')}
checked={updatesNotifEnabled}
onCheckedChange={toggleUpdatesNotif}
/>
{priceAlertsToggleEnabled ? (
<NotificationSettingRow
title={t('settings.setting.notifications.row.priceAlerts.title')}
description={t('settings.setting.notifications.row.priceAlerts.description')}
checked={priceAlertsNotifEnabled}
onCheckedChange={togglePriceAlertsNotif}
/>
) : undefined}
<Flex gap="$spacing12">
<Flex gap="$spacing4">
<Text variant="subheading2" color="$neutral1">
{t('settings.setting.notifications.row.activity.title')}
</Text>
......@@ -48,26 +80,53 @@ export function SettingsNotificationsScreen(): JSX.Element {
captionVariant="body3"
/>
</Flex>
<NotificationsSwitch address={account.address} />
<AddressNotificationsSwitch address={account.address} />
</Flex>
)
})}
</Flex>
</Flex>
</Flex>
</Screen>
)
}
function NotificationSettingRow({
title,
description,
checked,
onCheckedChange,
}: {
title: string
description: string
checked: boolean
onCheckedChange: (checked: boolean) => void
}): JSX.Element {
return (
<Flex row gap="$spacing12">
<Flex fill gap="$spacing4">
<Text variant="subheading2" color="$neutral1">
{title}
</Text>
<Text variant="body3" color="$neutral2">
{description}
</Text>
</Flex>
<Switch variant="branded" checked={checked} onCheckedChange={onCheckedChange} />
</Flex>
)
}
function onPermissionChanged(enabled: boolean): void {
sendAnalyticsEvent(MobileEventName.NotificationsToggled, { enabled })
}
function _NotificationsSwitch({ address }: { address: string }): JSX.Element {
const { isEnabled, isPending, toggle } = useNotificationToggle({
function _AddressNotificationsSwitch({ address }: { address: string }): JSX.Element {
const { isEnabled, isPending, toggle } = useAddressNotificationToggle({
address,
onPermissionChanged,
onToggle: onPermissionChanged,
})
return <Switch checked={isEnabled} disabled={isPending} variant="branded" onCheckedChange={toggle} />
}
const NotificationsSwitch = memo(_NotificationsSwitch)
const AddressNotificationsSwitch = memo(_AddressNotificationsSwitch)
......@@ -18,6 +18,10 @@ import { WalletSettings } from 'src/components/Settings/WalletSettings'
import { HeaderScrollScreen } from 'src/components/layout/screens/HeaderScrollScreen'
import { useBiometricContext } from 'src/features/biometrics/context'
import { useBiometricName, useDeviceSupportsBiometricAuth } from 'src/features/biometrics/hooks'
import {
NotificationPermission,
useNotificationOSPermissionsEnabled,
} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled'
import { useWalletRestore } from 'src/features/wallet/hooks'
import { useHapticFeedback } from 'src/utils/haptics/useHapticFeedback'
import { Flex, IconProps, Text, useSporeColors } from 'ui/src'
......@@ -105,6 +109,8 @@ export function SettingsScreen(): JSX.Element {
}, [setHapticsEnabled, hapticsEnabled])
const [isTestnetModalOpen, setIsTestnetModalOpen] = useState(false)
const { notificationPermissionsEnabled: notificationOSPermission } = useNotificationOSPermissionsEnabled()
const { isTestnetModeEnabled } = useEnabledChains()
const handleTestnetModeToggle = useCallback((): void => {
const newIsTestnetMode = !isTestnetModeEnabled
......@@ -186,6 +192,13 @@ export function SettingsScreen(): JSX.Element {
screen: MobileScreens.SettingsNotifications,
text: t('settings.setting.notifications.title'),
icon: <Bell {...iconProps} />,
checkIfCanProceed: (): boolean => {
if (notificationOSPermission === NotificationPermission.Enabled) {
return true
}
navigation.navigate(ModalName.NotificationsOSSettings)
return false
},
},
{
text: t('settings.setting.smallBalances.title'),
......@@ -345,6 +358,8 @@ export function SettingsScreen(): JSX.Element {
hasCloudBackup,
isTestnetModeEnabled,
handleTestnetModeToggle,
notificationOSPermission,
navigation,
])
const renderItem = ({
......@@ -356,7 +371,9 @@ export function SettingsScreen(): JSX.Element {
if ('component' in item) {
return item.component
}
return <SettingsRow key={item.screen} navigation={navigation} page={item} />
return (
<SettingsRow key={item.screen} navigation={navigation} page={item} checkIfCanProceed={item.checkIfCanProceed} />
)
}
const handleModalClose = useCallback(() => setIsTestnetModalOpen(false), [])
......@@ -373,13 +390,17 @@ export function SettingsScreen(): JSX.Element {
keyExtractor={(_item, index): string => 'settings' + index}
renderItem={renderItem}
renderSectionFooter={(): JSX.Element => <Flex pt="$spacing24" />}
renderSectionHeader={({ section: { subTitle } }): JSX.Element => (
renderSectionHeader={({ section: { subTitle } }): JSX.Element =>
subTitle ? (
<Flex backgroundColor="$surface1" py="$spacing12">
<Text color="$neutral2" variant="body1">
{subTitle}
</Text>
</Flex>
)}
) : (
<></>
)
}
sections={sections.filter((p) => !p.isHidden)}
showsVerticalScrollIndicator={false}
/>
......
......@@ -20,6 +20,7 @@ import {
import { BackHeader } from 'src/components/layout/BackHeader'
import { Screen } from 'src/components/layout/Screen'
import { openModal } from 'src/features/modals/modalSlice'
import { useWalletConnect } from 'src/features/walletConnect/useWalletConnect'
import { DeprecatedButton, Flex, Text, useSporeColors } from 'ui/src'
import GlobalIcon from 'ui/src/assets/icons/global.svg'
import TextEditIcon from 'ui/src/assets/icons/textEdit.svg'
......@@ -54,6 +55,7 @@ export function SettingsWallet({
const { unitag } = useUnitagByAddress(address)
const readonly = currentAccount?.type === AccountType.Readonly
const navigation = useNavigation<SettingsStackNavigationProp & OnboardingStackNavigationProp>()
const { sessions } = useWalletConnect(address)
const showEditProfile = !readonly
......@@ -83,12 +85,12 @@ export function SettingsWallet({
const sections: SettingsSection[] = [
{
subTitle: t('settings.setting.wallet.preferences.title'),
data: [
...(showEditProfile ? [] : [editNicknameSectionOption]),
{
screen: MobileScreens.SettingsWalletManageConnection,
text: t('settings.setting.wallet.connections.title'),
count: sessions.length,
icon: <GlobalIcon {...iconProps} />,
screenProps: { address },
isHidden: readonly,
......@@ -134,13 +136,17 @@ export function SettingsWallet({
keyExtractor={(_item, index): string => 'wallet_settings' + index}
renderItem={renderItem}
renderSectionFooter={(): JSX.Element => <Flex pt="$spacing24" />}
renderSectionHeader={({ section: { subTitle } }): JSX.Element => (
renderSectionHeader={({ section: { subTitle } }): JSX.Element =>
subTitle ? (
<Flex backgroundColor="$surface1" pb="$spacing12">
<Text color="$neutral2" variant="body1">
{subTitle}
</Text>
</Flex>
)}
) : (
<></>
)
}
sections={sections.filter((p) => !p.isHidden)}
showsVerticalScrollIndicator={false}
stickySectionHeadersEnabled={false}
......
......@@ -20,6 +20,7 @@
"typecheck": "tsc && yarn typecheck:cloud && yarn typecheck:cypress",
"typecheck:cloud": "tsc -p functions/tsconfig.json",
"typecheck:cypress": "tsc -p cypress/tsconfig.json",
"find:unused": "bash scripts/delete-unused-assets.sh",
"test": "craco test --watchAll=false",
"test:set1": "craco test --testPathPattern='src/components'",
"test:set2": "craco test --testPathPattern='src/(pages|state)'",
......@@ -194,29 +195,29 @@
"@tanstack/react-query": "5.51.16",
"@tanstack/react-table": "8.10.7",
"@types/poisson-disk-sampling": "2.2.4",
"@types/react-scroll-sync": "0.8.7",
"@types/react-scroll-sync": "0.9.0",
"@types/react-window-infinite-loader": "1.0.6",
"@uniswap/analytics": "1.7.0",
"@uniswap/analytics-events": "2.40.0",
"@uniswap/analytics-events": "2.41.0",
"@uniswap/client-explore": "0.0.14",
"@uniswap/client-pools": "0.0.12",
"@uniswap/liquidity-staker": "1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/permit2-sdk": "1.3.0",
"@uniswap/redux-multicall": "1.1.8",
"@uniswap/router-sdk": "1.18.0",
"@uniswap/sdk-core": "7.1.0",
"@uniswap/router-sdk": "1.21.0",
"@uniswap/sdk-core": "7.5.0",
"@uniswap/smart-order-router": "3.17.3",
"@uniswap/token-lists": "1.0.0-beta.33",
"@uniswap/uniswapx-sdk": "3.0.0-beta.1",
"@uniswap/universal-router-sdk": "4.10.0",
"@uniswap/universal-router-sdk": "4.14.0",
"@uniswap/v2-core": "1.0.1",
"@uniswap/v2-periphery": "1.1.0-beta.0",
"@uniswap/v2-sdk": "4.9.0",
"@uniswap/v2-sdk": "4.13.0",
"@uniswap/v3-core": "1.0.1",
"@uniswap/v3-periphery": "1.4.4",
"@uniswap/v3-sdk": "3.21.0",
"@uniswap/v4-sdk": "1.15.0",
"@uniswap/v3-sdk": "3.24.0",
"@uniswap/v4-sdk": "1.18.0",
"@vanilla-extract/css": "1.14.0",
"@vanilla-extract/dynamic": "2.1.0",
"@vanilla-extract/sprinkles": "1.6.1",
......
......@@ -126,16 +126,4 @@
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://app.uniswap.org/positions/create</loc>
<lastmod>2024-09-17T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/positions</loc>
<lastmod>2024-09-17T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
</urlset>
<svg width="257" height="256" viewBox="0 0 257 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M96.4347 60.2768C94.3434 59.9524 94.2552 59.9143 95.2394 59.7631C97.1254 59.473 101.579 59.8683 104.648 60.5981C111.812 62.3011 118.331 66.6637 125.29 74.4117L127.139 76.4701L129.784 76.0449C140.926 74.2544 152.26 75.6774 161.741 80.057C164.349 81.2619 168.461 83.6603 168.975 84.2766C169.138 84.473 169.439 85.7374 169.642 87.0866C170.347 91.7544 169.994 95.3323 168.567 98.0046C167.79 99.4588 167.746 99.9197 168.268 101.164C168.686 102.157 169.848 102.892 170.999 102.891C173.355 102.888 175.891 99.0791 177.066 93.78L177.532 91.6751L178.457 92.7224C183.528 98.4681 187.512 106.304 188.196 111.881L188.374 113.336L187.522 112.014C186.054 109.739 184.58 108.19 182.693 106.941C179.29 104.69 175.693 103.923 166.164 103.421C157.559 102.968 152.689 102.233 147.859 100.658C139.643 97.9792 135.501 94.4114 125.74 81.6059C121.405 75.918 118.726 72.7711 116.06 70.2368C110.004 64.4784 104.053 61.4584 96.4347 60.2768Z" fill="#FF007A"/>
<path d="M170.916 72.9763C171.132 69.1649 171.649 66.651 172.688 64.3552C173.099 63.4465 173.485 62.7027 173.544 62.7027C173.604 62.7027 173.425 63.3735 173.147 64.1931C172.391 66.4212 172.267 69.4687 172.788 73.0144C173.448 77.5132 173.824 78.1623 178.579 83.022C180.809 85.3014 183.403 88.1762 184.344 89.4105L186.054 91.6547L184.344 90.0508C182.253 88.0895 177.444 84.2644 176.381 83.7176C175.669 83.3509 175.563 83.3573 175.124 83.7946C174.719 84.1975 174.634 84.803 174.577 87.6654C174.49 92.1267 173.882 94.9901 172.414 97.8533C171.621 99.4019 171.495 99.0714 172.214 97.3235C172.75 96.0184 172.805 95.4446 172.801 91.1259C172.792 82.4485 171.762 80.3624 165.721 76.7887C164.19 75.8834 161.668 74.5778 160.117 73.8872C158.565 73.1965 157.333 72.595 157.378 72.5501C157.549 72.3798 163.441 74.0995 165.812 75.0117C169.339 76.3686 169.922 76.5444 170.35 76.3807C170.637 76.271 170.776 75.4347 170.916 72.9763Z" fill="#FF007A"/>
<path d="M100.497 87.8209C96.2514 81.9758 93.6246 73.0138 94.1933 66.3144L94.3691 64.2413L95.3355 64.4176C97.1504 64.7486 100.28 65.9133 101.745 66.8033C105.766 69.2453 107.506 72.4605 109.277 80.7164C109.796 83.1346 110.477 85.8712 110.79 86.7976C111.294 88.2889 113.199 91.7721 114.748 94.0343C115.864 95.6636 115.123 96.4356 112.657 96.213C108.89 95.873 103.788 92.3519 100.497 87.8209Z" fill="#FF007A"/>
<path d="M165.766 131.323C145.925 123.335 138.937 116.4 138.937 104.7C138.937 102.979 138.996 101.57 139.068 101.57C139.14 101.57 139.908 102.138 140.774 102.833C144.797 106.06 149.303 107.438 161.776 109.258C169.115 110.328 173.245 111.193 177.056 112.457C189.166 116.473 196.658 124.624 198.445 135.725C198.964 138.951 198.66 145 197.818 148.188C197.153 150.706 195.125 155.245 194.588 155.419C194.439 155.468 194.292 154.896 194.254 154.118C194.05 149.95 191.943 145.891 188.406 142.851C184.383 139.395 178.979 136.643 165.766 131.323Z" fill="#FF007A"/>
<path d="M151.837 134.642C151.588 133.163 151.157 131.273 150.879 130.444L150.372 128.935L151.313 129.991C152.614 131.451 153.642 133.32 154.514 135.81C155.179 137.71 155.254 138.275 155.249 141.362C155.244 144.393 155.161 145.029 154.546 146.739C153.578 149.436 152.376 151.348 150.359 153.4C146.735 157.089 142.075 159.131 135.351 159.978C134.182 160.125 130.775 160.373 127.78 160.529C120.232 160.922 115.264 161.733 110.801 163.3C110.159 163.525 109.586 163.662 109.528 163.604C109.347 163.425 112.386 161.613 114.897 160.404C118.436 158.699 121.96 157.768 129.854 156.454C133.754 155.804 137.781 155.016 138.804 154.702C148.461 151.741 153.426 144.1 151.837 134.642Z" fill="#FF007A"/>
<path d="M160.932 150.795C158.296 145.128 157.691 139.657 159.135 134.554C159.289 134.009 159.538 133.562 159.687 133.562C159.837 133.562 160.459 133.899 161.07 134.31C162.284 135.127 164.721 136.505 171.212 140.044C179.311 144.46 183.929 147.879 187.07 151.786C189.82 155.208 191.522 159.104 192.341 163.856C192.805 166.548 192.533 173.024 191.843 175.735C189.665 184.281 184.604 190.993 177.385 194.911C176.327 195.484 175.377 195.955 175.275 195.958C175.172 195.96 175.557 194.98 176.131 193.78C178.56 188.703 178.836 183.765 177 178.269C175.876 174.904 173.584 170.797 168.956 163.857C163.575 155.788 162.256 153.641 160.932 150.795Z" fill="#FF007A"/>
<path d="M86.4067 181.371C93.7696 175.154 102.931 170.738 111.276 169.382C114.872 168.798 120.864 169.03 124.194 169.882C129.532 171.248 134.307 174.308 136.791 177.954C139.218 181.517 140.259 184.622 141.343 191.53C141.771 194.255 142.236 196.992 142.377 197.611C143.191 201.192 144.775 204.054 146.739 205.491C149.857 207.773 155.227 207.915 160.509 205.855C161.405 205.505 162.184 205.263 162.238 205.318C162.43 205.508 159.77 207.288 157.894 208.226C155.369 209.487 153.361 209.975 150.693 209.975C145.855 209.975 141.839 207.514 138.487 202.495C137.828 201.508 136.346 198.55 135.193 195.922C131.655 187.851 129.907 185.393 125.799 182.702C122.223 180.361 117.612 179.941 114.143 181.642C109.586 183.876 108.315 189.699 111.579 193.39C112.876 194.856 115.295 196.121 117.273 196.367C120.973 196.828 124.153 194.013 124.153 190.277C124.153 187.851 123.221 186.467 120.873 185.407C117.668 183.961 114.222 185.652 114.238 188.663C114.245 189.947 114.805 190.754 116.092 191.336C116.918 191.71 116.937 191.74 116.263 191.6C113.322 190.99 112.633 187.444 114.998 185.09C117.837 182.264 123.709 183.511 125.725 187.368C126.572 188.988 126.67 192.215 125.932 194.164C124.279 198.525 119.46 200.819 114.571 199.571C111.243 198.721 109.887 197.801 105.874 193.667C98.9012 186.484 96.1941 185.092 86.141 183.523L84.2146 183.222L86.4067 181.371Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.8241 20.4645C57.1114 48.7285 93.0139 92.734 94.7963 95.1977C96.2679 97.232 95.7141 99.0609 93.1929 100.494C91.7909 101.291 88.9084 102.099 87.4652 102.099C85.8329 102.099 83.9939 101.313 82.6548 100.042C81.7084 99.1447 77.8882 93.4402 69.0694 79.7566C62.3216 69.2863 56.6747 60.6007 56.5206 60.4553C56.1644 60.119 56.1705 60.1303 68.3813 81.9787C76.0487 95.6979 78.6371 100.548 78.6371 101.197C78.6371 102.516 78.2771 103.21 76.6495 105.025C73.9363 108.052 72.7235 111.453 71.8479 118.492C70.8664 126.382 68.1064 131.955 60.4577 141.494C55.9803 147.078 55.2477 148.102 54.118 150.352C52.695 153.187 52.3037 154.774 52.1451 158.353C51.9775 162.137 52.3039 164.581 53.46 168.199C54.4721 171.366 55.5285 173.458 58.2292 177.641C60.5599 181.251 61.9019 183.933 61.9019 184.983C61.9019 185.818 62.0613 185.819 65.6729 185.003C74.316 183.052 81.3341 179.619 85.2812 175.412C87.724 172.808 88.2975 171.371 88.3161 167.802C88.3283 165.469 88.2462 164.98 87.6153 163.637C86.5884 161.452 84.7188 159.635 80.5983 156.818C75.1992 153.127 72.8932 150.156 72.2562 146.07C71.7337 142.717 72.3399 140.351 75.3267 134.091C78.4182 127.612 79.1843 124.85 79.7025 118.319C80.0372 114.1 80.5008 112.435 81.7131 111.1C82.9776 109.707 84.1158 109.235 87.245 108.808C92.3466 108.111 95.5951 106.791 98.2652 104.33C100.582 102.196 101.551 100.139 101.7 97.0427L101.813 94.6959L100.518 93.1867C95.8304 87.7209 30.6848 16.168 30.3963 16.168C30.3347 16.168 31.8773 18.1015 33.8241 20.4645ZM64.5333 162.634C65.5932 160.757 65.0301 158.344 63.2572 157.166C61.5821 156.052 58.9799 156.576 58.9799 158.028C58.9799 158.471 59.2248 158.793 59.7768 159.077C60.7063 159.555 60.7738 160.093 60.0425 161.192C59.3019 162.306 59.3616 163.284 60.2111 163.949C61.5802 165.021 63.5183 164.432 64.5333 162.634Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M105.032 110.039C102.637 110.774 100.309 113.312 99.5884 115.974C99.1487 117.598 99.3982 120.446 100.057 121.325C101.121 122.746 102.149 123.12 104.935 123.101C110.389 123.063 115.131 120.724 115.682 117.799C116.134 115.402 114.051 112.08 111.183 110.622C109.703 109.87 106.555 109.571 105.032 110.039ZM111.408 115.024C112.249 113.829 111.881 112.537 110.451 111.664C107.726 110 103.607 111.377 103.607 113.95C103.607 115.231 105.755 116.629 107.724 116.629C109.035 116.629 110.829 115.847 111.408 115.024Z" fill="#FF007A"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.64864 2L1 7.65256L10.5 17.1487L20 7.65256L14.3514 2H6.64864ZM6.13513 5.59458C8.5352 3.18398 12.4648 3.18396 14.8649 5.59456L16.9189 7.64866L14.8649 9.70272C12.4648 12.1133 8.5352 12.1133 6.13513 9.70274L4.08109 7.64866L6.13513 5.59458ZM7.54702 7.64848C7.54702 9.27987 8.86966 10.6012 10.4997 10.6012C12.1298 10.6012 13.4524 9.27987 13.4524 7.64848C13.4524 6.01708 12.1298 4.69576 10.4997 4.69576C8.86966 4.69576 7.54702 6.01708 7.54702 7.64848ZM10.4997 8.93225C9.791 8.93225 9.21593 8.35778 9.21593 7.64848C9.21593 6.93917 9.791 6.3647 10.4997 6.3647C11.2084 6.3647 11.7835 6.93917 11.7835 7.64848C11.7835 8.35778 11.2084 8.93225 10.4997 8.93225Z" fill="#9B9B9B"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 1C5.0302 1 1 5.0302 1 10C1 14.9698 5.0302 19 10 19C14.9698 19 19 14.9698 19 10C19 5.0302 14.9716 1 10 1ZM5.4406 10.3024L5.4784 10.2412L7.8202 6.5782C7.8544 6.526 7.9354 6.5314 7.9606 6.589C8.3512 7.4656 8.6896 8.5564 8.5312 9.235C8.4646 9.514 8.2792 9.892 8.0704 10.2412C8.0434 10.2916 8.0146 10.342 7.9822 10.3906C7.9678 10.4122 7.9426 10.4248 7.9156 10.4248H5.509C5.4442 10.4248 5.4064 10.3546 5.4406 10.3024ZM15.8752 11.5624C15.8752 11.5966 15.8554 11.6254 15.8266 11.638C15.6448 11.7154 15.0238 12.0016 14.7664 12.3598C14.1076 13.276 13.6054 14.5864 12.4804 14.5864H7.7896C6.1264 14.5864 4.78 13.2346 4.78 11.566V11.512C4.78 11.4688 4.816 11.4328 4.861 11.4328H7.4746C7.5268 11.4328 7.5646 11.4796 7.561 11.5318C7.5412 11.701 7.5736 11.8756 7.6546 12.034C7.8094 12.349 8.1316 12.5452 8.479 12.5452H9.7732V11.5354H8.4934C8.4286 11.5354 8.389 11.4598 8.4268 11.4058C8.4412 11.3842 8.4556 11.3626 8.4736 11.3374C8.5942 11.1646 8.767 10.8982 8.9398 10.594C9.0568 10.3888 9.1702 10.1692 9.262 9.9496C9.28 9.91 9.2944 9.8686 9.3106 9.829C9.3358 9.7588 9.361 9.6922 9.379 9.6274C9.397 9.5716 9.4132 9.514 9.4276 9.46C9.4708 9.2728 9.4888 9.0748 9.4888 8.8696C9.4888 8.7886 9.4852 8.704 9.478 8.6248C9.4744 8.5366 9.4636 8.4484 9.4528 8.3602C9.4456 8.2828 9.4312 8.2054 9.4168 8.1262C9.397 8.0092 9.3718 7.8922 9.343 7.7752L9.3322 7.7302C9.3106 7.6492 9.2908 7.5736 9.2656 7.4926C9.1918 7.2406 9.109 6.994 9.019 6.7636C8.9866 6.6718 8.9506 6.5836 8.9128 6.4972C8.8588 6.364 8.803 6.2434 8.7526 6.13C8.7256 6.0778 8.704 6.031 8.6824 5.9824C8.6572 5.9284 8.632 5.8744 8.605 5.8222C8.587 5.7826 8.5654 5.7448 8.551 5.7088L8.3926 5.4172C8.371 5.3776 8.407 5.329 8.4502 5.3416L9.4402 5.6098H9.4438C9.4456 5.6098 9.4456 5.6098 9.4474 5.6098L9.577 5.6476L9.721 5.6872L9.7732 5.7016V5.1148C9.7732 4.8304 10 4.6 10.2826 4.6C10.423 4.6 10.5508 4.6576 10.6408 4.7512C10.7326 4.8448 10.7902 4.9726 10.7902 5.1148V5.9878L10.8964 6.0166C10.9036 6.0202 10.9126 6.0238 10.9198 6.0292C10.945 6.0472 10.9828 6.076 11.0296 6.112C11.0674 6.1408 11.107 6.1768 11.1538 6.2146C11.2492 6.292 11.3644 6.391 11.4886 6.5044C11.521 6.5332 11.5534 6.562 11.584 6.5926C11.7442 6.742 11.9242 6.9166 12.097 7.111C12.1456 7.1668 12.1924 7.2208 12.241 7.2802C12.2878 7.3396 12.34 7.3972 12.3832 7.4548C12.4426 7.5322 12.5038 7.6132 12.5596 7.6978C12.5848 7.7374 12.6154 7.7788 12.6388 7.8184C12.7108 7.9246 12.772 8.0344 12.8314 8.1442C12.8566 8.1946 12.8818 8.2504 12.9034 8.3044C12.97 8.452 13.0222 8.6014 13.0546 8.7526C13.0654 8.785 13.0726 8.8192 13.0762 8.8516V8.8588C13.087 8.902 13.0906 8.9488 13.0942 8.9974C13.1086 9.1504 13.1014 9.3052 13.069 9.46C13.0546 9.5248 13.0366 9.586 13.015 9.6526C12.9916 9.7156 12.97 9.7804 12.9412 9.8434C12.8854 9.9712 12.8206 10.1008 12.7432 10.2196C12.718 10.2646 12.6874 10.3114 12.6586 10.3564C12.6262 10.4032 12.592 10.4482 12.5632 10.4914C12.5218 10.5472 12.4786 10.6048 12.4336 10.657C12.394 10.711 12.3544 10.765 12.3094 10.8136C12.2482 10.8874 12.1888 10.9558 12.1258 11.0224C12.0898 11.0656 12.0502 11.1106 12.0088 11.1502C11.9692 11.1952 11.9278 11.2348 11.8918 11.2708C11.8288 11.3338 11.7784 11.3806 11.7352 11.422L11.6326 11.5138C11.6182 11.5282 11.5984 11.5354 11.5786 11.5354H10.7902V12.5452H11.782C12.0034 12.5452 12.214 12.4678 12.385 12.322C12.4426 12.2716 12.6964 12.052 12.997 11.7208C13.0078 11.7082 13.0204 11.701 13.0348 11.6974L15.7726 10.9054C15.8248 10.891 15.8752 10.9288 15.8752 10.9828V11.5624Z" fill="#9B9B9B"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.146 4.52803C15.767 3.18049 13.8805 2.35 11.8 2.35C7.57502 2.35 4.15 5.77502 4.15 10C4.15 14.225 7.57502 17.65 11.8 17.65C13.8805 17.65 15.767 16.8195 17.146 15.472C15.501 17.617 12.912 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C12.912 1 15.501 2.38301 17.146 4.52803Z" fill="#9B9B9B"/>
<path d="M6.08317 14.3776C7.18644 15.4556 8.69563 16.12 10.36 16.12C13.74 16.12 16.48 13.38 16.48 10C16.48 6.62002 13.74 3.88 10.36 3.88C8.69563 3.88 7.18644 4.54439 6.08317 5.62243C7.39916 3.90641 9.47037 2.8 11.8 2.8C15.7765 2.8 19 6.02355 19 10C19 13.9764 15.7764 17.2 11.8 17.2C9.47037 17.2 7.39916 16.0936 6.08317 14.3776Z" fill="#9B9B9B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4 10C15.4 12.9823 12.9823 15.4 10 15.4C7.01766 15.4 4.6 12.9823 4.6 10C4.6 7.01766 7.01766 4.6 10 4.6C12.9823 4.6 15.4 7.01766 15.4 10ZM13.6 10C13.6 11.9882 11.9882 13.6 10 13.6C8.01177 13.6 6.4 11.9882 6.4 10C6.4 8.01178 8.01177 6.4 10 6.4C11.9882 6.4 13.6 8.01178 13.6 10Z" fill="#9B9B9B"/>
</svg>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/bin/bash
# Array to hold the list of unreferenced files
unreferenced_files=()
# We look for assets in the /src and /public and then, check if they are referenced in
# the code, typically in either /src, /public (manifest.json) or /functions
while IFS= read -r -d $'\0' file; do
name="$(basename "$file")"
grep -rn -F -q "$name" ./src ./functions ./public
if [ $? -ne 0 ]; then
unreferenced_files+=("$file")
fi
done < <(find ./src ./public -type f \( -name "*.png" -o -name "*.svg" -o -name "*.jpg" \) -print0)
# If we found any unreferenced files, prompt for deletion
if [ ${#unreferenced_files[@]} -ne 0 ]; then
echo "Found the following unreferenced files:"
for file in "${unreferenced_files[@]}"; do
echo "$file"
done
read -p "Do you want to delete these files? (y/N) " confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
echo "Deleting files..."
for file in "${unreferenced_files[@]}"; do
rm "$file"
echo "Deleted: $file"
done
echo "Deletion complete."
exit 0
else
echo "Operation cancelled."
exit 1
fi
fi
# If no unreferenced files are found, exit successfully
exit 0
<svg width="19" height="14" viewBox="0 0 19 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.7501 2.99994V4.24994H0.750122V2.99994C0.750122 0.999939 1.75012 -6.10352e-05 3.75012 -6.10352e-05H15.7501C17.7501 -6.10352e-05 18.7501 0.999939 18.7501 2.99994ZM18.7501 5.74994V10.9999C18.7501 12.9999 17.7501 13.9999 15.7501 13.9999H3.75012C1.75012 13.9999 0.750122 12.9999 0.750122 10.9999V5.74994H18.7501ZM8.50012 9.99994C8.50012 9.58594 8.16412 9.24994 7.75012 9.24994H4.75012C4.33612 9.24994 4.00012 9.58594 4.00012 9.99994C4.00012 10.4139 4.33612 10.7499 4.75012 10.7499H7.75012C8.16412 10.7499 8.50012 10.4139 8.50012 9.99994Z" fill="currentColor"/>
</svg>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 13C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11C18.4477 11 18 11.4477 18 12C18 12.5523 18.4477 13 19 13Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 13C5.55228 13 6 12.5523 6 12C6 11.4477 5.55228 11 5 11C4.44772 11 4 11.4477 4 12C4 12.5523 4.44772 13 5 13Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="119" height="99" viewBox="0 0 119 99" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.097 29.6061C117.891 67.6376 86.8404 98.3016 48.7425 98.0958C12.5975 97.9006 -16.9048 69.9894 -19.659 34.6507L71.5199 35.1431L71.5825 23.5662L-19.5965 23.0738C-16.4608 -12.2332 13.3412 -39.824 49.4863 -39.6288C87.5842 -39.423 118.302 -8.4256 118.097 29.6061Z" fill="white" fill-opacity="0.1"/>
</svg>
This diff is collapsed.
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.5 3H18.5C19.328 3 20 3.672 20 4.5V6.75699C20 7.15499 19.842 7.53599 19.561 7.81799L14.4399 12.939C14.1589 13.22 14.001 13.602 14.001 14V21L10.001 18V14C10.001 13.602 9.84302 13.221 9.56202 12.939L4.44104 7.81799C4.16004 7.53699 4.00196 7.15499 4.00196 6.75699V4.5C3.99996 3.672 4.672 3 5.5 3Z" fill="#9B9B9B"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.3078 0.278393C15.7064 -0.103884 16.3395 -0.0906517 16.7217 0.307949L20.9689 4.73645C21.2428 4.97487 21.4729 5.26217 21.6459 5.58477C21.8481 5.93184 22 6.34831 22 6.8271V17.2013C22 18.7486 20.7455 20.0001 19.2 20.0001C17.6536 20.0001 16.4 18.7465 16.4 17.2001V14.9C16.4 14.3477 15.9523 13.9 15.4 13.9H15V21.0001C15 21.0691 14.9965 21.1374 14.9897 21.2046C14.9515 21.5802 14.8095 21.925 14.5927 22.2099C14.2274 22.6901 13.6499 23.0001 13 23.0001H5C4.30964 23.0001 3.70098 22.6503 3.34157 22.1183C3.12592 21.7991 3.00001 21.4143 3 21.0001V4.00003C3 2.34317 4.34315 1.00003 6 1.00003H12C13.6569 1.00003 15 2.34317 15 4.00003V11.9H15.4C17.0569 11.9 18.4 13.2432 18.4 14.9V17.2001C18.4 17.642 18.7582 18.0001 19.2 18.0001C19.6427 18.0001 20 17.6423 20 17.2013V9.82932C19.6872 9.93987 19.3506 10 19 10C17.3431 10 16 8.65688 16 7.00003C16 5.78863 16.718 4.74494 17.7517 4.27129L15.2783 1.6923C14.896 1.2937 14.9092 0.660671 15.3078 0.278393ZM19.6098 6.20743C19.441 6.07738 19.2296 6.00003 19 6.00003C18.4477 6.00003 18 6.44774 18 7.00003C18 7.55231 18.4477 8.00003 19 8.00003C19.5523 8.00003 20 7.55231 20 7.00003C20 6.84096 19.9629 6.69057 19.8968 6.55705C19.8303 6.45176 19.7349 6.33571 19.6098 6.20743ZM5 11C5 10.4477 5.44772 10 6 10H9H12C12.5523 10 13 10.4477 13 11C13 11.5523 12.5523 12 12 12H6C5.44772 12 5 11.5523 5 11ZM5 15C5 14.4477 5.44772 14 6 14H12C12.5523 14 13 14.4477 13 15C13 15.5523 12.5523 16 12 16H6C5.44772 16 5 15.5523 5 15ZM6 18C5.44772 18 5 18.4477 5 19C5 19.5523 5.44772 20 6 20H12C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18H6Z" fill="currentColor"/>
</svg>
\ No newline at end of file
This diff is collapsed.
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.2601 18.3918C12.9161 18.5558 12.5141 18.6779 12.0591 18.7599L5.48907 19.9099C3.29907 20.2999 2.00896 19.3999 1.62896 17.2099L0.0891657 8.45987C-0.300834 6.26987 0.599117 4.97988 2.78912 4.58988L4.58697 4.27384C4.79197 4.23784 4.97114 4.41686 4.93414 4.62186L3.75811 11.2799C3.22811 14.3099 4.65803 16.3499 7.67803 16.8799C7.67803 16.8799 12.8971 17.7969 13.1661 17.8489C13.4981 17.9088 13.5511 18.2628 13.2601 18.3918ZM19.9131 5.10587L18.3689 13.8598C17.997 15.9678 16.7811 16.8688 14.7361 16.5888C14.6581 16.5778 14.5881 16.5779 14.5071 16.5639L7.94195 15.4059C5.75295 15.0199 4.85209 13.7329 5.23809 11.5439L6.58111 3.92783L6.78204 2.78983C7.16804 0.600828 8.4551 -0.300151 10.6441 0.0858488L17.21 1.24387C19.398 1.62987 20.2991 2.91787 19.9131 5.10587ZM13.554 11.8958C13.626 11.4878 13.3541 11.0988 12.9461 11.0268L8.8421 10.3028C8.4361 10.2298 8.04518 10.5039 7.97418 10.9109C7.90218 11.3189 8.17409 11.7079 8.58209 11.7799L12.6861 12.5039C12.7301 12.5119 12.7739 12.5149 12.8169 12.5149C13.1739 12.5159 13.49 12.2598 13.554 11.8958ZM16.597 9.03482C16.669 8.62682 16.3971 8.23787 15.9891 8.16587L9.42413 7.00785C9.02013 6.93685 8.62696 7.20888 8.55596 7.61588C8.48396 8.02388 8.75612 8.41284 9.16412 8.48484L15.7291 9.64286C15.7731 9.65086 15.8172 9.65384 15.8602 9.65384C16.2172 9.65384 16.533 9.39782 16.597 9.03482ZM17.2972 5.77286C17.3692 5.36486 17.097 4.97584 16.689 4.90384L10.1241 3.74582C9.72008 3.67382 9.32716 3.94685 9.25616 4.35385C9.18416 4.76185 9.45607 5.15087 9.86407 5.22287L16.429 6.38083C16.473 6.38883 16.5171 6.39188 16.5601 6.39188C16.9171 6.39288 17.2332 6.13686 17.2972 5.77286Z" fill="#F5F6FC" />
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13559_129580)">
<rect width="40" height="40" rx="8" fill="url(#paint0_linear_13559_129580)"/>
<path d="M20 40C31.0457 40 40 31.0457 40 20C40 8.9543 31.0457 0 20 0C8.9543 0 0 8.9543 0 20C0 31.0457 8.9543 40 20 40Z" fill="url(#paint1_linear_13559_129580)"/>
<path d="M34.5576 20.2857H30.982C30.982 13.0516 25.0542 7.1875 17.7415 7.1875C10.5192 7.1875 4.64749 12.908 4.5038 20.0182C4.35511 27.3678 11.3254 33.75 18.7559 33.75H19.6906C26.2415 33.75 35.0217 28.6771 36.3936 22.4961C36.647 21.3567 35.737 20.2857 34.5576 20.2857ZM12.4279 20.6079C12.4279 21.5753 11.6281 22.3665 10.6502 22.3665C9.67227 22.3665 8.87249 21.575 8.87249 20.6079V17.7629C8.87249 16.7955 9.67227 16.0043 10.6502 16.0043C11.6281 16.0043 12.4279 16.7955 12.4279 17.7629V20.6079ZM18.6009 20.6079C18.6009 21.5753 17.8011 22.3665 16.8232 22.3665C15.8453 22.3665 15.0455 21.575 15.0455 20.6079V17.7629C15.0455 16.7955 15.8456 16.0043 16.8232 16.0043C17.8011 16.0043 18.6009 16.7955 18.6009 17.7629V20.6079Z" fill="url(#paint2_linear_13559_129580)"/>
</g>
<defs>
<linearGradient id="paint0_linear_13559_129580" x1="20" y1="0" x2="20" y2="40" gradientUnits="userSpaceOnUse">
<stop stop-color="#534BB1"/>
<stop offset="1" stop-color="#551BF9"/>
</linearGradient>
<linearGradient id="paint1_linear_13559_129580" x1="20" y1="0" x2="20" y2="40" gradientUnits="userSpaceOnUse">
<stop stop-color="#534BB1"/>
<stop offset="1" stop-color="#551BF9"/>
</linearGradient>
<linearGradient id="paint2_linear_13559_129580" x1="20.4688" y1="7.1875" x2="20.4688" y2="33.75" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.82"/>
</linearGradient>
<clipPath id="clip0_13559_129580">
<rect width="40" height="40" rx="8" fill="white"/>
</clipPath>
</defs>
</svg>
<svg width="20" height="20" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 18C19 18.5523 18.5523 19 18 19V21C19.6569 21 21 19.6569 21 18H19ZM18 19C17.4477 19 17 18.5523 17 18H15C15 19.6569 16.3431 21 18 21V19ZM17 18C17 17.4477 17.4477 17 18 17V15C16.3431 15 15 16.3431 15 18H17ZM18 17C18.5523 17 19 17.4477 19 18H21C21 16.3431 19.6569 15 18 15V17ZM8 7H16V5H8V7ZM16 11H8V13H16V11ZM8 19H16V17H8V19ZM4 15C4 17.2091 5.79086 19 8 19V17C6.89543 17 6 16.1046 6 15H4ZM8 11C5.79086 11 4 12.7909 4 15H6C6 13.8954 6.89543 13 8 13V11ZM18 9C18 10.1046 17.1046 11 16 11V13C18.2091 13 20 11.2091 20 9H18ZM16 7C17.1046 7 18 7.89543 18 9H20C20 6.79086 18.2091 5 16 5V7ZM7 6C7 6.55228 6.55228 7 6 7V9C7.65685 9 9 7.65685 9 6H7ZM6 7C5.44772 7 5 6.55228 5 6H3C3 7.65685 4.34315 9 6 9V7ZM5 6C5 5.44772 5.44772 5 6 5V3C4.34315 3 3 4.34315 3 6H5ZM6 5C6.55228 5 7 5.44772 7 6H9C9 4.34315 7.65685 3 6 3V5Z"/>
</svg>
This diff is collapsed.
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.3668 12.5808L33.0988 10.7617C33.0988 10.7617 32.1671 9.75101 31.0359 8.60559C29.9046 7.46023 27.509 8.13398 27.509 8.13398L24.7807 5H19.9895H15.1984L12.47 8.13398C12.47 8.13398 10.0744 7.46023 8.94319 8.60559C7.81191 9.75101 6.8803 10.7617 6.8803 10.7617L7.61228 12.5808L6.68066 15.2758C6.68066 15.2758 9.42061 25.7834 9.74169 27.0666C10.3739 29.5932 10.8064 30.5701 12.6031 31.8503C14.3998 33.1304 17.6604 35.3538 18.1929 35.6907C18.7252 36.0276 19.3906 36.6014 19.9895 36.6014C20.5884 36.6014 21.2539 36.0276 21.7862 35.6907C22.3185 35.3538 25.5793 33.1304 27.3759 31.8503C29.1726 30.5701 29.6052 29.5932 30.2374 27.0666C30.5584 25.7834 33.2984 15.2758 33.2984 15.2758L32.3668 12.5808Z" fill="url(#paint0_linear_13571_129901)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.7438 10.1216C26.7438 10.1216 30.253 14.4167 30.253 15.3347C30.253 16.2527 29.8116 16.4951 29.3678 16.9722C28.9238 17.4495 26.9853 19.5337 26.7364 19.8013C26.4875 20.069 25.9693 20.4748 26.2741 21.2052C26.5789 21.9357 27.0286 22.8651 26.5286 23.8078C26.0284 24.7506 25.1717 25.3797 24.6227 25.2757C24.0737 25.1717 22.7843 24.4895 22.3103 24.1778C21.8361 23.8662 20.3334 22.6114 20.3334 22.1313C20.3334 21.6512 21.8868 20.7892 22.1737 20.5934C22.4608 20.3976 23.7697 19.6396 23.7965 19.342C23.8233 19.0444 23.8131 18.9572 23.4268 18.222C23.0405 17.4867 22.3448 16.5056 22.4607 15.8527C22.5764 15.2 23.6983 14.8606 24.499 14.5544C25.2995 14.2483 26.841 13.6702 27.0336 13.5803C27.2262 13.4903 27.1764 13.4047 26.593 13.3487C26.0098 13.2927 24.3544 13.0702 23.6081 13.2809C22.8618 13.4915 21.5868 13.8119 21.4836 13.9819C21.3803 14.1518 21.2892 14.1575 21.3953 14.7437C21.5013 15.3298 22.0473 18.1425 22.1003 18.6421C22.1533 19.1417 22.257 19.472 21.725 19.5952C21.1929 19.7183 20.2973 19.9322 19.9895 19.9322C19.6818 19.9322 18.7861 19.7183 18.254 19.5952C17.7219 19.472 17.8256 19.1417 17.8787 18.6421C17.9317 18.1425 18.4777 15.3298 18.5838 14.7437C18.6898 14.1575 18.5987 14.1518 18.4954 13.9819C18.3922 13.8119 17.1171 13.4915 16.3708 13.2809C15.6245 13.0702 13.9693 13.2927 13.3859 13.3487C12.8026 13.4047 12.7528 13.4903 12.9454 13.5803C13.138 13.6702 14.6795 14.2483 15.48 14.5544C16.2806 14.8606 17.4026 15.2 17.5184 15.8527C17.6342 16.5056 16.9384 17.4867 16.5523 18.222C16.1659 18.9572 16.1557 19.0444 16.1825 19.342C16.2093 19.6396 17.5183 20.3976 17.8053 20.5934C18.0923 20.7892 19.6455 21.6512 19.6455 22.1313C19.6455 22.6114 18.1429 23.8662 17.6688 24.1778C17.1947 24.4895 15.9053 25.1717 15.3563 25.2757C14.8073 25.3797 13.9506 24.7506 13.4505 23.8078C12.9504 22.8651 13.4001 21.9357 13.7048 21.2052C14.0097 20.4748 13.4915 20.069 13.2425 19.8013C12.9937 19.5337 11.0551 17.4495 10.6113 16.9722C10.1673 16.4951 9.72601 16.2527 9.72601 15.3347C9.72601 14.4167 13.2353 10.1216 13.2353 10.1216C13.2353 10.1216 16.1965 10.6942 16.5958 10.6942C16.995 10.6942 17.8601 10.3574 18.6586 10.0879C19.4572 9.81841 19.9895 9.81641 19.9895 9.81641C19.9895 9.81641 20.5218 9.81841 21.3204 10.0879C22.1189 10.3574 22.984 10.6942 23.3833 10.6942C23.7825 10.6942 26.7438 10.1216 26.7438 10.1216ZM24.113 26.5516C24.3302 26.6893 24.1977 26.9489 23.9998 27.0905C23.8019 27.2322 21.1428 29.317 20.8848 29.5475C20.6266 29.778 20.2473 30.1586 19.9895 30.1586C19.7317 30.1586 19.3523 29.778 19.0943 29.5475C18.8362 29.317 16.177 27.2322 15.9792 27.0905C15.7813 26.9489 15.6488 26.6893 15.866 26.5516C16.0833 26.4139 16.7629 26.0665 17.7007 25.5751C18.6384 25.0838 19.8071 24.6661 19.9895 24.6661C20.1719 24.6661 21.3405 25.0838 22.2783 25.5751C23.2161 26.0665 23.8957 26.4139 24.113 26.5516Z" fill="white"/>
<path d="M27.509 8.13398L24.7807 5H19.9895H15.1983L12.47 8.13398C12.47 8.13398 10.0744 7.46023 8.94318 8.60558C8.94318 8.60558 12.1373 8.31367 13.2353 10.1216C13.2353 10.1216 16.1965 10.6942 16.5958 10.6942C16.995 10.6942 17.8601 10.3574 18.6586 10.0879C19.4572 9.81841 19.9895 9.81641 19.9895 9.81641C19.9895 9.81641 20.5218 9.81841 21.3204 10.0879C22.1189 10.3574 22.984 10.6942 23.3833 10.6942C23.7825 10.6942 26.7438 10.1216 26.7438 10.1216C27.8418 8.31367 31.0358 8.60558 31.0358 8.60558C29.9046 7.46023 27.509 8.13398 27.509 8.13398Z" fill="url(#paint1_linear_13571_129901)"/>
<defs>
<linearGradient id="paint0_linear_13571_129901" x1="6.68066" y1="1607.37" x2="2668.45" y2="1607.37" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF5500"/>
<stop offset="0.409877" stop-color="#FF5500"/>
<stop offset="0.581981" stop-color="#FF2000"/>
<stop offset="1" stop-color="#FF2000"/>
</linearGradient>
<linearGradient id="paint1_linear_13571_129901" x1="56.4079" y1="293.731" x2="2218.22" y2="293.731" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF452A"/>
<stop offset="1" stop-color="#FF2000"/>
</linearGradient>
</defs>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" fill="#4C82FB" fill-opacity="0.24"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.454 11.6305C14.6973 12.4936 11.8284 15.6822 11.4439 19.5998H15.6425C15.8586 16.7339 16.8329 13.9847 18.454 11.6305ZM22.546 11.6305C24.1671 13.9847 25.1413 16.7339 25.3574 19.5998H29.5561C29.1715 15.6822 26.3027 12.4936 22.546 11.6305ZM23.5517 19.5998C23.3127 16.7852 22.2508 14.103 20.5 11.8882C18.7491 14.103 17.6872 16.7852 17.4483 19.5998H23.5517ZM17.4483 21.3998H23.5516C23.3126 24.2143 22.2507 26.8963 20.5 29.111C18.7492 26.8963 17.6873 24.2143 17.4483 21.3998ZM15.6426 21.3998H11.4439C11.8286 25.3172 14.6974 28.5056 18.454 29.3687C16.833 27.0146 15.8587 24.2656 15.6426 21.3998ZM22.546 29.3687C24.167 27.0146 25.1412 24.2656 25.3574 21.3998H29.556C29.1713 25.3172 26.3026 28.5056 22.546 29.3687ZM20.5 31.3996C14.4801 31.3996 9.59998 26.5195 9.59998 20.4996C9.59998 14.4797 14.4801 9.59961 20.5 9.59961C26.5199 9.59961 31.4 14.4797 31.4 20.4996V20.4998C31.3999 26.5196 26.5198 31.3996 20.5 31.3996Z" fill="#4C82FB"/>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" fill="black"/>
<g clip-path="url(#clip0_13605_128411)">
<path d="M8 24.9635V30.8974H17.0274V29.5815H9.31532V24.9635H8ZM30.6847 24.9635V29.5815H22.9726V30.8971H32V24.9635H30.6847ZM17.0405 15.9334V24.9632H22.9726V23.7765H18.3559V15.9334H17.0405ZM8 9.99951V15.9334H9.31532V11.3152H17.0274V9.99951H8ZM22.9726 9.99951V11.3152H30.6847V15.9334H32V9.99951H22.9726Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_13605_128411">
<rect width="24" height="20.8979" fill="white" transform="translate(8 10)"/>
</clipPath>
</defs>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" fill="white"/>
<path d="M35.1513 21.043C36.2547 18.5678 30.7674 11.6192 25.5187 8.75633C22.2085 6.51967 18.7789 6.81789 18.0632 7.80202C16.5423 9.94921 23.133 11.7982 27.5466 13.9155C26.5923 14.3331 25.6977 15.0786 25.1907 16.0031C23.5505 14.2138 19.942 12.663 15.7073 13.9155C12.8443 14.7506 10.4884 16.7486 9.5639 19.7309C9.35514 19.6414 9.08674 19.5817 8.84817 19.5817C7.86404 19.5817 7.05884 20.3869 7.05884 21.3711C7.05884 22.3552 7.86404 23.1604 8.84817 23.1604C9.0271 23.1604 9.59372 23.0411 9.59372 23.0411L18.7789 23.1008C15.1108 28.9459 12.1882 29.7809 12.1882 30.7949C12.1882 31.8088 14.9617 31.5404 16.0055 31.1527C21.0156 29.3634 26.3836 23.727 27.3081 22.1166C31.1849 22.6236 34.4356 22.6534 35.1513 21.043Z" fill="url(#paint0_linear_13571_129896)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.5765 13.915C27.7853 13.8256 27.7554 13.5273 27.6958 13.2888C27.5765 12.752 25.3398 10.5451 23.2523 9.56101C20.3894 8.21901 18.3018 8.27865 18.0036 8.90492C18.5702 10.0978 21.284 11.2012 24.0873 12.3941C25.2504 12.8713 26.4731 13.3782 27.5765 13.915Z" fill="url(#paint1_linear_13571_129896)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.9382 25.9043C23.3715 25.6955 22.7155 25.4868 21.9699 25.3078C22.7453 23.9062 22.9242 21.7888 22.1787 20.4767C21.1349 18.6277 19.8227 17.6436 16.751 17.6436C15.081 17.6436 10.548 18.2102 10.4586 21.9976C10.4586 22.3853 10.4586 22.7431 10.4884 23.101H18.7789C17.6755 24.8605 16.6317 26.1727 15.7073 27.1568C16.8107 27.4252 17.7053 27.6638 18.5404 27.9024C19.3157 28.1111 20.0613 28.29 20.8068 28.4988C21.9401 27.6638 23.0137 26.7691 23.9382 25.9043Z" fill="url(#paint2_linear_13571_129896)"/>
<path d="M9.44462 22.6539C9.77266 25.5168 11.4129 26.6501 14.753 26.9781C18.093 27.3062 20.0017 27.0974 22.5365 27.3062C24.6539 27.4851 26.5625 28.5885 27.2484 28.2008C27.8747 27.8728 27.5168 26.6501 26.6818 25.8747C25.5784 24.8607 24.0575 24.1748 21.4033 23.9064C21.9401 22.4452 21.791 20.3874 20.956 19.284C19.7631 17.6736 17.5562 16.9579 14.753 17.2561C11.8304 17.614 9.02711 19.0753 9.44462 22.6539Z" fill="url(#paint3_linear_13571_129896)"/>
<defs>
<linearGradient id="paint0_linear_13571_129896" x1="15.4039" y1="18.7336" x2="34.9512" y2="24.2768" gradientUnits="userSpaceOnUse">
<stop stop-color="#8797FF"/>
<stop offset="1" stop-color="#AAA8FF"/>
</linearGradient>
<linearGradient id="paint1_linear_13571_129896" x1="30.9339" y1="19.1707" x2="16.8297" y2="5.03193" gradientUnits="userSpaceOnUse">
<stop stop-color="#3B22A0"/>
<stop offset="1" stop-color="#5156D8" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_13571_129896" x1="24.3273" y1="26.4153" x2="10.7786" y2="18.6257" gradientUnits="userSpaceOnUse">
<stop stop-color="#3B1E8F"/>
<stop offset="1" stop-color="#6A6FFB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear_13571_129896" x1="14.4596" y1="20.3417" x2="23.6175" y2="31.9778" gradientUnits="userSpaceOnUse">
<stop stop-color="#8898FF"/>
<stop offset="0.9839" stop-color="#5F47F1"/>
</linearGradient>
</defs>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13571_129905)">
<rect width="40" height="40" fill="#3375BB"/>
<mask id="mask0_13571_129905" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="-1" width="40" height="41">
<path d="M39.6306 -0.000488281H0.246033V39.3841H39.6306V-0.000488281Z" fill="white"/>
</mask>
<g mask="url(#mask0_13571_129905)">
<path d="M19.9383 39.3841C30.8141 39.3841 39.6306 30.5676 39.6306 19.6918C39.6306 8.81607 30.8141 -0.000488281 19.9383 -0.000488281C9.06258 -0.000488281 0.246033 8.81607 0.246033 19.6918C0.246033 30.5676 9.06258 39.3841 19.9383 39.3841Z" fill="#3375BB"/>
<path d="M20.0783 8.61572C23.97 11.8659 28.4329 11.6655 29.708 11.6655C29.4291 30.1503 27.3039 26.4848 20.0783 31.668C12.8527 26.4848 10.7408 30.1503 10.4619 11.6655C11.7237 11.6655 16.1865 11.8659 20.0783 8.61572Z" stroke="white" stroke-width="2.46154" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
<defs>
<clipPath id="clip0_13571_129905">
<rect width="40" height="40" fill="white"/>
</clipPath>
</defs>
</svg>
import { ButtonEmphasis, ButtonSize, LoadingButtonSpinner, ThemeButton } from 'components/Button/buttons'
import Column from 'components/deprecated/Column'
import Row from 'components/deprecated/Row'
import Tooltip from 'components/Tooltip'
import { MouseoverTooltip } from 'components/Tooltip'
import styled from 'lib/styled-components'
import { ReactNode, useReducer } from 'react'
import { Info } from 'react-feather'
......@@ -107,7 +107,7 @@ export function ActionTile({
{error && (
<ErrorContainer>
<ErrorText>{errorMessage}</ErrorText>
<Tooltip show={showTooltip} text={errorTooltip}>
<MouseoverTooltip forceShow={showTooltip} text={errorTooltip} disabled>
<ErrorLink
onMouseEnter={toggleTooltip}
onMouseLeave={toggleTooltip}
......@@ -116,7 +116,7 @@ export function ActionTile({
>
<StyledInfoIcon />
</ErrorLink>
</Tooltip>
</MouseoverTooltip>
</ErrorContainer>
)}
</Container>
......
......@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
import { analytics, getAnalyticsAtomDirect } from 'utilities/src/telemetry/analytics/analytics'
export function AnalyticsToggle() {
const [allowAnalytics, setAllowAnalytics] = useState(true)
const [allowAnalytics, setAllowAnalytics] = useState<boolean | null>(null)
const { t } = useTranslation()
useEffect(() => {
......@@ -21,6 +21,10 @@ export function AnalyticsToggle() {
setAllowAnalytics(!allowAnalytics)
}, [allowAnalytics])
if (allowAnalytics === null) {
return null
}
return (
<SettingsToggle
title={t('analytics.allow')}
......
import Tooltip from 'components/Tooltip'
import { MouseoverTooltip } from 'components/Tooltip'
import useCopyClipboard from 'hooks/useCopyClipboard'
import styled from 'lib/styled-components'
import { Trans } from 'react-i18next'
......@@ -17,12 +17,12 @@ export function GitVersionRow() {
staticCopy(process.env.REACT_APP_GIT_COMMIT_HASH as string)
}}
>
<Tooltip text="Copied" show={isCopied}>
<MouseoverTooltip text="Copied" forceShow={isCopied} disabled>
<ThemedText.BodySmall color="neutral3">
<Trans i18nKey="account.drawer.gitVersion" />
{' ' + process.env.REACT_APP_GIT_COMMIT_HASH.substring(0, 6)}
</ThemedText.BodySmall>
</Tooltip>
</MouseoverTooltip>
</Container>
) : null
}
......@@ -438,6 +438,9 @@ exports[`CancelOrdersDialog should render limit order text 1`] = `
data-aria-hidden="true"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: absolute; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.6); z-index: 1040; pointer-events: auto; opacity: 0;"
/>
<div
class="_display-flex _alignItems-stretch _flexBasis-auto _boxSizing-border-box _position-relative _minHeight-0px _minWidth-0px _flexShrink-0 _flexDirection-column _flexGrow-1 _maxHeight-calc100vh-32087695319 _borderTopLeftRadius-t-radius-ro291586449 _borderTopRightRadius-t-radius-ro291586449 _borderBottomRightRadius-t-radius-ro291586449 _borderBottomLeftRadius-t-radius-ro291586449 _justifyContent-center"
>
<div
class="_dsp_contents"
data-remove-scroll-container="true"
......@@ -450,7 +453,7 @@ exports[`CancelOrdersDialog should render limit order text 1`] = `
aria-labelledby="title-:r5:"
class="css-view-175oi2r"
id="content-:r5:"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: hidden; overflow-y: hidden; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: scroll; overflow-y: scroll; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
tabindex="-1"
>
<div
......@@ -618,6 +621,7 @@ exports[`CancelOrdersDialog should render limit order text 1`] = `
</div>
</div>
</div>
</div>
</span>
</span>
</div>
......@@ -1062,6 +1066,9 @@ exports[`CancelOrdersDialog should render order cancel correctly 1`] = `
data-aria-hidden="true"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: absolute; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.6); z-index: 1040; pointer-events: auto; opacity: 0;"
/>
<div
class="_display-flex _alignItems-stretch _flexBasis-auto _boxSizing-border-box _position-relative _minHeight-0px _minWidth-0px _flexShrink-0 _flexDirection-column _flexGrow-1 _maxHeight-calc100vh-32087695319 _borderTopLeftRadius-t-radius-ro291586449 _borderTopRightRadius-t-radius-ro291586449 _borderBottomRightRadius-t-radius-ro291586449 _borderBottomLeftRadius-t-radius-ro291586449 _justifyContent-center"
>
<div
class="_dsp_contents"
data-remove-scroll-container="true"
......@@ -1074,7 +1081,7 @@ exports[`CancelOrdersDialog should render order cancel correctly 1`] = `
aria-labelledby="title-:r1:"
class="css-view-175oi2r"
id="content-:r1:"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: hidden; overflow-y: hidden; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: scroll; overflow-y: scroll; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
tabindex="-1"
>
<div
......@@ -1242,6 +1249,7 @@ exports[`CancelOrdersDialog should render order cancel correctly 1`] = `
</div>
</div>
</div>
</div>
</span>
</span>
</div>
......
......@@ -774,7 +774,7 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = `
<svg
fill="none"
stroke-width="8"
style="color: rgb(252, 114, 255); width: 40px; height: 40px; min-width: 40px; background-color: rgb(254, 244, 255); border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; padding: 7px 7px 7px 7px;"
style="width: 40px; height: 40px; min-width: 40px; background-color: rgb(254, 244, 255); border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; padding: 7px 7px 7px 7px; color: rgb(252, 114, 255);"
viewBox="0 0 22 22"
>
<path
......
......@@ -18,10 +18,10 @@ import { useGesture } from 'react-use-gesture'
import { BREAKPOINTS } from 'theme'
import { ClickableStyle } from 'theme/components'
import { Z_INDEX } from 'theme/zIndex'
import { INTERFACE_NAV_HEIGHT } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { INTERFACE_NAV_HEIGHT } from 'uniswap/src/theme/heights'
import { isMobileWeb } from 'utilities/src/platform'
const DRAWER_WIDTH_XL = '390px'
......
import { InterfacePageName } from '@uniswap/analytics-events'
import { OutageBanner, getOutageBannerSessionStorageKey } from 'components/Banner/Outage/OutageBanner'
import { manualChainOutageAtom, useOutageBanners } from 'featureFlags/flags/outageBanner'
import { manualChainOutageAtom } from 'featureFlags/flags/outageBanner'
import { useAtomValue } from 'jotai/utils'
import { useMemo } from 'react'
import { useLocation } from 'react-router-dom'
......@@ -12,7 +12,6 @@ export function Banners() {
const { pathname } = useLocation()
const currentPage = getCurrentPageFromLocation(pathname)
const outageBanners = useOutageBanners()
const manualOutage = useAtomValue(manualChainOutageAtom)
// Calculate the chainId for the current page's contextual chain (e.g. /tokens/ethereum or /tokens/arbitrum), if it exists.
......@@ -26,7 +25,7 @@ export function Banners() {
return (
currentPage &&
pageChainId &&
(outageBanners[pageChainId] || currentPageHasManualOutage) &&
currentPageHasManualOutage &&
!sessionStorage.getItem(getOutageBannerSessionStorageKey(pageChainId)) &&
[
InterfacePageName.EXPLORE_PAGE,
......@@ -35,7 +34,7 @@ export function Banners() {
InterfacePageName.TOKENS_PAGE,
].includes(currentPage)
)
}, [currentPage, currentPageHasManualOutage, outageBanners, pageChainId])
}, [currentPage, currentPageHasManualOutage, pageChainId])
// Outage Banners should take precedence over other promotional banners
if (pageChainId && showOutageBanner) {
......
......@@ -143,10 +143,12 @@ exports[`BreadcrumbNav renders hover components correctly 1`] = `
<div
class="c5"
>
<div>
0x2260...C599
</div>
</div>
</div>
</div>
</span>
</span>
</span>
......
import { Currency } from '@uniswap/sdk-core'
import Tooltip, { TooltipSize } from 'components/Tooltip'
import { MouseoverTooltip, TooltipSize } from 'components/Tooltip'
import Row from 'components/deprecated/Row'
import { useScreenSize } from 'hooks/screenSize/useScreenSize'
import useCopyClipboard from 'hooks/useCopyClipboard'
......@@ -99,9 +99,15 @@ export const CurrentPageBreadcrumb = ({
isDisabled={!shouldEnableCopy}
onClick={shouldEnableCopy ? copy : undefined}
>
<Tooltip placement="bottom" size={TooltipSize.Max} show={isCopied} text={t('common.copied')}>
<MouseoverTooltip
placement="bottom"
size={TooltipSize.Max}
forceShow={isCopied}
text={t('common.copied')}
disabled
>
{shortenAddress(address)}
</Tooltip>
</MouseoverTooltip>
{shouldShowActions && (
<CopyIcon data-testid="breadcrumb-hover-copy" width={16} height={16} color={neutral2} />
)}
......
type Mutable<T extends object> = {
-readonly [K in keyof T]: T[K]
}
import { Mutable } from 'types/mutable'
export function cloneReadonly<T extends object>(obj: T): Mutable<T> {
return JSON.parse(JSON.stringify(obj))
......
......@@ -474,7 +474,14 @@ export function LiquidityPositionRangeChart({
overflow="hidden"
>
{priceData.loading && <LiquidityPositionRangeChartLoader size={chartWidth} />}
{dataUnavailable && <LoadingPriceCurve size={chartWidth} color="$neutral2" />}
{dataUnavailable && (
<LoadingPriceCurve
size={{ width: chartWidth ?? CHART_WIDTH, height: CHART_HEIGHT }}
color="$neutral2"
mt="$spacing8"
ml="$spacing4"
/>
)}
{shouldRenderChart && (
<Flex width={grow ? chartWidth : width} $md={{ width: grow ? chartWidth : '100%' }}>
<Chart Model={LPPriceChartModel} params={chartParams} height={CHART_HEIGHT} />
......
......@@ -11,6 +11,7 @@ import { PriceChartType } from 'components/Charts/utils'
import { DropdownSelector } from 'components/DropdownSelector'
import { useDensityChartData } from 'components/LiquidityChartRangeInput/hooks'
import { DataQuality } from 'components/Tokens/TokenDetails/ChartSection/util'
import { ZERO_ADDRESS } from 'constants/misc'
import { usePoolPriceChartData } from 'hooks/usePoolPriceChartData'
import {
getCurrencyAddressWithWrap,
......@@ -44,6 +45,7 @@ export function LiquidityRangeInput({
tickSpacing,
protocolVersion,
poolId,
hook,
minPrice,
maxPrice,
setMinPrice,
......@@ -56,6 +58,7 @@ export function LiquidityRangeInput({
tickSpacing?: number
protocolVersion: ProtocolVersion
poolId: string
hook?: string
minPrice?: number
maxPrice?: number
disableBrushInteraction?: boolean
......@@ -173,6 +176,7 @@ export function LiquidityRangeInput({
version: protocolVersion,
feeAmount: Number(feeTier),
tickSpacing,
hooks: hook ?? ZERO_ADDRESS,
})
const sortedFormattedData = useMemo(() => {
......
......@@ -2,7 +2,6 @@ import { InterfaceModalName } from '@uniswap/analytics-events'
import { AutoColumn } from 'components/deprecated/Column'
import styled from 'lib/styled-components'
import { PropsWithChildren } from 'react'
import { HeightAnimator } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
......@@ -23,20 +22,7 @@ export function SwapModal({
return (
<Trace modal={InterfaceModalName.CONFIRM_SWAP}>
<Modal name={ModalName.SwapReview} isModalOpen onClose={onDismiss} maxHeight="90vh" padding={0}>
<HeightAnimator
open={true}
width="100%"
minWidth="min-content"
overflow="hidden"
borderRadius="$rounded20"
backgroundColor="$surface1"
$md={{
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
}}
>
<Content>{children}</Content>
</HeightAnimator>
</Modal>
</Trace>
)
......
......@@ -10,7 +10,7 @@ import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { DoubleCurrencyLogo } from 'components/Logo/DoubleLogo'
import { StyledNumericalInput } from 'components/NumericalInput'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import Tooltip from 'components/Tooltip'
import { MouseoverTooltip } from 'components/Tooltip'
import { AutoColumn } from 'components/deprecated/Column'
import { RowBetween, RowFixed } from 'components/deprecated/Row'
import { PrefetchBalancesWrapper } from 'graphql/data/apollo/AdaptiveTokenBalancesProvider'
......@@ -329,8 +329,9 @@ const SwapCurrencyInputPanel = forwardRef<HTMLInputElement, SwapCurrencyInputPan
</div>
)}
<PrefetchBalancesWrapper>
<Tooltip
show={tooltipVisible && !modalOpen}
<MouseoverTooltip
disabled
forceShow={tooltipVisible && !modalOpen}
placement="bottom"
offsetY={14}
text={numericalInputSettings?.disabledTooltipBody}
......@@ -387,7 +388,7 @@ const SwapCurrencyInputPanel = forwardRef<HTMLInputElement, SwapCurrencyInputPan
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
</Aligner>
</CurrencySelect>
</Tooltip>
</MouseoverTooltip>
</PrefetchBalancesWrapper>
</InputRow>
{Boolean(!hideInput && !hideBalance) && (
......
......@@ -413,6 +413,9 @@ exports[`<Dialog /> renders different button types 1`] = `
data-aria-hidden="true"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: absolute; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.6); z-index: 1040; pointer-events: auto; opacity: 0;"
/>
<div
class="_display-flex _alignItems-stretch _flexBasis-auto _boxSizing-border-box _position-relative _minHeight-0px _minWidth-0px _flexShrink-0 _flexDirection-column _flexGrow-1 _maxHeight-calc100vh-32087695319 _borderTopLeftRadius-t-radius-ro291586449 _borderTopRightRadius-t-radius-ro291586449 _borderBottomRightRadius-t-radius-ro291586449 _borderBottomLeftRadius-t-radius-ro291586449 _justifyContent-center"
>
<div
class="_dsp_contents"
data-remove-scroll-container="true"
......@@ -425,7 +428,7 @@ exports[`<Dialog /> renders different button types 1`] = `
aria-labelledby="title-:rl:"
class="css-view-175oi2r"
id="content-:rl:"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: hidden; overflow-y: hidden; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: scroll; overflow-y: scroll; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
tabindex="-1"
>
<div
......@@ -555,6 +558,7 @@ exports[`<Dialog /> renders different button types 1`] = `
</div>
</div>
</div>
</div>
</span>
</span>
</div>
......@@ -916,6 +920,9 @@ exports[`<Dialog /> renders the Dialog component correctly 1`] = `
data-aria-hidden="true"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: absolute; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.6); z-index: 1040; pointer-events: auto; opacity: 0;"
/>
<div
class="_display-flex _alignItems-stretch _flexBasis-auto _boxSizing-border-box _position-relative _minHeight-0px _minWidth-0px _flexShrink-0 _flexDirection-column _flexGrow-1 _maxHeight-calc100vh-32087695319 _borderTopLeftRadius-t-radius-ro291586449 _borderTopRightRadius-t-radius-ro291586449 _borderBottomRightRadius-t-radius-ro291586449 _borderBottomLeftRadius-t-radius-ro291586449 _justifyContent-center"
>
<div
class="_dsp_contents"
data-remove-scroll-container="true"
......@@ -928,7 +935,7 @@ exports[`<Dialog /> renders the Dialog component correctly 1`] = `
aria-labelledby="title-:r1:"
class="css-view-175oi2r"
id="content-:r1:"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: hidden; overflow-y: hidden; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
style="display: flex; align-items: stretch; flex-basis: auto; box-sizing: border-box; position: relative; min-height: 0px; min-width: 0px; flex-shrink: 0; flex-direction: column; background-color: rgb(255, 255, 255); padding: 0px 0px 0px 0px; border-top-left-radius: 16px; border-top-right-radius: 16px; border-bottom-right-radius: 16px; border-bottom-left-radius: 16px; z-index: 1060; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: rgba(34,34,34,0.05); border-right-color: rgba(34,34,34,0.05); border-bottom-color: rgba(34,34,34,0.05); border-left-color: rgba(34,34,34,0.05); border-bottom-style: solid; border-top-style: solid; border-left-style: solid; border-right-style: solid; opacity: 0; gap: 4px; margin: 0px 0px 0px 0px; max-height: calc(100vh - 32px); max-width: 420px; overflow-x: scroll; overflow-y: scroll; width: calc(100vw - 32px); outline-style: none; box-shadow: 0px 3px 6px rgba(0,0,0,0.14901960784313725); transform: translateY(20px) translateX(0px);"
tabindex="-1"
>
<div
......@@ -1057,6 +1064,7 @@ exports[`<Dialog /> renders the Dialog component correctly 1`] = `
</div>
</div>
</div>
</div>
</span>
</span>
</div>
......
......@@ -14,9 +14,8 @@ import {
useShadowPropsMedium,
} from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons/RotatableChevron'
import { zIndices } from 'ui/src/theme'
import { INTERFACE_NAV_HEIGHT, zIndices } from 'ui/src/theme'
import { iconSizes } from 'ui/src/theme/iconSizes'
import { INTERFACE_NAV_HEIGHT } from 'uniswap/src/theme/heights'
export const InternalMenuItem = styled(Text, {
display: 'flex',
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment