diff --git a/.eslintignore b/.eslintignore
index 9792363..3e4ba80 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -4,4 +4,5 @@ config-overrides.js
build
dist
vite.config.ts
-.eslintrc.js
\ No newline at end of file
+.eslintrc.js
+storybook-static
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 487ed1d..5b173cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,7 +44,7 @@
"is-primitive": "^3.0.1",
"lodash": "^4.17.19",
"node-polyfill-webpack-plugin": "^1.1.2",
- "notistack": "^1.0.0",
+ "notistack": "^3.0.1",
"patch-package": "^6.4.7",
"rc-scrollbars": "^1.1.3",
"react": "^16.13.1",
@@ -107,6 +107,7 @@
"@types/storybook__addon-knobs": "^5.2.1",
"@types/storybook__react": "^5.2.1",
"@types/yup": "^0.29.3",
+ "cross-env": "^7.0.3",
"dotenv-webpack": "^7.0.3",
"eslint-config-standard-with-typescript": "^21.0.1",
"prettier": "^2.1.0",
@@ -18379,6 +18380,24 @@
"sha.js": "^2.4.8"
}
},
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
"node_modules/cross-fetch": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
@@ -28946,21 +28965,38 @@
}
},
"node_modules/notistack": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/notistack/-/notistack-1.0.10.tgz",
- "integrity": "sha512-z0y4jJaVtOoH3kc3GtNUlhNTY+5LE04QDeLVujX3VPhhzg67zw055mZjrBF+nzpv3V9aiPNph1EgRU4+t8kQTQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz",
+ "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==",
"dependencies": {
"clsx": "^1.1.0",
- "hoist-non-react-statics": "^3.3.0"
+ "goober": "^2.0.33"
+ },
+ "engines": {
+ "node": ">=12.0.0",
+ "npm": ">=6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/notistack"
},
"peerDependencies": {
- "@material-ui/core": "^4.0.0",
- "react": "^16.8.0 || ^17.0.0",
- "react-dom": "^16.8.0 || ^17.0.0"
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/notistack/node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "peer": true
+ },
+ "node_modules/notistack/node_modules/goober": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
+ "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
}
},
"node_modules/npm-run-path": {
diff --git a/package.json b/package.json
index cab6b40..917ef90 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"is-primitive": "^3.0.1",
"lodash": "^4.17.19",
"node-polyfill-webpack-plugin": "^1.1.2",
- "notistack": "^1.0.0",
+ "notistack": "^3.0.1",
"patch-package": "^6.4.7",
"rc-scrollbars": "^1.1.3",
"react": "^16.13.1",
@@ -76,8 +76,8 @@
"preview": "vite preview",
"lint": "eslint --ext .js,.ts,.tsx .",
"fix": "eslint --fix --fix-type suggestion --ext .js,.ts,.tsx .",
- "storybook": "start-storybook -p 9009 -c .storybook watch-css -s public",
- "build-storybook": "build-storybook -s public",
+ "storybook": "cross-env NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 9009 -c .storybook watch-css -s public --no-manager-cache",
+ "build-storybook": "cross-env NODE_OPTIONS=--openssl-legacy-provider build-storybook -s public ",
"build": "vite build",
"update-mainnet-list": "node ./src/fetchMainnetList.js"
},
@@ -129,6 +129,7 @@
"@types/storybook__addon-knobs": "^5.2.1",
"@types/storybook__react": "^5.2.1",
"@types/yup": "^0.29.3",
+ "cross-env": "^7.0.3",
"dotenv-webpack": "^7.0.3",
"eslint-config-standard-with-typescript": "^21.0.1",
"prettier": "^2.1.0",
diff --git a/src/components/HeaderButton/FaucetButton.tsx b/src/components/HeaderButton/FaucetButton.tsx
index bf21675..cd5ec07 100644
--- a/src/components/HeaderButton/FaucetButton.tsx
+++ b/src/components/HeaderButton/FaucetButton.tsx
@@ -1,7 +1,6 @@
import Faucet from '@components/Modals/Faucet/Faucet'
import { blurContent, unblurContent } from '@consts/uiUtils'
import { Button } from '@material-ui/core'
-import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'
import React from 'react'
import useStyles from './style'
diff --git a/src/components/Snackbar/LoadingSnackbar/LoadingSnackbar.stories.tsx b/src/components/Snackbar/LoadingSnackbar/LoadingSnackbar.stories.tsx
new file mode 100644
index 0000000..9408762
--- /dev/null
+++ b/src/components/Snackbar/LoadingSnackbar/LoadingSnackbar.stories.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import { storiesOf } from '@storybook/react'
+import LoadingSnackbar from './LoadingSnackbar'
+
+storiesOf('newUi/loadingSnackbar', module).add('default', () => {
+ return (
+
+ }}
+ id='id'
+ persist={false}
+ style={{}}
+ variant='pending'
+ />
+ )
+})
diff --git a/src/components/Snackbar/LoadingSnackbar/LoadingSnackbar.tsx b/src/components/Snackbar/LoadingSnackbar/LoadingSnackbar.tsx
new file mode 100644
index 0000000..60d2b2d
--- /dev/null
+++ b/src/components/Snackbar/LoadingSnackbar/LoadingSnackbar.tsx
@@ -0,0 +1,39 @@
+import React, { useCallback } from 'react'
+import icons from '@static/icons'
+import { Box, Grid } from '@material-ui/core'
+import {
+ StyledCircularProgress,
+ StyledCloseButton,
+ StyledContainer,
+ StyledSnackbarContent,
+ StyledTitle
+} from './style'
+import { CustomContentProps, useSnackbar } from 'notistack'
+
+const LoadingSnackbar = React.forwardRef(
+ ({ id, message }, ref) => {
+ const { closeSnackbar } = useSnackbar()
+
+ const handleDismiss = useCallback(() => {
+ closeSnackbar(id)
+ }, [id, closeSnackbar])
+
+ return (
+
+
+
+
+
+
+ {message}
+
+
+
+
+
+
+ )
+ }
+)
+
+export default LoadingSnackbar
diff --git a/src/components/Snackbar/LoadingSnackbar/style.ts b/src/components/Snackbar/LoadingSnackbar/style.ts
new file mode 100644
index 0000000..05d9e83
--- /dev/null
+++ b/src/components/Snackbar/LoadingSnackbar/style.ts
@@ -0,0 +1,75 @@
+import { colors, typography } from '@static/theme'
+import { CircularProgress, styled, Typography } from '@material-ui/core'
+import { SnackbarContent } from 'notistack'
+
+export const StyledSnackbarContent = styled(SnackbarContent)(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ backgroundColor: colors.invariant.component,
+ borderStyle: 'solid',
+ borderWidth: 1,
+ borderColor: colors.invariant.component,
+ borderRadius: 10,
+ maxWidth: 330,
+ width: 330,
+ padding: '7px 16px',
+ minWidth: 100,
+ ...typography.body2,
+
+ boxShadow:
+ '0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)',
+
+ '& .MuiCircularProgress-colorPrimary': {
+ color: colors.invariant.textGrey
+ },
+
+ [theme.breakpoints.down('xs')]: {
+ maxWidth: 'none',
+ width: 'auto'
+ }
+}))
+
+export const StyledContainer = styled('div')({
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ width: 'auto',
+ '& div > div': {
+ margin: 0
+ }
+})
+
+export const StyledTitle = styled(Typography)({
+ marginLeft: 8,
+ color: colors.invariant.text,
+ ...typography.body2
+})
+
+export const StyledCloseButton = styled('button')({
+ backgroundColor: 'transparent',
+ border: 'none',
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: 30,
+ width: 'fit-content',
+ cursor: 'pointer',
+ margin: 0,
+ '&:hover': {
+ '& img': {
+ transition: '.2s all ease-in',
+ transform: 'scale(1.2)'
+ }
+ }
+})
+
+export const StyledCircularProgress = styled(CircularProgress)({
+ color: colors.invariant.textGrey,
+ display: 'flex',
+ alignItems: 'center',
+
+ '& SVG': {
+ width: 13,
+ height: 13
+ }
+})
diff --git a/src/components/Snackbar/Snackbar.tsx b/src/components/Snackbar/Snackbar.tsx
index a04013f..2da9c0e 100644
--- a/src/components/Snackbar/Snackbar.tsx
+++ b/src/components/Snackbar/Snackbar.tsx
@@ -1,27 +1,47 @@
import React from 'react'
import { SnackbarProvider } from 'notistack'
-import useStyles from './style'
+import { StyledMaterialDesignContent } from './style'
+import { Color } from '@material-ui/lab/Alert'
+import LoadingSnackbar from './LoadingSnackbar/LoadingSnackbar'
+import { useMediaQuery } from '@material-ui/core'
+import { theme } from '@static/theme'
+
+type ExtraVariants = 'pending'
+
+export type SnackbarVariant = Color | ExtraVariants
+
+declare module 'notistack' {
+ interface VariantOverrides {
+ pending: true
+ }
+}
interface ISnackbarProps {
children: JSX.Element[]
maxSnack: number
}
+
export const Snackbar: React.FC = ({ children, maxSnack }) => {
- const classes = useStyles()
+ const isExSmall = useMediaQuery(theme.breakpoints.down('xs'))
return (
+ dense
+ maxSnack={isExSmall ? 5 : maxSnack}
+ anchorOrigin={
+ isExSmall
+ ? { vertical: 'top', horizontal: 'left' }
+ : { vertical: 'bottom', horizontal: 'left' }
+ }
+ Components={{
+ success: StyledMaterialDesignContent,
+ error: StyledMaterialDesignContent,
+ info: StyledMaterialDesignContent,
+ warning: StyledMaterialDesignContent,
+ pending: LoadingSnackbar
+ }}>
{children}
-
)
}
export default Snackbar
diff --git a/src/components/Snackbar/style.ts b/src/components/Snackbar/style.ts
index 5b61fa3..46a2cbf 100644
--- a/src/components/Snackbar/style.ts
+++ b/src/components/Snackbar/style.ts
@@ -1,19 +1,26 @@
-import { makeStyles, Theme } from '@material-ui/core/styles'
import { colors, typography } from '@static/theme'
+import { styled } from '@material-ui/core'
+import { MaterialDesignContent } from 'notistack'
-const useStyles = makeStyles((theme: Theme) => ({
- success: {
+export const StyledMaterialDesignContent = styled(MaterialDesignContent)(({ theme }) => ({
+ '&.notistack-MuiContent-success': {
backgroundColor: colors.invariant.component,
borderStyle: 'solid',
borderWidth: 1,
borderColor: colors.invariant.component,
borderRadius: 10,
...typography.body2,
- maxWidth: 350,
- padding: '6px 16px',
- maxHeight: 64,
+ maxWidth: 330,
+ width: 330,
+ padding: '4px 16px',
minWidth: 100,
+
+ '& > div:first-child': {
+ flex: 1
+ },
+
'& SVG': {
+ fontSize: '16px !important',
color: colors.invariant.green,
marginTop: -2,
[theme.breakpoints.down('xs')]: {
@@ -21,24 +28,28 @@ const useStyles = makeStyles((theme: Theme) => ({
}
},
[theme.breakpoints.down('xs')]: {
- ...typography.body3,
- maxWidth: 255,
- maxHeight: 32,
- padding: '0px 8px 5px 4px'
+ maxWidth: 'none',
+ width: 'auto'
}
},
- error: {
+ '&.notistack-MuiContent-error': {
backgroundColor: colors.invariant.component,
borderStyle: 'solid',
borderWidth: 1,
borderColor: colors.invariant.component,
borderRadius: 10,
- padding: 10,
...typography.body2,
- maxWidth: 450,
- maxHeight: 64,
+ maxWidth: 330,
+ width: 330,
+ padding: '4px 16px',
minWidth: 100,
+
+ '& > div:first-child': {
+ flex: 1
+ },
+
'& SVG': {
+ fontSize: '16px !important',
color: colors.invariant.Error,
marginTop: -2,
[theme.breakpoints.down('xs')]: {
@@ -46,24 +57,28 @@ const useStyles = makeStyles((theme: Theme) => ({
}
},
[theme.breakpoints.down('xs')]: {
- ...typography.body3,
- maxWidth: 255,
- maxHeight: 32,
- padding: '0px 8px 5px 4px'
+ maxWidth: 'none',
+ width: 'auto'
}
},
- info: {
+ '&.notistack-MuiContent-info': {
backgroundColor: colors.invariant.component,
borderStyle: 'solid',
borderWidth: 1,
borderColor: colors.invariant.component,
borderRadius: 10,
- padding: 10,
...typography.body2,
- maxWidth: 350,
- maxHeight: 64,
+ maxWidth: 330,
+ width: 330,
+ padding: '6px 16px',
minWidth: 100,
+
+ '& > div:first-child': {
+ flex: 1
+ },
+
'& SVG': {
+ fontSize: '16px !important',
color: colors.invariant.textGrey,
marginTop: -2,
[theme.breakpoints.down('xs')]: {
@@ -71,24 +86,28 @@ const useStyles = makeStyles((theme: Theme) => ({
}
},
[theme.breakpoints.down('xs')]: {
- ...typography.body3,
- maxWidth: 255,
- maxHeight: 32,
- padding: '0px 8px 5px 4px'
+ maxWidth: 'none',
+ width: 'auto'
}
},
- warning: {
+ '&.notistack-MuiContent-warning': {
backgroundColor: colors.invariant.component,
borderStyle: 'solid',
borderWidth: 1,
borderColor: colors.invariant.component,
borderRadius: 10,
- padding: 10,
...typography.body2,
- maxWidth: 350,
- maxHeight: 64,
+ maxWidth: 330,
+ width: 330,
+ padding: '6px 16px',
minWidth: 100,
+
+ '& > div:first-child': {
+ flex: 1
+ },
+
'& SVG': {
+ fontSize: '16px !important',
color: colors.invariant.warning,
marginTop: -2,
[theme.breakpoints.down('xs')]: {
@@ -96,12 +115,8 @@ const useStyles = makeStyles((theme: Theme) => ({
}
},
[theme.breakpoints.down('xs')]: {
- ...typography.body3,
- maxWidth: 255,
- maxHeight: 32,
- padding: '0px 8px 5px 4px'
+ maxWidth: 'none',
+ width: 'auto'
}
}
}))
-
-export default useStyles
diff --git a/src/containers/Notifier/Notifier.tsx b/src/containers/Notifier/Notifier.tsx
index 8f4bc1f..7bf7b72 100644
--- a/src/containers/Notifier/Notifier.tsx
+++ b/src/containers/Notifier/Notifier.tsx
@@ -7,7 +7,6 @@ import { snackbars } from '@selectors/snackbars'
import { network } from '@selectors/solanaConnection'
import useStyles from './style'
import { getExplorer } from '@consts/utils'
-import { Network } from '@invariant-labs/sdk-eclipse'
import { NetworkType } from '@consts/static'
let displayed: string[] = []
diff --git a/src/containers/Notifier/style.ts b/src/containers/Notifier/style.ts
index 9680a56..ae451af 100644
--- a/src/containers/Notifier/style.ts
+++ b/src/containers/Notifier/style.ts
@@ -15,6 +15,8 @@ const useStyles = makeStyles((theme: Theme) => ({
transition: '0.2s all cubic-bezier(0.25, 0.46, 0.45, 0.94)',
backfaceVisibility: 'hidden',
fontSmoothing: 'subpixel-antialiased',
+ padding: '0 4px',
+
'&:hover': {
transform: 'scale(1.15) translateY(0px)'
},
@@ -48,6 +50,13 @@ const useStyles = makeStyles((theme: Theme) => ({
detailsWrapper: {
display: 'flex',
alignItems: 'center'
+ },
+ pendingContainer: {
+ display: 'flex',
+ flexDirection: 'column'
+ },
+ pendingMessage: {
+ width: 100
}
}))
diff --git a/src/static/icons.ts b/src/static/icons.ts
index d88c745..795202d 100644
--- a/src/static/icons.ts
+++ b/src/static/icons.ts
@@ -29,6 +29,7 @@ import activeIcon from './svg/active.svg'
import inactiveIcon from './svg/inactive.svg'
import allIcon from './svg/all.svg'
import docsIcon from './svg/docsCircle.svg'
+import closeSmallIcon from './svg/closeSmall.svg'
const icons: { [key: string]: string } = {
USDT: USDIcon,
@@ -62,6 +63,7 @@ const icons: { [key: string]: string } = {
activeIcon: activeIcon,
inactiveIcon: inactiveIcon,
allIcon: allIcon,
+ closeSmallIcon: closeSmallIcon,
docsIcon
}
diff --git a/src/static/svg/closeSmall.svg b/src/static/svg/closeSmall.svg
new file mode 100644
index 0000000..6daa98a
--- /dev/null
+++ b/src/static/svg/closeSmall.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/store/consts/static.ts b/src/store/consts/static.ts
index 0ec5b77..4e9ff61 100644
--- a/src/store/consts/static.ts
+++ b/src/store/consts/static.ts
@@ -2,6 +2,7 @@ import { FEE_TIERS } from '@invariant-labs/sdk-eclipse/lib/utils'
import { BN } from '@project-serum/anchor'
import { PublicKey } from '@solana/web3.js'
import { TokenPriceData } from './utils'
+import { ISnackbar } from '@reducers/snackbars'
declare global {
interface Window {
@@ -247,3 +248,9 @@ export const ALL_FEE_TIERS_DATA = FEE_TIERS.map((tier, index) => ({
export { DEFAULT_PUBLICKEY, EclipseNetworks, MAX_U64, NetworkType }
export const POSITIONS_PER_PAGE = 5
+
+export const SIGNING_SNACKBAR_CONFIG: Omit = {
+ message: 'Signing transactions',
+ variant: 'pending',
+ persist: true
+}
diff --git a/src/store/consts/utils.ts b/src/store/consts/utils.ts
index 954b216..f935597 100644
--- a/src/store/consts/utils.ts
+++ b/src/store/consts/utils.ts
@@ -1119,3 +1119,5 @@ export const getExplorer = (networkType: NetworkType) => {
return 'https://explorer.dev.eclipsenetwork.xyz/'
}
}
+
+export const createLoaderKey = () => (new Date().getMilliseconds() + Math.random()).toString()
diff --git a/src/store/reducers/snackbars.ts b/src/store/reducers/snackbars.ts
index 1c35fd6..5de5213 100644
--- a/src/store/reducers/snackbars.ts
+++ b/src/store/reducers/snackbars.ts
@@ -1,11 +1,12 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { PayloadType } from './types'
-import { Color } from '@material-ui/lab/Alert'
+import { SnackbarVariant } from '@components/Snackbar/Snackbar'
+import { createLoaderKey } from '@consts/utils'
export interface ISnackbar {
message: string
key?: string
- variant: Color
+ variant: SnackbarVariant
open: boolean
action?: (key: number) => JSX.Element
persist?: boolean
@@ -27,7 +28,7 @@ const snackbarsSlice = createSlice({
reducers: {
add(state, action: PayloadAction>) {
state.snackbars.push({
- key: (new Date().getMilliseconds() + Math.random()).toString(),
+ key: action.payload.key ? action.payload.key : createLoaderKey(),
...action.payload,
open: true
})
diff --git a/src/store/sagas/positions.ts b/src/store/sagas/positions.ts
index 9026e7f..f8fbd16 100644
--- a/src/store/sagas/positions.ts
+++ b/src/store/sagas/positions.ts
@@ -15,6 +15,7 @@ import { poolsArraySortedByFees, tokens } from '@selectors/pools'
import { Pair } from '@invariant-labs/sdk-eclipse'
import {
createLiquidityPlot,
+ createLoaderKey,
createPlaceholderLiquidityPlot,
getPositionsAddressesFromRange
} from '@consts/utils'
@@ -27,11 +28,12 @@ import {
// PublicKey
} from '@solana/web3.js'
import { NATIVE_MINT, Token, TOKEN_PROGRAM_ID } from '@solana/spl-token'
-import { WRAPPED_ETH_ADDRESS } from '@consts/static'
+import { SIGNING_SNACKBAR_CONFIG, WRAPPED_ETH_ADDRESS } from '@consts/static'
import { positionsWithPoolsData, singlePositionData } from '@selectors/positions'
import { GuardPredicate } from '@redux-saga/types'
// import { createClaimAllPositionRewardsTx } from './farms'
import { network, rpcAddress } from '@selectors/solanaConnection'
+import { closeSnackbar } from 'notistack'
// import { actions as farmsActions } from '@reducers/farms'
// import { stakesForPosition } from '@selectors/farms'
// import { getStakerProgram } from '@web3/programs/staker'
@@ -47,7 +49,18 @@ function* handleInitPositionAndPoolWithETH(action: PayloadAction): Ge
return yield* call(handleInitPositionAndPoolWithETH, action)
}
+ const loaderCreatePosition = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
try {
+ yield put(
+ snackbarsActions.add({
+ message: 'Creating position',
+ variant: 'pending',
+ persist: true,
+ key: loaderCreatePosition
+ })
+ )
+
const connection = yield* call(getConnection)
const wallet = yield* call(getWallet)
const networkType = yield* select(network)
@@ -368,8 +408,13 @@ function* handleInitPositionWithETH(action: PayloadAction): Ge
combinedTransaction.recentBlockhash = blockhash.blockhash
combinedTransaction.feePayer = wallet.publicKey
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], combinedTransaction)
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
signedTx.partialSign(wrappedEthAccount)
if (poolSigners.length) {
@@ -383,6 +428,9 @@ function* handleInitPositionWithETH(action: PayloadAction): Ge
if (!txId.length) {
yield put(actions.setInitPositionSuccess(false))
+ closeSnackbar(loaderCreatePosition)
+ yield put(snackbarsActions.remove(loaderCreatePosition))
+
return yield put(
snackbarsActions.add({
message: 'Position adding failed. Please try again.',
@@ -405,11 +453,19 @@ function* handleInitPositionWithETH(action: PayloadAction): Ge
}
yield put(actions.setInitPositionSuccess(true))
+
+ closeSnackbar(loaderCreatePosition)
+ yield put(snackbarsActions.remove(loaderCreatePosition))
} catch (error) {
console.log(error)
yield put(actions.setInitPositionSuccess(false))
+ closeSnackbar(loaderCreatePosition)
+ yield put(snackbarsActions.remove(loaderCreatePosition))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to send. Please try again.',
@@ -421,6 +477,9 @@ function* handleInitPositionWithETH(action: PayloadAction): Ge
}
export function* handleInitPosition(action: PayloadAction): Generator {
+ const loaderCreatePosition = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
const allTokens = yield* select(tokens)
@@ -433,6 +492,15 @@ export function* handleInitPosition(action: PayloadAction): Ge
return yield* call(handleInitPositionWithETH, action)
}
+ yield put(
+ snackbarsActions.add({
+ message: 'Creating position',
+ variant: 'pending',
+ persist: true,
+ key: loaderCreatePosition
+ })
+ )
+
const connection = yield* call(getConnection)
const wallet = yield* call(getWallet)
const networkType = yield* select(network)
@@ -502,8 +570,14 @@ export function* handleInitPosition(action: PayloadAction): Ge
const blockhash = yield* call([connection, connection.getRecentBlockhash])
tx.recentBlockhash = blockhash.blockhash
tx.feePayer = wallet.publicKey
+
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], tx)
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
if (poolSigners.length) {
signedTx.partialSign(...poolSigners)
}
@@ -535,11 +609,19 @@ export function* handleInitPosition(action: PayloadAction): Ge
yield put(actions.getPositionsList())
}
+
+ closeSnackbar(loaderCreatePosition)
+ yield put(snackbarsActions.remove(loaderCreatePosition))
} catch (error) {
console.log(error)
yield put(actions.setInitPositionSuccess(false))
+ closeSnackbar(loaderCreatePosition)
+ yield put(snackbarsActions.remove(loaderCreatePosition))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to send. Please try again.',
@@ -650,7 +732,19 @@ export function* handleGetPositionsList() {
}
export function* handleClaimFeeWithETH(positionIndex: number) {
+ const loaderClaimFee = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
+ yield put(
+ snackbarsActions.add({
+ message: 'Claiming fee',
+ variant: 'pending',
+ persist: true,
+ key: loaderClaimFee
+ })
+ )
+
const connection = yield* call(getConnection)
const networkType = yield* select(network)
const rpc = yield* select(rpcAddress)
@@ -726,7 +820,14 @@ export function* handleClaimFeeWithETH(positionIndex: number) {
const blockhash = yield* call([connection, connection.getRecentBlockhash])
tx.recentBlockhash = blockhash.blockhash
tx.feePayer = wallet.publicKey
+
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], tx)
+
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
signedTx.partialSign(wrappedEthAccount)
const txid = yield* call(sendAndConfirmRawTransaction, connection, signedTx.serialize(), {
@@ -754,8 +855,17 @@ export function* handleClaimFeeWithETH(positionIndex: number) {
}
yield put(actions.getSinglePosition(positionIndex))
+
+ closeSnackbar(loaderClaimFee)
+ yield put(snackbarsActions.remove(loaderClaimFee))
} catch (error) {
console.log(error)
+
+ closeSnackbar(loaderClaimFee)
+ yield put(snackbarsActions.remove(loaderClaimFee))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to claim fee. Please try again.',
@@ -767,6 +877,9 @@ export function* handleClaimFeeWithETH(positionIndex: number) {
}
export function* handleClaimFee(action: PayloadAction) {
+ const loaderClaimFee = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
const allTokens = yield* select(tokens)
const allPositionsData = yield* select(positionsWithPoolsData)
@@ -779,6 +892,15 @@ export function* handleClaimFee(action: PayloadAction) {
return yield* call(handleClaimFeeWithETH, action.payload)
}
+ yield put(
+ snackbarsActions.add({
+ message: 'Claiming fee',
+ variant: 'pending',
+ persist: true,
+ key: loaderClaimFee
+ })
+ )
+
const connection = yield* call(getConnection)
const networkType = yield* select(network)
const rpc = yield* select(rpcAddress)
@@ -819,8 +941,14 @@ export function* handleClaimFee(action: PayloadAction) {
const blockhash = yield* call([connection, connection.getRecentBlockhash])
tx.recentBlockhash = blockhash.blockhash
tx.feePayer = wallet.publicKey
+
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], tx)
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
const txid = yield* call(sendAndConfirmRawTransaction, connection, signedTx.serialize(), {
skipPreflight: false
})
@@ -846,8 +974,17 @@ export function* handleClaimFee(action: PayloadAction) {
}
yield put(actions.getSinglePosition(action.payload))
+
+ closeSnackbar(loaderClaimFee)
+ yield put(snackbarsActions.remove(loaderClaimFee))
} catch (error) {
console.log(error)
+
+ closeSnackbar(loaderClaimFee)
+ yield put(snackbarsActions.remove(loaderClaimFee))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to claim fee. Please try again.',
@@ -859,7 +996,19 @@ export function* handleClaimFee(action: PayloadAction) {
}
export function* handleClosePositionWithETH(data: ClosePositionData) {
+ const loaderClosePosition = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
+ yield put(
+ snackbarsActions.add({
+ message: 'Closing position',
+ variant: 'pending',
+ persist: true,
+ key: loaderClosePosition
+ })
+ )
+
const connection = yield* call(getConnection)
const networkType = yield* select(network)
const rpc = yield* select(rpcAddress)
@@ -951,7 +1100,14 @@ export function* handleClosePositionWithETH(data: ClosePositionData) {
const blockhash = yield* call([connection, connection.getRecentBlockhash])
tx.recentBlockhash = blockhash.blockhash
tx.feePayer = wallet.publicKey
+
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], tx)
+
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
signedTx.partialSign(wrappedEthAccount)
const txid = yield* call(sendAndConfirmRawTransaction, connection, signedTx.serialize(), {
@@ -984,8 +1140,17 @@ export function* handleClosePositionWithETH(data: ClosePositionData) {
// yield* put(farmsActions.getUserStakes())
data.onSuccess()
+
+ closeSnackbar(loaderClosePosition)
+ yield put(snackbarsActions.remove(loaderClosePosition))
} catch (error) {
console.log(error)
+
+ closeSnackbar(loaderClosePosition)
+ yield put(snackbarsActions.remove(loaderClosePosition))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to close position. Please try again.',
@@ -1005,6 +1170,9 @@ export function* handleClosePositionWithETH(data: ClosePositionData) {
// }
export function* handleClosePosition(action: PayloadAction) {
+ const loaderClosePosition = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
const allTokens = yield* select(tokens)
const allPositionsData = yield* select(positionsWithPoolsData)
@@ -1017,6 +1185,15 @@ export function* handleClosePosition(action: PayloadAction) {
return yield* call(handleClosePositionWithETH, action.payload)
}
+ yield put(
+ snackbarsActions.add({
+ message: 'Closing position',
+ variant: 'pending',
+ persist: true,
+ key: loaderClosePosition
+ })
+ )
+
const connection = yield* call(getConnection)
const networkType = yield* select(network)
const rpc = yield* select(rpcAddress)
@@ -1074,8 +1251,14 @@ export function* handleClosePosition(action: PayloadAction) {
const blockhash = yield* call([connection, connection.getRecentBlockhash])
tx.recentBlockhash = blockhash.blockhash
tx.feePayer = wallet.publicKey
+
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], tx)
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
const txid = yield* call(sendAndConfirmRawTransaction, connection, signedTx.serialize(), {
skipPreflight: false
})
@@ -1106,8 +1289,17 @@ export function* handleClosePosition(action: PayloadAction) {
// yield* put(farmsActions.getUserStakes())
action.payload.onSuccess()
+
+ closeSnackbar(loaderClosePosition)
+ yield put(snackbarsActions.remove(loaderClosePosition))
} catch (error) {
console.log(error)
+
+ closeSnackbar(loaderClosePosition)
+ yield put(snackbarsActions.remove(loaderClosePosition))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to close position. Please try again.',
diff --git a/src/store/sagas/swap.ts b/src/store/sagas/swap.ts
index 657fb52..10373d2 100644
--- a/src/store/sagas/swap.ts
+++ b/src/store/sagas/swap.ts
@@ -10,10 +10,15 @@ import { Pair } from '@invariant-labs/sdk-eclipse'
import { getConnection } from './connection'
import { Keypair, sendAndConfirmRawTransaction, SystemProgram, Transaction } from '@solana/web3.js'
import { NATIVE_MINT, Token, TOKEN_PROGRAM_ID } from '@solana/spl-token'
-import { WRAPPED_ETH_ADDRESS } from '@consts/static'
+import { SIGNING_SNACKBAR_CONFIG, WRAPPED_ETH_ADDRESS } from '@consts/static'
import { network, rpcAddress } from '@selectors/solanaConnection'
+import { createLoaderKey } from '@consts/utils'
+import { closeSnackbar } from 'notistack'
export function* handleSwapWithETH(): Generator {
+ const loaderSwappingTokens = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
const allTokens = yield* select(tokens)
const allPools = yield* select(poolsArraySortedByFees)
@@ -44,6 +49,15 @@ export function* handleSwapWithETH(): Generator {
return
}
+ yield put(
+ snackbarsActions.add({
+ message: 'Swapping tokens',
+ variant: 'pending',
+ persist: true,
+ key: loaderSwappingTokens
+ })
+ )
+
const isXtoY = tokenFrom.equals(swapPool.tokenX)
const wrappedEthAccount = Keypair.generate()
@@ -129,11 +143,16 @@ export function* handleSwapWithETH(): Generator {
unwrapTx.recentBlockhash = unwrapBlockhash.blockhash
unwrapTx.feePayer = wallet.publicKey
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const [initialSignedTx, swapSignedTx, unwrapSignedTx] = yield* call(
[wallet, wallet.signAllTransactions],
[initialTx, swapTx, unwrapTx]
)
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
initialSignedTx.partialSign(wrappedEthAccount)
const initialTxid = yield* call(
@@ -148,6 +167,9 @@ export function* handleSwapWithETH(): Generator {
if (!initialTxid.length) {
yield put(swapActions.setSwapSuccess(false))
+ closeSnackbar(loaderSwappingTokens)
+ yield put(snackbarsActions.remove(loaderSwappingTokens))
+
return yield put(
snackbarsActions.add({
message: 'ETH wrapping failed. Please try again.',
@@ -220,11 +242,19 @@ export function* handleSwapWithETH(): Generator {
})
)
}
+
+ closeSnackbar(loaderSwappingTokens)
+ yield put(snackbarsActions.remove(loaderSwappingTokens))
} catch (error) {
console.log(error)
yield put(swapActions.setSwapSuccess(false))
+ closeSnackbar(loaderSwappingTokens)
+ yield put(snackbarsActions.remove(loaderSwappingTokens))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message:
@@ -237,6 +267,9 @@ export function* handleSwapWithETH(): Generator {
}
export function* handleSwap(): Generator {
+ const loaderSwappingTokens = createLoaderKey()
+ const loaderSigningTx = createLoaderKey()
+
try {
const allTokens = yield* select(tokens)
const allPools = yield* select(poolsArraySortedByFees)
@@ -273,6 +306,15 @@ export function* handleSwap(): Generator {
return
}
+ yield put(
+ snackbarsActions.add({
+ message: 'Swapping tokens',
+ variant: 'pending',
+ persist: true,
+ key: loaderSwappingTokens
+ })
+ )
+
const isXtoY = tokenFrom.equals(swapPool.tokenX)
let fromAddress = tokensAccounts[tokenFrom.toString()]
@@ -306,7 +348,13 @@ export function* handleSwap(): Generator {
swapTx.recentBlockhash = blockhash.blockhash
swapTx.feePayer = wallet.publicKey
+ yield put(snackbarsActions.add({ ...SIGNING_SNACKBAR_CONFIG, key: loaderSigningTx }))
+
const signedTx = yield* call([wallet, wallet.signTransaction], swapTx)
+
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
const txid = yield* call(sendAndConfirmRawTransaction, connection, signedTx.serialize(), {
skipPreflight: false
})
@@ -332,11 +380,19 @@ export function* handleSwap(): Generator {
})
)
}
+
+ closeSnackbar(loaderSwappingTokens)
+ yield put(snackbarsActions.remove(loaderSwappingTokens))
} catch (error) {
console.log(error)
yield put(swapActions.setSwapSuccess(false))
+ closeSnackbar(loaderSwappingTokens)
+ yield put(snackbarsActions.remove(loaderSwappingTokens))
+ closeSnackbar(loaderSigningTx)
+ yield put(snackbarsActions.remove(loaderSigningTx))
+
yield put(
snackbarsActions.add({
message: 'Failed to send. Please try again.',
diff --git a/src/store/sagas/wallet.ts b/src/store/sagas/wallet.ts
index 82f9c48..99bd439 100644
--- a/src/store/sagas/wallet.ts
+++ b/src/store/sagas/wallet.ts
@@ -33,6 +33,8 @@ import airdropAdmin from '@consts/airdropAdmin'
import { network } from '@selectors/solanaConnection'
import { tokens } from '@selectors/pools'
import { actions as poolsActions } from '@reducers/pools'
+import { createLoaderKey } from '@consts/utils'
+import { closeSnackbar } from 'notistack'
// import { actions as farmsActions } from '@reducers/farms'
// import { actions as bondsActions } from '@reducers/bonds'
@@ -112,6 +114,16 @@ export function* handleAirdrop(): Generator {
return
}
+ const loaderKey = createLoaderKey()
+ yield put(
+ snackbarsActions.add({
+ message: 'Airdrop in progress',
+ variant: 'pending',
+ persist: true,
+ key: loaderKey
+ })
+ )
+
const connection = yield* call(getConnection)
const networkType = yield* select(network)
const wallet = yield* call(getWallet)
@@ -125,7 +137,7 @@ export function* handleAirdrop(): Generator {
airdropTokens[networkType],
airdropQuantities[networkType]
)
-
+
yield put(
snackbarsActions.add({
message: 'You will soon receive airdrop of tokens',
@@ -149,6 +161,9 @@ export function* handleAirdrop(): Generator {
})
)
}
+
+ closeSnackbar(loaderKey)
+ yield put(snackbarsActions.remove(loaderKey))
}
export function* setEmptyAccounts(collateralsAddresses: PublicKey[]): Generator {
diff --git a/src/store/selectors/solanaWallet.ts b/src/store/selectors/solanaWallet.ts
index c909177..4d67983 100644
--- a/src/store/selectors/solanaWallet.ts
+++ b/src/store/selectors/solanaWallet.ts
@@ -7,11 +7,9 @@ import { tokens } from './pools'
import {
WRAPPED_ETH_ADDRESS,
WETH_POOL_INIT_LAMPORTS,
- WETH_POSITION_INIT_LAMPORTS,
NetworkType,
WETH_POOL_INIT_LAMPORTS_TEST
} from '@consts/static'
-import { Network } from '@invariant-labs/sdk-eclipse'
const store = (s: AnyProps) => s[solanaWalletSliceName] as ISolanaWallet