ci(release): publish latest release

parent 9be8c36b
diff --git a/dist/esm/connectors/injected.js b/dist/esm/connectors/injected.js
index 26f420d68ed9a12deea30a3dca195e2bcf3b3c44..70fc93a7db7b9f4db10e71edd73ee81bd0e28f1e 100644
--- a/dist/esm/connectors/injected.js
+++ b/dist/esm/connectors/injected.js
@@ -405,6 +405,18 @@ export function injected(parameters = {}) {
onChainChanged(chain) {
console.log('[injected] onChainChanged', chain);
const chainId = Number(chain);
+ if (this.id === 'io.metamask')
+ this.getProvider()
+ .then((provider) =>
+ provider
+ ?.request({
+ method: 'wallet_switchEthereumChain',
+ params: [{ chainId: numberToHex(chainId) }],
+ })
+ .then(() => {})
+ .catch(() => {}),
+ )
+ .catch(() => {})
config.emitter.emit('change', { chainId });
},
async onConnect(connectInfo) {
diff --git a/dist/index.js b/dist/index.js
index f38d06e2de52b5035560d63d6889dc54d5ca021b..4f8fa194b60c73a901dbce4a742f598b1c95c026 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -62,7 +62,7 @@ class CoinbaseWallet extends types_1.Connector {
return __awaiter(this, void 0, void 0, function* () {
if (this.eagerConnection)
return;
- yield (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@coinbase/wallet-sdk'))).then((m) => {
+ yield (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@coinbase/wallet-sdk'))).then((m) => {
const _a = this.options, { url } = _a, options = __rest(_a, ["url"]);
this.coinbaseWallet = new m.default(options);
this.provider = this.coinbaseWallet.makeWeb3Provider(url);
diff --git a/dist/index.js b/dist/index.js
index 015a33c37fe87f13f31559d462351acd7ae9bac7..4cd7cdeb4437f30c1c063c0ffb8fd5692a399dbf 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -68,8 +68,8 @@ class GnosisSafe extends types_1.Connector {
if (this.eagerConnection)
return;
// kick off import early to minimize waterfalls
- const SafeAppProviderPromise = Promise.resolve().then(() => __importStar(require('@safe-global/safe-apps-provider'))).then(({ SafeAppProvider }) => SafeAppProvider);
- yield (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@safe-global/safe-apps-sdk'))).then((m) => __awaiter(this, void 0, void 0, function* () {
+ const SafeAppProviderPromise = Promise.resolve().then(async () => __importStar(await import('@safe-global/safe-apps-provider'))).then(({ SafeAppProvider }) => SafeAppProvider);
+ yield (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@safe-global/safe-apps-sdk'))).then((m) => __awaiter(this, void 0, void 0, function* () {
this.sdk = new m.default(this.options);
const safe = yield Promise.race([
this.sdk.safe.getInfo(),
diff --git a/dist/index.js b/dist/index.js
index c8476dd9b01c0599dfc545f4c86432081bd0fcec..c0bfce759654232df771724e59a650c92b392f78 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -54,7 +54,7 @@ class MetaMask extends types_1.Connector {
return __awaiter(this, void 0, void 0, function* () {
if (this.eagerConnection)
return;
- return (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@metamask/detect-provider'))).then((m) => __awaiter(this, void 0, void 0, function* () {
+ return (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@metamask/detect-provider'))).then((m) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const provider = yield m.default(this.options);
if (provider) {
diff --git a/dist/index.js b/dist/index.js
index 1a36d14c5d7c9ee55b2eccd11216c8adb6839daf..908b8c57a2d8cd565030e34e15c56caf7d182cfd 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -84,7 +84,7 @@ class WalletConnect extends types_1.Connector {
return __awaiter(this, void 0, void 0, function* () {
const rpcMap = this.rpcMap ? (0, utils_1.getBestUrlMap)(this.rpcMap, this.timeout) : undefined;
const chainProps = this.getChainProps(this.chains, this.optionalChains, desiredChainId);
- const ethProviderModule = yield Promise.resolve().then(() => __importStar(require('@walletconnect/ethereum-provider')));
+ const ethProviderModule = yield Promise.resolve().then(async () => __importStar(await import('@walletconnect/ethereum-provider')));
this.provider = yield ethProviderModule.default.init(Object.assign(Object.assign(Object.assign({}, this.options), chainProps), { rpcMap: yield rpcMap }));
return this.provider
.on('disconnect', this.disconnectListener)
diff --git a/dist/utils.js b/dist/utils.js
index 17539b6f910e65aeaebcc116395dce56ae4ce193..9ea637118844ebbdc55db71009b44849992603d2 100644
--- a/dist/utils.js
+++ b/dist/utils.js
@@ -62,8 +62,8 @@ function getBestUrl(urls, timeout) {
if (urls.length === 1)
return urls[0];
const [HttpConnection, JsonRpcProvider] = yield Promise.all([
- Promise.resolve().then(() => __importStar(require('@walletconnect/jsonrpc-http-connection'))).then(({ HttpConnection }) => HttpConnection),
- Promise.resolve().then(() => __importStar(require('@walletconnect/jsonrpc-provider'))).then(({ JsonRpcProvider }) => JsonRpcProvider),
+ Promise.resolve().then(async () => __importStar(await import('@walletconnect/jsonrpc-http-connection'))).then(({ HttpConnection }) => HttpConnection),
+ Promise.resolve().then(async () => __importStar(await import('@walletconnect/jsonrpc-provider'))).then(({ JsonRpcProvider }) => JsonRpcProvider),
]);
// the below returns the first url for which there's been a successful call, prioritized by index
return new Promise((resolve) => {
diff --git a/lib/browser/eip1193.js b/lib/browser/eip1193.js
index ce028c25a164d8af8d513bc0eae4cf104234f6e8..81ba2f29d379f18c04c57b5a560cc6c4f4b57e0d 100644
--- a/lib/browser/eip1193.js
+++ b/lib/browser/eip1193.js
@@ -70,6 +70,7 @@ class Eip1193 extends eip1193_bridge_1.Eip1193Bridge {
yield _super.send.call(this, method, params);
// Providers will not "rewind" to an older block number nor notice chain changes, so they must be reset.
this.utils.providers.forEach((provider) => provider.reset());
+ this.emit('chainChanged', params[0].chainId);
break;
default:
result = yield _super.send.call(this, method, params);
* @uniswap/web-admins
We are back with some new updates! Here’s the latest: IPFS hash of the deployment:
- CIDv0: `QmQgATjf3hM9GXkqFXMBe9mH8iJad7JWMaJNzp53pDQV2a`
Token Warnings: See more information about the tokens you’re attempting to swap, enriched with data from Blockaid. - CIDv1: `bafybeibcw4gnryq4dypbj63jzx2md33b63cvh6eezail7ijyv4twkf72qm`
Transaction Failure improvements: See more granular warnings when your slippage is too low, and increase it straight from the swap review screen. The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
Homescreen improvements: We added labels to the pink Buy/Sell/Receive buttons, and moved the QR code scanner to a button on the top left of the screen. 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.
Other changes: **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.
- Various bug fixes and performance improvements
IPFS gateways:
- https://bafybeibcw4gnryq4dypbj63jzx2md33b63cvh6eezail7ijyv4twkf72qm.ipfs.dweb.link/
- https://bafybeibcw4gnryq4dypbj63jzx2md33b63cvh6eezail7ijyv4twkf72qm.ipfs.cf-ipfs.com/
- [ipfs://QmQgATjf3hM9GXkqFXMBe9mH8iJad7JWMaJNzp53pDQV2a/](ipfs://QmQgATjf3hM9GXkqFXMBe9mH8iJad7JWMaJNzp53pDQV2a/)
### 5.63.2 (2024-12-19)
### Bug Fixes
* **web:** switch to new marketing events - prod (#14706) ad67ff4
mobile/1.42 web/5.63.2
\ No newline at end of file \ No newline at end of file
ignores: [ ignores: [
# Dependencies that depcheck thinks are unused but are actually used # Dependencies that depcheck thinks are unused but are actually used
"react-native-web", 'react-native-web',
"jest-environment-jsdom", 'jest-environment-jsdom',
"webpack-cli", 'webpack-cli',
# Dependencies that depcheck thinks are missing but are actually present or never used # Dependencies that depcheck thinks are missing but are actually present or never used
## Internal packages / workspaces ## Internal packages / workspaces
"src", 'src',
"tsconfig", 'tsconfig',
# Webpack plugins # Webpack plugins
"@svgr/webpack", '@svgr/webpack',
"tamagui-loader", 'tamagui-loader',
"esbuild-loader", 'esbuild-loader',
"swc-loader", 'style-loader',
'css-loader',
'swc-loader',
## Testing ## Testing
"@testing-library/dom", '@testing-library/dom',
] ]
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
"clean-webpack-plugin": "4.0.0", "clean-webpack-plugin": "4.0.0",
"concurrently": "8.2.2", "concurrently": "8.2.2",
"copy-webpack-plugin": "11.0.0", "copy-webpack-plugin": "11.0.0",
"css-loader": "6.11.0",
"esbuild-loader": "3.2.0", "esbuild-loader": "3.2.0",
"eslint": "8.44.0", "eslint": "8.44.0",
"jest": "29.7.0", "jest": "29.7.0",
...@@ -77,6 +78,7 @@ ...@@ -77,6 +78,7 @@
"react-refresh": "0.14.0", "react-refresh": "0.14.0",
"serve": "14.2.4", "serve": "14.2.4",
"statsig-js": "4.41.0", "statsig-js": "4.41.0",
"style-loader": "3.3.2",
"swc-loader": "0.2.6", "swc-loader": "0.2.6",
"tamagui-loader": "1.114.4", "tamagui-loader": "1.114.4",
"typescript": "5.3.3", "typescript": "5.3.3",
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Uniswap Extension", "name": "Uniswap Extension",
"description": "The Uniswap Extension is a self-custody crypto wallet that's built for swapping.", "description": "The Uniswap Extension is a self-custody crypto wallet that's built for swapping.",
"version": "1.12.0", "version": "1.13.0",
"minimum_chrome_version": "116", "minimum_chrome_version": "116",
"icons": { "icons": {
"16": "assets/icon16.png", "16": "assets/icon16.png",
......
...@@ -31,6 +31,19 @@ const normalizedStories = [ ...@@ -31,6 +31,19 @@ const normalizedStories = [
/^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/ /^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/
), ),
}, },
{
titlePrefix: "",
directory: "../../packages/ui/src",
files: "**/*.mdx",
importPathMatcher:
/^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.mdx)$/,
// @ts-ignore
req: require.context(
"../../../packages/ui/src",
true,
/^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.mdx)$/
),
},
]; ];
declare global { declare global {
......
...@@ -89,9 +89,9 @@ if (isCI && datadogPropertiesAvailable && !isDetox) { ...@@ -89,9 +89,9 @@ if (isCI && datadogPropertiesAvailable && !isDetox) {
apply from: "../../../../node_modules/@datadog/mobile-react-native/datadog-sourcemaps.gradle" apply from: "../../../../node_modules/@datadog/mobile-react-native/datadog-sourcemaps.gradle"
} }
def devVersionName = "1.42" def devVersionName = "1.43"
def betaVersionName = "1.42" def betaVersionName = "1.43"
def prodVersionName = "1.42" def prodVersionName = "1.43"
android { android {
ndkVersion rootProject.ext.ndkVersion ndkVersion rootProject.ext.ndkVersion
......
...@@ -2204,7 +2204,7 @@ ...@@ -2204,7 +2204,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2257,7 +2257,7 @@ ...@@ -2257,7 +2257,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
...@@ -2310,7 +2310,7 @@ ...@@ -2310,7 +2310,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
...@@ -2363,7 +2363,7 @@ ...@@ -2363,7 +2363,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
...@@ -2401,7 +2401,7 @@ ...@@ -2401,7 +2401,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2437,7 +2437,7 @@ ...@@ -2437,7 +2437,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
...@@ -2472,7 +2472,7 @@ ...@@ -2472,7 +2472,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
...@@ -2507,7 +2507,7 @@ ...@@ -2507,7 +2507,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
...@@ -2554,7 +2554,7 @@ ...@@ -2554,7 +2554,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2600,7 +2600,7 @@ ...@@ -2600,7 +2600,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
...@@ -2646,7 +2646,7 @@ ...@@ -2646,7 +2646,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets;
...@@ -2692,7 +2692,7 @@ ...@@ -2692,7 +2692,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
...@@ -2734,7 +2734,7 @@ ...@@ -2734,7 +2734,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2777,7 +2777,7 @@ ...@@ -2777,7 +2777,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
...@@ -2820,7 +2820,7 @@ ...@@ -2820,7 +2820,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension;
...@@ -2863,7 +2863,7 @@ ...@@ -2863,7 +2863,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
...@@ -2899,7 +2899,7 @@ ...@@ -2899,7 +2899,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -2937,7 +2937,7 @@ ...@@ -2937,7 +2937,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3137,7 +3137,7 @@ ...@@ -3137,7 +3137,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -3181,7 +3181,7 @@ ...@@ -3181,7 +3181,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
...@@ -3292,7 +3292,7 @@ ...@@ -3292,7 +3292,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3363,7 +3363,7 @@ ...@@ -3363,7 +3363,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
...@@ -3474,7 +3474,7 @@ ...@@ -3474,7 +3474,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3545,7 +3545,7 @@ ...@@ -3545,7 +3545,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.42; MARKETING_VERSION = 1.43;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension;
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
"firestore:deploy:rules": "firebase deploy --only firestore:rules", "firestore:deploy:rules": "firebase deploy --only firestore:rules",
"link:assets": "react-native-asset", "link:assets": "react-native-asset",
"graphql:generate:swift": "cd ios && ./Pods/Apollo/apollo-ios-cli generate", "graphql:generate:swift": "cd ios && ./Pods/Apollo/apollo-ios-cli generate",
"check:circular": "../../scripts/check-circular-imports.sh ./src/app/App.tsx 8", "check:circular": "../../scripts/check-circular-imports.sh ./src/app/App.tsx 2",
"ios": "yarn ios:prebuild && SKIP_BUNDLING=1 react-native run-ios", "ios": "yarn ios:prebuild && SKIP_BUNDLING=1 react-native run-ios",
"ios:prebuild": "yarn graphql:generate:swift && cd ios/WidgetsCore/MobileSchema && rm -rf !(README.md) && cd ../../.. && yarn graphql:generate:swift && yarn env:local:copy:swift", "ios:prebuild": "yarn graphql:generate:swift && cd ios/WidgetsCore/MobileSchema && rm -rf !(README.md) && cd ../../.. && yarn graphql:generate:swift && yarn env:local:copy:swift",
"ios:smol": "SKIP_BUNDLING=1 react-native run-ios --simulator=\"iPhone SE (3rd generation)\"", "ios:smol": "SKIP_BUNDLING=1 react-native run-ios --simulator=\"iPhone SE (3rd generation)\"",
......
import { DdRumReactNavigationTracking } from '@datadog/mobile-react-navigation' import { DdRumReactNavigationTracking } from '@datadog/mobile-react-navigation'
import { import {
createNavigationContainerRef,
DefaultTheme, DefaultTheme,
NavigationContainer as NativeNavigationContainer, NavigationContainer as NativeNavigationContainer,
NavigationContainerRefWithCurrent, NavigationContainerRefWithCurrent,
...@@ -9,6 +8,7 @@ import { SharedEventName } from '@uniswap/analytics-events' ...@@ -9,6 +8,7 @@ import { SharedEventName } from '@uniswap/analytics-events'
import React, { FC, PropsWithChildren, useCallback, useState } from 'react' import React, { FC, PropsWithChildren, useCallback, useState } from 'react'
import { Linking } from 'react-native' import { Linking } from 'react-native'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { navigationRef } from 'src/app/navigation/navigationRef'
import { RootParamList } from 'src/app/navigation/types' import { RootParamList } from 'src/app/navigation/types'
import { openDeepLink } from 'src/features/deepLinking/handleDeepLinkSaga' import { openDeepLink } from 'src/features/deepLinking/handleDeepLinkSaga'
import { DIRECT_LOG_ONLY_SCREENS } from 'src/features/telemetry/directLogScreens' import { DIRECT_LOG_ONLY_SCREENS } from 'src/features/telemetry/directLogScreens'
...@@ -26,8 +26,6 @@ interface Props { ...@@ -26,8 +26,6 @@ interface Props {
onReady?: (navigationRef: NavigationContainerRefWithCurrent<ReactNavigation.RootParamList>) => void onReady?: (navigationRef: NavigationContainerRefWithCurrent<ReactNavigation.RootParamList>) => void
} }
export const navigationRef = createNavigationContainerRef()
/** Wrapped `NavigationContainer` with telemetry tracing. */ /** Wrapped `NavigationContainer` with telemetry tracing. */
export const NavigationContainer: FC<PropsWithChildren<Props>> = ({ children, onReady }: PropsWithChildren<Props>) => { export const NavigationContainer: FC<PropsWithChildren<Props>> = ({ children, onReady }: PropsWithChildren<Props>) => {
const colors = useSporeColors() const colors = useSporeColors()
......
...@@ -11,8 +11,8 @@ import React, { useEffect } from 'react' ...@@ -11,8 +11,8 @@ import React, { useEffect } from 'react'
import { DevSettings } from 'react-native' import { DevSettings } from 'react-native'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import StorybookUIRoot from 'src/../.storybook' import StorybookUIRoot from 'src/../.storybook'
import { navigationRef } from 'src/app/navigation/NavigationContainer'
import { renderHeaderBackButton, renderHeaderBackImage } from 'src/app/navigation/components' import { renderHeaderBackButton, renderHeaderBackImage } from 'src/app/navigation/components'
import { navigationRef } from 'src/app/navigation/navigationRef'
import { import {
AppStackParamList, AppStackParamList,
AppStackScreenProp, AppStackScreenProp,
......
import { createNavigationContainerRef } from '@react-navigation/native'
// this was moved to its own file to avoid circular dependencies
export const navigationRef = createNavigationContainerRef()
import { NavigationAction, NavigationState } from '@react-navigation/core' import { NavigationAction, NavigationState } from '@react-navigation/core'
import { navigationRef } from 'src/app/navigation/NavigationContainer' import { navigationRef } from 'src/app/navigation/navigationRef'
import { RootParamList } from 'src/app/navigation/types' import { RootParamList } from 'src/app/navigation/types'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
......
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { ScrollView, StyleSheet } from 'react-native' import { StyleSheet } from 'react-native'
import { FlatList } from 'react-native-gesture-handler'
import { Flex, Text } from 'ui/src' import { Flex, Text } from 'ui/src'
import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions'
import { spacing } from 'ui/src/theme' import { spacing } from 'ui/src/theme'
...@@ -12,6 +13,11 @@ import { Account } from 'wallet/src/features/wallet/accounts/types' ...@@ -12,6 +13,11 @@ import { Account } from 'wallet/src/features/wallet/accounts/types'
const ADDRESS_ROW_HEIGHT = 40 const ADDRESS_ROW_HEIGHT = 40
interface SortedAddressData {
address: string
balance: number
}
type Portfolio = NonNullable<NonNullable<NonNullable<AccountListQuery['portfolios']>[0]>> type Portfolio = NonNullable<NonNullable<NonNullable<AccountListQuery['portfolios']>[0]>>
function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Element { function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Element {
...@@ -26,17 +32,27 @@ function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Ele ...@@ -26,17 +32,27 @@ function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Ele
.filter((portfolio): portfolio is Portfolio => Boolean(portfolio)) .filter((portfolio): portfolio is Portfolio => Boolean(portfolio))
.map((portfolio) => ({ .map((portfolio) => ({
address: portfolio.ownerAddress, address: portfolio.ownerAddress,
balance: portfolio.tokensTotalDenominatedValue?.value, balance: portfolio.tokensTotalDenominatedValue?.value ?? 0,
})) }))
.sort((a, b) => (b.balance ?? 0) - (a.balance ?? 0)) .sort((a, b) => b.balance - a.balance)
// set max height to around 30% screen size, so we always cut the last visible element
// this way user is aware if there are more elements to see
const accountsScrollViewHeight = const accountsScrollViewHeight =
Math.floor((fullHeight * 0.3) / ADDRESS_ROW_HEIGHT) * ADDRESS_ROW_HEIGHT + Math.floor((fullHeight * 0.3) / ADDRESS_ROW_HEIGHT) * ADDRESS_ROW_HEIGHT +
ADDRESS_ROW_HEIGHT / 2 + ADDRESS_ROW_HEIGHT / 2 +
spacing.spacing12 // 12 is the ScrollView vertical padding spacing.spacing12 // 12 is the ScrollView vertical padding
const renderItem = ({ item, index }: { item: SortedAddressData; index: number }): JSX.Element => {
return (
<AssociatedAccountRow
address={item.address}
balance={item.balance}
index={index}
loading={loading}
totalCount={accounts.length}
/>
)
}
return ( return (
<Flex <Flex
borderColor="$surface3" borderColor="$surface3"
...@@ -46,17 +62,14 @@ function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Ele ...@@ -46,17 +62,14 @@ function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Ele
px="$spacing12" px="$spacing12"
width="100%" width="100%"
> >
<ScrollView bounces={false} contentContainerStyle={styles.accounts}> <FlatList
{sortedAddressesByBalance.map(({ address, balance }, index) => ( data={sortedAddressesByBalance}
<AssociatedAccountRow keyExtractor={(item) => item.address}
address={address} renderItem={renderItem}
balance={balance} bounces={false}
index={index} contentContainerStyle={[styles.accounts, { paddingBottom: spacing.spacing12 }]}
loading={loading} keyboardShouldPersistTaps="handled"
totalCount={accounts.length} />
/>
))}
</ScrollView>
</Flex> </Flex>
) )
} }
......
...@@ -2,7 +2,7 @@ import * as ExpoClipboard from 'expo-clipboard' ...@@ -2,7 +2,7 @@ import * as ExpoClipboard from 'expo-clipboard'
import { State } from 'react-native-gesture-handler' import { State } from 'react-native-gesture-handler'
import { fireGestureHandler, getByGestureTestId } from 'react-native-gesture-handler/jest-utils' import { fireGestureHandler, getByGestureTestId } from 'react-native-gesture-handler/jest-utils'
import { MobileState } from 'src/app/mobileReducer' import { MobileState } from 'src/app/mobileReducer'
import { navigationRef } from 'src/app/navigation/NavigationContainer' import { navigationRef } from 'src/app/navigation/navigationRef'
import { AccountHeader } from 'src/components/accounts/AccountHeader' import { AccountHeader } from 'src/components/accounts/AccountHeader'
import { fireEvent, render, screen, waitFor, within } from 'src/test/test-utils' import { fireEvent, render, screen, waitFor, within } from 'src/test/test-utils'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
......
import type { Meta, StoryObj } from '@storybook/react' import type { Meta, StoryObj } from '@storybook/react'
import { CopyTextButton } from 'src/components/buttons/CopyTextButton' import { CopyTextButton } from 'src/components/buttons/CopyTextButton'
import { StorybookTitles } from 'ui/src/storybook'
const meta = { const meta = {
title: StorybookTitles.Atoms, title: 'Components/Buttons',
component: CopyTextButton, component: CopyTextButton,
} satisfies Meta<typeof CopyTextButton> } satisfies Meta<typeof CopyTextButton>
......
...@@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useState } from 'react' ...@@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ListRenderItem, ListRenderItemInfo, StyleSheet } from 'react-native' import { ListRenderItem, ListRenderItemInfo, StyleSheet } from 'react-native'
import { FlatList } from 'react-native-gesture-handler' import { FlatList } from 'react-native-gesture-handler'
import { FadeIn, FadeOut, useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated' import { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { FavoriteTokensGrid } from 'src/components/explore/FavoriteTokensGrid' import { FavoriteTokensGrid } from 'src/components/explore/FavoriteTokensGrid'
import { FavoriteWalletsGrid } from 'src/components/explore/FavoriteWalletsGrid' import { FavoriteWalletsGrid } from 'src/components/explore/FavoriteWalletsGrid'
...@@ -13,7 +13,7 @@ import { TokenItemData } from 'src/components/explore/TokenItemData' ...@@ -13,7 +13,7 @@ import { TokenItemData } from 'src/components/explore/TokenItemData'
import { AnimatedBottomSheetFlatList } from 'src/components/layout/AnimatedFlatList' import { AnimatedBottomSheetFlatList } from 'src/components/layout/AnimatedFlatList'
import { AutoScrollProps } from 'src/components/sortableGrid/types' import { AutoScrollProps } from 'src/components/sortableGrid/types'
import { getTokenMetadataDisplayType } from 'src/features/explore/utils' import { getTokenMetadataDisplayType } from 'src/features/explore/utils'
import { AnimatedTouchableArea, Flex, Loader, Text, useSporeColors } from 'ui/src' import { Flex, Loader, Text, TouchableArea, useSporeColors } from 'ui/src'
import { iconSizes, spacing } from 'ui/src/theme' import { iconSizes, spacing } from 'ui/src/theme'
import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard'
import { NetworkLogo } from 'uniswap/src/components/CurrencyLogo/NetworkLogo' import { NetworkLogo } from 'uniswap/src/components/CurrencyLogo/NetworkLogo'
...@@ -191,7 +191,7 @@ function NetworkPillsRow({ ...@@ -191,7 +191,7 @@ function NetworkPillsRow({
const renderItem: ListRenderItem<UniverseChainId> = useCallback( const renderItem: ListRenderItem<UniverseChainId> = useCallback(
({ item }: ListRenderItemInfo<UniverseChainId>) => { ({ item }: ListRenderItemInfo<UniverseChainId>) => {
return ( return (
<AnimatedTouchableArea entering={FadeIn} exiting={FadeOut} onPress={() => onSelectNetwork(item)}> <TouchableArea onPress={() => onSelectNetwork(item)}>
<NetworkPill <NetworkPill
key={item} key={item}
showIcon showIcon
...@@ -207,7 +207,7 @@ function NetworkPillsRow({ ...@@ -207,7 +207,7 @@ function NetworkPillsRow({
showBackgroundColor={false} showBackgroundColor={false}
textVariant="buttonLabel3" textVariant="buttonLabel3"
/> />
</AnimatedTouchableArea> </TouchableArea>
) )
}, },
[colors.neutral1.val, onSelectNetwork, selectedNetwork], [colors.neutral1.val, onSelectNetwork, selectedNetwork],
......
...@@ -176,6 +176,8 @@ export function SearchResultsSection({ ...@@ -176,6 +176,8 @@ export function SearchResultsSection({
return ( return (
<Flex grow gap="$spacing8" pb="$spacing36"> <Flex grow gap="$spacing8" pb="$spacing36">
<AnimatedBottomSheetFlashList <AnimatedBottomSheetFlashList
// when switching networks, we want to rerender the list to prevent any layout misalignments
key={selectedChain}
estimatedItemSize={ESTIMATED_ITEM_SIZE} estimatedItemSize={ESTIMATED_ITEM_SIZE}
ListEmptyComponent={ ListEmptyComponent={
<AnimatedFlex entering={FadeIn} exiting={FadeOut} gap="$spacing8" mx="$spacing20"> <AnimatedFlex entering={FadeIn} exiting={FadeOut} gap="$spacing8" mx="$spacing20">
......
import React, { useEffect, useState } from 'react' import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { StyleSheet } from 'react-native'
import Svg, { Circle } from 'react-native-svg'
import { BackButtonView } from 'src/components/layout/BackButtonView' import { BackButtonView } from 'src/components/layout/BackButtonView'
import { SeedPhraseDisplay } from 'src/components/mnemonic/SeedPhraseDisplay' import { SeedPhraseDisplay } from 'src/components/mnemonic/SeedPhraseDisplay'
import { APP_STORE_LINK } from 'src/constants/urls' import { APP_STORE_LINK } from 'src/constants/urls'
import { UpgradeStatus } from 'src/features/forceUpgrade/types' import { UpgradeStatus } from 'src/features/forceUpgrade/types'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src' import { Button, Flex, Image, Text, TouchableArea, isWeb, useSporeColors } from 'ui/src'
import { UNISWAP_LOGO } from 'ui/src/assets'
import { imageSizes } from 'ui/src/theme'
import { Modal } from 'uniswap/src/components/modals/Modal' import { Modal } from 'uniswap/src/components/modals/Modal'
import { WarningModal } from 'uniswap/src/components/modals/WarningModal/WarningModal' import { NewTag } from 'uniswap/src/components/pill/NewTag'
import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types'
import { DynamicConfigs, ForceUpgradeConfigKey } from 'uniswap/src/features/gating/configs' import { DynamicConfigs, ForceUpgradeConfigKey } from 'uniswap/src/features/gating/configs'
import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
...@@ -19,7 +22,7 @@ export function ForceUpgradeModal(): JSX.Element { ...@@ -19,7 +22,7 @@ export function ForceUpgradeModal(): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
const colors = useSporeColors() const colors = useSporeColors()
const forceUpgradeStatusString = useDynamicConfigValue( const forceUpgradeStatusString = useDynamicConfigValue(
DynamicConfigs.MobileForceUpgrade, DynamicConfigs.ForceUpgrade,
ForceUpgradeConfigKey.Status, ForceUpgradeConfigKey.Status,
'' as string, '' as string,
) )
...@@ -64,26 +67,83 @@ export function ForceUpgradeModal(): JSX.Element { ...@@ -64,26 +67,83 @@ export function ForceUpgradeModal(): JSX.Element {
// the force upgrade screen on error, hence we fallback to the global error boundary // the force upgrade screen on error, hence we fallback to the global error boundary
return ( return (
<> <>
<WarningModal <Modal
acknowledgeText={t('forceUpgrade.action.confirm')} backgroundColor={colors.surface1.val}
hideHandlebar={upgradeStatus === UpgradeStatus.Required} hideHandlebar={upgradeStatus === UpgradeStatus.Required}
isDismissible={upgradeStatus !== UpgradeStatus.Required} isDismissible={upgradeStatus !== UpgradeStatus.Required}
isOpen={isVisible} isModalOpen={isVisible}
modalName={ModalName.ForceUpgradeModal} name={ModalName.ForceUpgradeModal}
severity={WarningSeverity.High}
title={t('forceUpgrade.title')}
onClose={onClose} onClose={onClose}
onAcknowledge={onPressConfirm}
> >
<Text color="$neutral2" textAlign="center" variant="body2"> <Flex
{t('forceUpgrade.description')} centered
</Text> gap="$spacing24"
{mnemonicId && ( pb={isWeb ? '$none' : '$spacing12'}
<Text color="$accent1" variant="buttonLabel2" onPress={onPressViewRecovery}> pt={upgradeStatus === UpgradeStatus.Required ? '$spacing24' : '$spacing12'}
{t('forceUpgrade.action.recoveryPhrase')} px={isWeb ? '$none' : '$spacing24'}
</Text> >
)} <Flex
</WarningModal> centered
width="100%"
height={160}
borderRadius="$rounded16"
borderWidth={1}
borderColor="$surface3"
overflow="hidden"
>
<Flex
centered
borderRadius="$rounded16"
style={{
shadowColor: colors.accent1.val,
elevation: 8,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 20,
}}
borderWidth={1}
borderColor="$surface3"
elevationAndroid={8}
>
<Flex position="relative">
<Image
height={imageSizes.image64}
resizeMode="contain"
source={UNISWAP_LOGO}
width={imageSizes.image64}
/>
<Flex position="absolute" top={-15} right={-8} transform={[{ rotate: '10deg' }]}>
<NewTag exclamation backgroundColor="$accent1" textColor="$white" />
</Flex>
</Flex>
</Flex>
<BackgroundDotPattern />
</Flex>
<Flex gap="$spacing8">
<Text textAlign="center" variant="subheading1">
{t('forceUpgrade.title')}
</Text>
<Text color="$neutral2" textAlign="center" variant="body3">
{t('forceUpgrade.description')}
</Text>
</Flex>
<Flex centered gap="$spacing8" pb={isWeb ? '$none' : '$spacing12'} width="100%">
<Button size="medium" theme="primary" width="100%" onPress={onPressConfirm}>
<Text color="$white" variant="buttonLabel2">
{t('forceUpgrade.action.confirm')}
</Text>
</Button>
{mnemonicId && (
<Button size="medium" theme="secondary" width="100%" onPress={onPressViewRecovery}>
<Text color="$neutral1" variant="buttonLabel2">
{t('forceUpgrade.action.recoveryPhrase')}
</Text>
</Button>
)}
</Flex>
</Flex>
</Modal>
{mnemonicId && showSeedPhrase && ( {mnemonicId && showSeedPhrase && (
<Modal fullScreen backgroundColor={colors.surface1.val} name={ModalName.ForceUpgradeModal} onClose={onDismiss}> <Modal fullScreen backgroundColor={colors.surface1.val} name={ModalName.ForceUpgradeModal} onClose={onDismiss}>
<Flex fill gap="$spacing16" px="$spacing24" py="$spacing24"> <Flex fill gap="$spacing16" px="$spacing24" py="$spacing24">
...@@ -103,3 +163,48 @@ export function ForceUpgradeModal(): JSX.Element { ...@@ -103,3 +163,48 @@ export function ForceUpgradeModal(): JSX.Element {
} }
const BACK_BUTTON_SIZE = 24 const BACK_BUTTON_SIZE = 24
function BackgroundDotPattern(): JSX.Element {
const colors = useSporeColors()
const dotGrid = useMemo(() => {
return Array.from({ length: 100 }).map((_, row) => {
return Array.from({ length: 100 }).map((__, col) => {
const x = col * 2 + 1
const y = row * 2 + 1
const distX = Math.abs(x - 50)
const distY = Math.abs(y - 50)
const dist = Math.sqrt(distX * distX + distY * distY)
if (dist < 45) {
const size = 0.1 + (45 - dist) / 20
return <Circle key={`${row}-${col}`} cx={`${x}%`} cy={`${y}%`} r={size} fill={colors.pinkThemed.val} />
}
return null
})
})
}, [colors])
return (
<Svg width={400} height={400} style={[styles.backgroundPattern, styles.centered]}>
{dotGrid}
</Svg>
)
}
const styles = StyleSheet.create({
backgroundPattern: {
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0,
zIndex: -1,
},
centered: {
left: '50%',
top: '50%',
transform: [{ translateX: -200 }, { translateY: -200 }],
},
})
import { expectSaga } from 'redux-saga-test-plan' import { expectSaga } from 'redux-saga-test-plan'
import { call } from 'redux-saga/effects' import { call } from 'redux-saga/effects'
import { navigationRef } from 'src/app/navigation/NavigationContainer' import { navigationRef } from 'src/app/navigation/navigationRef'
import { import {
handleDeepLink, handleDeepLink,
handleUniswapAppDeepLink, handleUniswapAppDeepLink,
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM, UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM,
UNISWAP_WALLETCONNECT_URL, UNISWAP_WALLETCONNECT_URL,
} from 'src/features/deepLinking/constants' } from 'src/features/deepLinking/constants'
import { handleOffRampReturnLink } from 'src/features/deepLinking/handleOffRampReturnLinkSaga'
import { handleOnRampReturnLink } from 'src/features/deepLinking/handleOnRampReturnLinkSaga' import { handleOnRampReturnLink } from 'src/features/deepLinking/handleOnRampReturnLinkSaga'
import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga' import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga'
import { handleTransactionLink } from 'src/features/deepLinking/handleTransactionLinkSaga' import { handleTransactionLink } from 'src/features/deepLinking/handleTransactionLinkSaga'
...@@ -207,6 +208,7 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) { ...@@ -207,6 +208,7 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
const screen = url.searchParams.get('screen') const screen = url.searchParams.get('screen')
const userAddress = url.searchParams.get('userAddress') const userAddress = url.searchParams.get('userAddress')
const fiatOnRamp = url.searchParams.get('fiatOnRamp') === 'true' const fiatOnRamp = url.searchParams.get('fiatOnRamp') === 'true'
const fiatOffRamp = url.searchParams.get('fiatOffRamp') === 'true'
const activeAccount = yield* select(selectActiveAccount) const activeAccount = yield* select(selectActiveAccount)
if (!activeAccount) { if (!activeAccount) {
...@@ -271,6 +273,8 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) { ...@@ -271,6 +273,8 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
case 'transaction': case 'transaction':
if (fiatOnRamp) { if (fiatOnRamp) {
yield* call(handleOnRampReturnLink) yield* call(handleOnRampReturnLink)
} else if (fiatOffRamp) {
yield* call(handleOffRampReturnLink, url)
} else { } else {
yield* call(handleTransactionLink) yield* call(handleTransactionLink)
} }
......
import { navigate } from 'src/app/navigation/rootNavigation'
import { openModal } from 'src/features/modals/modalSlice'
import { call, put } from 'typed-redux-saga'
import { AssetType, TradeableAsset } from 'uniswap/src/entities/assets'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
import { FiatOffRampMetaData, OffRampTransferDetailsResponse } from 'uniswap/src/features/fiatOnRamp/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TransactionScreen } from 'uniswap/src/features/transactions/TransactionModal/TransactionModalContext'
import { CurrencyField } from 'uniswap/src/types/currency'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { createTransactionId } from 'uniswap/src/utils/createTransactionId'
import { logger } from 'utilities/src/logger/logger'
import { fetchOffRampTransferDetails } from 'wallet/src/features/fiatOnRamp/api'
import { dismissInAppBrowser } from 'wallet/src/utils/linking'
export function* handleOffRampReturnLink(url: URL) {
try {
yield* call(_handleOffRampReturnLink, url)
} catch (error) {
// TODO: handle error in UI
// Alert.alert(i18n.t('walletConnect.error.general.title'), i18n.t('walletConnect.error.general.message'))
// yield* put(openModal({ name: ModalName.Send, initialState: initialSendState }))
}
}
function* _handleOffRampReturnLink(url: URL) {
const externalTransactionId = url.searchParams.get('externalTransactionId')
if (!externalTransactionId) {
throw new Error('Missing externalTransactionId in fiat offramp deep link')
}
let offRampTransferDetails: OffRampTransferDetailsResponse | undefined
try {
offRampTransferDetails = yield* call(fetchOffRampTransferDetails, externalTransactionId)
} catch (error) {
logger.error(error, {
tags: { file: 'handleOffRampReturnLinkSaga', function: 'handleOffRampReturnLink' },
})
throw new Error('Failed to fetch offramp transfer details')
}
if (!offRampTransferDetails) {
throw new Error('Missing offRampTransferDetails in fiat offramp deep link')
}
const { tokenAddress, baseCurrencyCode, baseCurrencyAmount, depositWalletAddress, logos, provider, chainId } =
offRampTransferDetails
const currencyTradeableAsset: TradeableAsset = {
address: tokenAddress,
chainId: Number(chainId) as UniverseChainId,
type: AssetType.Currency,
}
const fiatOffRampMetaData: FiatOffRampMetaData = {
name: provider,
logoUrl: logos.lightLogo,
// TODO: update activity feed once transaction is submitted
onSubmitCallback: () => {},
moonpayCurrencyCode: baseCurrencyCode,
meldCurrencyCode: baseCurrencyCode,
}
const txnId = createTransactionId()
const initialSendState = {
txId: txnId,
[CurrencyField.INPUT]: currencyTradeableAsset,
[CurrencyField.OUTPUT]: null,
exactCurrencyField: CurrencyField.INPUT,
exactAmountToken: baseCurrencyAmount.toString(),
focusOnCurrencyField: null,
recipient: depositWalletAddress,
isFiatInput: false,
showRecipientSelector: false,
fiatOffRampMetaData,
sendScreen: TransactionScreen.Review,
}
yield* call(navigate, MobileScreens.Home)
yield* put(openModal({ name: ModalName.Send, initialState: initialSendState }))
yield* call(dismissInAppBrowser)
}
...@@ -7,6 +7,7 @@ import { FiatOnRampModalState } from 'src/screens/FiatOnRampModalState' ...@@ -7,6 +7,7 @@ import { FiatOnRampModalState } from 'src/screens/FiatOnRampModalState'
import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState' import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState'
import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TransactionScreen } from 'uniswap/src/features/transactions/TransactionModal/TransactionModalContext'
import { TransactionState } from 'uniswap/src/features/transactions/types/transactionState' import { TransactionState } from 'uniswap/src/features/transactions/types/transactionState'
import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants' import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
...@@ -34,7 +35,7 @@ export interface ModalsState { ...@@ -34,7 +35,7 @@ export interface ModalsState {
[ModalName.RemoveWallet]: AppModalState<RemoveWalletModalState> [ModalName.RemoveWallet]: AppModalState<RemoveWalletModalState>
[ModalName.RestoreWallet]: AppModalState<undefined> [ModalName.RestoreWallet]: AppModalState<undefined>
[ModalName.Scantastic]: AppModalState<ScantasticModalState> [ModalName.Scantastic]: AppModalState<ScantasticModalState>
[ModalName.Send]: AppModalState<TransactionState> [ModalName.Send]: AppModalState<TransactionState & { sendScreen: TransactionScreen }>
[ModalName.Swap]: AppModalState<TransactionState> [ModalName.Swap]: AppModalState<TransactionState>
[ModalName.TestnetSwitchModal]: AppModalState<TestnetSwitchModalState> [ModalName.TestnetSwitchModal]: AppModalState<TestnetSwitchModalState>
[ModalName.UnitagsIntro]: AppModalState<{ [ModalName.UnitagsIntro]: AppModalState<{
......
...@@ -9,6 +9,7 @@ import { TestnetSwitchModalState } from 'src/features/testnetMode/TestnetSwitchM ...@@ -9,6 +9,7 @@ import { TestnetSwitchModalState } from 'src/features/testnetMode/TestnetSwitchM
import { FiatOnRampModalState } from 'src/screens/FiatOnRampModalState' import { FiatOnRampModalState } from 'src/screens/FiatOnRampModalState'
import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState' import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TransactionScreen } from 'uniswap/src/features/transactions/TransactionModal/TransactionModalContext'
import { TransactionState } from 'uniswap/src/features/transactions/types/transactionState' import { TransactionState } from 'uniswap/src/features/transactions/types/transactionState'
import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { getKeys } from 'utilities/src/primitives/objects' import { getKeys } from 'utilities/src/primitives/objects'
...@@ -80,7 +81,12 @@ type WalletConnectModalParams = { ...@@ -80,7 +81,12 @@ type WalletConnectModalParams = {
type SwapModalParams = { name: typeof ModalName.Swap; initialState?: TransactionState } type SwapModalParams = { name: typeof ModalName.Swap; initialState?: TransactionState }
type SendModalParams = { name: typeof ModalName.Send; initialState?: TransactionState } type SendModalParams = {
name: typeof ModalName.Send
initialState?: TransactionState & {
sendScreen?: TransactionScreen
}
}
type UnitagsIntroParams = { type UnitagsIntroParams = {
name: typeof ModalName.UnitagsIntro name: typeof ModalName.UnitagsIntro
......
...@@ -115,9 +115,10 @@ describe('useNotificationToggle', () => { ...@@ -115,9 +115,10 @@ describe('useNotificationToggle', () => {
result.current.toggle() result.current.toggle()
await new Promise(requestAnimationFrame) await new Promise(requestAnimationFrame)
}) })
await waitFor(() => {
expect(mockDispatch).toHaveBeenCalled() expect(mockDispatch).toHaveBeenCalled()
expect(result.current.isEnabled).toBe(false) expect(result.current.isEnabled).toBe(false)
})
}) })
it('handles OS permission prompt flow successfully', async () => { it('handles OS permission prompt flow successfully', async () => {
...@@ -133,9 +134,11 @@ describe('useNotificationToggle', () => { ...@@ -133,9 +134,11 @@ describe('useNotificationToggle', () => {
await new Promise(requestAnimationFrame) await new Promise(requestAnimationFrame)
}) })
expect(mockPermissionPrompt).toHaveBeenCalled() await waitFor(() => {
expect(mockDispatch).toHaveBeenCalled() expect(mockPermissionPrompt).toHaveBeenCalled()
expect(result.current.isEnabled).toBe(true) expect(mockDispatch).toHaveBeenCalled()
expect(result.current.isEnabled).toBe(true)
})
}) })
it('handles OS permission prompt flow failure', async () => { it('handles OS permission prompt flow failure', async () => {
......
...@@ -44,16 +44,20 @@ export function SendFlow(): JSX.Element { ...@@ -44,16 +44,20 @@ export function SendFlow(): JSX.Element {
onClose={onClose} onClose={onClose}
> >
<SendContextProvider prefilledTransactionState={initialState}> <SendContextProvider prefilledTransactionState={initialState}>
<CurrentScreen /> <CurrentScreen screenOverride={initialState?.sendScreen} />
</SendContextProvider> </SendContextProvider>
</TransactionModal> </TransactionModal>
) )
} }
function CurrentScreen(): JSX.Element { function CurrentScreen({ screenOverride }: { screenOverride?: TransactionScreen }): JSX.Element {
const { screen } = useTransactionModalContext() const { screen, setScreen } = useTransactionModalContext()
const { recipient } = useSendContext() const { recipient } = useSendContext()
if (screenOverride) {
setScreen(screenOverride)
}
// If no recipient, force full screen recipient select. Need to render this outside of `SendFormScreen` to ensure that // If no recipient, force full screen recipient select. Need to render this outside of `SendFormScreen` to ensure that
// the modals are rendered correctly, and animations can properly measure the available space for the decimal pad. // the modals are rendered correctly, and animations can properly measure the available space for the decimal pad.
if (!recipient) { if (!recipient) {
......
...@@ -106,7 +106,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | ...@@ -106,7 +106,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element |
refundWalletAddress: activeAccountAddress, refundWalletAddress: activeAccountAddress,
externalCustomerId: activeAccountAddress, externalCustomerId: activeAccountAddress,
externalSessionId: externalTransactionId, externalSessionId: externalTransactionId,
redirectUrl: `${uniswapUrls.redirectUrlBase}?screen=transaction&fiatOffRamp=true&userAddress=${activeAccountAddress}`, redirectUrl: `${uniswapUrls.redirectUrlBase}?screen=transaction&fiatOffRamp=true&userAddress=${activeAccountAddress}&externalTransactionId=${externalTransactionId}`,
} }
: skipToken, : skipToken,
) )
......
...@@ -366,6 +366,14 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { ...@@ -366,6 +366,14 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
}) })
const onSelectCurrency = (currency: FORCurrencyOrBalance): void => { const onSelectCurrency = (currency: FORCurrencyOrBalance): void => {
if (isTokenInputMode) {
resetAmount()
} else {
setSelectedQuote(undefined)
// This is done for formatting reasons. The existing value may change if max decimals of new currency is different
onChangeValue(value, 'changeAsset')
}
setShowTokenSelector(false) setShowTokenSelector(false)
if (isSupportedFORCurrency(currency)) { if (isSupportedFORCurrency(currency)) {
setQuoteCurrency(currency) setQuoteCurrency(currency)
...@@ -427,14 +435,18 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { ...@@ -427,14 +435,18 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
} }
}, [navigateToSwapFlow, unsupportedCurrency]) }, [navigateToSwapFlow, unsupportedCurrency])
const onPillToggle = (option: string | number): void => { const resetAmount = useCallback(() => {
setIsOffRamp(option === RampToggle.SELL)
setValue('') setValue('')
setFiatAmount(0) setFiatAmount(0)
setTokenAmount(0) setTokenAmount(0)
valueRef.current = '' valueRef.current = ''
resetSelection({ start: 0 }) resetSelection({ start: 0 })
setSelectedQuote(undefined)
}, [setValue, setFiatAmount, setTokenAmount, valueRef, resetSelection, setSelectedQuote])
const onPillToggle = (option: string | number): void => {
setIsOffRamp(option === RampToggle.SELL)
resetAmount()
setQuoteCurrency(defaultCurrency) setQuoteCurrency(defaultCurrency)
sendAnalyticsEvent(FiatOffRampEventName.FORBuySellToggled, { sendAnalyticsEvent(FiatOffRampEventName.FORBuySellToggled, {
......
...@@ -12,7 +12,7 @@ import { ...@@ -12,7 +12,7 @@ import {
import React, { PropsWithChildren } from 'react' import React, { PropsWithChildren } from 'react'
import { MobileWalletNavigationProvider } from 'src/app/MobileWalletNavigationProvider' import { MobileWalletNavigationProvider } from 'src/app/MobileWalletNavigationProvider'
import type { MobileState } from 'src/app/mobileReducer' import type { MobileState } from 'src/app/mobileReducer'
import { navigationRef } from 'src/app/navigation/NavigationContainer' import { navigationRef } from 'src/app/navigation/navigationRef'
import { store as appStore, persistedReducer } from 'src/app/store' import { store as appStore, persistedReducer } from 'src/app/store'
import { BlankUrlProvider } from 'uniswap/src/contexts/UrlContext' import { BlankUrlProvider } from 'uniswap/src/contexts/UrlContext'
import { Resolvers } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { Resolvers } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
......
...@@ -13,6 +13,7 @@ ignores: [ ...@@ -13,6 +13,7 @@ ignores: [
'process', 'process',
'madge', 'madge',
# Dependencies that depcheck thinks are missing but are actually present or never used # Dependencies that depcheck thinks are missing but are actually present or never used
'stories',
## package.json scripts ## package.json scripts
'esbuild-register', 'esbuild-register',
## GraphQL ## GraphQL
...@@ -30,6 +31,17 @@ ignores: [ ...@@ -30,6 +31,17 @@ ignores: [
'@babel/preset-env', '@babel/preset-env',
'eslint-plugin-import', 'eslint-plugin-import',
'terser-webpack-plugin', 'terser-webpack-plugin',
## Storybook
'@svgr/webpack',
'ts-loader',
'@chromatic-com/storybook',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-onboarding',
'@storybook/blocks',
'@storybook/preset-create-react-app',
'eslint-plugin-storybook',
'prop-types',
## Testing ## Testing
'@types/testing-library__cypress', '@types/testing-library__cypress',
## i18n ## i18n
......
...@@ -3,3 +3,4 @@ babel.config.js ...@@ -3,3 +3,4 @@ babel.config.js
jest.config.js jest.config.js
metro.config.js metro.config.js
node_modules node_modules
.storybook/stories
...@@ -8,7 +8,7 @@ rulesDirPlugin.RULES_DIR = 'eslint_rules' ...@@ -8,7 +8,7 @@ rulesDirPlugin.RULES_DIR = 'eslint_rules'
module.exports = { module.exports = {
root: true, root: true,
extends: ['@uniswap/eslint-config/react'], extends: ['@uniswap/eslint-config/react', 'plugin:storybook/recommended'],
plugins: ['rulesdir'], plugins: ['rulesdir'],
rules: { rules: {
......
...@@ -54,3 +54,5 @@ cypress/screenshots ...@@ -54,3 +54,5 @@ cypress/screenshots
.vercel .vercel
.wrangler .wrangler
*storybook.log
import type { StorybookConfig } from '@storybook/react-webpack5'
import { dirname, join, resolve } from 'path'
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, 'package.json')))
}
const config: StorybookConfig = {
stories: ['../../../packages/ui/**/*.stories.?(ts|tsx)', '../../../packages/ui/**/*.mdx'],
addons: [
getAbsolutePath('@storybook/preset-create-react-app'),
getAbsolutePath('@storybook/addon-onboarding'),
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@chromatic-com/storybook'),
getAbsolutePath('@storybook/addon-interactions'),
],
framework: {
name: getAbsolutePath('@storybook/react-webpack5'),
options: {},
},
staticDirs: ['../public'],
webpackFinal: (config) => {
// This modifies the existing image rule to exclude `.svg` files
// since we handle those with `@svgr/webpack`.
const imageRule =
config?.module?.rules &&
config.module.rules.find((rule) => {
if (rule && typeof rule !== 'string' && rule.test instanceof RegExp) {
return rule.test.test('.svg')
}
return
})
if (imageRule && typeof imageRule !== 'string') {
imageRule.exclude = /\.svg$/i
}
config?.module?.rules &&
config.module.rules.push({
test: /\.svg$/i,
use: ['@svgr/webpack'],
})
config?.module?.rules &&
config.module.rules.push({
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
})
config.resolve ??= {}
config.resolve = {
...config.resolve,
alias: {
...config?.resolve?.alias,
'react-native$': 'react-native-web',
},
}
config.resolve.modules = [resolve(__dirname, '../src'), 'node_modules']
return config
},
}
export default config
import type { Preview } from '@storybook/react'
import { TamaguiProvider } from '../src/theme/tamaguiProvider'
const preview: Preview = {
decorators: [
(Story) => (
<TamaguiProvider>
{/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */}
<Story />
</TamaguiProvider>
),
],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
}
export default preview
...@@ -5,7 +5,7 @@ const { execSync } = require('child_process') ...@@ -5,7 +5,7 @@ const { execSync } = require('child_process')
const { readFileSync } = require('fs') const { readFileSync } = require('fs')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const path = require('path') const path = require('path')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin') const ModuleScopePlugin = require(path.resolve(__dirname, '..', '..','node_modules/react-scripts/node_modules/react-dev-utils/ModuleScopePlugin'))
const { IgnorePlugin, ProvidePlugin, DefinePlugin } = require('webpack') const { IgnorePlugin, ProvidePlugin, DefinePlugin } = require('webpack')
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin') const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
......
...@@ -30,7 +30,9 @@ ...@@ -30,7 +30,9 @@
"test:cloud": "yarn jest functions --config=functions/jest.config.json", "test:cloud": "yarn jest functions --config=functions/jest.config.json",
"cypress:open": "cypress open --browser chrome --e2e", "cypress:open": "cypress open --browser chrome --e2e",
"cypress:run": "cypress run --browser chrome --e2e", "cypress:run": "cypress run --browser chrome --e2e",
"deduplicate": "yarn-deduplicate --strategy=highest" "deduplicate": "yarn-deduplicate --strategy=highest",
"storybook:run": "storybook dev -p 6006",
"storybook:build": "storybook build"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
...@@ -70,10 +72,19 @@ ...@@ -70,10 +72,19 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "7.23.3", "@babel/preset-env": "7.23.3",
"@chromatic-com/storybook": "3.2.2",
"@cloudflare/workers-types": "4.20231025.0", "@cloudflare/workers-types": "4.20231025.0",
"@craco/craco": "7.1.0", "@craco/craco": "7.1.0",
"@crowdin/cli": "3.14.0", "@crowdin/cli": "3.14.0",
"@ethersproject/experimental": "5.7.0", "@ethersproject/experimental": "5.7.0",
"@storybook/addon-essentials": "8.4.2",
"@storybook/addon-interactions": "8.4.2",
"@storybook/addon-onboarding": "8.4.2",
"@storybook/blocks": "8.4.2",
"@storybook/preset-create-react-app": "8.4.2",
"@storybook/react": "8.4.2",
"@storybook/react-webpack5": "8.4.2",
"@storybook/test": "8.4.2",
"@swc/core": "1.3.72", "@swc/core": "1.3.72",
"@swc/jest": "0.2.29", "@swc/jest": "0.2.29",
"@swc/plugin-styled-components": "1.5.97", "@swc/plugin-styled-components": "1.5.97",
...@@ -114,12 +125,14 @@ ...@@ -114,12 +125,14 @@
"concurrently": "8.2.2", "concurrently": "8.2.2",
"cypress": "12.17.4", "cypress": "12.17.4",
"cypress-hardhat": "2.5.3", "cypress-hardhat": "2.5.3",
"depcheck": "1.4.7",
"dotenv": "16.0.3", "dotenv": "16.0.3",
"dotenv-cli": "7.1.0", "dotenv-cli": "7.1.0",
"esbuild-register": "3.6.0", "esbuild-register": "3.6.0",
"eslint": "8.44.0", "eslint": "8.44.0",
"eslint-plugin-import": "2.27.5", "eslint-plugin-import": "2.27.5",
"eslint-plugin-rulesdir": "0.2.2", "eslint-plugin-rulesdir": "0.2.2",
"eslint-plugin-storybook": "0.8.0",
"hardhat": "2.22.16", "hardhat": "2.22.16",
"husky": "8.0.3", "husky": "8.0.3",
"jest": "29.7.0", "jest": "29.7.0",
...@@ -133,11 +146,13 @@ ...@@ -133,11 +146,13 @@
"path-browserify": "1.0.1", "path-browserify": "1.0.1",
"postinstall-postinstall": "2.1.0", "postinstall-postinstall": "2.1.0",
"process": "0.11.10", "process": "0.11.10",
"prop-types": "15.8.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"resize-observer-polyfill": "1.5.1", "resize-observer-polyfill": "1.5.1",
"serve": "14.2.4", "serve": "14.2.4",
"source-map-explorer": "2.5.3", "source-map-explorer": "2.5.3",
"start-server-and-test": "2.0.0", "start-server-and-test": "2.0.0",
"storybook": "8.4.2",
"swc-loader": "0.2.6", "swc-loader": "0.2.6",
"terser": "5.24.0", "terser": "5.24.0",
"terser-webpack-plugin": "5.3.9", "terser-webpack-plugin": "5.3.9",
...@@ -172,6 +187,7 @@ ...@@ -172,6 +187,7 @@
"@sentry/core": "7.80.0", "@sentry/core": "7.80.0",
"@sentry/react": "7.80.0", "@sentry/react": "7.80.0",
"@sentry/types": "7.80.0", "@sentry/types": "7.80.0",
"@svgr/webpack": "8.0.1",
"@tamagui/core": "1.114.4", "@tamagui/core": "1.114.4",
"@tamagui/portal": "1.114.4", "@tamagui/portal": "1.114.4",
"@tamagui/react-native-svg": "1.114.4", "@tamagui/react-native-svg": "1.114.4",
...@@ -269,6 +285,7 @@ ...@@ -269,6 +285,7 @@
"styled-components": "5.3.11", "styled-components": "5.3.11",
"tamagui": "1.114.4", "tamagui": "1.114.4",
"tiny-invariant": "1.3.1", "tiny-invariant": "1.3.1",
"ts-loader": "9.5.1",
"typed-redux-saga": "1.5.0", "typed-redux-saga": "1.5.0",
"ui": "workspace:^", "ui": "workspace:^",
"uniswap": "workspace:^", "uniswap": "workspace:^",
......
...@@ -126,16 +126,4 @@ ...@@ -126,16 +126,4 @@
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.5</priority> <priority>0.5</priority>
</url> </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> </urlset>
<!DOCTYPE html> <!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
...@@ -83,6 +83,17 @@ ...@@ -83,6 +83,17 @@
padding: 0; padding: 0;
} }
/* Only apply overflow-x: hidden on desktop */
/* This is to prevent ugly horizontal scrollbar from appearing on desktop */
/* We need to set it on html element specifically because otherwise we break */
/* sticky positioning of some child elements. */
/* Applying this on mobile breaks tamagui/remove-scroll. */
@media (min-width: 768px) {
html {
overflow-x: hidden;
}
}
button { button {
user-select: none; user-select: none;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -10,9 +10,11 @@ import styled, { useTheme } from 'lib/styled-components' ...@@ -10,9 +10,11 @@ import styled, { useTheme } from 'lib/styled-components'
import { Slash } from 'react-feather' import { Slash } from 'react-feather'
import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types' import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types'
import { ExternalLink, ThemedText } from 'theme/components' import { ExternalLink, ThemedText } from 'theme/components'
import { AdaptiveWebModal, Flex } from 'ui/src' import { Flex } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal'
import { nativeOnChain } from 'uniswap/src/constants/tokens' import { nativeOnChain } from 'uniswap/src/constants/tokens'
import { UniverseChainId } from 'uniswap/src/features/chains/types' import { UniverseChainId } from 'uniswap/src/features/chains/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { useUSDCValue } from 'uniswap/src/features/transactions/swap/hooks/useUSDCPrice' import { useUSDCValue } from 'uniswap/src/features/transactions/swap/hooks/useUSDCPrice'
import { Plural, Trans, t } from 'uniswap/src/i18n' import { Plural, Trans, t } from 'uniswap/src/i18n'
import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking' import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking'
...@@ -94,7 +96,7 @@ export function CancelOrdersDialog( ...@@ -94,7 +96,7 @@ export function CancelOrdersDialog(
(cancelState === CancellationState.CANCELLED || cancelState === CancellationState.PENDING_CONFIRMATION) && (cancelState === CancellationState.CANCELLED || cancelState === CancellationState.PENDING_CONFIRMATION) &&
cancelTxHash cancelTxHash
return ( return (
<AdaptiveWebModal isOpen onClose={onCancel} maxHeight="90vh" p={0}> <Modal name={ModalName.CancelOrders} isModalOpen onClose={onCancel} maxHeight="90vh" padding={0}>
<Container gap="lg"> <Container gap="lg">
<ModalHeader closeModal={onCancel} /> <ModalHeader closeModal={onCancel} />
<LogoContainer>{icon}</LogoContainer> <LogoContainer>{icon}</LogoContainer>
...@@ -116,7 +118,7 @@ export function CancelOrdersDialog( ...@@ -116,7 +118,7 @@ export function CancelOrdersDialog(
)} )}
</Row> </Row>
</Container> </Container>
</AdaptiveWebModal> </Modal>
) )
} else if (cancelState === CancellationState.REVIEWING_CANCELLATION) { } else if (cancelState === CancellationState.REVIEWING_CANCELLATION) {
return ( return (
......
...@@ -32,9 +32,9 @@ import { useOrder } from 'state/signatures/hooks' ...@@ -32,9 +32,9 @@ import { useOrder } from 'state/signatures/hooks'
import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types' import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types'
import { Divider, ThemedText } from 'theme/components' import { Divider, ThemedText } from 'theme/components'
import { UniswapXOrderStatus } from 'types/uniswapx' import { UniswapXOrderStatus } from 'types/uniswapx'
import { AdaptiveWebModal } from 'ui/src' import { Modal } from 'uniswap/src/components/modals/Modal'
import { UniverseChainId } from 'uniswap/src/features/chains/types' import { UniverseChainId } from 'uniswap/src/features/chains/types'
import { InterfaceEventNameLocal } from 'uniswap/src/features/telemetry/constants' import { InterfaceEventNameLocal, ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
import { CurrencyField } from 'uniswap/src/types/currency' import { CurrencyField } from 'uniswap/src/types/currency'
...@@ -382,11 +382,12 @@ export function OffchainActivityModal() { ...@@ -382,11 +382,12 @@ export function OffchainActivityModal() {
cancelTxHash={cancelTxHash} cancelTxHash={cancelTxHash}
/> />
)} )}
<AdaptiveWebModal <Modal
name={ModalName.OffchainActivity}
maxWidth={375} maxWidth={375}
isOpen={!!selectedOrderAtomValue?.modalOpen && cancelState === CancellationState.NOT_STARTED} isModalOpen={!!selectedOrderAtomValue?.modalOpen && cancelState === CancellationState.NOT_STARTED}
onClose={reset} onClose={reset}
p={0} padding={0}
> >
<Wrapper data-testid="offchain-activity-modal"> <Wrapper data-testid="offchain-activity-modal">
<Row justify="space-between"> <Row justify="space-between">
...@@ -405,7 +406,7 @@ export function OffchainActivityModal() { ...@@ -405,7 +406,7 @@ export function OffchainActivityModal() {
/> />
)} )}
</Wrapper> </Wrapper>
</AdaptiveWebModal> </Modal>
</> </>
) )
} }
...@@ -7,42 +7,6 @@ exports[`OrderContent should render without error, filled order 1`] = ` ...@@ -7,42 +7,6 @@ exports[`OrderContent should render without error, filled order 1`] = `
min-width: 0; min-width: 0;
} }
.c2 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c11 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 12px;
}
.c6 { .c6 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
...@@ -93,6 +57,42 @@ exports[`OrderContent should render without error, filled order 1`] = ` ...@@ -93,6 +57,42 @@ exports[`OrderContent should render without error, filled order 1`] = `
background-color: #22222212; background-color: #22222212;
} }
.c2 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c11 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 12px;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -463,42 +463,6 @@ exports[`OrderContent should render without error, limit order 1`] = ` ...@@ -463,42 +463,6 @@ exports[`OrderContent should render without error, limit order 1`] = `
min-width: 0; min-width: 0;
} }
.c2 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c11 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 12px;
}
.c6 { .c6 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
...@@ -621,6 +585,42 @@ exports[`OrderContent should render without error, limit order 1`] = ` ...@@ -621,6 +585,42 @@ exports[`OrderContent should render without error, limit order 1`] = `
background-color: transparent; background-color: transparent;
} }
.c2 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c11 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 12px;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -1035,42 +1035,6 @@ exports[`OrderContent should render without error, open order 1`] = ` ...@@ -1035,42 +1035,6 @@ exports[`OrderContent should render without error, open order 1`] = `
min-width: 0; min-width: 0;
} }
.c2 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c11 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 12px;
}
.c6 { .c6 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
...@@ -1174,6 +1138,42 @@ exports[`OrderContent should render without error, open order 1`] = ` ...@@ -1174,6 +1138,42 @@ exports[`OrderContent should render without error, open order 1`] = `
background-color: transparent; background-color: transparent;
} }
.c2 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c11 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 12px;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
......
...@@ -7,6 +7,29 @@ exports[`LimitDetailActivityRow should render with valid details 1`] = ` ...@@ -7,6 +7,29 @@ exports[`LimitDetailActivityRow should render with valid details 1`] = `
min-width: 0; min-width: 0;
} }
.c5 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c10 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c11 {
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c1 { .c1 {
width: 100%; width: 100%;
display: -webkit-box; display: -webkit-box;
...@@ -42,29 +65,6 @@ exports[`LimitDetailActivityRow should render with valid details 1`] = ` ...@@ -42,29 +65,6 @@ exports[`LimitDetailActivityRow should render with valid details 1`] = `
gap: 4px; gap: 4px;
} }
.c5 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c10 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c11 {
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c6 { .c6 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
......
...@@ -3,7 +3,9 @@ import MobileAppLogo from 'assets/svg/uniswap_app_logo.svg' ...@@ -3,7 +3,9 @@ import MobileAppLogo from 'assets/svg/uniswap_app_logo.svg'
import { useConnect } from 'hooks/useConnect' import { useConnect } from 'hooks/useConnect'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { CloseIcon } from 'theme/components' import { CloseIcon } from 'theme/components'
import { AdaptiveWebModal, Button, Flex, Image, QRCodeDisplay, Separator, Text, useSporeColors } from 'ui/src' import { Button, Flex, Image, QRCodeDisplay, Separator, Text, useSporeColors } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { useTranslation } from 'uniswap/src/i18n' import { useTranslation } from 'uniswap/src/i18n'
import { isWebAndroid, isWebIOS } from 'utilities/src/platform' import { isWebAndroid, isWebIOS } from 'utilities/src/platform'
...@@ -47,7 +49,7 @@ export default function UniwalletModal() { ...@@ -47,7 +49,7 @@ export default function UniwalletModal() {
const colors = useSporeColors() const colors = useSporeColors()
return ( return (
<AdaptiveWebModal isOpen={open} onClose={close} p={0}> <Modal name={ModalName.UniWalletConnect} isModalOpen={open} onClose={close} padding={0}>
<Flex shrink grow p="$spacing20"> <Flex shrink grow p="$spacing20">
<Flex row justifyContent="space-between"> <Flex row justifyContent="space-between">
<Text variant="subheading1">{t('account.drawer.modal.scan')}</Text> <Text variant="subheading1">{t('account.drawer.modal.scan')}</Text>
...@@ -87,6 +89,6 @@ export default function UniwalletModal() { ...@@ -87,6 +89,6 @@ export default function UniwalletModal() {
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
</AdaptiveWebModal> </Modal>
) )
} }
...@@ -39,6 +39,95 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = ` ...@@ -39,6 +39,95 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = `
flex: 1; flex: 1;
} }
.c36 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c37 {
-webkit-text-decoration: none;
text-decoration: none;
cursor: pointer;
-webkit-transition-duration: 125ms;
transition-duration: 125ms;
color: #FC72FF;
stroke: #FC72FF;
font-weight: 500;
}
.c37:hover {
opacity: 0.6;
}
.c37:active {
opacity: 0.4;
}
.c4 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
.c10 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 16px;
}
.c11 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c26 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
.c8 { .c8 {
width: 100%; width: 100%;
display: -webkit-box; display: -webkit-box;
...@@ -158,95 +247,6 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = ` ...@@ -158,95 +247,6 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = `
margin: !important; margin: !important;
} }
.c36 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c37 {
-webkit-text-decoration: none;
text-decoration: none;
cursor: pointer;
-webkit-transition-duration: 125ms;
transition-duration: 125ms;
color: #FC72FF;
stroke: #FC72FF;
font-weight: 500;
}
.c37:hover {
opacity: 0.6;
}
.c37:active {
opacity: 0.4;
}
.c4 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
.c10 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 16px;
}
.c11 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c26 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
.c32 { .c32 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -730,13 +730,16 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = ` ...@@ -730,13 +730,16 @@ exports[`AccountDrawer tests AccountDrawer default styles 1`] = `
class="c6" class="c6"
data-testid="wallet-modal" data-testid="wallet-modal"
> >
<div
class="_display-flex _alignItems-stretch _flexBasis-auto _boxSizing-border-box _position-relative _minHeight-0px _minWidth-0px _flexShrink-0 _flexDirection-column"
/>
<span <span
class="font_unset _display-inline _boxSizing-border-box _wordWrap-break-word _whiteSpace-pre-wrap _position-absolute _width-1px _height-1px _mt--1px _mr--1px _mb--1px _ml--1px _zIndex--10000 _overflowX-hidden _overflowY-hidden _opacity-1e-8 _pointerEvents-none" class="font_unset _display-inline _boxSizing-border-box _wordWrap-break-word _whiteSpace-pre-wrap _position-absolute _width-1px _height-1px _mt--1px _mr--1px _mb--1px _ml--1px _zIndex--10000 _overflowX-hidden _overflowY-hidden _opacity-1e-8 _pointerEvents-none"
> >
<h2 <h2
class="is_DialogTitle font_heading _display-inline _boxSizing-border-box _wordWrap-break-word _fontFamily-f-family _color-color _fontWeight-f-weight-me3083741 _fontSize-f-size-medi3736 _lineHeight-f-lineHeigh507465454 _userSelect-auto _whiteSpace-normal _mt-0px _mr-0px _mb-0px _ml-0px" class="is_DialogTitle font_heading _display-inline _boxSizing-border-box _wordWrap-break-word _fontFamily-f-family _color-color _fontWeight-f-weight-me3083741 _fontSize-f-size-medi3736 _lineHeight-f-lineHeigh507465454 _userSelect-auto _whiteSpace-normal _mt-0px _mr-0px _mb-0px _ml-0px"
data-disable-theme="true" data-disable-theme="true"
id="title-:r0:" id="title-:r1:"
role="heading" role="heading"
/> />
</span> </span>
......
...@@ -7,11 +7,13 @@ import { useCallback } from 'react' ...@@ -7,11 +7,13 @@ import { useCallback } from 'react'
import { useModalIsOpen, useOpenModal, useToggleModal } from 'state/application/hooks' import { useModalIsOpen, useOpenModal, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import { ThemedText } from 'theme/components' import { ThemedText } from 'theme/components'
import { AdaptiveWebModal, Flex, QRCodeDisplay, Text, useSporeColors } from 'ui/src' import { Flex, QRCodeDisplay, Text, useSporeColors } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal'
import { NetworkLogos } from 'uniswap/src/components/network/NetworkLogos' import { NetworkLogos } from 'uniswap/src/components/network/NetworkLogos'
import { useAddressColorProps } from 'uniswap/src/features/address/color' import { useAddressColorProps } from 'uniswap/src/features/address/color'
import { useOrderedChainIds } from 'uniswap/src/features/chains/hooks/useOrderedChainIds' import { useOrderedChainIds } from 'uniswap/src/features/chains/hooks/useOrderedChainIds'
import { SUPPORTED_CHAIN_IDS } from 'uniswap/src/features/chains/types' import { SUPPORTED_CHAIN_IDS } from 'uniswap/src/features/chains/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
...@@ -35,7 +37,7 @@ export function AddressQRModal({ accountAddress }: { accountAddress: Address }) ...@@ -35,7 +37,7 @@ export function AddressQRModal({ accountAddress }: { accountAddress: Address })
}, [toggleModal, openReceiveCryptoModal]) }, [toggleModal, openReceiveCryptoModal])
return ( return (
<AdaptiveWebModal isOpen={isOpen} onClose={toggleModal} width={420}> <Modal isModalOpen={isOpen} onClose={toggleModal} maxWidth={420} name={ModalName.AddressQR}>
<Flex pb="$spacing16" gap="$spacing24"> <Flex pb="$spacing16" gap="$spacing24">
<GetHelpHeader goBack={goBack} closeModal={toggleModal} /> <GetHelpHeader goBack={goBack} closeModal={toggleModal} />
<Flex gap="$spacing12"> <Flex gap="$spacing12">
...@@ -76,6 +78,6 @@ export function AddressQRModal({ accountAddress }: { accountAddress: Address }) ...@@ -76,6 +78,6 @@ export function AddressQRModal({ accountAddress }: { accountAddress: Address })
<NetworkLogos chains={orderedChainIds} /> <NetworkLogos chains={orderedChainIds} />
</Flex> </Flex>
</Flex> </Flex>
</AdaptiveWebModal> </Modal>
) )
} }
import { RowBetween } from 'components/deprecated/Row'
import styled, { DefaultTheme } from 'lib/styled-components' import styled, { DefaultTheme } from 'lib/styled-components'
import { darken } from 'polished' import { darken } from 'polished'
import { forwardRef } from 'react' import { forwardRef } from 'react'
import { ChevronDown } from 'react-feather'
import { ButtonProps as ButtonPropsOriginal, Button as RebassButton } from 'rebass/styled-components' import { ButtonProps as ButtonPropsOriginal, Button as RebassButton } from 'rebass/styled-components'
export { default as LoadingButtonSpinner } from './LoadingButtonSpinner' export { default as LoadingButtonSpinner } from './LoadingButtonSpinner'
...@@ -306,17 +304,6 @@ export function ButtonError({ error, ...rest }: { error?: boolean } & BaseButton ...@@ -306,17 +304,6 @@ export function ButtonError({ error, ...rest }: { error?: boolean } & BaseButton
} }
} }
export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
return (
<ButtonOutlined {...rest} disabled={disabled}>
<RowBetween>
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
<ChevronDown size={24} />
</RowBetween>
</ButtonOutlined>
)
}
export enum ButtonSize { export enum ButtonSize {
small, small,
medium, medium,
......
import { Currency } from '@uniswap/sdk-core' import { Currency, Percent } from '@uniswap/sdk-core'
import { AxisRight } from 'components/Charts/ActiveLiquidityChart/AxisRight' import { AxisRight } from 'components/Charts/ActiveLiquidityChart/AxisRight'
import { Brush2 } from 'components/Charts/ActiveLiquidityChart/Brush2' import { Brush2 } from 'components/Charts/ActiveLiquidityChart/Brush2'
import { HorizontalArea } from 'components/Charts/ActiveLiquidityChart/HorizontalArea' import { HorizontalArea } from 'components/Charts/ActiveLiquidityChart/HorizontalArea'
...@@ -7,7 +7,9 @@ import { TickTooltip } from 'components/Charts/ActiveLiquidityChart/TickTooltip' ...@@ -7,7 +7,9 @@ import { TickTooltip } from 'components/Charts/ActiveLiquidityChart/TickTooltip'
import { ChartEntry } from 'components/LiquidityChartRangeInput/types' import { ChartEntry } from 'components/LiquidityChartRangeInput/types'
import { max as getMax, scaleLinear } from 'd3' import { max as getMax, scaleLinear } from 'd3'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { useSporeColors } from 'ui/src' import { Flex, Text, useSporeColors } from 'ui/src'
import { opacify } from 'ui/src/theme'
import { useFormatter } from 'utils/formatNumbers'
const xAccessor = (d: ChartEntry) => d.activeLiquidity const xAccessor = (d: ChartEntry) => d.activeLiquidity
const yAccessor = (d: ChartEntry) => d.price0 const yAccessor = (d: ChartEntry) => d.price0
...@@ -53,6 +55,11 @@ function findClosestElementBinarySearch(data: ChartEntry[], target?: number) { ...@@ -53,6 +55,11 @@ function findClosestElementBinarySearch(data: ChartEntry[], target?: number) {
return closestElement return closestElement
} }
function scaleToInteger(a: number, precision = 18) {
const scaleFactor = Math.pow(10, precision)
return Math.round(a * scaleFactor)
}
/** /**
* A horizontal version of the active liquidity area chart, which uses the * A horizontal version of the active liquidity area chart, which uses the
* x-y coordinate plane to show the data, but with the axes flipped so lower * x-y coordinate plane to show the data, but with the axes flipped so lower
...@@ -69,6 +76,8 @@ export function ActiveLiquidityChart2({ ...@@ -69,6 +76,8 @@ export function ActiveLiquidityChart2({
brushDomain, brushDomain,
onBrushDomainChange, onBrushDomainChange,
disableBrushInteraction, disableBrushInteraction,
showDiffIndicators,
isMobile,
}: { }: {
id?: string id?: string
currency0: Currency currency0: Currency
...@@ -80,10 +89,13 @@ export function ActiveLiquidityChart2({ ...@@ -80,10 +89,13 @@ export function ActiveLiquidityChart2({
max?: number max?: number
} }
disableBrushInteraction?: boolean disableBrushInteraction?: boolean
showDiffIndicators?: boolean
dimensions: { width: number; height: number; contentWidth: number; axisLabelPaneWidth: number } dimensions: { width: number; height: number; contentWidth: number; axisLabelPaneWidth: number }
brushDomain?: [number, number] brushDomain?: [number, number]
onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void
isMobile?: boolean
}) { }) {
const { formatPercent } = useFormatter()
const colors = useSporeColors() const colors = useSporeColors()
const svgRef = useRef<SVGSVGElement | null>(null) const svgRef = useRef<SVGSVGElement | null>(null)
const [hoverY, setHoverY] = useState<number>() const [hoverY, setHoverY] = useState<number>()
...@@ -124,6 +136,9 @@ export function ActiveLiquidityChart2({ ...@@ -124,6 +136,9 @@ export function ActiveLiquidityChart2({
} }
}, [brushDomain, onBrushDomainChange, yScale]) }, [brushDomain, onBrushDomainChange, yScale])
const southHandleInView = brushDomain && yScale(brushDomain[0]) >= 0 && yScale(brushDomain[0]) <= height
const northHandleInView = brushDomain && yScale(brushDomain[1]) >= 0 && yScale(brushDomain[1]) <= height
return ( return (
<> <>
{hoverY && hoveredTick && ( {hoverY && hoveredTick && (
...@@ -138,6 +153,42 @@ export function ActiveLiquidityChart2({ ...@@ -138,6 +153,42 @@ export function ActiveLiquidityChart2({
currency1={currency1} currency1={currency1}
/> />
)} )}
{showDiffIndicators && (
<>
{southHandleInView && (
<Flex
borderRadius="$rounded12"
backgroundColor="$surface2"
borderColor="$surface3"
borderWidth={1}
p="$padding8"
position="absolute"
left={0}
top={yScale(brushDomain[0]) - 16}
>
<Text variant="body4">
{formatPercent(new Percent(scaleToInteger(brushDomain[0] - current), scaleToInteger(current)))}
</Text>
</Flex>
)}
{northHandleInView && (
<Flex
borderRadius="$rounded12"
backgroundColor="$surface2"
borderColor="$surface3"
borderWidth={1}
p="$padding8"
position="absolute"
left={0}
top={yScale(brushDomain[1]) - 16}
>
<Text variant="body4">
{formatPercent(new Percent(scaleToInteger(brushDomain[1] - current), scaleToInteger(current)))}
</Text>
</Flex>
)}
</>
)}
<svg <svg
ref={svgRef} ref={svgRef}
width="100%" width="100%"
...@@ -186,8 +237,8 @@ export function ActiveLiquidityChart2({ ...@@ -186,8 +237,8 @@ export function ActiveLiquidityChart2({
xValue={xAccessor} xValue={xAccessor}
yValue={yAccessor} yValue={yAccessor}
brushDomain={brushDomain} brushDomain={brushDomain}
fill={brushDomain ? colors.neutral1.val : colors.accent1.val} fill={opacify(isMobile ? 10 : 100, brushDomain ? colors.neutral1.val : colors.accent1.val)}
selectedFill={colors.accent1.val} selectedFill={opacify(isMobile ? 10 : 100, colors.accent1.val)}
containerHeight={height} containerHeight={height}
containerWidth={width - axisLabelPaneWidth} containerWidth={width - axisLabelPaneWidth}
/> />
...@@ -209,14 +260,16 @@ export function ActiveLiquidityChart2({ ...@@ -209,14 +260,16 @@ export function ActiveLiquidityChart2({
/> />
)} )}
<AxisRight {isMobile ? null : (
yScale={yScale} <AxisRight
offset={width - contentWidth} yScale={yScale}
min={brushDomain?.[0]} offset={width - contentWidth}
current={current} min={brushDomain?.[0]}
max={brushDomain?.[1]} current={current}
height={height} max={brushDomain?.[1]}
/> height={height}
/>
)}
</g> </g>
<Brush2 <Brush2
......
import { NumberValue, ScaleLinear, axisRight, Axis as d3Axis, select } from 'd3' import { NumberValue, ScaleLinear, axisRight, Axis as d3Axis, select } from 'd3'
import styled from 'lib/styled-components' import styled from 'lib/styled-components'
import { useMemo } from 'react' import { useMemo } from 'react'
import { NumberType, useFormatter } from 'utils/formatNumbers'
const StyledGroup = styled.g` const StyledGroup = styled.g`
line { line {
...@@ -12,7 +13,7 @@ const StyledGroup = styled.g` ...@@ -12,7 +13,7 @@ const StyledGroup = styled.g`
} }
` `
const TEXT_Y_OFFSET = 10 const TEXT_Y_OFFSET = 5
const Axis = ({ const Axis = ({
axisGenerator, axisGenerator,
...@@ -61,6 +62,7 @@ export const AxisRight = ({ ...@@ -61,6 +62,7 @@ export const AxisRight = ({
current?: number current?: number
max?: number max?: number
}) => { }) => {
const { formatNumber } = useFormatter()
const tickValues = useMemo(() => { const tickValues = useMemo(() => {
const minCoordinate = min ? yScale(min) : undefined const minCoordinate = min ? yScale(min) : undefined
const maxCoordinate = max ? yScale(max) : undefined const maxCoordinate = max ? yScale(max) : undefined
...@@ -76,7 +78,18 @@ export const AxisRight = ({ ...@@ -76,7 +78,18 @@ export const AxisRight = ({
return ( return (
<StyledGroup transform={`translate(${offset}, 0)`}> <StyledGroup transform={`translate(${offset}, 0)`}>
<Axis axisGenerator={axisRight(yScale).tickValues(tickValues)} height={height} yScale={yScale} /> <Axis
axisGenerator={axisRight(yScale)
.tickValues(tickValues)
.tickFormat((d) =>
formatNumber({
input: d as number,
type: NumberType.TokenQuantityStats,
}),
)}
height={height}
yScale={yScale}
/>
</StyledGroup> </StyledGroup>
) )
} }
...@@ -68,13 +68,17 @@ export const Brush2 = ({ ...@@ -68,13 +68,17 @@ export const Brush2 = ({
// keep local and external brush extent in sync // keep local and external brush extent in sync
// i.e. snap to ticks on brush end // i.e. snap to ticks on brush end
const [brushInProgress, setBrushInProgress] = useState(false)
useEffect(() => { useEffect(() => {
if (brushInProgress) {
return
}
setLocalBrushExtent(brushExtent) setLocalBrushExtent(brushExtent)
}, [brushExtent]) }, [brushExtent, brushInProgress])
// initialize the brush // initialize the brush
useEffect(() => { useEffect(() => {
if (!brushRef.current) { if (!brushRef.current || brushInProgress) {
return return
} }
...@@ -90,8 +94,14 @@ export const Brush2 = ({ ...@@ -90,8 +94,14 @@ export const Brush2 = ({
]) ])
.handleSize(30) .handleSize(30)
.filter(() => interactive) .filter(() => interactive)
.filter((event) => {
// Allow interactions only if the event target is part of the brush selection or handles
const target = event.target as SVGElement
return target.classList.contains('selection') || target.classList.contains('handle')
})
.on('brush', (event: D3BrushEvent<unknown>) => { .on('brush', (event: D3BrushEvent<unknown>) => {
const { selection } = event const { selection } = event
setBrushInProgress(true)
if (!selection) { if (!selection) {
setLocalBrushExtent(null) setLocalBrushExtent(null)
...@@ -116,6 +126,7 @@ export const Brush2 = ({ ...@@ -116,6 +126,7 @@ export const Brush2 = ({
setBrushExtent(priceExtent, mode) setBrushExtent(priceExtent, mode)
} }
setLocalBrushExtent(priceExtent) setLocalBrushExtent(priceExtent)
setBrushInProgress(false)
}) })
brushBehavior.current(select(brushRef.current)) brushBehavior.current(select(brushRef.current))
...@@ -126,6 +137,8 @@ export const Brush2 = ({ ...@@ -126,6 +137,8 @@ export const Brush2 = ({
.call(brushBehavior.current.move as any, scaledExtent) .call(brushBehavior.current.move as any, scaledExtent)
} }
select(brushRef.current).selectAll('.overlay').attr('cursor', 'default')
// brush linear gradient // brush linear gradient
select(brushRef.current) select(brushRef.current)
.selectAll('.selection') .selectAll('.selection')
...@@ -133,7 +146,7 @@ export const Brush2 = ({ ...@@ -133,7 +146,7 @@ export const Brush2 = ({
.attr('fill-opacity', '0.1') .attr('fill-opacity', '0.1')
.attr('fill', `url(#${id}-gradient-selection)`) .attr('fill', `url(#${id}-gradient-selection)`)
.attr('cursor', 'grab') .attr('cursor', 'grab')
}, [brushExtent, id, height, interactive, previousBrushExtent, yScale, width, setBrushExtent]) }, [brushExtent, id, height, interactive, previousBrushExtent, yScale, width, setBrushExtent, brushInProgress])
// respond to yScale changes only // respond to yScale changes only
useEffect(() => { useEffect(() => {
......
...@@ -3,7 +3,6 @@ import { ScaleLinear } from 'd3' ...@@ -3,7 +3,6 @@ import { ScaleLinear } from 'd3'
import styled from 'lib/styled-components' import styled from 'lib/styled-components'
const Bar = styled.rect<{ fill?: string }>` const Bar = styled.rect<{ fill?: string }>`
opacity: 0.5;
stroke: ${({ fill, theme }) => fill ?? theme.accent1}; stroke: ${({ fill, theme }) => fill ?? theme.accent1};
fill: ${({ fill, theme }) => fill ?? theme.accent1}; fill: ${({ fill, theme }) => fill ?? theme.accent1};
` `
......
...@@ -12,7 +12,6 @@ import { ...@@ -12,7 +12,6 @@ import {
} from 'components/Charts/ChartModel' } from 'components/Charts/ChartModel'
import { PriceChartData } from 'components/Charts/PriceChart' import { PriceChartData } from 'components/Charts/PriceChart'
import { PriceChartType, formatTickMarks } from 'components/Charts/utils' import { PriceChartType, formatTickMarks } from 'components/Charts/utils'
import { MissingDataIcon } from 'components/Table/icons'
import { DataQuality } from 'components/Tokens/TokenDetails/ChartSection/util' import { DataQuality } from 'components/Tokens/TokenDetails/ChartSection/util'
import { usePoolPriceChartData } from 'hooks/usePoolPriceChartData' import { usePoolPriceChartData } from 'hooks/usePoolPriceChartData'
import { useTheme } from 'lib/styled-components' import { useTheme } from 'lib/styled-components'
...@@ -25,12 +24,11 @@ import { ...@@ -25,12 +24,11 @@ import {
} from 'pages/Pool/Positions/create/utils' } from 'pages/Pool/Positions/create/utils'
import { useLayoutEffect, useMemo, useRef, useState } from 'react' import { useLayoutEffect, useMemo, useRef, useState } from 'react'
import { opacify } from 'theme/utils' import { opacify } from 'theme/utils'
import { Flex, FlexProps, Shine, TamaguiElement, Text, assertWebElement } from 'ui/src' import { Flex, FlexProps, Shine, TamaguiElement, assertWebElement } from 'ui/src'
import { LoadingPriceCurve } from 'ui/src/components/icons/LoadingPriceCurve' import { LoadingPriceCurve } from 'ui/src/components/icons/LoadingPriceCurve'
import { HistoryDuration } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { HistoryDuration } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { getChainInfo } from 'uniswap/src/features/chains/chainInfo' import { getChainInfo } from 'uniswap/src/features/chains/chainInfo'
import { UniverseChainId } from 'uniswap/src/features/chains/types' import { UniverseChainId } from 'uniswap/src/features/chains/types'
import { useTranslation } from 'uniswap/src/i18n'
const CHART_HEIGHT = 52 const CHART_HEIGHT = 52
export const CHART_WIDTH = 224 export const CHART_WIDTH = 224
...@@ -115,7 +113,7 @@ export class LPPriceChartModel extends ChartModel<PriceChartData> { ...@@ -115,7 +113,7 @@ export class LPPriceChartModel extends ChartModel<PriceChartData> {
Math.pow(10, params.positionPriceLower.baseCurrency.decimals), Math.pow(10, params.positionPriceLower.baseCurrency.decimals),
), ),
) )
?.toSignificant(params.positionPriceLower.baseCurrency.decimals) ?? 0, ?.toSignificant(params.positionPriceLower.baseCurrency.decimals || 6) ?? 0,
) )
this.positionRangeMax = this.positionRangeMax =
typeof params.positionPriceUpper === 'number' typeof params.positionPriceUpper === 'number'
...@@ -128,7 +126,7 @@ export class LPPriceChartModel extends ChartModel<PriceChartData> { ...@@ -128,7 +126,7 @@ export class LPPriceChartModel extends ChartModel<PriceChartData> {
Math.pow(10, params.positionPriceUpper.baseCurrency.decimals), Math.pow(10, params.positionPriceUpper.baseCurrency.decimals),
), ),
) )
?.toSignificant(params.positionPriceUpper.baseCurrency.decimals) ?? 0, ?.toSignificant(params.positionPriceUpper.baseCurrency.decimals || 6) ?? 0,
) )
if (isEffectivelyInfinity(this.positionRangeMin)) { if (isEffectivelyInfinity(this.positionRangeMin)) {
...@@ -392,7 +390,6 @@ export function LiquidityPositionRangeChart({ ...@@ -392,7 +390,6 @@ export function LiquidityPositionRangeChart({
grow = false, grow = false,
}: LiquidityPositionRangeChartProps) { }: LiquidityPositionRangeChartProps) {
const theme = useTheme() const theme = useTheme()
const { t } = useTranslation()
const isV2 = version === ProtocolVersion.V2 const isV2 = version === ProtocolVersion.V2
const isV3 = version === ProtocolVersion.V3 const isV3 = version === ProtocolVersion.V3
const isV4 = version === ProtocolVersion.V4 const isV4 = version === ProtocolVersion.V4
...@@ -477,14 +474,7 @@ export function LiquidityPositionRangeChart({ ...@@ -477,14 +474,7 @@ export function LiquidityPositionRangeChart({
overflow="hidden" overflow="hidden"
> >
{priceData.loading && <LiquidityPositionRangeChartLoader size={chartWidth} />} {priceData.loading && <LiquidityPositionRangeChartLoader size={chartWidth} />}
{dataUnavailable && ( {dataUnavailable && <LoadingPriceCurve size={chartWidth} color="$neutral2" />}
<Flex row alignItems="center" gap="$gap12">
<MissingDataIcon height={36} width={36} />
<Text variant="body3" color="$neutral2">
{t('common.dataUnavailable')}
</Text>
</Flex>
)}
{shouldRenderChart && ( {shouldRenderChart && (
<Flex width={grow ? chartWidth : width} $md={{ width: grow ? chartWidth : '100%' }}> <Flex width={grow ? chartWidth : width} $md={{ width: grow ? chartWidth : '100%' }}>
<Chart Model={LPPriceChartModel} params={chartParams} height={CHART_HEIGHT} /> <Chart Model={LPPriceChartModel} params={chartParams} height={CHART_HEIGHT} />
......
import { isMobileWeb } from 'utilities/src/platform'
const RIGHT_AXIS_WIDTH = 64
const CHART_CONTAINER_WIDTH = 452 + RIGHT_AXIS_WIDTH
const LIQUIDITY_CHART_WIDTH = 68
const INTER_CHART_PADDING = 12
const CHART_HEIGHT = 164
const BOTTOM_AXIS_HEIGHT = 28
const loadedPriceChartWidth = CHART_CONTAINER_WIDTH - LIQUIDITY_CHART_WIDTH - INTER_CHART_PADDING - RIGHT_AXIS_WIDTH
const desktopSizes = {
rightAxisWidth: RIGHT_AXIS_WIDTH,
chartContainerWidth: CHART_CONTAINER_WIDTH,
liquidityChartWidth: LIQUIDITY_CHART_WIDTH,
interChartPadding: INTER_CHART_PADDING,
chartHeight: CHART_HEIGHT,
bottomAxisHeight: BOTTOM_AXIS_HEIGHT,
loadedPriceChartWidth,
}
const mobileSizes = {
rightAxisWidth: 0,
chartContainerWidth: 290,
liquidityChartWidth: 48,
interChartPadding: 0,
chartHeight: CHART_HEIGHT,
bottomAxisHeight: BOTTOM_AXIS_HEIGHT,
loadedPriceChartWidth: 290,
}
export function useRangeInputSizes(parentWidth?: number) {
return isMobileWeb
? {
...mobileSizes,
chartContainerWidth: parentWidth ?? mobileSizes.chartContainerWidth,
loadedPriceChartWidth: parentWidth ?? mobileSizes.loadedPriceChartWidth,
}
: desktopSizes
}
...@@ -2,8 +2,10 @@ import { InterfaceModalName } from '@uniswap/analytics-events' ...@@ -2,8 +2,10 @@ import { InterfaceModalName } from '@uniswap/analytics-events'
import { AutoColumn } from 'components/deprecated/Column' import { AutoColumn } from 'components/deprecated/Column'
import styled from 'lib/styled-components' import styled from 'lib/styled-components'
import { PropsWithChildren } from 'react' import { PropsWithChildren } from 'react'
import { AdaptiveWebModal, HeightAnimator } from 'ui/src' import { HeightAnimator } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
const Content = styled(AutoColumn)` const Content = styled(AutoColumn)`
background-color: ${({ theme }) => theme.surface1}; background-color: ${({ theme }) => theme.surface1};
...@@ -20,7 +22,7 @@ export function SwapModal({ ...@@ -20,7 +22,7 @@ export function SwapModal({
}>) { }>) {
return ( return (
<Trace modal={InterfaceModalName.CONFIRM_SWAP}> <Trace modal={InterfaceModalName.CONFIRM_SWAP}>
<AdaptiveWebModal isOpen onClose={onDismiss} maxHeight="90vh" p={0}> <Modal name={ModalName.SwapReview} isModalOpen onClose={onDismiss} maxHeight="90vh" padding={0}>
<HeightAnimator <HeightAnimator
open={true} open={true}
width="100%" width="100%"
...@@ -35,7 +37,7 @@ export function SwapModal({ ...@@ -35,7 +37,7 @@ export function SwapModal({
> >
<Content>{children}</Content> <Content>{children}</Content>
</HeightAnimator> </HeightAnimator>
</AdaptiveWebModal> </Modal>
</Trace> </Trace>
) )
} }
...@@ -50,41 +50,6 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap ...@@ -50,41 +50,6 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap
min-height: 24px; min-height: 24px;
} }
.c13 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 8px;
}
.c15 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
}
.c10 { .c10 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
...@@ -120,6 +85,41 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap ...@@ -120,6 +85,41 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap
opacity: 0.4; opacity: 0.4;
} }
.c13 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 8px;
}
.c15 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -372,41 +372,6 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap ...@@ -372,41 +372,6 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap
min-height: 24px; min-height: 24px;
} }
.c13 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 8px;
}
.c15 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
}
.c10 { .c10 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
...@@ -442,6 +407,41 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap ...@@ -442,6 +407,41 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap
opacity: 0.4; opacity: 0.4;
} }
.c13 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 8px;
}
.c15 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -714,6 +714,22 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap ...@@ -714,6 +714,22 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap
min-height: 24px; min-height: 24px;
} }
.c12 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c13 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c15 { .c15 {
width: 100%; width: 100%;
display: -webkit-box; display: -webkit-box;
...@@ -749,22 +765,6 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap ...@@ -749,22 +765,6 @@ exports[`Pending - classic trade titles renders classic trade correctly, with ap
justify-content: center; justify-content: center;
} }
.c12 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c13 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -1003,6 +1003,22 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app ...@@ -1003,6 +1003,22 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app
min-width: 0; min-width: 0;
} }
.c10 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c11 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c13 { .c13 {
width: 100%; width: 100%;
display: -webkit-box; display: -webkit-box;
...@@ -1021,22 +1037,6 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app ...@@ -1021,22 +1037,6 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app
gap: 8px; gap: 8px;
} }
.c10 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c11 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -1272,41 +1272,6 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app ...@@ -1272,41 +1272,6 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app
min-height: 24px; min-height: 24px;
} }
.c13 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 8px;
}
.c15 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
}
.c10 { .c10 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
...@@ -1342,6 +1307,41 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app ...@@ -1342,6 +1307,41 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app
opacity: 0.4; opacity: 0.4;
} }
.c13 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 8px;
}
.c15 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -1614,6 +1614,22 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app ...@@ -1614,6 +1614,22 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app
min-height: 24px; min-height: 24px;
} }
.c12 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c13 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c15 { .c15 {
width: 100%; width: 100%;
display: -webkit-box; display: -webkit-box;
...@@ -1649,22 +1665,6 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app ...@@ -1649,22 +1665,6 @@ exports[`Pending - uniswapX trade titles renders limit order correctly, with app
justify-content: center; justify-content: center;
} }
.c12 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c13 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
-ms-letter-spacing: -0.01em;
letter-spacing: -0.01em;
}
.c0 { .c0 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
......
...@@ -22,6 +22,7 @@ import { useSwapTransactionStatus } from 'state/transactions/hooks' ...@@ -22,6 +22,7 @@ import { useSwapTransactionStatus } from 'state/transactions/hooks'
import { ThemeProvider } from 'theme' import { ThemeProvider } from 'theme'
import { FadePresence } from 'theme/components/FadePresence' import { FadePresence } from 'theme/components/FadePresence'
import { UniswapXOrderStatus } from 'types/uniswapx' import { UniswapXOrderStatus } from 'types/uniswapx'
// eslint-disable-next-line no-restricted-imports
import { ADAPTIVE_MODAL_ANIMATION_DURATION } from 'ui/src/components/modal/AdaptiveWebModal' import { ADAPTIVE_MODAL_ANIMATION_DURATION } from 'ui/src/components/modal/AdaptiveWebModal'
import { TransactionStatus } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { TransactionStatus } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
......
...@@ -2,7 +2,9 @@ import Column from 'components/deprecated/Column' ...@@ -2,7 +2,9 @@ import Column from 'components/deprecated/Column'
import styled, { useTheme } from 'lib/styled-components' import styled, { useTheme } from 'lib/styled-components'
import { Slash } from 'react-feather' import { Slash } from 'react-feather'
import { CopyHelper, ExternalLink, ThemedText } from 'theme/components' import { CopyHelper, ExternalLink, ThemedText } from 'theme/components'
import { AdaptiveWebModal, Flex, Text } from 'ui/src' import { Flex, Text } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
const ContentWrapper = styled(Column)` const ContentWrapper = styled(Column)`
...@@ -19,7 +21,7 @@ interface ConnectedAccountBlockedProps { ...@@ -19,7 +21,7 @@ interface ConnectedAccountBlockedProps {
export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedProps) { export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedProps) {
const theme = useTheme() const theme = useTheme()
return ( return (
<AdaptiveWebModal isOpen={props.isOpen} onClose={Function.prototype()} p={0}> <Modal name={ModalName.AccountBlocked} isModalOpen={props.isOpen} onClose={Function.prototype()} padding={0}>
<ContentWrapper> <ContentWrapper>
<Slash size="22px" color={theme.neutral2} /> <Slash size="22px" color={theme.neutral2} />
<ThemedText.DeprecatedLargeHeader lineHeight={2} marginBottom={1} marginTop={1}> <ThemedText.DeprecatedLargeHeader lineHeight={2} marginBottom={1} marginTop={1}>
...@@ -55,6 +57,6 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr ...@@ -55,6 +57,6 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr
/> />
</ThemedText.DeprecatedMain> </ThemedText.DeprecatedMain>
</ContentWrapper> </ContentWrapper>
</AdaptiveWebModal> </Modal>
) )
} }
...@@ -343,7 +343,7 @@ exports[`LimitPriceInputPanel should render correct subheader with inputCurrency ...@@ -343,7 +343,7 @@ exports[`LimitPriceInputPanel should render correct subheader with inputCurrency
<h2 <h2
class="is_DialogTitle font_heading _display-inline _boxSizing-border-box _wordWrap-break-word _fontFamily-f-family _color-color _fontWeight-f-weight-me3083741 _fontSize-f-size-medi3736 _lineHeight-f-lineHeigh507465454 _userSelect-auto _whiteSpace-normal _mt-0px _mr-0px _mb-0px _ml-0px" class="is_DialogTitle font_heading _display-inline _boxSizing-border-box _wordWrap-break-word _fontFamily-f-family _color-color _fontWeight-f-weight-me3083741 _fontSize-f-size-medi3736 _lineHeight-f-lineHeigh507465454 _userSelect-auto _whiteSpace-normal _mt-0px _mr-0px _mb-0px _ml-0px"
data-disable-theme="true" data-disable-theme="true"
id="title-:r1:" id="title-:r3:"
role="heading" role="heading"
/> />
</span> </span>
...@@ -696,7 +696,7 @@ exports[`LimitPriceInputPanel should render the component with no currencies sel ...@@ -696,7 +696,7 @@ exports[`LimitPriceInputPanel should render the component with no currencies sel
<h2 <h2
class="is_DialogTitle font_heading _display-inline _boxSizing-border-box _wordWrap-break-word _fontFamily-f-family _color-color _fontWeight-f-weight-me3083741 _fontSize-f-size-medi3736 _lineHeight-f-lineHeigh507465454 _userSelect-auto _whiteSpace-normal _mt-0px _mr-0px _mb-0px _ml-0px" class="is_DialogTitle font_heading _display-inline _boxSizing-border-box _wordWrap-break-word _fontFamily-f-family _color-color _fontWeight-f-weight-me3083741 _fontSize-f-size-medi3736 _lineHeight-f-lineHeigh507465454 _userSelect-auto _whiteSpace-normal _mt-0px _mr-0px _mb-0px _ml-0px"
data-disable-theme="true" data-disable-theme="true"
id="title-:r0:" id="title-:r1:"
role="heading" role="heading"
/> />
</span> </span>
......
...@@ -6,7 +6,8 @@ import styled, { DefaultTheme } from 'lib/styled-components' ...@@ -6,7 +6,8 @@ import styled, { DefaultTheme } from 'lib/styled-components'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { Gap } from 'theme' import { Gap } from 'theme'
import { ThemedText } from 'theme/components' import { ThemedText } from 'theme/components'
import { AdaptiveWebModal } from 'ui/src' import { Modal } from 'uniswap/src/components/modals/Modal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
export const Container = styled(ColumnCenter)` export const Container = styled(ColumnCenter)`
background-color: ${({ theme }) => theme.surface1}; background-color: ${({ theme }) => theme.surface1};
...@@ -160,11 +161,11 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }: ...@@ -160,11 +161,11 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }:
*/ */
export function Dialog(props: DialogProps) { export function Dialog(props: DialogProps) {
return ( return (
<AdaptiveWebModal isOpen={props.isVisible} onClose={props.onCancel} p={0}> <Modal name={ModalName.Dialog} isModalOpen={props.isVisible} onClose={props.onCancel} padding={0}>
<Container gap="lg"> <Container gap="lg">
<DialogHeader closeModal={props.onCancel} closeDataTestId="Dialog-closeButton" /> <DialogHeader closeModal={props.onCancel} closeDataTestId="Dialog-closeButton" />
<DialogContent {...props} /> <DialogContent {...props} />
</Container> </Container>
</AdaptiveWebModal> </Modal>
) )
} }
...@@ -8,7 +8,7 @@ import { X } from 'react-feather' ...@@ -8,7 +8,7 @@ import { X } from 'react-feather'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks' import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import { BREAKPOINTS } from 'theme' import { BREAKPOINTS } from 'theme'
import { AdaptiveWebModal } from 'ui/src' import { Modal } from 'uniswap/src/components/modals/Modal'
import { SUPPORTED_CHAIN_IDS } from 'uniswap/src/features/chains/types' import { SUPPORTED_CHAIN_IDS } from 'uniswap/src/features/chains/types'
import { import {
DynamicConfigKeys, DynamicConfigKeys,
...@@ -19,6 +19,7 @@ import { ...@@ -19,6 +19,7 @@ import {
import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags' import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags'
import { useFeatureFlagWithExposureLoggingDisabled } from 'uniswap/src/features/gating/hooks' import { useFeatureFlagWithExposureLoggingDisabled } from 'uniswap/src/features/gating/hooks'
import { Statsig } from 'uniswap/src/features/gating/sdk/statsig' import { Statsig } from 'uniswap/src/features/gating/sdk/statsig'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
const Wrapper = styled(Column)` const Wrapper = styled(Column)`
padding: 20px 16px; padding: 20px 16px;
...@@ -203,7 +204,7 @@ export default function FeatureFlagModal() { ...@@ -203,7 +204,7 @@ export default function FeatureFlagModal() {
const closeModal = useCloseModal() const closeModal = useCloseModal()
return ( return (
<AdaptiveWebModal isOpen={open} onClose={closeModal} p={0}> <Modal name={ModalName.FeatureFlags} isModalOpen={open} onClose={closeModal} padding={0}>
<Wrapper> <Wrapper>
<Header> <Header>
<Row width="100%" justify="space-between"> <Row width="100%" justify="space-between">
...@@ -292,6 +293,6 @@ export default function FeatureFlagModal() { ...@@ -292,6 +293,6 @@ export default function FeatureFlagModal() {
</FlagsColumn> </FlagsColumn>
<SaveButton onClick={() => window.location.reload()}>Reload</SaveButton> <SaveButton onClick={() => window.location.reload()}>Reload</SaveButton>
</Wrapper> </Wrapper>
</AdaptiveWebModal> </Modal>
) )
} }
...@@ -71,15 +71,18 @@ export default function FeeSelector({ ...@@ -71,15 +71,18 @@ export default function FeeSelector({
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB) const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB)
// get pool data on-chain for latest states // get pool data on-chain for latest states
const pools = usePools([ const pools = usePools(
[currencyA, currencyB, FeeAmount.LOWEST], [
[currencyA, currencyB, FeeAmount.LOW_200], [currencyA, currencyB, FeeAmount.LOWEST],
[currencyA, currencyB, FeeAmount.LOW_300], [currencyA, currencyB, FeeAmount.LOW_200],
[currencyA, currencyB, FeeAmount.LOW_400], [currencyA, currencyB, FeeAmount.LOW_300],
[currencyA, currencyB, FeeAmount.LOW], [currencyA, currencyB, FeeAmount.LOW_400],
[currencyA, currencyB, FeeAmount.MEDIUM], [currencyA, currencyB, FeeAmount.LOW],
[currencyA, currencyB, FeeAmount.HIGH], [currencyA, currencyB, FeeAmount.MEDIUM],
]) [currencyA, currencyB, FeeAmount.HIGH],
],
chainId,
)
const poolsByFeeTier: Record<FeeAmount, PoolState> = useMemo( const poolsByFeeTier: Record<FeeAmount, PoolState> = useMemo(
() => () =>
......
export const MOONPAY_SUPPORTED_CURRENCY_CODES = [
'eth',
'eth_arbitrum',
'eth_optimism',
'eth_polygon',
'eth_base',
'weth',
'wbtc',
'matic_polygon',
'polygon',
'usdc_arbitrum',
'usdc_optimism',
'usdc_polygon',
'usdc_base',
'usdc',
'usdt',
] as const
export type MoonpaySupportedCurrencyCode = (typeof MOONPAY_SUPPORTED_CURRENCY_CODES)[number]
import Circle from 'assets/images/blue-loader.svg'
import { MOONPAY_SUPPORTED_CURRENCY_CODES } from 'components/FiatOnrampModal/constants'
import { getDefaultCurrencyCode, parsePathParts } from 'components/FiatOnrampModal/utils'
import { useAccount } from 'hooks/useAccount'
import styled, { useTheme } from 'lib/styled-components'
import { useCallback, useEffect, useState } from 'react'
import { useHref } from 'react-router-dom'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { CustomLightSpinner, ThemedText } from 'theme/components'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { AdaptiveWebModal } from 'ui/src'
import { useUrlContext } from 'uniswap/src/contexts/UrlContext'
import { getChainInfo } from 'uniswap/src/features/chains/chainInfo'
import { Trans } from 'uniswap/src/i18n'
import { logger } from 'utilities/src/logger/logger'
import { getChainIdFromChainUrlParam } from 'utils/chainParams'
const MOONPAY_DARK_BACKGROUND = '#1c1c1e'
const Wrapper = styled.div<{ isDarkMode: boolean }>`
// #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023
background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)};
border-radius: 20px;
box-shadow: ${({ theme }) => theme.deprecated_deepShadow};
display: flex;
flex-flow: column nowrap;
margin: 0;
flex: 1 1;
min-width: 375px;
position: relative;
width: 100%;
`
const ErrorText = styled(ThemedText.BodyPrimary)`
color: ${({ theme }) => theme.critical};
margin: auto !important;
text-align: center;
width: 90%;
`
const StyledIframe = styled.iframe<{ isDarkMode: boolean }>`
// #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023
background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)};
border-radius: 12px;
bottom: 0;
left: 0;
height: calc(100% - 16px);
margin: 8px;
padding: 0;
position: absolute;
right: 0;
top: 0;
width: calc(100% - 16px);
`
const StyledSpinner = styled(CustomLightSpinner)`
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
`
const MoonpayTextWrapper = styled.div`
position: absolute;
bottom: 20px;
margin: auto;
left: 0;
right: 0;
width: 100%;
text-align: center;
`
export default function FiatOnrampModal() {
const account = useAccount()
const theme = useTheme()
const isDarkMode = useIsDarkMode()
const closeModal = useCloseModal()
const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP)
const { chainId, tokenAddress } = parsePathParts(location.pathname)
const chainInfo = chainId ? getChainInfo(chainId) : undefined
const { useParsedQueryString } = useUrlContext()
const parsedChainName = useParsedQueryString().chain
const queryChainId = typeof parsedChainName === 'string' ? getChainIdFromChainUrlParam(parsedChainName) : undefined
const queryChainInfo = queryChainId ? getChainInfo(queryChainId) : undefined
const accountChainInfo = account.chainId ? getChainInfo(account.chainId) : undefined
const [signedIframeUrl, setSignedIframeUrl] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const swapUrl = useHref('/swap')
const fetchSignedIframeUrl = useCallback(async () => {
if (!account.isConnected) {
setError('Please connect an account before making a purchase.')
return
}
setLoading(true)
setError(null)
try {
const signedIframeUrlFetchEndpoint = process.env.REACT_APP_MOONPAY_LINK as string
const res = await fetch(signedIframeUrlFetchEndpoint, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify({
theme: isDarkMode ? 'dark' : 'light',
colorCode: theme.accent1,
defaultCurrencyCode: getDefaultCurrencyCode(
tokenAddress,
chainInfo?.backendChain.chain ?? queryChainInfo?.backendChain.chain ?? accountChainInfo?.backendChain.chain,
),
redirectUrl: swapUrl,
walletAddresses: JSON.stringify(
MOONPAY_SUPPORTED_CURRENCY_CODES.reduce(
(acc, currencyCode) => ({
...acc,
[currencyCode]: account.address,
}),
{},
),
),
}),
})
const { url } = await res.json()
setSignedIframeUrl(url)
} catch (e) {
logger.info('FiatOnrampModal', 'fetchSingedIframeUrl', 'there was an error fetching the link', e)
setError(e.toString())
} finally {
setLoading(false)
}
}, [
account.address,
account.isConnected,
accountChainInfo?.backendChain.chain,
chainInfo?.backendChain.chain,
isDarkMode,
queryChainInfo?.backendChain.chain,
swapUrl,
theme.accent1,
tokenAddress,
])
useEffect(() => {
fetchSignedIframeUrl()
}, [fetchSignedIframeUrl])
return (
<AdaptiveWebModal isOpen={fiatOnrampModalOpen} onClose={() => closeModal()}>
<Wrapper data-testid="fiat-onramp-modal" isDarkMode={isDarkMode}>
{error ? (
<>
<ThemedText.MediumHeader>
<Trans i18nKey="moonpay.rampIframe" />
</ThemedText.MediumHeader>
<ErrorText>
<Trans i18nKey="common.error.somethingWrong" />
<br />
{error}
</ErrorText>
</>
) : loading ? (
<StyledSpinner src={Circle} alt="loading spinner" size="90px" />
) : (
<StyledIframe
src={signedIframeUrl ?? ''}
frameBorder="0"
title="fiat-onramp-iframe"
isDarkMode={isDarkMode}
/>
)}
</Wrapper>
<MoonpayTextWrapper>
<ThemedText.BodySmall color="neutral3">
<Trans i18nKey="moonpay.poweredBy" />
</ThemedText.BodySmall>
</MoonpayTextWrapper>
</AdaptiveWebModal>
)
}
import { WETH9 } from '@uniswap/sdk-core'
import { getDefaultCurrencyCode, parsePathParts } from 'components/FiatOnrampModal/utils'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import {
MATIC_MAINNET,
USDC_ARBITRUM,
USDC_MAINNET,
USDC_OPTIMISM,
USDC_POLYGON,
USDT,
WBTC,
WETH_POLYGON,
} from 'uniswap/src/constants/tokens'
import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
describe('getDefaultCurrencyCode', () => {
it('NATIVE/arbitrum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(NATIVE_CHAIN_ID, Chain.Arbitrum)).toBe('eth_arbitrum')
})
it('NATIVE/optimism should return the correct currency code', () => {
expect(getDefaultCurrencyCode(NATIVE_CHAIN_ID, Chain.Optimism)).toBe('eth_optimism')
})
it('WETH/polygon should return the correct currency code', () => {
expect(getDefaultCurrencyCode(WETH_POLYGON.address, Chain.Polygon)).toBe('eth_polygon')
})
it('WETH/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(WETH9[UniverseChainId.Mainnet].address, Chain.Ethereum)).toBe('weth')
})
it('WBTC/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(WBTC.address, Chain.Ethereum)).toBe('wbtc')
})
it('NATIVE/polygon should return the correct currency code', () => {
expect(getDefaultCurrencyCode(NATIVE_CHAIN_ID, Chain.Polygon)).toBe('matic_polygon')
})
it('MATIC/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(MATIC_MAINNET.address, Chain.Ethereum)).toBe('polygon')
})
it('USDC/arbitrum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_ARBITRUM.address, Chain.Arbitrum)).toBe('usdc_arbitrum')
})
it('USDC/optimism should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_OPTIMISM.address, Chain.Optimism)).toBe('usdc_optimism')
})
it('USDC/polygon should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_POLYGON.address, Chain.Polygon)).toBe('usdc_polygon')
})
it('native/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(NATIVE_CHAIN_ID, Chain.Ethereum)).toBe('eth')
})
it('usdc/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_MAINNET.address, Chain.Ethereum)).toBe('usdc')
})
it('usdt/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDT.address, Chain.Ethereum)).toBe('usdt')
})
it('chain/token mismatch should default to eth', () => {
expect(getDefaultCurrencyCode(USDC_ARBITRUM.address, Chain.Ethereum)).toBe('eth')
expect(getDefaultCurrencyCode(USDC_OPTIMISM.address, Chain.Ethereum)).toBe('eth')
expect(getDefaultCurrencyCode(USDC_POLYGON.address, Chain.Ethereum)).toBe('eth')
expect(getDefaultCurrencyCode(MATIC_MAINNET.address, Chain.Arbitrum)).toBe('eth')
})
})
describe('parseLocation', () => {
it('should parse the URL correctly', () => {
expect(parsePathParts('/tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')).toEqual({
chainId: UniverseChainId.Mainnet,
tokenAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
})
expect(parsePathParts('tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')).toEqual({
chainId: UniverseChainId.Mainnet,
tokenAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
})
expect(parsePathParts('/swap')).toEqual({
chainId: undefined,
tokenAddress: undefined,
})
})
})
import { WETH9 } from '@uniswap/sdk-core'
import { MoonpaySupportedCurrencyCode } from 'components/FiatOnrampModal/constants'
import {
MATIC_MAINNET,
USDC_ARBITRUM,
USDC_BASE,
USDC_MAINNET,
USDC_OPTIMISM,
USDC_POLYGON,
USDT,
WBTC,
WETH_POLYGON,
} from 'uniswap/src/constants/tokens'
import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { GqlChainId, UniverseChainId } from 'uniswap/src/features/chains/types'
import { getChainIdFromChainUrlParam } from 'utils/chainParams'
type MoonpaySupportedChain = Chain.Ethereum | Chain.Polygon | Chain.Arbitrum | Chain.Optimism | Chain.Base
const moonPaySupportedChains = [Chain.Ethereum, Chain.Polygon, Chain.Arbitrum, Chain.Optimism, Chain.Base]
const isMoonpaySupportedChain = (chain?: Chain): chain is MoonpaySupportedChain =>
!!chain && moonPaySupportedChains.includes(chain)
const CURRENCY_CODES: {
[K in MoonpaySupportedChain]: {
[key: string]: MoonpaySupportedCurrencyCode
native: MoonpaySupportedCurrencyCode
}
} = {
[Chain.Ethereum]: {
[WETH9[UniverseChainId.Mainnet]?.address.toLowerCase()]: 'weth',
[USDC_MAINNET.address.toLowerCase()]: 'usdc',
[USDT.address.toLowerCase()]: 'usdt',
[WBTC.address.toLowerCase()]: 'wbtc',
[MATIC_MAINNET.address.toLowerCase()]: 'polygon',
native: 'eth',
},
[Chain.Arbitrum]: {
[USDC_ARBITRUM.address.toLowerCase()]: 'usdc_arbitrum',
native: 'eth_arbitrum',
},
[Chain.Optimism]: {
[USDC_OPTIMISM.address.toLowerCase()]: 'usdc_optimism',
native: 'eth_optimism',
},
[Chain.Polygon]: {
[USDC_POLYGON.address.toLowerCase()]: 'usdc_polygon',
[WETH_POLYGON.address.toLowerCase()]: 'eth_polygon',
native: 'matic_polygon',
},
[Chain.Base]: {
[USDC_BASE.address.toLowerCase()]: 'usdc_base',
native: 'eth_base',
},
}
export function getDefaultCurrencyCode(address?: string, gqlChain?: GqlChainId): MoonpaySupportedCurrencyCode {
if (!gqlChain) {
return 'eth'
}
if (!address) {
return isMoonpaySupportedChain(gqlChain) ? CURRENCY_CODES[gqlChain]?.native : 'eth'
}
if (isMoonpaySupportedChain(gqlChain)) {
const code = CURRENCY_CODES[gqlChain]?.[address.toLowerCase()]
return code ?? 'eth'
}
return 'eth'
}
/**
* You should use useParams() from react-router-dom instead of this function if possible.
* This function is only used in the case where we need to parse the path outside the scope of the router.
*/
export function parsePathParts(pathname: string) {
const pathParts = pathname.split('/')
// Matches the /tokens/<network>/<tokenAddress> path.
const chainSlug = pathParts.length > 2 ? pathParts[pathParts.length - 2] : undefined
const chainId = getChainIdFromChainUrlParam(chainSlug)
const tokenAddress = pathParts.length > 2 ? pathParts[pathParts.length - 1] : undefined
return { chainId, tokenAddress }
}
...@@ -2,13 +2,14 @@ import { LiquidityModalHeader } from 'components/Liquidity/LiquidityModalHeader' ...@@ -2,13 +2,14 @@ import { LiquidityModalHeader } from 'components/Liquidity/LiquidityModalHeader'
import { WebUniswapProvider } from 'components/Web3Provider/WebUniswapContext' import { WebUniswapProvider } from 'components/Web3Provider/WebUniswapContext'
import { act, fireEvent, render } from 'test-utils/render' import { act, fireEvent, render } from 'test-utils/render'
import { TransactionSettingsContextProvider } from 'uniswap/src/features/transactions/settings/contexts/TransactionSettingsContext' import { TransactionSettingsContextProvider } from 'uniswap/src/features/transactions/settings/contexts/TransactionSettingsContext'
import { TransactionSettingKey } from 'uniswap/src/features/transactions/settings/slice'
describe('LiquidityModalHeader', () => { describe('LiquidityModalHeader', () => {
it('should render with given title and call close callback', () => { it('should render with given title and call close callback', () => {
const onClose = jest.fn() const onClose = jest.fn()
const { getByText, getByTestId } = render( const { getByText, getByTestId } = render(
<WebUniswapProvider> <WebUniswapProvider>
<TransactionSettingsContextProvider> <TransactionSettingsContextProvider settingKey={TransactionSettingKey.Swap}>
<LiquidityModalHeader title="Test Title" closeModal={onClose} /> <LiquidityModalHeader title="Test Title" closeModal={onClose} />
</TransactionSettingsContextProvider> </TransactionSettingsContextProvider>
, ,
......
...@@ -7,7 +7,7 @@ import { MouseoverTooltip } from 'components/Tooltip' ...@@ -7,7 +7,7 @@ import { MouseoverTooltip } from 'components/Tooltip'
import { useScreenSize } from 'hooks/screenSize/useScreenSize' import { useScreenSize } from 'hooks/screenSize/useScreenSize'
import { TextLoader } from 'pages/Pool/Positions/shared' import { TextLoader } from 'pages/Pool/Positions/shared'
import { Dispatch, SetStateAction } from 'react' import { Dispatch, SetStateAction } from 'react'
import { ClickableTamaguiStyle } from 'theme/components' import { ClickableTamaguiStyle, EllipsisTamaguiStyle } from 'theme/components'
import { Flex, Text, styled } from 'ui/src' import { Flex, Text, styled } from 'ui/src'
import { ArrowUpDown } from 'ui/src/components/icons/ArrowUpDown' import { ArrowUpDown } from 'ui/src/components/icons/ArrowUpDown'
import { InfoCircleFilled } from 'ui/src/components/icons/InfoCircleFilled' import { InfoCircleFilled } from 'ui/src/components/icons/InfoCircleFilled'
...@@ -168,8 +168,17 @@ export function LiquidityPositionFeeStats({ ...@@ -168,8 +168,17 @@ export function LiquidityPositionFeeStats({
<SecondaryText flexShrink={0}> <SecondaryText flexShrink={0}>
<Trans i18nKey="common.max" /> <Trans i18nKey="common.max" />
</SecondaryText> </SecondaryText>
<SecondaryText color="$neutral1"> <SecondaryText color="$neutral1" display="flex" alignItems="center" gap="$gap4">
{maxPrice} {tokenASymbol} / {tokenBSymbol} <span
style={{
...EllipsisTamaguiStyle,
}}
>
{maxPrice}
</span>
<span>
{tokenASymbol} / {tokenBSymbol}
</span>
</SecondaryText> </SecondaryText>
<Flex <Flex
height="100%" height="100%"
......
...@@ -17,6 +17,7 @@ const InnerTile = styled(Flex, { ...@@ -17,6 +17,7 @@ const InnerTile = styled(Flex, {
}) })
interface LiquidityPositionPriceRangeTileProps { interface LiquidityPositionPriceRangeTileProps {
token1: Currency
priceOrdering: PriceOrdering priceOrdering: PriceOrdering
token0CurrentPrice: Price<Currency, Currency> token0CurrentPrice: Price<Currency, Currency>
token1CurrentPrice: Price<Currency, Currency> token1CurrentPrice: Price<Currency, Currency>
...@@ -26,6 +27,7 @@ interface LiquidityPositionPriceRangeTileProps { ...@@ -26,6 +27,7 @@ interface LiquidityPositionPriceRangeTileProps {
} }
export function LiquidityPositionPriceRangeTile({ export function LiquidityPositionPriceRangeTile({
token1,
priceOrdering, priceOrdering,
token0CurrentPrice, token0CurrentPrice,
token1CurrentPrice, token1CurrentPrice,
...@@ -56,9 +58,7 @@ export function LiquidityPositionPriceRangeTile({ ...@@ -56,9 +58,7 @@ export function LiquidityPositionPriceRangeTile({
throw new Error('LiquidityPositionPriceRangeTile: Currency symbols are required') throw new Error('LiquidityPositionPriceRangeTile: Currency symbols are required')
} }
const { minPrice, maxPrice, currentPrice, tokenASymbol, tokenBSymbol } = useGetRangeDisplay({ const { minPrice, maxPrice, tokenASymbol, tokenBSymbol } = useGetRangeDisplay({
token0CurrentPrice,
token1CurrentPrice,
priceOrdering, priceOrdering,
feeTier, feeTier,
tickLower, tickLower,
...@@ -66,6 +66,19 @@ export function LiquidityPositionPriceRangeTile({ ...@@ -66,6 +66,19 @@ export function LiquidityPositionPriceRangeTile({
pricesInverted, pricesInverted,
}) })
const currentPrice = useMemo(() => {
const { base } = priceOrdering
if (!base) {
return undefined
}
if (!pricesInverted) {
return base?.equals(token1) ? token1CurrentPrice : token0CurrentPrice
}
return base?.equals(token1) ? token0CurrentPrice : token1CurrentPrice
}, [priceOrdering, token0CurrentPrice, token1CurrentPrice, token1, pricesInverted])
return ( return (
<Flex backgroundColor="$surface2" borderRadius="$rounded12" p="$padding12" width="100%" gap="$gap12"> <Flex backgroundColor="$surface2" borderRadius="$rounded12" p="$padding12" width="100%" gap="$gap12">
<Flex row width="100%" justifyContent="space-between" alignItems="center"> <Flex row width="100%" justifyContent="space-between" alignItems="center">
......
This diff is collapsed.
This diff is collapsed.
...@@ -165,7 +165,6 @@ exports[`disable nft on searchbar should render text without nfts 1`] = ` ...@@ -165,7 +165,6 @@ exports[`disable nft on searchbar should render text without nfts 1`] = `
style="--placeholderTextColor: #7D7D7D;" style="--placeholderTextColor: #7D7D7D;"
tabindex="0" tabindex="0"
value="" value=""
virtualkeyboardpolicy="auto"
/> />
</span> </span>
<div <div
......
export const PRIVACY_SHARING_OPT_OUT_STORAGE_KEY = 'optOutPrivacySharing'
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