diff --git a/package.json b/package.json index 359d46a2f..c3cfaa818 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,6 @@ "dependencies": {}, "devDependencies": { "husky": "9.1.4", - "turbo": "2.0.12" + "turbo": "2.0.13" } } diff --git a/packages/react-app-revamp/components/Comments/components/Comment/index.tsx b/packages/react-app-revamp/components/Comments/components/Comment/index.tsx index 5965d03a8..49aa59f87 100644 --- a/packages/react-app-revamp/components/Comments/components/Comment/index.tsx +++ b/packages/react-app-revamp/components/Comments/components/Comment/index.tsx @@ -37,7 +37,7 @@ const Comment: FC = ({ comment, selectedCommentIds, toggleCommentS )} diff --git a/packages/react-app-revamp/components/UI/GradientText/index.tsx b/packages/react-app-revamp/components/UI/GradientText/index.tsx new file mode 100644 index 000000000..031f1561c --- /dev/null +++ b/packages/react-app-revamp/components/UI/GradientText/index.tsx @@ -0,0 +1,23 @@ +import React, { FC } from "react"; + +interface GradientTextProps { + text: string; + isStrikethrough: boolean; +} + +const GradientText: FC = ({ text, isStrikethrough }) => { + return ( +
+ + {isStrikethrough && ( + + + + )} + {text} + +
+ ); +}; + +export default GradientText; diff --git a/packages/react-app-revamp/components/Voting/index.tsx b/packages/react-app-revamp/components/Voting/index.tsx index 5630cb047..ca5473774 100644 --- a/packages/react-app-revamp/components/Voting/index.tsx +++ b/packages/react-app-revamp/components/Voting/index.tsx @@ -7,6 +7,7 @@ import { formatNumber } from "@helpers/formatNumber"; import { ChevronRightIcon } from "@heroicons/react/24/outline"; import { useCastVotesStore } from "@hooks/useCastVotes/store"; import { useContestStore } from "@hooks/useContest/store"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; import { useFetchUserVotesOnProposal } from "@hooks/useFetchUserVotesOnProposal"; import { switchChain } from "@wagmi/core"; import { usePathname } from "next/navigation"; @@ -41,6 +42,8 @@ const VotingWidget: FC = ({ proposalId, amountOfVotes, downvo const isCorrectNetwork = chainId === accountChainId; const showVoteCharge = charge && charge.type.costToVote && accountData && isCorrectNetwork; const { currentUserVotesOnProposal } = useFetchUserVotesOnProposal(contestAddress, proposalId); + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; const handleClick = (value: boolean) => { setIsUpvote(value); @@ -113,6 +116,8 @@ const VotingWidget: FC = ({ proposalId, amountOfVotes, downvo await switchChain(config, { chainId }); }; + if (isContestCanceled) return null; + return (

diff --git a/packages/react-app-revamp/components/_pages/Contest/Contest/index.tsx b/packages/react-app-revamp/components/_pages/Contest/Contest/index.tsx index 4a67335e8..a1e257de6 100644 --- a/packages/react-app-revamp/components/_pages/Contest/Contest/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/Contest/index.tsx @@ -3,6 +3,7 @@ import DialogModalSendProposal from "@components/_pages/DialogModalSendProposal" import ListProposals from "@components/_pages/ListProposals"; import useContest from "@hooks/useContest"; import { useContestStore } from "@hooks/useContest/store"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; import { ContestStatus, useContestStatusStore } from "@hooks/useContestStatus/store"; import { useProposalStore } from "@hooks/useProposal/store"; import { useSubmitProposalStore } from "@hooks/useSubmitProposal/store"; @@ -38,8 +39,12 @@ const ContestTab = () => { const isMobile = useMediaQuery({ maxWidth: 768 }); const [blurProposals, setBlurProposals] = useState(false); const isInPwaMode = window.matchMedia("(display-mode: standalone)").matches; + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; const renderSubmitButton = () => { + if (isContestCanceled) return null; + if (!isConnected) { return ( { return (
-
- +
+
+ {isContestCanceled ? ( +
+
+

+ {isMobile + ? "this contest was canceled by the creator" + : "This contest was canceled by the creator and is no longer active"} +

+
+
+ ) : null} + +
{contestStatus === ContestStatus.SubmissionOpen &&
{renderSubmitButton()}
} diff --git a/packages/react-app-revamp/components/_pages/Contest/Rewards/components/Create/steps/FundPool/components/List/index.tsx b/packages/react-app-revamp/components/_pages/Contest/Rewards/components/Create/steps/FundPool/components/List/index.tsx index 4732bc507..ae2b5228f 100644 --- a/packages/react-app-revamp/components/_pages/Contest/Rewards/components/Create/steps/FundPool/components/List/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/Rewards/components/Create/steps/FundPool/components/List/index.tsx @@ -34,7 +34,7 @@ const FundPoolTokenList: FC = ({ tokens, onRemoveToken } handleRemoveToken(token.address)} />
diff --git a/packages/react-app-revamp/components/_pages/Contest/components/CancelContest/components/Modal/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/CancelContest/components/Modal/index.tsx new file mode 100644 index 000000000..010d8147d --- /dev/null +++ b/packages/react-app-revamp/components/_pages/Contest/components/CancelContest/components/Modal/index.tsx @@ -0,0 +1,64 @@ +import { Dialog, DialogBackdrop, DialogPanel } from "@headlessui/react"; +import Image from "next/image"; +import { FC } from "react"; + +interface CancelContestModalProps { + isCloseContestModalOpen: boolean; + setIsCloseContestModalOpen: (isOpen: boolean) => void; + cancelContestHandler: () => void; +} + +const CancelContestModal: FC = ({ + isCloseContestModalOpen, + setIsCloseContestModalOpen, + cancelContestHandler, +}) => { + return ( + + + +
+
+ +
+
+

cancel contest?? 😬

+ close setIsCloseContestModalOpen(false)} + /> +
+
+

if you proceed, you will cancel this contest.

+

🚨 players will not be able to keep playing.

+

+ 🚨 the contest and entries will remain visible on our site. +

+

🚨 you can withdraw any funds on the rewards page.

+

are you really, really, really sure you want to proceed?

+
+ +
+
+
+
+
+ ); +}; + +export default CancelContestModal; diff --git a/packages/react-app-revamp/components/_pages/Contest/components/CancelContest/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/CancelContest/index.tsx new file mode 100644 index 000000000..b7269b209 --- /dev/null +++ b/packages/react-app-revamp/components/_pages/Contest/components/CancelContest/index.tsx @@ -0,0 +1,57 @@ +import { chains, config } from "@config/wagmi"; +import { extractPathSegments } from "@helpers/extractPath"; +import { TrashIcon } from "@heroicons/react/24/outline"; +import { useContestStore } from "@hooks/useContest/store"; +import { useContestState } from "@hooks/useContestState"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; +import { switchChain } from "@wagmi/core"; +import { usePathname } from "next/navigation"; +import { useState } from "react"; +import { useAccount } from "wagmi"; +import CancelContestModal from "./components/Modal"; + +const CancelContest = () => { + const pathname = usePathname(); + const { chainName } = extractPathSegments(pathname); + const contestChainId = chains.find(chain => chain.name.toLowerCase() === chainName.toLowerCase())?.id; + const { address, chainId } = useAccount(); + const isUserOnCorrectChain = contestChainId === chainId; + const { contestAuthorEthereumAddress } = useContestStore(state => state); + const { cancelContest, isLoading, isConfirmed } = useContestState(); + const { contestState } = useContestStateStore(state => state); + const [isCloseContestModalOpen, setIsCloseContestModalOpen] = useState(false); + const isAuthor = address === contestAuthorEthereumAddress; + const isNotCanceled = contestState !== ContestStateEnum.Canceled; + const shouldRender = isAuthor && isNotCanceled && (!isConfirmed || isLoading); + + if (!shouldRender) return null; + + const handleOpenModal = () => setIsCloseContestModalOpen(true); + + const handleCancelContest = async () => { + if (!contestChainId) return; + + if (!isUserOnCorrectChain) { + await switchChain(config, { chainId: contestChainId }); + } + + cancelContest(); + setIsCloseContestModalOpen(false); + }; + + return ( + <> + + + + + ); +}; + +export default CancelContest; diff --git a/packages/react-app-revamp/components/_pages/Contest/components/ContestName/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/ContestName/index.tsx new file mode 100644 index 000000000..d20c01bb5 --- /dev/null +++ b/packages/react-app-revamp/components/_pages/Contest/components/ContestName/index.tsx @@ -0,0 +1,24 @@ +import GradientText from "@components/UI/GradientText"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; +import { FC } from "react"; +import CancelContest from "../CancelContest"; + +interface ContestNameProps { + contestName: string; + address: string; + chainName: string; +} + +const ContestName: FC = ({ contestName, address, chainName }) => { + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; + + return ( +
+ + +
+ ); +}; + +export default ContestName; diff --git a/packages/react-app-revamp/components/_pages/Contest/components/Prompt/components/Page/components/Layout/V3/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/Prompt/components/Page/components/Layout/V3/index.tsx index 5c95c5226..5f63f5bf5 100644 --- a/packages/react-app-revamp/components/_pages/Contest/components/Prompt/components/Page/components/Layout/V3/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/components/Prompt/components/Page/components/Layout/V3/index.tsx @@ -1,5 +1,6 @@ import { ChevronUpIcon } from "@heroicons/react/24/outline"; import { useContestStore } from "@hooks/useContest/store"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; import { ContestStatus, useContestStatusStore } from "@hooks/useContestStatus/store"; import { Interweave } from "interweave"; import { UrlMatcher } from "interweave-autolink"; @@ -16,6 +17,8 @@ const ContestPromptPageV3Layout: FC = ({ prompt const [isDescriptionOpen, setIsDescriptionOpen] = useState(true); const [isExpanded, setIsExpanded] = useState(false); const { contestStatus } = useContestStatusStore(state => state); + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; const [contestType, contestTitle, contestSummary, contestEvaluate, contestContactDetails] = prompt.split("|"); const isVotingOpenOrClosed = contestStatus === ContestStatus.VotingOpen || contestStatus === ContestStatus.VotingClosed; @@ -86,7 +89,11 @@ const ContestPromptPageV3Layout: FC = ({ prompt return (
-

{contestTitle}

+

+ {contestTitle} +

{contestType}
@@ -101,7 +108,7 @@ const ContestPromptPageV3Layout: FC = ({ prompt
diff --git a/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx index e8199f5ba..75cc43a12 100644 --- a/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx @@ -4,6 +4,7 @@ import { SortOptions, useProposalStore } from "@hooks/useProposal/store"; import { FC, useMemo } from "react"; import ProposalStatisticsPanel from "./components/Panel"; import SortProposalsDropdown from "./components/SortDropdown"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; interface ProposalStatisticsProps { contestStatus: ContestStatus; @@ -15,6 +16,8 @@ const ProposalStatistics: FC = ({ contestStatus, onMenu const { sortProposalData } = useProposal(); const isSubmissionOrVotingOpen = contestStatus === ContestStatus.SubmissionOpen || contestStatus === ContestStatus.VotingOpen; + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; const handleSortTypeChange = (value: string) => { sortProposalData(value as SortOptions); @@ -37,15 +40,17 @@ const ProposalStatistics: FC = ({ contestStatus, onMenu return (
-
- {isSubmissionOrVotingOpen ? ( - - - - - ) : null} -

{contestStatusTitle}

-
+ {!isContestCanceled ? ( +
+ {isSubmissionOrVotingOpen ? ( + + + + + ) : null} +

{contestStatusTitle}

+
+ ) : null}
{submissionsCount > 1 ? ( diff --git a/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/index.tsx index ba86dec74..e1c3a544f 100644 --- a/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/index.tsx @@ -1,13 +1,22 @@ import useContestEvents from "@hooks/useContestEvents"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; import { ContestStatus, useContestStatusStore } from "@hooks/useContestStatus/store"; import ContestCountdown from "./components/Countdown"; import VotingContestQualifier from "./components/VotingQualifier"; const ContestStickyCards = () => { const contestStatus = useContestStatusStore(state => state.contestStatus); + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; const { displayReloadBanner } = useContestEvents(); - if (contestStatus === ContestStatus.VotingClosed) return null; + if (isContestCanceled || contestStatus === ContestStatus.VotingClosed) { + return ( +
+
+
+ ); + } return (
= ({ const canVote = currentUserAvailableVotesAmount > 0; const isProposalTweet = proposal.tweet.isTweet; const contestStatus = useContestStatusStore(state => state.contestStatus); + const { contestState } = useContestStateStore(state => state); + const isContestCanceled = contestState === ContestStateEnum.Canceled; const setPickProposal = useCastVotesStore(state => state.setPickedProposal); const [isContentHidden, setIsContentHidden] = useState(false); const formattedVotingOpen = moment(votesOpen); @@ -98,6 +101,11 @@ const ProposalContent: FC = ({ }, [contestAddress, proposal.id]); const handleVotingModalOpen = () => { + if (isContestCanceled) { + alert("This contest has been canceled and voting is terminated."); + return; + } + if (contestStatus === ContestStatus.VotingClosed) { alert("Voting is closed for this contest."); return; @@ -211,13 +219,13 @@ const ProposalContent: FC = ({
)} diff --git a/packages/react-app-revamp/config/wagmi/custom-chains/evmos.ts b/packages/react-app-revamp/config/wagmi/custom-chains/evmos.ts index e878c67bd..848bc4f8d 100644 --- a/packages/react-app-revamp/config/wagmi/custom-chains/evmos.ts +++ b/packages/react-app-revamp/config/wagmi/custom-chains/evmos.ts @@ -11,10 +11,10 @@ export const evmos: Chain = { }, rpcUrls: { public: { - http: ["https://evmos-evm.publicnode.com"], + http: ["https://evmos.lava.build"], }, default: { - http: ["https://evmos-evm.publicnode.com"], + http: ["https://evmos.lava.build"], }, }, blockExplorers: { diff --git a/packages/react-app-revamp/hooks/useContest/index.ts b/packages/react-app-revamp/hooks/useContest/index.ts index ceb787b38..1ae659ad8 100644 --- a/packages/react-app-revamp/hooks/useContest/index.ts +++ b/packages/react-app-revamp/hooks/useContest/index.ts @@ -5,6 +5,7 @@ import { extractPathSegments } from "@helpers/extractPath"; import getContestContractVersion from "@helpers/getContestContractVersion"; import getRewardsModuleContractVersion from "@helpers/getRewardsModuleContractVersion"; import { MAX_MS_TIMEOUT } from "@helpers/timeout"; +import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; import { ContestStatus, useContestStatusStore } from "@hooks/useContestStatus/store"; import { SplitFeeDestinationType, VoteType } from "@hooks/useDeployContest/types"; import { useError } from "@hooks/useError"; @@ -80,6 +81,7 @@ export function useContest() { const { checkIfCurrentUserQualifyToVote, checkIfCurrentUserQualifyToSubmit } = useUser(); const { fetchProposalsIdsList } = useProposal(); const { contestStatus } = useContestStatusStore(state => state); + const { setContestState } = useContestStateStore(state => state); const { error: errorMessage, handleError } = useError(); const alchemyRpc = chains .filter((chain: { name: string }) => chain.name.toLowerCase().replace(" ", "") === chainName.toLowerCase())?.[0] @@ -150,29 +152,30 @@ export function useContest() { const votesOpenDate = new Date(Number(results[6].result) * 1000 + 1000); const contestPrompt = results[7].result as string; const isDownvotingAllowed = Number(results[8].result) === 1; + const contestState = results[9].result as ContestStateEnum; if (compareVersions(version, "4.0") >= 0) { - const percentageToCreator = Number(results[9].result); - const costToPropose = Number(results[10].result); + const percentageToCreator = Number(results[10].result); + const costToPropose = Number(results[11].result); let costToVote = 0; let payPerVote = 0; let creatorSplitDestination = ""; if (compareVersions(version, "4.2") >= 0) { - const sortingEnabled = Number(results[11].result) === 1; + const sortingEnabled = Number(results[12].result) === 1; setSortingEnabled(sortingEnabled); } if (compareVersions(version, "4.23") >= 0) { if (compareVersions(version, "4.25") >= 0) { - payPerVote = Number(results[13].result); + payPerVote = Number(results[14].result); } - costToVote = Number(results[12].result); + costToVote = Number(results[13].result); } if (compareVersions(version, "4.29") >= 0) { - creatorSplitDestination = results[14].result as string; + creatorSplitDestination = results[15].result as string; } setCharge({ @@ -200,6 +203,7 @@ export function useContest() { setVotesOpen(votesOpenDate); setContestPrompt(contestPrompt); setDownvotingAllowed(isDownvotingAllowed); + setContestState(contestState); // We want to track VoteCast event only 2H before the end of the contest, and only if alchemy support is enabled and if alchemy is configured if (isBefore(new Date(), closingVoteDate) && alchemyRpc && isAlchemyConfigured) { diff --git a/packages/react-app-revamp/hooks/useContest/v3v4/contracts.ts b/packages/react-app-revamp/hooks/useContest/v3v4/contracts.ts index c7d41d6e0..1e57febd6 100644 --- a/packages/react-app-revamp/hooks/useContest/v3v4/contracts.ts +++ b/packages/react-app-revamp/hooks/useContest/v3v4/contracts.ts @@ -11,6 +11,7 @@ export function getContracts(contractConfig: any, version: string) { "voteStart", "prompt", "downvotingAllowed", + "state", ]; const v4FunctionNames = ["percentageToCreator", "costToPropose"]; diff --git a/packages/react-app-revamp/hooks/useContestState/index.ts b/packages/react-app-revamp/hooks/useContestState/index.ts new file mode 100644 index 000000000..a0de4d337 --- /dev/null +++ b/packages/react-app-revamp/hooks/useContestState/index.ts @@ -0,0 +1,63 @@ +import { toastDismiss, toastLoading, toastSuccess } from "@components/UI/Toast"; +import { chains, config } from "@config/wagmi"; +import { extractPathSegments } from "@helpers/extractPath"; +import { useContestStore } from "@hooks/useContest/store"; +import { simulateContract, waitForTransactionReceipt, writeContract } from "@wagmi/core"; +import { usePathname } from "next/navigation"; +import { useState } from "react"; +import { handleError } from "utils/error"; +import { ContestStateEnum, useContestStateStore } from "./store"; + +interface CancelContestResult { + cancelContest: () => Promise; + isLoading: boolean; + isConfirmed: boolean; +} + +export function useContestState(): CancelContestResult { + const asPath = usePathname(); + const { chainName, address } = extractPathSegments(asPath ?? ""); + const chainId = chains.find(chain => chain.name === chainName)?.id; + const { contestAbi: abi } = useContestStore(state => state); + const { setContestState } = useContestStateStore(state => state); + + const [isLoading, setIsLoading] = useState(false); + const [isConfirmed, setIsConfirmed] = useState(false); + + const cancelContest = async (): Promise => { + setIsLoading(true); + setIsConfirmed(false); + toastLoading("Cancelling contest..."); + + try { + const { request } = await simulateContract(config, { + chainId, + abi, + address: address as `0x${string}`, + functionName: "cancel", + }); + + const txHash = await writeContract(config, request); + + const receipt = await waitForTransactionReceipt(config, { hash: txHash }); + + if (receipt.status === "success") { + setIsConfirmed(true); + toastSuccess("Contest cancelled successfully"); + setContestState(ContestStateEnum.Canceled); + } else { + handleError("An error occurred while cancelling the contest"); + } + } catch (err: any) { + handleError(err); + } finally { + setIsLoading(false); + } + }; + + return { + cancelContest, + isLoading, + isConfirmed, + }; +} diff --git a/packages/react-app-revamp/hooks/useContestState/store.ts b/packages/react-app-revamp/hooks/useContestState/store.ts new file mode 100644 index 000000000..998b751b6 --- /dev/null +++ b/packages/react-app-revamp/hooks/useContestState/store.ts @@ -0,0 +1,19 @@ +import { create } from "zustand"; + +export enum ContestStateEnum { + NotStarted, + Active, + Canceled, + Queued, + Completed, +} + +type Store = { + contestState: ContestStateEnum; + setContestState: (state: ContestStateEnum) => void; +}; + +export const useContestStateStore = create(set => ({ + contestState: ContestStateEnum.NotStarted, + setContestState: (state: ContestStateEnum) => set({ contestState: state }), +})); diff --git a/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx b/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx index b23838031..09723fa52 100644 --- a/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx +++ b/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx @@ -8,6 +8,7 @@ import ContestTab from "@components/_pages/Contest/Contest"; import ContestExtensions from "@components/_pages/Contest/Extensions"; import ContestParameters from "@components/_pages/Contest/Parameters"; import ContestRewards from "@components/_pages/Contest/Rewards"; +import ContestName from "@components/_pages/Contest/components/ContestName"; import ContestRewardsInfo from "@components/_pages/Contest/components/RewardsInfo"; import ContestTabs, { Tab } from "@components/_pages/Contest/components/Tabs"; import { useShowRewardsStore } from "@components/_pages/Create/pages/ContestDeploying"; @@ -201,23 +202,11 @@ const LayoutViewContest = ({ children }: { children: React.ReactNode }) => { )}
-
-
- {contestName} -
-
- navigator.share({ - url: generateUrlContest(address, chainName), - }) - } - > - share -
-
+ -
+
{ {rewardsModuleAddress && rewardsAbi ? ( ) : null} -
+ {isMobile ? ( +
+ navigator.share({ + url: generateUrlContest(address, chainName), + }) + } + > + share +
+ ) : ( -
+ )} +
window.location.reload()} diff --git a/yarn.lock b/yarn.lock index 95a9da94d..a74e5acd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16096,47 +16096,47 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4 resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -turbo-darwin-64@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.0.12.tgz#3ca5b781adf0bb68a5e35bc70ce35c791ae52145" - integrity sha512-NAgfgbXxX/JScWQmmQnGbPuFZq7LIswHfcMk5JwyBXQM/xmklNOxxac7MnGGIOf19Z2f6S3qHy17VIj0SeGfnA== - -turbo-darwin-arm64@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.0.12.tgz#563af59a4629f2741f4e1fda68f47ff756f3c71c" - integrity sha512-cP02uer5KSJ+fXL+OfRRk5hnVjV0c60hxDgNcJxrZpfhun7HHoKDDR7w2xhQntiA45aC6ZZEXRqMKpj6GAmKbg== - -turbo-linux-64@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.0.12.tgz#7714dd67f8f8514ef19cce47d42de185c45f52ac" - integrity sha512-+mQgGfg1eq5qF+wenK/FKJaNMNAo5DQLC4htQy+8osW+fx6U+8+6UlPQPaycAWDEqwOI7NwuqkeHfkEQLQUTyQ== - -turbo-linux-arm64@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.0.12.tgz#04f54cddbb80db532151efffec556e363399737d" - integrity sha512-KFyEZDXfPU1DK4zimxdCcqAcK7IIttX4mfsgB7NsSEOmH0dhHOih/YFYiyEDC1lTRx0C2RlzQ0Kjjdz48AN5Eg== - -turbo-windows-64@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.0.12.tgz#b10d9190bee532d4989ab7acf637e4643c50eac3" - integrity sha512-kJj4KCkZTkDTDCqsSw1m1dbO4WeoQq1mYUm/thXOH0OkeqYbSMt0EyoTcJOgKUDsrMnzZD2gPfYrlYHtV69lVA== - -turbo-windows-arm64@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.0.12.tgz#35548e30614d6cc15bc4486d609335dfc971cd76" - integrity sha512-TY3ROxguDilN2olCwcZMaePdW01Xhma0pZU7bNhsQEqca9RGAmsZBuzfGnTMcWPmv4tpnb/PlX1hrt1Hod/44Q== - -turbo@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.0.12.tgz#0a3221d1cfc83b4631552a18f1b982e84ec3fb0e" - integrity sha512-8s2KwqjwQj7z8Z53SUZSKVkQOZ2/Sl4D2F440oaBY/k2lGju60dW6srEpnn8/RIDeICZmQn3pQHF79Jfnc5Skw== +turbo-darwin-64@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.0.13.tgz#103c37bbefbd55010e14cdd8dcd5ed4063de4277" + integrity sha512-1qoFGvSE/kG1Njl1a1b35+AlJyCBE/+cb7GcglxuUM0Fh0JNR6FIWGhiubUhFLoIdFkdRbPpTH5smhPpRk757w== + +turbo-darwin-arm64@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.0.13.tgz#8b2640b477ff67fd1d32c8a39cfb41e6e9ce7b4c" + integrity sha512-3fgbjKeRjvZTKJhn6eOyA6SaFXlRHx/z7qAWYcTbq7iu2zd+n0OHRyah9Voy2ZihiGAI0Bw4QfVWo3d1qTabgA== + +turbo-linux-64@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.0.13.tgz#6a145045f6c9f307630e308f30f080ae73e91ce5" + integrity sha512-1nMO5NWHWs4jI5SV8SMlGtR3/jBlud1ld/ltayYwOXm6zdznj0/mTNNE9AjfOs/aAei2wZE1HWUHXe46q/nZtw== + +turbo-linux-arm64@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.0.13.tgz#7d7341139eac1bbd87efe988363623f692ba4f24" + integrity sha512-JkQYAAMbEW3jkzhbtY4hi0Q61tlSd46qLEVBQqnur9VymC4TfD6kllVMpRtbmXv8jdfGPu+rEIeYrzjHpyqMfg== + +turbo-windows-64@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.0.13.tgz#44a6c0a2007b3208fb4602ba5c96d63b5df13463" + integrity sha512-QYJfYPnmb3j16CR4mucYicC+tlY1fsFws6fkqZe2b8jBpRyOslxkEk4XJWCsvUizPSYpOdAnTL9baunLH7hWrA== + +turbo-windows-arm64@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.0.13.tgz#c50f040079b2cb379c3549c887abb5f9a8e6bda5" + integrity sha512-dnVN19dq7jszcKVBy4+T1IMmMY1ergigJZdl76Bbc99QOwaXsTR7ci4sle89HvJb+F1z/MXSq8ePvRTcy6dKRw== + +turbo@2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.0.13.tgz#679123a0aa2244e54de87f8fd75f2ffe07301c7d" + integrity sha512-xZYCpvy+Q5X05HQLVbup/nUva/EPd8QKX/WLfztz8Yhh7yH+VN9t+P9ACG2U0ThlrqX4uFS0tuE0jaCLvL1Imw== optionalDependencies: - turbo-darwin-64 "2.0.12" - turbo-darwin-arm64 "2.0.12" - turbo-linux-64 "2.0.12" - turbo-linux-arm64 "2.0.12" - turbo-windows-64 "2.0.12" - turbo-windows-arm64 "2.0.12" + turbo-darwin-64 "2.0.13" + turbo-darwin-arm64 "2.0.13" + turbo-linux-64 "2.0.13" + turbo-linux-arm64 "2.0.13" + turbo-windows-64 "2.0.13" + turbo-windows-arm64 "2.0.13" tween-functions@^1.2.0: version "1.2.0"