diff --git a/next.config.js b/next.config.js index aacdda70798..d2b027d9137 100644 --- a/next.config.js +++ b/next.config.js @@ -81,6 +81,14 @@ module.exports = (phase, { defaultConfig }) => { protocol: "https", hostname: "crowdin-static.downloads.crowdin.com", }, + { + protocol: "https", + hostname: "avatars.githubusercontent.com", + }, + { + protocol: "https", + hostname: "coin-images.coingecko.com", + }, ], }, async headers() { diff --git a/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md b/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md index 5d4fc3f0e59..05e659dfe1f 100644 --- a/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md +++ b/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md @@ -177,7 +177,7 @@ The law of proximity states that items that are close together are perceived as Ultimately, there are pluses and minuses for both options, but it is interesting how the trend appears to be towards token on the right. -# Button behavior {#button-behavior} +## Button behavior {#button-behavior} Don’t have a separate button for Approve. Also don’t have a separate click for Approve. The user wants to Swap, so just say “swap” on the button and initiate the approval as the first step. A modal can show progress with a stepper, or a simple “tx 1 of 2 - approving” notification. @@ -185,7 +185,7 @@ Don’t have a separate button for Approve. Also don’t have a separate click f ![A UI with one button that says approve](./15.png) -## Button as contextual help {#button-as-contextual-help} +### Button as contextual help {#button-as-contextual-help} The button can do double duty as an alert! diff --git a/public/content/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/developers/tutorials/erc-721-vyper-annotated-code/index.md index 0a31224d7cb..64ca43a6d71 100644 --- a/public/content/developers/tutorials/erc-721-vyper-annotated-code/index.md +++ b/public/content/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -278,8 +278,7 @@ def supportsInterface(_interfaceID: bytes32) -> bool: ``` In contrast to Python, Vyper is a [static typed language](https://wikipedia.org/wiki/Type_system#Static_type_checking). -You can't declare a variable, or a function parameter, without identifying the [data -type](https://vyper.readthedocs.io/en/latest/types.html). In this case the input parameter is `bytes32`, a 256-bit value +You can't declare a variable, or a function parameter, without identifying the [data type](https://vyper.readthedocs.io/en/latest/types.html). In this case the input parameter is `bytes32`, a 256-bit value (256 bits is the native word size of the [Ethereum Virtual Machine](/developers/docs/evm/)). The output is a boolean value. By convention, the names of function parameters start with an underscore (`_`). @@ -689,7 +688,7 @@ Anybody who is allowed to transfer a token is allowed to burn it. While a burn a transfer to the zero address, the zero address does not actually receives the token. This allows us to free up all the storage that was used for the token, which can reduce the gas cost of the transaction. -# Using this Contract {#using-contract} +## Using this Contract {#using-contract} In contrast to Solidity, Vyper does not have inheritance. This is a deliberate design choice to make the code clearer and therefore easier to secure. So to create your own Vyper ERC-721 contract you take [this diff --git a/public/content/developers/tutorials/waffle-test-simple-smart-contract/index.md b/public/content/developers/tutorials/waffle-test-simple-smart-contract/index.md index 0594f138f44..117011e3e1c 100644 --- a/public/content/developers/tutorials/waffle-test-simple-smart-contract/index.md +++ b/public/content/developers/tutorials/waffle-test-simple-smart-contract/index.md @@ -21,7 +21,7 @@ published: 2021-02-26 - You have used some package managers like yarn or npm - You possess very basic knowledge of smart contracts and Solidity -# Getting started {#getting-started} +## Getting started {#getting-started} The tutorial demonstrates test setup and run using yarn, but there is no problem if you prefer npm - I will provide proper references to the official Waffle [documentation](https://ethereum-waffle.readthedocs.io/en/latest/index.html). @@ -100,7 +100,7 @@ Testing with Waffle requires using Chai matchers and Mocha, so you need to [add] If you want to [execute](https://ethereum-waffle.readthedocs.io/en/latest/getting-started.html#running-tests) your tests, just run `yarn test` . -# Testing {#testing} +## Testing {#testing} Now create the `test` directory and create the new file `test\EtherSplitter.test.ts`. Copy the snippet below and paste it to our test file. @@ -194,7 +194,7 @@ it("Reverts when Vei amount uneven", async () => { The test, if passed, will assure us that the transaction was reverted indeed. However, there must be also an exact match between the messages that we passed in `require` statement and the message we expect in `revertedWith`. If we go back to the code of EtherSplitter contract, in the `require` statement for the wei amount, we provide the message: 'Uneven wei amount not allowed'. This matches the message we expect in our test. If they were not equal, the test would fail. -# Congratulations! {#congratulations} +## Congratulations! {#congratulations} You've made your first big step towards testing smart contracts with Waffle! You might be interested in other Waffle tutorials: diff --git a/src/components/Contributors.tsx b/src/components/Contributors.tsx index 7f89fda78af..a3353e91734 100644 --- a/src/components/Contributors.tsx +++ b/src/components/Contributors.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react" import shuffle from "lodash/shuffle" +import { Image } from "@/components/Image" import { Flex } from "@/components/ui/flex" import InlineLink from "@/components/ui/Link" import { LinkBox, LinkOverlay } from "@/components/ui/link-box" @@ -39,7 +40,7 @@ const Contributors = () => { className="m-2 max-w-[132px] transform shadow transition-transform duration-100 hover:scale-[1.02] hover:rounded hover:bg-background-highlight focus:scale-[1.02] focus:rounded" key={contributor.login} > - {contributor.name}({ } previousExpandedRef.current = expanded - }, [expanded]) + }, [expanded, matomoEventCategory, table]) useEffect(() => { if (JSON.stringify(data) !== JSON.stringify(previousDataRef.current)) { @@ -111,7 +111,7 @@ const DataTable = ({ return () => clearTimeout(timer) } - }, [data]) + }, [data, table]) return (
diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index 3996f8d78f2..d52b4ce94ae 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -45,6 +45,7 @@ const EventCard: React.FC = ({
{imageUrl ? ( + // eslint-disable-next-line @next/next/no-img-element {title}(null) - const calculateNewTime = (clientX: number) => { - if (!scrubBarRef.current) return 0 - const rect = scrubBarRef.current.getBoundingClientRect() - const position = Math.max( - 0, - Math.min(1, (clientX - rect.left) / rect.width) - ) - return duration * position - } + const calculateNewTime = useCallback( + (clientX: number) => { + if (!scrubBarRef.current) return 0 + const rect = scrubBarRef.current.getBoundingClientRect() + const position = Math.max( + 0, + Math.min(1, (clientX - rect.left) / rect.width) + ) + return duration * position + }, + [duration] + ) const handleMouseDown = (e: React.MouseEvent) => { setIsDragging(true) @@ -91,11 +94,14 @@ const PlayerWidget = ({ onSeek(newTime) } - const handleMouseMove = (e: MouseEvent) => { - if (!isDragging) return - const newTime = calculateNewTime(e.clientX) - onSeek(newTime) - } + const handleMouseMove = useCallback( + (e: MouseEvent) => { + if (!isDragging) return + const newTime = calculateNewTime(e.clientX) + onSeek(newTime) + }, + [isDragging, calculateNewTime, onSeek] + ) const handleMouseUp = () => { setIsDragging(false) @@ -110,7 +116,7 @@ const PlayerWidget = ({ document.removeEventListener("mousemove", handleMouseMove) document.removeEventListener("mouseup", handleMouseUp) } - }, [isDragging]) + }, [handleMouseMove, isDragging]) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { diff --git a/src/components/ListenToPlayer/index.tsx b/src/components/ListenToPlayer/index.tsx index ac8a941243c..cb5c5e14fc1 100644 --- a/src/components/ListenToPlayer/index.tsx +++ b/src/components/ListenToPlayer/index.tsx @@ -86,12 +86,14 @@ const ListenToPlayer = ({ slug }: { slug: string }) => { } audioPlayer.unload() } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentTrackIndex]) useEffect(() => { if (sound && autoplay && isPlaying) { sound.play() } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [sound]) useEffect(() => { diff --git a/src/components/ProductTable/index.tsx b/src/components/ProductTable/index.tsx index 76a5f3f2568..d2d2e412e62 100644 --- a/src/components/ProductTable/index.tsx +++ b/src/components/ProductTable/index.tsx @@ -94,6 +94,7 @@ const ProductTable = ({ // TODO: Fix this, removed to avoid infinite re-renders // router.replace(pathname, undefined, { shallow: true }) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.query]) // Update or remove preset filters diff --git a/src/components/Quiz/QuizWidget/useQuizWidget.tsx b/src/components/Quiz/QuizWidget/useQuizWidget.tsx index 3a07b09c452..77384ed2cc8 100644 --- a/src/components/Quiz/QuizWidget/useQuizWidget.tsx +++ b/src/components/Quiz/QuizWidget/useQuizWidget.tsx @@ -64,6 +64,7 @@ export const useQuizWidget = ({ setQuizData(quiz) } + // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(initialize, [quizKey]) const currentQuestionIndex = userQuizProgress.length diff --git a/src/components/StablecoinsTable.tsx b/src/components/StablecoinsTable.tsx index 1e9973ecc8c..9a578a9e62e 100644 --- a/src/components/StablecoinsTable.tsx +++ b/src/components/StablecoinsTable.tsx @@ -1,3 +1,5 @@ +import { cn } from "@/lib/utils/cn" + import { ButtonLink } from "./ui/buttons/Button" import { Flex } from "./ui/flex" import { @@ -8,6 +10,7 @@ import { TableHeader, TableRow, } from "./ui/table" +import { Image } from "./Image" import { useRtlFlip } from "@/hooks/useRtlFlip" import { useTranslation } from "@/hooks/useTranslation" @@ -31,7 +34,7 @@ const StablecoinsTable = ({ content, hasError, }: StablecoinsTableProps) => { - const { flipForRtl } = useRtlFlip() + const { twFlipForRtl } = useRtlFlip() const { t } = useTranslation("page-stablecoins") const stablecoinsType = { @@ -51,9 +54,7 @@ const StablecoinsTable = ({ {content && content[0]?.url && ( - - ↗ - + )} @@ -71,7 +72,7 @@ const StablecoinsTable = ({ - {image && } + {image && } <>{name} diff --git a/src/components/Translatathon/CountdownBanner.tsx b/src/components/Translatathon/CountdownBanner.tsx index b772a988b46..3666672ccb7 100644 --- a/src/components/Translatathon/CountdownBanner.tsx +++ b/src/components/Translatathon/CountdownBanner.tsx @@ -1,12 +1,18 @@ -import { useEffect, useState } from "react" +import { useEffect, useMemo, useState } from "react" import BannerNotification from "@/components/Banners/BannerNotification" export const CountdownBanner = () => { const [countdown, setCountdown] = useState("") - const translatathonStartDate = new Date("August 9, 2024 12:00:00 UTC") - const translatathonEndDate = new Date("August 18, 2024 12:00:00 UTC") + const translatathonStartDate = useMemo( + () => new Date("August 9, 2024 12:00:00 UTC"), + [] + ) + const translatathonEndDate = useMemo( + () => new Date("August 18, 2024 12:00:00 UTC"), + [] + ) const calculateCountdown = (targetDate: Date) => { const currentTime = new Date() @@ -36,7 +42,7 @@ export const CountdownBanner = () => { return () => { clearInterval(interval) } - }, []) + }, [translatathonEndDate, translatathonStartDate]) return new Date() < translatathonStartDate ? ( <> diff --git a/src/components/icons/listen-to/expand.tsx b/src/components/icons/listen-to/expand.tsx index ac062c4f093..a67546b3bde 100644 --- a/src/components/icons/listen-to/expand.tsx +++ b/src/components/icons/listen-to/expand.tsx @@ -6,14 +6,14 @@ export const ExpandIcon = createIconBase({ children: ( diff --git a/src/components/icons/staking/BedrockGlyphIcon.tsx b/src/components/icons/staking/BedrockGlyphIcon.tsx index 05942232469..58d53358dd4 100644 --- a/src/components/icons/staking/BedrockGlyphIcon.tsx +++ b/src/components/icons/staking/BedrockGlyphIcon.tsx @@ -9,8 +9,8 @@ export const BedrockGlyphIcon = createIconBase({ ...commonIconDefaultAttrs, children: ( ), diff --git a/src/components/icons/staking/EthpoolGlyphIcon.tsx b/src/components/icons/staking/EthpoolGlyphIcon.tsx index 1264f7b44a0..aa2bc204ffc 100644 --- a/src/components/icons/staking/EthpoolGlyphIcon.tsx +++ b/src/components/icons/staking/EthpoolGlyphIcon.tsx @@ -6,8 +6,8 @@ export const EthpoolGlyphIcon = createIconBase({ className: "size-[1em]", children: ( diff --git a/src/components/ui/Link.tsx b/src/components/ui/Link.tsx index 8242a18fd6c..e99df1ea7c7 100644 --- a/src/components/ui/Link.tsx +++ b/src/components/ui/Link.tsx @@ -56,7 +56,7 @@ export const BaseLink = forwardRef(function Link( const { twFlipForRtl } = useRtlFlip() if (!href) { - console.warn("Link component is missing href prop") + console.warn(`Link component missing href prop, pathname: ${pathname}`) return } diff --git a/src/lib/utils/data/dataLoader.ts b/src/lib/utils/data/dataLoader.ts index 4a0a882e2a3..10101394a58 100644 --- a/src/lib/utils/data/dataLoader.ts +++ b/src/lib/utils/data/dataLoader.ts @@ -2,6 +2,7 @@ import { cacheAsyncFn } from "./cacheAsyncFn" import { loadMockData } from "./loadMockData" const USE_MOCK_DATA = process.env.USE_MOCK_DATA === "true" +if (USE_MOCK_DATA) console.warn("Using mock data") type DataLoaderFunction = () => Promise @@ -28,7 +29,6 @@ export function dataLoader( }, cacheTimeout?: number ): () => Promise { - if (USE_MOCK_DATA) console.warn("Using mock data") const cachedLoaders = loaders.map(([key, loader]) => { const cachedLoader = cacheAsyncFn(key, loader, { cacheTimeout,