Commit 1802f501 authored by Mike Grabowski's avatar Mike Grabowski Committed by GitHub

chore: use @uniswap eslint preset (#5556)

* feat: replace eslint with preset

* chore: update

* add empty line

* Revert changes

* chore: replace colors

* chore: tweaks

* Revert "chore: replace colors"

This reverts commit 3462420ecbdd9c5275a895643dad1586e92226a0.

* bring lint back

* chore: tweaks

* chore: add note

* chore: fix yarn lock

* chore: fix yarn.lock 2

* chore: use ESLint from npm

* Chore: update lockfile

* tweaks

* chore: initial take fixing some lint issues

* tweaks

* chore: another take

* chore: further tweaks

* chore: fix further

* feat: ignore Jest for cypress

* revert change

* chore: update to latest preset

* tmp lets see if this works

* chore: turn error into warning

* chore: remove warnings

* chore: deduplicate yarn lock

* feat: add recommended ESLint extension in case someone has Prettier instead

* upgrade to latest uniswap config

* chore: update todo

* remove patch

* find to some

* name

* chore: tweak yarn lock

* update

* cleanup

* update name for filter

* nl

* no unsafe finally

* chore: update doc

* fix

* fix

* one more fix

* one more file

* chore: Fix two last build issues

* add generated back

* fix lint after merge

* chore: fix tests

* remove

* one more
parent 2aa1b18d
......@@ -2,3 +2,4 @@
*.d.ts
/src/graphql/data/__generated__/types-and-hooks.ts
/src/graphql/thegraph/__generated__/types-and-hooks.ts
/src/schema/schema.graphql
/* eslint-env node */
require('@uniswap/eslint-config/load')
module.exports = {
extends: '@uniswap/eslint-config/react',
}
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
// Allows for the parsing of JSX
"jsx": true
}
},
"settings": {
"react": {
"version": "detect"
},
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
},
"ignorePatterns": [
"src/types/v3",
"src/abis/types",
"src/locales/**/*.js",
"src/locales/**/en-US.po",
"node_modules",
"coverage",
"build",
"dist",
".DS_Store",
".env.local",
".env.development.local",
".env.test.local",
".env.production.local",
".idea/",
".vscode/",
"package-lock.json",
"yarn.lock"
],
"extends": [
"react-app",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended",
"plugin:import/typescript"
],
"plugins": ["import", "simple-import-sort", "unused-imports"],
"rules": {
"import/no-unused-modules": [2, { "unusedExports": true }],
"unused-imports/no-unused-imports": "error",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"prettier/prettier": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-curly-brace-presence": ["error", { "props": "never", "children": "never" }],
"object-shorthand": ["error", "always"],
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "ethers",
"message": "Please import from '@ethersproject/module' directly to support tree-shaking."
},
{
"name": "styled-components",
"message": "Please import from styled-components/macro."
},
{
"name": "@lingui/macro",
"importNames": ["t"],
"message": "Please use <Trans> instead of t."
}
],
"patterns": [
{
"group": ["**/dist"],
"message": "Do not import from dist/ - this is an implementation detail, and breaks tree-shaking."
},
{
"group": ["!styled-components/macro"]
}
]
}
],
"@typescript-eslint/no-restricted-imports": [
"error",
{
"paths": [
{
"name": "@ethersproject/providers",
"message": "Please only use Providers instantiated in constants/providers to improve traceability.",
"allowTypeImports": true
}
]
}
]
}
}
/src/schema/schema.graphql
\ No newline at end of file
{
"semi": false,
"singleQuote": true,
"printWidth": 120
}
{
"recommendations": [
"dbaeumer.vscode-eslint"
],
"unwantedRecommendations": []
}
......@@ -5,15 +5,12 @@
"editor.formatOnSaveMode": "file",
"editor.tabCompletion": "on",
"editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.formatOnSave": false,
"editor.inlineSuggest.enabled": true,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"files.eol": "\n",
"eslint.enable": true,
"eslint.debug": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
"eslint.debug": true
}
/* eslint-env node */
import type { CodegenConfig } from '@graphql-codegen/cli'
// Generates TS objects from the schemas returned by graphql queries
......
/* eslint-env node */
import type { CodegenConfig } from '@graphql-codegen/cli'
// Generates TS objects from the schemas returned by graphql queries
......
/* eslint-env node */
const isDev = process.env.NODE_ENV === 'development'
module.exports = {
......
......@@ -8,7 +8,8 @@ describe(
},
() => {
it('loads swap page', () => {
// We *must* wait in order to space out the retry attempts.
// TODO: We *must* wait in order to space out the retry attempts. Find a better way to do this.
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(ONE_MINUTE)
.visit('/', {
retryOnStatusCodeFailure: true,
......
......@@ -85,8 +85,7 @@ beforeEach(() => {
})
})
Cypress.on('uncaught:exception', (_err, _runnable) => {
// returning false here prevents Cypress from
// failing the test
Cypress.on('uncaught:exception', () => {
// returning false here prevents Cypress from failing the test
return false
})
// Utility to match GraphQL mutation based on the query name
export const hasQuery = (req: any, queryName: string) => {
const { body } = req
return body.hasOwnProperty('query') && body.query.includes(queryName)
return Object.prototype.hasOwnProperty.call(body, 'query') && body.query.includes(queryName)
}
// Alias query if queryName matches
......
/* eslint-disable */
/* eslint-env node */
require('dotenv').config({ path: '.env.production' })
const { exec } = require('child_process')
const dataConfig = require('./graphql.config')
const thegraphConfig = require('./graphql_thegraph.config')
/* eslint-enable */
function fetchSchema(url, outputFile) {
exec(
......
/* eslint-env node */
module.exports = {
src: './src',
language: 'typescript',
......
// eslint-disable-next-line @typescript-eslint/no-var-requires
/* eslint-env node */
const defaultConfig = require('./graphql.config')
module.exports = {
......
// eslint-disable-next-line @typescript-eslint/no-var-requires
/* eslint-env node */
const { exec } = require('child_process')
const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)
......
......@@ -14,18 +14,15 @@ import {
CollectFeesTransactionInfo,
CreateV3PoolTransactionInfo,
DelegateTransactionInfo,
DepositLiquidityStakingTransactionInfo,
ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo,
ExecuteTransactionInfo,
MigrateV2LiquidityToV3TransactionInfo,
QueueTransactionInfo,
RemoveLiquidityV3TransactionInfo,
SubmitProposalTransactionInfo,
TransactionInfo,
TransactionType,
VoteTransactionInfo,
WithdrawLiquidityStakingTransactionInfo,
WrapTransactionInfo,
} from '../../state/transactions/types'
......@@ -83,7 +80,7 @@ function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransa
)
}
function SubmitProposalTransactionSummary(_: { info: SubmitProposalTransactionInfo }) {
function SubmitProposalTransactionSummary() {
return <Trans>Submit new proposal</Trans>
}
......@@ -175,13 +172,13 @@ function WrapSummary({ info: { chainId, currencyAmountRaw, unwrapped } }: { info
}
}
function DepositLiquidityStakingSummary(_: { info: DepositLiquidityStakingTransactionInfo }) {
function DepositLiquidityStakingSummary() {
// not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts
// todo: deprecate and delete the code paths that allow this, show user more information
return <Trans>Deposit liquidity</Trans>
}
function WithdrawLiquidityStakingSummary(_: { info: WithdrawLiquidityStakingTransactionInfo }) {
function WithdrawLiquidityStakingSummary() {
return <Trans>Withdraw deposited liquidity</Trans>
}
......@@ -319,10 +316,10 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) {
return <ClaimSummary info={info} />
case TransactionType.DEPOSIT_LIQUIDITY_STAKING:
return <DepositLiquidityStakingSummary info={info} />
return <DepositLiquidityStakingSummary />
case TransactionType.WITHDRAW_LIQUIDITY_STAKING:
return <WithdrawLiquidityStakingSummary info={info} />
return <WithdrawLiquidityStakingSummary />
case TransactionType.SWAP:
return <SwapSummary info={info} />
......@@ -358,6 +355,6 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) {
return <ExecuteSummary info={info} />
case TransactionType.SUBMIT_PROPOSAL:
return <SubmitProposalTransactionSummary info={info} />
return <SubmitProposalTransactionSummary />
}
}
......@@ -39,26 +39,32 @@ const getCurrency = ({ info, chainId }: { info: TransactionInfo; chainId: number
switch (info.type) {
case TransactionType.ADD_LIQUIDITY_V3_POOL:
case TransactionType.REMOVE_LIQUIDITY_V3:
case TransactionType.CREATE_V3_POOL:
case TransactionType.CREATE_V3_POOL: {
const { baseCurrencyId, quoteCurrencyId } = info
return { currencyId0: baseCurrencyId, currencyId1: quoteCurrencyId }
case TransactionType.SWAP:
}
case TransactionType.SWAP: {
const { inputCurrencyId, outputCurrencyId } = info
return { currencyId0: inputCurrencyId, currencyId1: outputCurrencyId }
case TransactionType.WRAP:
}
case TransactionType.WRAP: {
const { unwrapped } = info
const native = info.chainId ? nativeOnChain(info.chainId) : undefined
const base = 'ETH'
const wrappedCurrency = native?.wrapped.address ?? 'WETH'
return { currencyId0: unwrapped ? wrappedCurrency : base, currencyId1: unwrapped ? base : wrappedCurrency }
case TransactionType.COLLECT_FEES:
}
case TransactionType.COLLECT_FEES: {
const { currencyId0, currencyId1 } = info
return { currencyId0, currencyId1 }
case TransactionType.APPROVAL:
}
case TransactionType.APPROVAL: {
return { currencyId0: info.tokenAddress, currencyId1: undefined }
case TransactionType.CLAIM:
}
case TransactionType.CLAIM: {
const uniAddress = chainId ? UNI_ADDRESS[chainId] : undefined
return { currencyId0: uniAddress, currencyId1: undefined }
}
default:
return { currencyId0: undefined, currencyId1: undefined }
}
......
......@@ -164,7 +164,7 @@ function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string }
)
}
function FeatureFlagOption({ variant, featureFlag, value, label }: FeatureFlagProps) {
function FeatureFlagOption({ variant, featureFlag, label }: FeatureFlagProps) {
const updateFlag = useUpdateFlag()
const [count, setCount] = useState(0)
const featureFlags = useAtomValue(featureFlagSettings)
......
......@@ -67,7 +67,7 @@ const MOONPAY_SUPPORTED_CURRENCY_CODES = [
export default function FiatOnrampModal() {
const { account } = useWeb3React()
const theme = useTheme()
const closeModal = useCloseModal(ApplicationModal.FIAT_ONRAMP)
const closeModal = useCloseModal()
const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP)
const [signedIframeUrl, setSignedIframeUrl] = useState<string | null>(null)
......
......@@ -76,7 +76,7 @@ export function AddRemoveTabs({
// detect if back should redirect to v3 or v2 pool page
const poolLink = location.pathname.includes('add/v2')
? '/pool/v2'
: '/pool' + (!!positionID ? `/${positionID.toString()}` : '')
: '/pool' + (positionID ? `/${positionID.toString()}` : '')
return (
<Tabs>
......
......@@ -51,7 +51,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string; isInit
${({ isActive, isInitialToggleLoad }) => (isInitialToggleLoad ? 'none' : isActive ? turnOnToggle : turnOffToggle)}
ease-in;
background: ${({ theme, bgColor, isActive }) =>
isActive ? bgColor ?? theme.accentAction : !!bgColor ? theme.deprecated_bg4 : theme.textTertiary};
isActive ? bgColor ?? theme.accentAction : bgColor ? theme.deprecated_bg4 : theme.textTertiary};
border-radius: 50%;
height: 24px;
:hover {
......
......@@ -222,7 +222,7 @@ export default function TokenDetailsSkeleton() {
const { chainName } = useParams<{ chainName?: string }>()
return (
<LeftPanel>
<BreadcrumbNavLink to={{ chainName } ? `/tokens/${chainName}` : `/explore`}>
<BreadcrumbNavLink to={chainName ? `/tokens/${chainName}` : `/explore`}>
<ArrowLeft size={14} /> Tokens
</BreadcrumbNavLink>
<TokenInfoContainer>
......
......@@ -51,24 +51,13 @@ export const StatsWrapper = styled.div`
type NumericStat = number | undefined | null
function Stat({
value,
title,
description,
isPrice = false,
}: {
value: NumericStat
title: ReactNode
description?: ReactNode
isPrice?: boolean
}) {
function Stat({ value, title, description }: { value: NumericStat; title: ReactNode; description?: ReactNode }) {
return (
<StatWrapper>
<StatTitle>
{title}
{description && <InfoTip text={description}></InfoTip>}
</StatTitle>
<StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice>
</StatWrapper>
)
......@@ -106,8 +95,8 @@ export default function StatsSection(props: StatsSectionProps) {
/>
</StatPair>
<StatPair>
<Stat value={priceLow52W} title={<Trans>52W low</Trans>} isPrice={true} />
<Stat value={priceHigh52W} title={<Trans>52W high</Trans>} isPrice={true} />
<Stat value={priceLow52W} title={<Trans>52W low</Trans>} />
<Stat value={priceHigh52W} title={<Trans>52W high</Trans>} />
</StatPair>
</TokenStatsSection>
</StatsWrapper>
......
......@@ -192,7 +192,7 @@ const AuthenticatedHeader = () => {
explorer,
} = getChainInfoOrDefault(chainId ? chainId : SupportedChainId.MAINNET)
const navigate = useNavigate()
const closeModal = useCloseModal(ApplicationModal.WALLET_DROPDOWN)
const closeModal = useCloseModal()
const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
const resetSellAssets = useSellAsset((state) => state.reset)
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
......
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import * as connectionUtils from 'connection/utils'
import { ApplicationModal } from 'state/application/reducer'
import { nativeOnChain } from '../../constants/tokens'
import { render, screen } from '../../test-utils'
......@@ -20,7 +18,7 @@ jest.mock('utils/userAgent', () => ({
jest.mock('.../../state/application/hooks', () => {
return {
useModalIsOpen: (_modal: ApplicationModal) => true,
useModalIsOpen: () => true,
useToggleWalletModal: () => {
return
},
......@@ -29,7 +27,7 @@ jest.mock('.../../state/application/hooks', () => {
jest.mock('hooks/useStablecoinPrice', () => {
return {
useStablecoinValue: (_currencyAmount: CurrencyAmount<Currency> | undefined | null) => {
useStablecoinValue: () => {
return
},
}
......@@ -38,10 +36,10 @@ jest.mock('hooks/useStablecoinPrice', () => {
jest.mock('lib/hooks/useCurrencyBalance', () => {
return {
__esModule: true,
default: (account?: string, currency?: Currency) => {
default: () => {
return
},
useTokenBalance: (account?: string, token?: Token) => {
useTokenBalance: () => {
return
},
}
......
......@@ -300,7 +300,7 @@ export default function Web3Status() {
const allTransactions = useAllTransactions()
const ref = useRef<HTMLDivElement>(null)
const walletRef = useRef<HTMLDivElement>(null)
const closeModal = useCloseModal(ApplicationModal.WALLET_DROPDOWN)
const closeModal = useCloseModal()
const isOpen = useModalIsOpen(ApplicationModal.WALLET_DROPDOWN)
useOnClickOutside(ref, isOpen ? closeModal : undefined, [walletRef])
......
......@@ -3,8 +3,10 @@ import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from './chains'
describe('chains', () => {
describe('ALL_SUPPORTED_CHAIN_IDS', () => {
it('contains all the values in the SupportedChainId enum', () => {
Object.values(SupportedChainId).forEach((chainId) => {
if (typeof chainId === 'number') expect(ALL_SUPPORTED_CHAIN_IDS.includes(chainId as number)).toBeTruthy()
Object.values(SupportedChainId)
.filter((chainId) => typeof chainId === 'number')
.forEach((chainId) => {
expect(ALL_SUPPORTED_CHAIN_IDS.includes(chainId as number)).toBeTruthy()
})
})
......
......@@ -36,7 +36,7 @@ const V2_SWAP_HOP_GAS_ESTIMATE = 50_000
* https://github.com/Uniswap/smart-order-router/blob/main/src/routers/alpha-router/gas-models/v2/v2-heuristic-gas-model.ts
*/
function guesstimateGas(trade: Trade<Currency, Currency, TradeType> | undefined): number | undefined {
if (!!trade) {
if (trade) {
let gas = 0
for (const { route } of trade.swaps) {
if (route.protocol === Protocol.V2) {
......
import * as Sentry from '@sentry/react'
import { Token } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
import uriToHttp from 'lib/utils/uriToHttp'
......@@ -37,7 +38,9 @@ async function getColorFromToken(token: Token): Promise<string | null> {
try {
logoURI = URIForEthToken(address)
return await getColorFromUriPath(logoURI)
} catch (e) {}
} catch (error) {
Sentry.captureMessage(error.toString())
}
}
return null
......
......@@ -19,7 +19,7 @@ export default function useIsWindowVisible(): boolean {
useEffect(() => {
if (!isVisibilityStateSupported()) return undefined
setFocused((focused) => isWindowVisible())
setFocused(() => isWindowVisible())
document.addEventListener('visibilitychange', listener)
return () => {
......
......@@ -9,7 +9,7 @@ import usePrevious from './usePrevious'
import useSelectChain from './useSelectChain'
function getChainIdFromName(name: string) {
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([_, n]) => n === name)
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([, n]) => n === name)
const chainId = entry?.[0]
return chainId ? parseInt(chainId) : undefined
}
......
......@@ -29,7 +29,7 @@ import UserUpdater from './state/user/updater'
import ThemeProvider, { ThemedGlobalStyle } from './theme'
import RadialGradientByChainUpdater from './theme/components/RadialGradientByChainUpdater'
if (!!window.ethereum) {
if (window.ethereum) {
window.ethereum.autoRefreshOnNetworkChange = false
}
......
......@@ -11,30 +11,30 @@ describe('useInterval', () => {
})
describe('with a synchronous function', () => {
it('it runs on an interval', () => {
it('runs on an interval', () => {
jest.useFakeTimers()
renderHook(() => useInterval(spy, 100))
expect(spy).toHaveBeenCalledTimes(1)
jest.runTimersToTime(100)
jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalledTimes(2)
})
})
describe('with an async funtion', () => {
it('it runs on an interval exclusive of fn resolving', async () => {
it('runs on an interval exclusive of fn resolving', async () => {
jest.useFakeTimers()
spy.mockImplementation(() => Promise.resolve(undefined))
renderHook(() => useInterval(spy, 100))
expect(spy).toHaveBeenCalledTimes(1)
jest.runTimersToTime(100)
jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalledTimes(1)
await spy.mock.results[0].value
jest.runTimersToTime(100)
jest.advanceTimersByTime(100)
expect(spy).toHaveBeenCalledTimes(2)
})
})
......
......@@ -5,7 +5,7 @@ describe.skip('fetchTokenList', () => {
it('throws on an invalid list url', async () => {
const url = 'https://example.com'
await expect(fetchTokenList(url, resolver)).rejects.toThrowError(`failed to fetch list: ${url}`)
await expect(fetchTokenList(url, resolver)).rejects.toThrow(`failed to fetch list: ${url}`)
expect(resolver).not.toHaveBeenCalled()
})
......
import { i18n } from '@lingui/core'
import { I18nProvider } from '@lingui/react'
import * as Sentry from '@sentry/react'
import { DEFAULT_LOCALE, SupportedLocale } from 'constants/locales'
import {
af,
......@@ -82,7 +83,9 @@ export async function dynamicActivate(locale: SupportedLocale) {
const catalog = await import(`locales/${locale}.js`)
// Bundlers will either export it as default or as a named export named default.
i18n.load(locale, catalog.messages || catalog.default.messages)
} catch {}
} catch (error) {
Sentry.captureMessage(error.toString())
}
i18n.activate(locale)
}
......
......@@ -15,6 +15,7 @@ function expectedOutput(l: SupportedLocale): string {
case 'zh-TW':
return `4,000,000.123`
case 'fr-FR':
// eslint-disable-next-line no-irregular-whitespace
return `4 000 000,123`
case 'ar-SA':
return `٤٬٠٠٠٬٠٠٠٫١٢٣`
......@@ -28,6 +29,7 @@ function expectedOutput(l: SupportedLocale): string {
case 'ru-RU':
case 'sv-SE':
case 'uk-UA':
// eslint-disable-next-line no-irregular-whitespace
return `4 000 000,123`
case 'ca-ES':
case 'da-DK':
......
......@@ -11,15 +11,18 @@ export default function uriToHttp(uri: string): string[] {
return [uri]
case 'http':
return ['https' + uri.substr(4), uri]
case 'ipfs':
case 'ipfs': {
const hash = uri.match(/^ipfs:(\/\/)?(.*)$/i)?.[2]
return [`https://cloudflare-ipfs.com/ipfs/${hash}/`, `https://ipfs.io/ipfs/${hash}/`]
case 'ipns':
}
case 'ipns': {
const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2]
return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`]
case 'ar':
}
case 'ar': {
const tx = uri.match(/^ar:(\/\/)?(.*)$/i)?.[2]
return [`https://arweave.net/${tx}`]
}
default:
return []
}
......
......@@ -186,7 +186,7 @@ export const ListingButton = ({ onClick, buttonText, showWarningOverride = false
<Box marginLeft="4" marginRight="8">
{warningMessage}
</Box>
{!!disableListButton ? (
{disableListButton ? (
<Box paddingTop="6">
<XMarkIcon fill={themeVars.colors.textSecondary} height="20" width="20" />
</Box>
......
......@@ -316,7 +316,7 @@ interface RankingProps {
details?: boolean
}
const Ranking = ({ details, rarity, collectionName, rarityVerified }: RankingProps) => {
const Ranking = ({ rarity, collectionName, rarityVerified }: RankingProps) => {
const rarityProviderLogo = getRarityProviderLogo(rarity.source)
return (
......
......@@ -577,7 +577,7 @@ interface ProfileNftDetailsProps {
const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => {
const assetName = () => {
if (!asset.name && !asset.tokenId) return
return !!asset.name ? asset.name : `#${asset.tokenId}`
return asset.name ? asset.name : `#${asset.tokenId}`
}
const shouldShowUserListedPrice = !asset.notForSale && asset.asset_contract.tokenType !== NftStandard.Erc1155
......
......@@ -387,12 +387,11 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
{totalSupplyStr}
</StatsItem>
) : null}
{Boolean(uniqueOwnersPercentage && stats.standard !== TokenType.ERC1155) ? (
{uniqueOwnersPercentage && stats.standard !== TokenType.ERC1155 ? (
<StatsItem label="Unique owners" shouldHide={isSmallContainer ?? false}>
{uniqueOwnersPercentage}%
</StatsItem>
) : null}
{stats.stats?.total_listings && stats.standard !== TokenType.ERC1155 ? (
<StatsItem label="Listed" shouldHide={isSmallContainer ?? false}>
{listedPercentageStr}%
......
......@@ -263,7 +263,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
return MediaType.Audio
} else if (isVideo(asset.animationUrl ?? '')) {
return MediaType.Video
} else if (!!asset.animationUrl) {
} else if (asset.animationUrl) {
return MediaType.Embed
}
return MediaType.Image
......
......@@ -3,7 +3,8 @@ import styled, { useTheme } from 'styled-components/macro'
import { themeVars, vars } from '../css/sprinkles.css'
type SVGProps = React.SVGProps<SVGSVGElement>
// ESLint reports `fill` is missing, whereas it exists on an SVGProps type
type SVGProps = React.SVGProps<SVGSVGElement> & { fill?: string }
export const UniIcon = (props: SVGProps) => (
<svg {...props} fill="none" xmlns="http://www.w3.org/2000/svg">
......@@ -599,7 +600,7 @@ export const ActivityTransferIcon = (props: SVGProps) => (
</svg>
)
export const ActivityExternalLinkIcon = (_props: SVGProps) => (
export const ActivityExternalLinkIcon = () => (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-outside-1_3799_46574" maskUnits="userSpaceOnUse" x="2" y="2" width="15" height="15" fill="black">
<rect fill="white" x="2" y="2" width="15" height="15" />
......
......@@ -70,7 +70,7 @@ export const SetDurationModal = () => {
const [errorState, setErrorState] = useState(ErrorState.valid)
const setGlobalExpiration = useSellAsset((state) => state.setGlobalExpiration)
const setCustomExpiration = (event: React.ChangeEvent<HTMLInputElement>) => {
setAmount(!!event.target.value.length ? event.target.value : '')
setAmount(event.target.value.length ? event.target.value : '')
setDuration(displayDuration)
}
const selectDuration = (duration: Duration) => {
......
......@@ -90,7 +90,7 @@ export const ProfilePage = () => {
isFetchingNextPage,
isSuccess,
} = useInfiniteQuery(['ownerCollections', { address }], getOwnerCollections, {
getNextPageParam: (lastGroup, _allGroups) => (lastGroup.data.length === 0 ? undefined : lastGroup.nextPage),
getNextPageParam: (lastGroup) => (lastGroup.data.length === 0 ? undefined : lastGroup.nextPage),
refetchInterval: 15000,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
......
......@@ -25,7 +25,7 @@ export const useNFTSelect = create<SelectNFTState>()(
selectNFT: (nft) =>
set(({ selectedNFTs }) => {
if (selectedNFTs.length === 0) return { selectedNFTs: [nft] }
else if (!!selectedNFTs.find((x) => x.id === nft.id))
else if (selectedNFTs.some((x) => x.id === nft.id))
return { selectedNFTs: selectedNFTs.filter((n) => n.id !== nft.id) }
else return { selectedNFTs: [...selectedNFTs, nft] }
}),
......
......@@ -39,7 +39,7 @@ export const useWalletCollections = create<WalletCollectionState>()(
setCollectionFilters: (address) =>
set(({ collectionFilters }) => {
if (collectionFilters.length === 0) return { collectionFilters: [address] }
else if (!!collectionFilters.find((x) => x === address))
else if (collectionFilters.some((x) => x === address))
return { collectionFilters: collectionFilters.filter((n) => n !== address) }
else return { collectionFilters: [...collectionFilters, address] }
}),
......
......@@ -49,7 +49,7 @@ const ProfileContent = () => {
{/* <Head> TODO: figure out metadata tagging
<title>Genie | Sell</title>
</Head> */}
{!!account ? (
{account ? (
<Box style={{ width: `calc(100% - ${cartExpanded ? SHOPPING_BAG_WIDTH : 0}px)` }}>
{sellPageState === ProfilePageStateType.VIEWING ? <ProfilePage /> : <ListPage />}
</Box>
......
......@@ -168,7 +168,7 @@ export async function signListing(
else setStatus(ListingStatus.FAILED)
return false
}
case 'LooksRare':
case 'LooksRare': {
const addresses = addressesByNetwork[SupportedChainId.MAINNET]
const currentTime = Math.round(Date.now() / 1000)
const makerOrder: MakerOrder = {
......@@ -235,8 +235,8 @@ export async function signListing(
else setStatus(ListingStatus.FAILED)
return false
}
case 'X2Y2':
}
case 'X2Y2': {
const orderItem: OfferItem = {
price: parseEther(listingPrice.toString()),
tokens: [
......@@ -269,7 +269,7 @@ export async function signListing(
else setStatus(ListingStatus.FAILED)
return false
}
}
default:
return false
}
......
......@@ -137,12 +137,11 @@ export const syncLocalFiltersWithURL = (state: CollectionFilters) => {
const query: Record<string, any> = {}
urlFilterItems.forEach((key) => {
switch (key) {
case 'traits':
case 'traits': {
const traits = state.traits.map(({ trait_type, trait_value }) => `("${trait_type}","${trait_value}")`)
query['traits'] = traits
break
}
case 'all':
query['all'] = !state.buyNow
break
......
......@@ -511,7 +511,7 @@ export function PositionPage() {
provider,
])
const owner = useSingleCallResult(!!tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
const ownsNFT = owner === account || positionDetails?.operator === account
const feeValueUpper = inverted ? feeValue0 : feeValue1
......
......@@ -172,7 +172,7 @@ export default function Swap({ className }: { className?: string }) {
urlLoadedTokens &&
urlLoadedTokens
.filter((token: Token) => {
return !Boolean(token.address in defaultTokens)
return !(token.address in defaultTokens)
})
.filter((token: Token) => {
// Any token addresses that are loaded from the shorthands map do not need to show the import URL
......
......@@ -106,15 +106,15 @@ describe('document', () => {
let fetched: Response
const FETCHED_ETAGS = 'fetched'
const expectFetchToHaveBeenCalledWithRequestUrl = () => {
expect(fetch).toHaveBeenCalledWith(requestUrl, expect.anything())
}
beforeEach(() => {
fetched = new Response('test_body', { headers: { etag: FETCHED_ETAGS } })
fetch.mockReturnValueOnce(fetched)
})
afterEach(() => {
expect(fetch).toHaveBeenCalledWith(requestUrl, expect.anything())
})
describe('with a cached response', () => {
let cached: Response
......@@ -132,6 +132,7 @@ describe('document', () => {
await handleDocument(options)
const abortSignal = fetch.mock.calls[0][1].signal
expect(abortSignal.aborted).toBeTruthy()
expectFetchToHaveBeenCalledWithRequestUrl()
})
it('returns the cached response', async () => {
......@@ -141,18 +142,21 @@ describe('document', () => {
expect(await response.text()).toBe(
'<html><head></head><body><script>window.__isDocumentCached=true</script>mock</body></html>'
)
expectFetchToHaveBeenCalledWithRequestUrl()
})
})
it(`returns the fetched response with mismatched etags`, async () => {
const response = await handleDocument(options)
expect(response.body).toBe(fetched.body)
expectFetchToHaveBeenCalledWithRequestUrl()
})
})
it(`returns the fetched response with no cached response`, async () => {
const response = await handleDocument(options)
expect(response.body).toBe(fetched.body)
expectFetchToHaveBeenCalledWithRequestUrl()
})
})
})
......
......@@ -49,7 +49,7 @@ type HandlerContext = {
*
* In addition, this handler may serve an offline document if there is no internet connection.
*/
export async function handleDocument(this: HandlerContext, { event, request }: RouteHandlerCallbackOptions) {
export async function handleDocument(this: HandlerContext, { request }: RouteHandlerCallbackOptions) {
// If we are offline, serve the offline document.
if ('onLine' in navigator && !navigator.onLine) return this?.offlineDocument?.clone() || fetch(request)
......
......@@ -64,8 +64,7 @@ export function useFiatOnrampAvailability(shouldCheck: boolean, callback?: () =>
setError('Error, try again later.')
dispatch(setFiatOnrampAvailability(false))
} finally {
if (stale) return
setLoading(false)
if (!stale) setLoading(false)
}
}
......@@ -88,7 +87,7 @@ export function useToggleModal(modal: ApplicationModal): () => void {
return useCallback(() => dispatch(setOpenModal(isOpen ? null : modal)), [dispatch, modal, isOpen])
}
export function useCloseModal(_modal: ApplicationModal): () => void {
export function useCloseModal(): () => void {
const dispatch = useAppDispatch()
return useCallback(() => dispatch(setOpenModal(null)), [dispatch])
}
......
......@@ -6,7 +6,7 @@ import { useAppDispatch } from 'state/hooks'
import { supportedChainId } from 'utils/supportedChainId'
import { useCloseModal } from './hooks'
import { ApplicationModal, updateChainId } from './reducer'
import { updateChainId } from './reducer'
export default function Updater(): null {
const { account, chainId, provider } = useWeb3React()
......@@ -15,7 +15,7 @@ export default function Updater(): null {
const [activeChainId, setActiveChainId] = useState(chainId)
const closeModal = useCloseModal(ApplicationModal.WALLET_DROPDOWN)
const closeModal = useCloseModal()
const previousAccountValue = useRef(account)
useEffect(() => {
if (account && account !== previousAccountValue.current) {
......
......@@ -386,17 +386,18 @@ describe('list reducer', () => {
it('each of those initialized lists is empty', () => {
const byUrl = store.getState().byUrl
// note we don't expect the uniswap default list to be prepopulated
// this is ok.
Object.keys(byUrl).forEach((url) => {
if (url !== 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json') {
expect(byUrl[url]).toEqual({
Object.entries(byUrl)
// We don't expect the Uniswap default list to be prepopulated
.filter(
([url]) => url !== 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json'
)
.forEach(([, state]) => {
expect(state).toEqual({
error: null,
current: null,
loadingRequestId: null,
pendingUpdate: null,
})
}
})
})
......
......@@ -60,7 +60,7 @@ export default function Updater(): null {
case VersionUpgrade.NONE:
throw new Error('unexpected no version bump')
case VersionUpgrade.PATCH:
case VersionUpgrade.MINOR:
case VersionUpgrade.MINOR: {
const min = minVersionBump(list.current.tokens, list.pendingUpdate.tokens)
// automatically update minor/patch as long as bump matches the min update
if (bump >= min) {
......@@ -71,7 +71,7 @@ export default function Updater(): null {
)
}
break
}
// update any active or inactive lists
case VersionUpgrade.MAJOR:
dispatch(acceptListUpdate(listUrl))
......
......@@ -11,8 +11,8 @@ interface TagInfo extends TagDetails {
* Token instances created from token info on a token list.
*/
export class WrappedTokenInfo implements Token {
public readonly isNative: false = false
public readonly isToken: true = true
public readonly isNative = false as const
public readonly isToken = true as const
public readonly list?: TokenList
public readonly tokenInfo: TokenInfo
......
......@@ -92,13 +92,13 @@ export interface ExactOutputSwapTransactionInfo extends BaseSwapTransactionInfo
maximumInputCurrencyAmountRaw: string
}
export interface DepositLiquidityStakingTransactionInfo {
interface DepositLiquidityStakingTransactionInfo {
type: TransactionType.DEPOSIT_LIQUIDITY_STAKING
token0Address: string
token1Address: string
}
export interface WithdrawLiquidityStakingTransactionInfo {
interface WithdrawLiquidityStakingTransactionInfo {
type: TransactionType.WITHDRAW_LIQUIDITY_STAKING
token0Address: string
token1Address: string
......@@ -164,7 +164,7 @@ export interface RemoveLiquidityV3TransactionInfo {
expectedAmountQuoteRaw: string
}
export interface SubmitProposalTransactionInfo {
interface SubmitProposalTransactionInfo {
type: TransactionType.SUBMIT_PROPOSAL
}
......
......@@ -48,7 +48,7 @@ export default function RadialGradientByChainUpdater(): null {
switch (chainId) {
case SupportedChainId.ARBITRUM_ONE:
case SupportedChainId.ARBITRUM_RINKEBY:
case SupportedChainId.ARBITRUM_RINKEBY: {
setBackground(backgroundResetStyles)
const arbitrumLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(205, 232, 251, 0.7) 0%, rgba(252, 243, 249, 0.6536) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
......@@ -56,8 +56,9 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0.7) 0%, rgba(34, 30, 48, 0.6536) 49.48%, rgba(31, 33, 40, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? arbitrumDarkGradient : arbitrumLightGradient
break
}
case SupportedChainId.OPTIMISM:
case SupportedChainId.OPTIMISM_GOERLI:
case SupportedChainId.OPTIMISM_GOERLI: {
setBackground(backgroundResetStyles)
const optimismLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0.8) 0%, rgba(255, 244, 249, 0.6958) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
......@@ -65,8 +66,9 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0.8) 0%, rgba(44, 31, 45, 0.6958) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? optimismDarkGradient : optimismLightGradient
break
}
case SupportedChainId.POLYGON:
case SupportedChainId.POLYGON_MUMBAI:
case SupportedChainId.POLYGON_MUMBAI: {
setBackground(backgroundResetStyles)
const polygonLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0.2) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #FFFFFF'
......@@ -74,8 +76,9 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0.2) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? polygonDarkGradient : polygonLightGradient
break
}
case SupportedChainId.CELO:
case SupportedChainId.CELO_ALFAJORES:
case SupportedChainId.CELO_ALFAJORES: {
setBackground(backgroundResetStyles)
const celoLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(186, 228, 210, 0.7) 0%, rgba(252, 243, 249, 0.6536) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
......@@ -83,13 +86,15 @@ export default function RadialGradientByChainUpdater(): null {
'radial-gradient(100% 100% at 50% 0%, rgba(20, 49, 37, 0.29) 0%, rgba(12, 31, 23, 0.6536) 49.48%, rgba(31, 33, 40, 0) 100%, rgba(31, 33, 40, 0) 100%), #0D0E0E'
backgroundRadialGradientElement.style.background = darkMode ? celoDarkGradient : celoLightGradient
break
default:
}
default: {
setBackground(initialStyles)
const defaultLightGradient =
'radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF'
const defaultDarkGradient = 'linear-gradient(180deg, #202738 0%, #070816 100%)'
backgroundRadialGradientElement.style.background = darkMode ? defaultDarkGradient : defaultLightGradient
}
}
}, [darkMode, chainId, isNftPage])
return null
}
......@@ -17,6 +17,17 @@ export const MEDIA_WIDTHS = {
deprecated_upToLarge: 1280,
}
const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(
MEDIA_WIDTHS
).reduce((acc, size) => {
acc[size] = (a: any, b: any, c: any) => css`
@media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) {
${css(a, b, c)}
}
`
return acc
}, {} as any)
export const BREAKPOINTS = {
xs: 396,
sm: 640,
......@@ -53,17 +64,6 @@ const fonts = {
code: 'courier, courier new, serif',
}
const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(
MEDIA_WIDTHS
).reduce((accumulator, size) => {
;(accumulator as any)[size] = (a: any, b: any, c: any) => css`
@media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) {
${css(a, b, c)}
}
`
return accumulator
}, {}) as any
function getSettings(darkMode: boolean) {
return {
grids: {
......
......@@ -7,15 +7,10 @@ describe('#anonymizeLink', () => {
it('anonymizes any addresses in etherscan urls', () => {
expect(anonymizeLink('https://etherscan.io/address/0xabcd')).toEqual('https://etherscan.io/address/***')
})
it('anonymizes any addresses in etherscan urls', () => {
expect(anonymizeLink('https://etherscan.io/address/0xabcd')).toEqual('https://etherscan.io/address/***')
})
it('anonymizes any addresses in testnet etherscan urls', () => {
expect(anonymizeLink('https://goerli.etherscan.io/address/0xabcd')).toEqual(
'https://goerli.etherscan.io/address/***'
)
})
it('anonymizes any addresses in testnet etherscan urls', () => {
expect(anonymizeLink('https://ropsten.etherscan.io/address/0xabcd')).toEqual(
'https://ropsten.etherscan.io/address/***'
)
......
......@@ -88,6 +88,7 @@ describe('formatTransactionAmount', () => {
expect(formatTransactionAmount(1234567.8901)).toEqual('1,234,567.89')
})
it('Number ≥ 1M extra long', () => {
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
expect(formatTransactionAmount(1234567890123456.789)).toEqual('1.234568e+15')
})
})
......
......@@ -10,7 +10,7 @@ function waitRandom(min: number, max: number): Promise<void> {
* This error is thrown if the function is cancelled before completing
*/
class CancelledError extends Error {
public isCancelledError: true = true
public isCancelledError = true as const
constructor() {
super('Cancelled')
}
......@@ -20,7 +20,7 @@ class CancelledError extends Error {
* Throw this error if the function should retry
*/
export class RetryableError extends Error {
public isRetryableError: true = true
public isRetryableError = true as const
}
export interface RetryOptions {
......@@ -42,8 +42,10 @@ export function retry<T>(
): { promise: Promise<T>; cancel: () => void } {
let completed = false
let rejectCancelled: (error: Error) => void
// eslint-disable-next-line no-async-promise-executor
const promise = new Promise<T>(async (resolve, reject) => {
rejectCancelled = reject
// eslint-disable-next-line no-constant-condition
while (true) {
let result: T
try {
......
......@@ -15,7 +15,7 @@ export function swapErrorToUserReadableMessage(error: any): string {
}
}
while (Boolean(error)) {
while (error) {
reason = error.reason ?? error.message ?? reason
error = error.error ?? error.data?.originalError
}
......
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