From c5d39b54d9078ee9a0628d6325f2e8af2cf0772f Mon Sep 17 00:00:00 2001 From: evilpeach Date: Mon, 18 Sep 2023 00:50:54 +0700 Subject: [PATCH 01/15] feat: implement new Amplitude structure --- CHANGELOG.md | 1 + package.json | 1 + src/lib/amplitude/index.ts | 3 + src/lib/amplitude/track-event/index.ts | 47 +++ .../track-event/useMandatoryProperties.ts | 40 +++ .../amplitude/track-event/useTrackAction.ts | 29 ++ .../track-event/useTrackComponent.ts | 20 ++ .../amplitude/track-event/useTrackExternal.ts | 61 ++++ .../track-event/useTrackInteraction.ts | 290 +++++++++++++++ .../amplitude/track-event/useTrackToPage.ts | 68 ++++ src/lib/amplitude/track-event/useTrackTx.ts | 27 ++ src/lib/amplitude/types.ts | 175 +++++++++ src/lib/amplitude/useAmplitudeInit.ts | 47 +++ src/lib/app-fns/tx/clearAdmin.tsx | 6 +- src/lib/app-fns/tx/common/catchTxError.tsx | 13 +- src/lib/app-fns/tx/execute.tsx | 7 +- src/lib/app-fns/tx/instantiate.tsx | 6 +- src/lib/app-fns/tx/migrate.tsx | 6 +- src/lib/app-fns/tx/resend.tsx | 15 +- src/lib/app-fns/tx/submitProposal.tsx | 18 +- src/lib/app-fns/tx/updateAdmin.tsx | 7 +- src/lib/app-fns/tx/upload.tsx | 10 +- src/lib/app-provider/contexts/nav.tsx | 7 +- src/lib/app-provider/hooks/index.ts | 2 +- src/lib/app-provider/hooks/useAmplitude.ts | 47 --- .../app-provider/hooks/usePreviousPathname.ts | 14 + src/lib/app-provider/tx/catchTxError.ts | 17 + src/lib/app-provider/tx/clearAdmin.ts | 14 +- src/lib/app-provider/tx/execute.ts | 13 +- src/lib/app-provider/tx/instantiate.ts | 13 +- src/lib/app-provider/tx/migrate.ts | 14 +- src/lib/app-provider/tx/resend.ts | 16 +- src/lib/app-provider/tx/submitProposal.ts | 38 +- src/lib/app-provider/tx/updateAdmin.ts | 13 +- src/lib/app-provider/tx/upload.ts | 13 +- src/lib/components/ConnectWalletAlert.tsx | 7 +- src/lib/components/CopyLink.tsx | 5 +- src/lib/components/ExplorerLink.tsx | 5 +- src/lib/components/InputWithIcon.tsx | 44 +-- src/lib/components/StickySidebar.tsx | 5 +- .../components/ViewPermissionAddresses.tsx | 5 +- src/lib/components/Wallet.tsx | 7 +- src/lib/components/button/BackButton.tsx | 5 +- src/lib/components/button/ConnectWallet.tsx | 5 +- .../components/button/NewProposalButton.tsx | 5 +- src/lib/components/button/ResendButton.tsx | 7 +- src/lib/components/copy/Copier.tsx | 50 +-- src/lib/components/copy/CopyButton.tsx | 54 +-- src/lib/components/json/JsonReadOnly.tsx | 5 +- src/lib/components/links/GitHubLink.tsx | 5 +- src/lib/components/modal/ActionModal.tsx | 5 +- src/lib/components/modal/CodeSnippet.tsx | 5 +- .../modal/UnsupportedTokensModal.tsx | 5 +- .../modal/code/CodeDetailsTemplate.tsx | 8 +- src/lib/components/modal/code/RemoveCode.tsx | 7 +- src/lib/components/modal/code/SaveNewCode.tsx | 6 +- .../modal/contract/AddToOtherList.tsx | 5 +- .../components/modal/contract/ClearAdmin.tsx | 7 +- .../contract/ContractDetailsTemplate.tsx | 5 +- .../modal/contract/RemoveContract.tsx | 5 +- .../modal/contract/SaveNewContract.tsx | 5 +- .../components/modal/list/CreateNewList.tsx | 6 +- .../components/modal/list/EditListName.tsx | 7 +- src/lib/components/modal/list/RemoveList.tsx | 6 +- src/lib/components/pagination/Next.tsx | 5 +- src/lib/components/pagination/PageDetail.tsx | 80 +++-- src/lib/components/pagination/Previous.tsx | 5 +- .../select-code/CodeSelectDrawerButton.tsx | 5 +- .../select-code/CodeSelectSection.tsx | 5 +- .../select-contract/SelectContractAdmin.tsx | 5 +- .../SelectContractInstantiator.tsx | 9 +- src/lib/components/state/InvalidState.tsx | 8 +- src/lib/components/table/ViewMore.tsx | 41 ++- .../components/table/codes/CodeNameCell.tsx | 5 +- .../table/contracts/ContractNameCell.tsx | 5 +- .../components/table/contracts/EditTags.tsx | 5 +- .../table/proposals/ProposalsTableRow.tsx | 5 +- .../upload/InstantiatePermissionRadio.tsx | 26 +- src/lib/components/upload/UploadSection.tsx | 6 +- src/lib/hooks/index.ts | 1 + src/lib/hooks/useLocalStorage.ts | 7 +- src/lib/layout/Footer.tsx | 171 ++++----- src/lib/layout/Header.tsx | 6 +- src/lib/layout/Searchbar.tsx | 7 +- src/lib/layout/mobile/NavDrawer.tsx | 7 +- src/lib/layout/navbar/Collapse.tsx | 5 +- src/lib/layout/navbar/Expand.tsx | 120 ++++--- .../components/asset/index.tsx | 5 +- .../components/delegations/DelegationInfo.tsx | 8 +- .../delegations/DelegationsBody.tsx | 5 +- .../components/delegations/index.tsx | 5 +- src/lib/pages/account-details/index.tsx | 13 +- src/lib/pages/admin/index.tsx | 15 +- src/lib/pages/block-details/index.tsx | 7 +- src/lib/pages/blocks/index.tsx | 8 +- src/lib/pages/code-details/index.tsx | 7 +- src/lib/pages/codes/index.tsx | 7 +- src/lib/pages/contract-details/index.tsx | 7 +- src/lib/pages/contract-list/index.tsx | 7 +- src/lib/pages/contract-list/slug.tsx | 11 +- src/lib/pages/contracts/index.tsx | 12 +- src/lib/pages/deploy/index.tsx | 7 +- .../pages/execute/components/JsonExecute.tsx | 6 +- .../execute/components/MsgSuggestion.tsx | 6 +- .../components/schema-execute/ExecuteBox.tsx | 6 +- .../components/schema-execute/index.tsx | 12 +- src/lib/pages/execute/index.tsx | 10 +- src/lib/pages/faucet/index.tsx | 15 +- src/lib/pages/home/index.tsx | 9 +- .../component/InstantiateOffchainForm.tsx | 5 +- src/lib/pages/instantiate/instantiate.tsx | 17 +- .../migrate/components/MigrateContract.tsx | 6 +- src/lib/pages/migrate/index.tsx | 8 +- src/lib/pages/not-found/index.tsx | 7 +- src/lib/pages/past-txs/index.tsx | 7 +- .../pools/components/FilterByPoolType.tsx | 33 +- .../pool-details/JsonModalButton.tsx | 5 +- .../components/pool-details/PoolAssets.tsx | 6 +- .../pool-details/PoolRelatedTxs.tsx | 19 +- .../components/pool-details/header/index.tsx | 7 +- .../tables/pool-txs/PoolTxsMsg.tsx | 5 +- .../components/supportedSection/PoolCard.tsx | 5 +- .../components/supportedSection/index.tsx | 16 +- .../UnsupportedPoolCard.tsx | 9 +- .../components/unsupportedSection/index.tsx | 18 +- src/lib/pages/pools/index.tsx | 23 +- src/lib/pages/pools/poolId.tsx | 7 +- src/lib/pages/proposal/constants.tsx | 13 +- src/lib/pages/proposal/store-code/index.tsx | 30 +- src/lib/pages/proposal/whitelist/index.tsx | 34 +- .../components/ProposalStatusFilter.tsx | 11 +- .../components/ProposalTypeFilter.tsx | 7 +- src/lib/pages/proposals/index.tsx | 11 +- .../public-project/components/AllProject.tsx | 7 +- .../components/BookmarkButton.tsx | 11 +- .../public-project/components/SocialMedia.tsx | 10 +- src/lib/pages/public-project/index.tsx | 7 +- src/lib/pages/public-project/slug.tsx | 7 +- src/lib/pages/query/components/JsonQuery.tsx | 7 +- .../pages/query/components/SchemaQuery.tsx | 5 +- .../query/components/SchemaQueryComponent.tsx | 8 +- src/lib/pages/query/index.tsx | 7 +- src/lib/pages/saved-codes/index.tsx | 7 +- src/lib/pages/stored-codes/index.tsx | 7 +- .../pages/tx-details/components/TxHeader.tsx | 5 +- .../components/tx-message/EventBox.tsx | 5 +- .../components/tx-message/TxMsgExpand.tsx | 6 +- .../tx-message/msg-receipts/CoinComponent.tsx | 6 +- src/lib/pages/tx-details/index.tsx | 7 +- src/lib/pages/txs/index.tsx | 8 +- src/lib/pages/upload/upload.tsx | 7 +- src/lib/providers/amplitude.tsx | 9 +- src/lib/providers/index.tsx | 8 +- src/lib/services/amplitude.tsx | 338 ------------------ yarn.lock | 68 ++++ 155 files changed, 1878 insertions(+), 1122 deletions(-) create mode 100644 src/lib/amplitude/index.ts create mode 100644 src/lib/amplitude/track-event/index.ts create mode 100644 src/lib/amplitude/track-event/useMandatoryProperties.ts create mode 100644 src/lib/amplitude/track-event/useTrackAction.ts create mode 100644 src/lib/amplitude/track-event/useTrackComponent.ts create mode 100644 src/lib/amplitude/track-event/useTrackExternal.ts create mode 100644 src/lib/amplitude/track-event/useTrackInteraction.ts create mode 100644 src/lib/amplitude/track-event/useTrackToPage.ts create mode 100644 src/lib/amplitude/track-event/useTrackTx.ts create mode 100644 src/lib/amplitude/types.ts create mode 100644 src/lib/amplitude/useAmplitudeInit.ts delete mode 100644 src/lib/app-provider/hooks/useAmplitude.ts create mode 100644 src/lib/app-provider/hooks/usePreviousPathname.ts create mode 100644 src/lib/app-provider/tx/catchTxError.ts delete mode 100644 src/lib/services/amplitude.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c4a546d..f9f1a32fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements +- [#532](https://github.com/alleslabs/celatone-frontend/pull/532) Implement new Amplitude structure - [#529](https://github.com/alleslabs/celatone-frontend/pull/529) Improve developer features ux writing and transition - [#528](https://github.com/alleslabs/celatone-frontend/pull/528) Improve developer features toggle for a smoother ux - [#519](https://github.com/alleslabs/celatone-frontend/pull/519) Validate schema input when initialFormData has changed diff --git a/package.json b/package.json index 4094740c9..063a62df2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@amplitude/analytics-browser": "^1.8.0", + "@amplitude/marketing-analytics-browser": "^1.0.7", "@chain-registry/types": "^0.13.1", "@chakra-ui/anatomy": "^2.1.0", "@chakra-ui/card": "^2.1.1", diff --git a/src/lib/amplitude/index.ts b/src/lib/amplitude/index.ts new file mode 100644 index 000000000..815ea2ff3 --- /dev/null +++ b/src/lib/amplitude/index.ts @@ -0,0 +1,3 @@ +export * from "./track-event"; +export * from "./useAmplitudeInit"; +export * from "./types"; diff --git a/src/lib/amplitude/track-event/index.ts b/src/lib/amplitude/track-event/index.ts new file mode 100644 index 000000000..fbbcc3792 --- /dev/null +++ b/src/lib/amplitude/track-event/index.ts @@ -0,0 +1,47 @@ +import { track as amplitudeTrack } from "@amplitude/analytics-browser"; +import { useCallback } from "react"; + +import type { AmpEvent, ActionAmpEvent, SpecialAmpEvent } from "../types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; +import { useTrackAction } from "./useTrackAction"; +import { useTrackComponent } from "./useTrackComponent"; +import { useTrackExternal } from "./useTrackExternal"; +import { useTrackInteraction } from "./useTrackInteraction"; +import { useTrackToPage } from "./useTrackToPage"; +import { useTrackTx } from "./useTrackTx"; + +export const useTrack = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackAction = useTrackAction(); + const trackComponent = useTrackComponent(); + const trackExternal = useTrackExternal(); + const trackInteraction = useTrackInteraction(); + const trackToPage = useTrackToPage(); + const trackTx = useTrackTx(); + + const track = useCallback( + ( + event: Exclude, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + properties?: Record + ) => { + amplitudeTrack(event, { + ...mandatoryProperties, + ...properties, + }); + }, + [mandatoryProperties] + ); + + return { + track, + ...trackAction, + ...trackComponent, + ...trackExternal, + ...trackInteraction, + ...trackToPage, + ...trackTx, + }; +}; diff --git a/src/lib/amplitude/track-event/useMandatoryProperties.ts b/src/lib/amplitude/track-event/useMandatoryProperties.ts new file mode 100644 index 000000000..bfa287340 --- /dev/null +++ b/src/lib/amplitude/track-event/useMandatoryProperties.ts @@ -0,0 +1,40 @@ +import { createHash } from "crypto"; +import { useRouter } from "next/router"; +import { useMemo } from "react"; + +import { useCelatoneApp, useNavContext } from "lib/app-provider/contexts"; +import { useCurrentChain, useMobile } from "lib/app-provider/hooks"; + +export const useMandatoryProperties = () => { + const { currentChainId } = useCelatoneApp(); + const { isExpand, isDevMode, prevPathname } = useNavContext(); + const { address } = useCurrentChain(); + const isMobile = useMobile(); + const router = useRouter(); + + // TODO: make util function + const walletAddress = address + ? createHash("sha256").update(address).digest("hex") + : "Not Connected"; + + return useMemo( + () => ({ + page: router.pathname.replace("/[network]", ""), + prevPathname, + walletAddress, + chain: currentChainId, + mobile: isMobile, + navOpen: isExpand, + devMode: isDevMode, + }), + [ + currentChainId, + isDevMode, + isExpand, + isMobile, + prevPathname, + router.pathname, + walletAddress, + ] + ); +}; diff --git a/src/lib/amplitude/track-event/useTrackAction.ts b/src/lib/amplitude/track-event/useTrackAction.ts new file mode 100644 index 000000000..a52c3ab95 --- /dev/null +++ b/src/lib/amplitude/track-event/useTrackAction.ts @@ -0,0 +1,29 @@ +import { track } from "@amplitude/analytics-browser"; +import { useCallback } from "react"; + +import type { ActionAmpEvent } from "../types"; +import type { AttachFundsType } from "lib/components/fund/types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; + +export const useTrackAction = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackAction = useCallback( + ( + event: ActionAmpEvent, + funds: number, + attachFundsOption: AttachFundsType + ) => + track(event, { + ...mandatoryProperties, + funds, + attachFundsOption, + }), + [mandatoryProperties] + ); + + // TODO: implement custom action here + + return { trackAction }; +}; diff --git a/src/lib/amplitude/track-event/useTrackComponent.ts b/src/lib/amplitude/track-event/useTrackComponent.ts new file mode 100644 index 000000000..eb29f6295 --- /dev/null +++ b/src/lib/amplitude/track-event/useTrackComponent.ts @@ -0,0 +1,20 @@ +import { track } from "@amplitude/analytics-browser"; +import { useCallback } from "react"; + +import { AmpEvent } from "../types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; + +export const useTrackComponent = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackInvalidState = useCallback( + (title: string) => + track(AmpEvent.INVALID_STATE, { ...mandatoryProperties, title }), + [mandatoryProperties] + ); + + return { + trackInvalidState, + }; +}; diff --git a/src/lib/amplitude/track-event/useTrackExternal.ts b/src/lib/amplitude/track-event/useTrackExternal.ts new file mode 100644 index 000000000..446011bd8 --- /dev/null +++ b/src/lib/amplitude/track-event/useTrackExternal.ts @@ -0,0 +1,61 @@ +import { track } from "@amplitude/analytics-browser"; +import { useCallback } from "react"; + +import { AmpEvent } from "../types"; +import type { Dict } from "lib/types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; + +export const useTrackExternal = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackMintScan = useCallback( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (type: string, properties?: Dict, section?: string) => { + track(AmpEvent.MINTSCAN, { + ...mandatoryProperties, + type, + properties, + section, + }); + }, + [mandatoryProperties] + ); + + const trackWebsite = useCallback( + (url: string, section?: string) => + track(AmpEvent.WEBSITE, { + ...mandatoryProperties, + url, + section, + }), + [mandatoryProperties] + ); + + const trackSocial = useCallback( + (url: string, section?: string) => + track(AmpEvent.SOCIAL, { + ...mandatoryProperties, + url, + section, + }), + [mandatoryProperties] + ); + + const trackCelatone = useCallback( + (url: string, section?: string) => + track(AmpEvent.CELATONE, { + ...mandatoryProperties, + url, + section, + }), + [mandatoryProperties] + ); + + return { + trackMintScan, + trackWebsite, + trackSocial, + trackCelatone, + }; +}; diff --git a/src/lib/amplitude/track-event/useTrackInteraction.ts b/src/lib/amplitude/track-event/useTrackInteraction.ts new file mode 100644 index 000000000..cb6a4e16b --- /dev/null +++ b/src/lib/amplitude/track-event/useTrackInteraction.ts @@ -0,0 +1,290 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { track } from "@amplitude/analytics-browser"; +import big from "big.js"; +import { useCallback } from "react"; + +import { AmpEvent } from "../types"; +import type { Token, Option } from "lib/types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; + +// TODO: implement custom event properties including section +export const useTrackInteraction = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackUseMainSearch = useCallback( + (isClick: boolean, section?: string) => + track(AmpEvent.USE_MAIN_SEARCH, { + ...mandatoryProperties, + isClick, + section, + }), + [mandatoryProperties] + ); + + const trackUseTab = useCallback( + (tab: string, section?: string) => + track(AmpEvent.USE_TAB, { + ...mandatoryProperties, + tab, + section, + }), + [mandatoryProperties] + ); + + const trackUseRadio = useCallback( + (radio: string, section?: string) => + track(AmpEvent.USE_RADIO, { + ...mandatoryProperties, + radio, + section, + }), + [mandatoryProperties] + ); + + const trackUseOtherModal = useCallback( + (title: string, section?: string) => + track(AmpEvent.USE_OTHER_MODAL, { + ...mandatoryProperties, + title, + section, + }), + [mandatoryProperties] + ); + + const trackUseViewJSON = useCallback( + (section?: string) => + track(AmpEvent.USE_VIEW_JSON, { + ...mandatoryProperties, + section, + }), + [mandatoryProperties] + ); + + const trackUseUnsupportedToken = useCallback( + (section?: string) => + track(AmpEvent.USE_UNSUPPORTED_ASSETS, { + ...mandatoryProperties, + section, + }), + [mandatoryProperties] + ); + + const trackUseCopier = useCallback( + (type: string, section?: string) => + track(AmpEvent.USE_COPIER, { + ...mandatoryProperties, + type, + section, + }), + [mandatoryProperties] + ); + + const trackUseExpand = useCallback( + ({ + action, + component, + info, + section, + }: { + action: "expand" | "collapse"; + component: + | "assets" + | "json" + | "permission_address" + | "event_box" + | "unsupported_pool" + | "pool_tx_msg"; + info?: object; + section?: string; + }) => + track(AmpEvent.USE_EXPAND, { + ...mandatoryProperties, + action, + component, + info, + section, + }), + [mandatoryProperties] + ); + + const trackUseExpandAll = useCallback( + (action: "expand" | "collapse", section?: string) => + track(AmpEvent.USE_EXPAND, { + ...mandatoryProperties, + action, + section, + }), + [mandatoryProperties] + ); + + const trackUseClickWallet = useCallback( + (component?: string) => + track(AmpEvent.USE_CLICK_WALLET, { + ...mandatoryProperties, + component, + }), + [mandatoryProperties] + ); + + const trackUseRightHelperPanel = useCallback( + (action: string, section?: string) => + track(AmpEvent.USE_RIGHT_HELPER_PANEL, { + ...mandatoryProperties, + action, + section, + }), + [mandatoryProperties] + ); + + const trackUseUnpin = useCallback( + (check: boolean, section?: string) => + track(AmpEvent.USE_UNPIN, { + ...mandatoryProperties, + check, + section, + }), + [mandatoryProperties] + ); + + const trackUseInstantiatePermission = useCallback( + ( + type: string, + emptyAddressesLength: number, + addressesLength: number, + section?: string + ) => + track(AmpEvent.USE_INSTANTIATE_PERMISSION, { + ...mandatoryProperties, + type, + emptyAddressesLength, + addressesLength, + section, + }), + [mandatoryProperties] + ); + + const trackUseWhitelistedAddress = useCallback( + ( + emptyAddressesLength: number, + filledAddressesLength: number, + section?: string + ) => + track(AmpEvent.USE_WHITELISTED_ADDRESSES, { + ...mandatoryProperties, + emptyAddressesLength, + filledAddressesLength, + section, + }), + [mandatoryProperties] + ); + + const trackUseDepositFill = useCallback( + (amount: Token, section?: string) => { + track(AmpEvent.USE_DEPOSIT_FILL, { + ...mandatoryProperties, + amount, + section, + }); + }, + [mandatoryProperties] + ); + + const trackUseSubmitProposal = useCallback( + ( + properties: { + initialDeposit: string; + minDeposit: Option; + assetDenom: Option; + [key: string]: any; + }, + section?: string + ) => { + const proposalPeriod = big(properties.initialDeposit).lt( + properties.minDeposit || "0" + ) + ? "Deposit" + : "Voting"; + track(AmpEvent.USE_SUBMIT_PROPOSAL, { + ...mandatoryProperties, + ...properties, + proposalPeriod, + section, + }); + }, + [mandatoryProperties] + ); + + const trackUseFilter = useCallback( + ( + event: + | AmpEvent.USE_FILTER_POOL_TYPE + | AmpEvent.USE_FILTER_PROPOSALS_TYPE + | AmpEvent.USE_FILTER_PROPOSALS_STATUS, + filters: string[], + action: string + ) => track(event, { ...mandatoryProperties, action, filters }), + [mandatoryProperties] + ); + + const trackUsePaginationNavigate = useCallback( + (navigate: string, pageSize: number, currentPage: number) => + track(AmpEvent.USE_PAGINATION_NAVIGATION, { + ...mandatoryProperties, + navigate, + pageSize, + currentPage, + }), + [mandatoryProperties] + ); + + const trackUseSort = useCallback( + (order: "ascending" | "descending") => + track(AmpEvent.USE_SORT, { ...mandatoryProperties, order }), + [mandatoryProperties] + ); + + const trackUseView = useCallback( + (view: string) => + track(AmpEvent.USE_VIEW, { ...mandatoryProperties, view }), + [mandatoryProperties] + ); + + const trackUseToggle = useCallback( + (name: string, isActive: boolean) => + track(AmpEvent.USE_TOGGLE, { ...mandatoryProperties, name, isActive }), + [mandatoryProperties] + ); + + // ASK + const trackUseAlertCTA = useCallback( + (action: string) => + track(AmpEvent.USE_ALERT_CTA, { ...mandatoryProperties, action }), + [mandatoryProperties] + ); + + return { + trackUseMainSearch, + trackUseTab, + trackUseRadio, + trackUseOtherModal, + trackUseViewJSON, + trackUseUnsupportedToken, + trackUseCopier, + trackUseExpand, + trackUseExpandAll, + trackUseClickWallet, + trackUseRightHelperPanel, + trackUseUnpin, + trackUseInstantiatePermission, + trackUseWhitelistedAddress, + trackUseDepositFill, + trackUseSubmitProposal, + trackUseFilter, + trackUsePaginationNavigate, + trackUseSort, + trackUseView, + trackUseToggle, + trackUseAlertCTA, + }; +}; diff --git a/src/lib/amplitude/track-event/useTrackToPage.ts b/src/lib/amplitude/track-event/useTrackToPage.ts new file mode 100644 index 000000000..7c480108a --- /dev/null +++ b/src/lib/amplitude/track-event/useTrackToPage.ts @@ -0,0 +1,68 @@ +import { track } from "@amplitude/analytics-browser"; +import { useCallback } from "react"; + +import { AmpEvent } from "../types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; + +export const useTrackToPage = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackToQuery = useCallback( + (contract: boolean, msg: boolean) => + track(AmpEvent.TO_QUERY, { + ...mandatoryProperties, + contract, + msg, + }), + [mandatoryProperties] + ); + + const trackToExecute = useCallback( + (contract: boolean, msg: boolean) => + track(AmpEvent.TO_EXECUTE, { + ...mandatoryProperties, + contract, + msg, + }), + [mandatoryProperties] + ); + + const trackToInstantiate = useCallback( + (msg: boolean, codeId: boolean, section?: string) => + track(AmpEvent.TO_INSTANTIATE, { + ...mandatoryProperties, + codeId, + msg, + section, + }), + [mandatoryProperties] + ); + + const trackToMigrate = useCallback( + (contract: boolean, codeId: boolean) => + track(AmpEvent.TO_MIGRATE, { + ...mandatoryProperties, + codeId, + contract, + }), + [mandatoryProperties] + ); + + const trackToAdminUpdate = useCallback( + (contract: boolean) => + track(AmpEvent.TO_ADMIN_UPDATE, { + ...mandatoryProperties, + contract, + }), + [mandatoryProperties] + ); + + return { + trackToQuery, + trackToExecute, + trackToInstantiate, + trackToMigrate, + trackToAdminUpdate, + }; +}; diff --git a/src/lib/amplitude/track-event/useTrackTx.ts b/src/lib/amplitude/track-event/useTrackTx.ts new file mode 100644 index 000000000..3e7c5fd27 --- /dev/null +++ b/src/lib/amplitude/track-event/useTrackTx.ts @@ -0,0 +1,27 @@ +import { track } from "@amplitude/analytics-browser"; +import { useCallback } from "react"; + +import { AmpEvent } from "../types"; + +import { useMandatoryProperties } from "./useMandatoryProperties"; + +export const useTrackTx = () => { + const mandatoryProperties = useMandatoryProperties(); + + const trackTxSucceed = useCallback( + () => track(AmpEvent.TX_SUCCEED, { ...mandatoryProperties }), + [mandatoryProperties] + ); + + const trackTxFailed = useCallback( + () => track(AmpEvent.TX_FAILED, { ...mandatoryProperties }), + [mandatoryProperties] + ); + + const trackTxRejected = useCallback( + () => track(AmpEvent.TX_REJECTED, { ...mandatoryProperties }), + [mandatoryProperties] + ); + + return { trackTxSucceed, trackTxFailed, trackTxRejected }; +}; diff --git a/src/lib/amplitude/types.ts b/src/lib/amplitude/types.ts new file mode 100644 index 000000000..a39054929 --- /dev/null +++ b/src/lib/amplitude/types.ts @@ -0,0 +1,175 @@ +export enum AmpEvent { + INVALID_STATE = "To Invalid State", + // CODE + CODE_SAVE = "Code Save", + CODE_EDIT = "Code Edit", + CODE_REMOVE = "Code Remove", + // CONTRACT + CONTRACT_SAVE_AFTER_INIT = "Contract Save After Init", + CONTRACT_SAVE = "Contract Save", + CONTRACT_EDIT = "Contract Edit", + CONTRACT_EDIT_TAGS = "Contract Edit Tags", + CONTRACT_EDIT_LISTS = "Contract Edit Lists", + CONTRACT_REMOVE = "Contract Remove", + // TAG + TAG_CREATE = "Tag Create", + // LIST + LIST_CREATE = "List Create", + LIST_EDIT = "List Edit", + LIST_REMOVE = "List Remove", + // PUBLIC PROJECT + PUBLIC_SAVE = "Public Project Save", + PUBLIC_REMOVE = "Public Project Remove", + // NAVIGATE + TO_OVERVIEW = "To Overview", + TO_BLOCKS = "To Blocks", + TO_BLOCK_DETAIL = "To Block Detail", + TO_TXS = "To Txs", + TO_PAST_TXS = "To Past Txs", + TO_DEPLOY = "To Deploy", + TO_UPLOAD = "To Upload", + TO_INSTANTIATE = "To Instantiate", + TO_PROPOSAL_LIST = "To Proposal List", + TO_QUERY = "To Query", + TO_EXECUTE = "To Execute", + TO_MIGRATE = "To Migrate", + TO_ADMIN_UPDATE = "To Admin Update", + TO_MY_SAVED_CODES = "To My Saved Codes", + TO_MY_STORED_CODES = "To My Stored Codes", + TO_RECENT_CODES = "To Recent Codes", + TO_RECENT_CONTRACT = "To Recent Contract", + TO_INSTANTIATED_BY_ME = "To Instantiated By Me", + TO_SAVED_CONTRACT = "To Saved Contract", + TO_LIST_OTHERS = "To List Others", + TO_ALL_LISTS = "To All Lists", + TO_ALL_PROJECTS = "To All Public Projects", + TO_ACCOUNT_DETAIL = "To Account Detail", + TO_YOUR_ACCOUNT = "To Your Account", + TO_CONTRACT_DETAIL = "To Contract Detail", + TO_CODE_DETAIL = "To Code Detail", + TO_PROJECT_DETAIL = "To Public Project Detail", + TO_TRANSACTION_DETAIL = "To Transaction Detail", + TO_NOT_FOUND = "To 404 Not Found", + TO_FAUCET = "To Faucet", + TO_POOL_LIST = "To Pool List", + TO_POOL_DETAIL = "To Pool Detail", + TO_PROPOSAL_TO_STORE_CODE = "To Proposal To Store Code", + TO_PROPOSAL_TO_WHITELIST = "To Proposal To Whitelist", + // ACTIONS + ACTION_UPLOAD = "Act Upload", + ACTION_INSTANTIATE = "Action Instantiate", + ACTION_EXECUTE = "Action Execute", + ACTION_QUERY = "Action Query", + ACTION_MIGRATE = "Action Migrate", + ACTION_ADMIN_UPDATE = "Action Admin Update", + ACTION_ADMIN_CLEAR = "Action Admin Clear", + ACTION_RESEND = "Action Resend", + ACTION_FAUCET = "Action Faucet", + ACTION_ATTACH_JSON = "Action Attach Json", + // INTERACTS + USE_SELECT_NETWORK = "Use Select Network", + USE_CLICK_WALLET = "Use Click Wallet", + USE_MAIN_SEARCH = "Use Main Search", + USE_SIDEBAR = "Use Sidebar", + USE_TOPBAR = "Use Topbar", + USE_TAB = "Use Tab", + USE_RADIO = "Use Radio", + USE_VIEW_MORE = "Use View More", + USE_CODE_SELECT = "Use Code Select", + USE_CODE_MODAL = "Use Code Modal", + USE_CODE_FILL = "Use Code Fill", + USE_ASSIGN_ME = "Use Assign Me", + USE_ATTACHED_JSON_DRAWER = "Use Attached Json Drawer", // change to modal later + USE_VIEW_ATTACHED_JSON = "Use View Attached Json", + USE_EDIT_ATTACHED_JSON = "Use Edit Attached Json", + USE_REMOVE_ATTACHED_JSON = "Use Remove Attached Json", + USE_SWITCH_JSON_INPUT = "Use Switch Json Input", + USE_CONTRACT_FORM = "Use Contract Form", + USE_CONTRACT_MODAL = "Use Contract Modal", + USE_CONTRACT_MODAL_SEARCH = "Use Contract Modal Search", + USE_CONTRACT_MODAL_LISTS = "Use Contract Modal Lists", + USE_CONTRACT_SNIPPET = "Use Contract Snippet", + USE_CMD_QUERY = "Use Command Query", + USE_CMD_EXECUTE = "Use Command Execute", + USE_SEE_REDELEGATIONS = "Use See Redelegations", + USE_BACK_BUTTON = "Use Back Button", + USE_COPY_BUTTON = "Use Copy Button", + USE_COPIER = "Use Copier", + USE_QUICK_EDIT_CONTRACT = "Use Quick Edit Contract", + USE_QUICK_EDIT_CODE = "Use Quick Edit Code", + USE_UNSUPPORTED_ASSETS_MODAL = "Use Unsupported Assets Modal", + USE_OTHER_MODAL = "Use Other Modal", + USE_SUBMIT_PROJECT = "Use Submit Project", + USE_VIEW_JSON = "Use View Json", + USE_UNSUPPORTED_ASSETS = "Use Unsupported Assets", + USE_TX_MSG_EXPAND = "Use Transaction Message Expand", + USE_EXPAND = "Use General Expand", + USE_EXPAND_ALL = "Use Expand All", + USE_RIGHT_HELPER_PANEL = "Use Right Helper Panel", // Sticky panel + USE_UNPIN = "Use Unpin", + USE_INSTANTIATE_PERMISSION = "Use Instantiate Permission", + USE_WHITELISTED_ADDRESSES = "Use Whitelisted Addresses", + USE_DEPOSIT_FILL = "Use Deposit Fill", + USE_SUBMIT_PROPOSAL = "Use Submit Proposal", + USE_SEARCH_INPUT = "Use Search Input", + USE_FILTER_MY_PROPOSALS = "Use Filter My Proposals", + USE_FILTER_PROPOSALS_STATUS = "Use Filter Proposals Status", + USE_FILTER_PROPOSALS_TYPE = "Use Filter Proposals Types", + USE_FILTER_POOL_TYPE = "Use Filter Pool Types", + USE_PAGINATION_PAGE_SIZE = "Use Pagination Page Size", + USE_PAGINATION_NAVIGATION = "Use Pagination Navigation", + USE_CREATE_NEW_PROPOSAL = "Use Create New Proposal", + USE_SORT = "Use Sort", + USE_VIEW = "Use View", + USE_TOGGLE = "Use Toggle", + USE_ALERT_CTA = "Use Alert CTA", + // TX + TX_SUCCEED = "Tx Succeed", + TX_FAILED = "Tx Failed", + TX_REJECTED = "Tx Rejected", + // EXTERNAL + MINTSCAN = "Mintscan", + WEBSITE = "Website", + SOCIAL = "Social", + CELATONE = "Celatone", + FEEDBACK = "Feedback", + ALLESLABS = "AllesLabs", +} + +export type ActionAmpEvent = + | AmpEvent.ACTION_INSTANTIATE + | AmpEvent.ACTION_EXECUTE; + +export type SpecialAmpEvent = + | AmpEvent.INVALID_STATE + | AmpEvent.TO_QUERY + | AmpEvent.TO_EXECUTE + | AmpEvent.TO_INSTANTIATE + | AmpEvent.TO_MIGRATE + | AmpEvent.TO_ADMIN_UPDATE + | AmpEvent.USE_MAIN_SEARCH + | AmpEvent.USE_TAB + | AmpEvent.USE_RADIO + | AmpEvent.USE_COPIER + | AmpEvent.USE_OTHER_MODAL + | AmpEvent.MINTSCAN + | AmpEvent.WEBSITE + | AmpEvent.SOCIAL + | AmpEvent.CELATONE + | AmpEvent.USE_VIEW_JSON + | AmpEvent.USE_UNSUPPORTED_ASSETS + | AmpEvent.USE_COPIER + | AmpEvent.USE_EXPAND + | AmpEvent.USE_RIGHT_HELPER_PANEL + | AmpEvent.USE_UNPIN + | AmpEvent.USE_INSTANTIATE_PERMISSION + | AmpEvent.USE_DEPOSIT_FILL + | AmpEvent.USE_EXPAND_ALL + | AmpEvent.USE_PAGINATION_NAVIGATION + | AmpEvent.USE_FILTER_PROPOSALS_STATUS + | AmpEvent.USE_FILTER_PROPOSALS_TYPE + | AmpEvent.USE_FILTER_POOL_TYPE + | AmpEvent.USE_ALERT_CTA + | AmpEvent.USE_SORT + | AmpEvent.USE_VIEW + | AmpEvent.USE_TOGGLE; diff --git a/src/lib/amplitude/useAmplitudeInit.ts b/src/lib/amplitude/useAmplitudeInit.ts new file mode 100644 index 000000000..8f4449631 --- /dev/null +++ b/src/lib/amplitude/useAmplitudeInit.ts @@ -0,0 +1,47 @@ +import { Identify, identify, init } from "@amplitude/analytics-browser"; +import { createHash } from "crypto"; + +import { useCelatoneApp, useNavContext } from "lib/app-provider/contexts"; +import { useCurrentChain } from "lib/app-provider/hooks"; +import { useLocalStorage } from "lib/hooks/useLocalStorage"; + +export const useAmplitudeInit = () => { + const { currentChainId } = useCelatoneApp(); + const { address } = useCurrentChain(); + const { isDevMode, isExpand } = useNavContext(); + const [wallets, setWallets] = useLocalStorage("wallets", []); + const [networks, setNetworks] = useLocalStorage("networks", []); + + if (typeof window !== "undefined") { + init(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY ?? "", undefined, { + trackingOptions: { + attribution: true, + region: false, + ipAddress: false, + }, + // serverUrl: "/amplitude", + }); + + if (address) { + const addressHash = createHash("sha256").update(address).digest("hex"); + + if (!wallets.includes(addressHash)) { + setWallets([...wallets, addressHash]); + } + } + + if (!networks.includes(currentChainId)) { + setNetworks([...networks, currentChainId]); + } + + // Custom user properties + const identifyEvent = new Identify(); + identifyEvent.set("Wallets", wallets); + identifyEvent.set("Wallets Count", wallets.length); + identifyEvent.set("Nav Open", isExpand); + identifyEvent.set("Dev Mode", isDevMode ?? ""); + identifyEvent.set("Networks", networks); + identifyEvent.set("Networks Count", networks.length); + identify(identifyEvent); + } +}; diff --git a/src/lib/app-fns/tx/clearAdmin.tsx b/src/lib/app-fns/tx/clearAdmin.tsx index c8892ac36..4d24fea05 100644 --- a/src/lib/app-fns/tx/clearAdmin.tsx +++ b/src/lib/app-fns/tx/clearAdmin.tsx @@ -6,14 +6,13 @@ import type { StdFee } from "@cosmjs/stargate"; import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { TxStreamPhase } from "lib/types"; import type { TxResultRendering, ContractAddr, HumanAddr } from "lib/types"; import { formatUFee } from "lib/utils"; -import { catchTxError } from "./common/catchTxError"; import { postTx } from "./common/post"; import { sendingTx } from "./common/sending"; @@ -24,6 +23,7 @@ interface ClearAdminTxParams { memo?: string; client: SigningCosmWasmClient; onTxSucceed?: () => void; + catchTxError: CatchTxError; } export const clearAdminTx = ({ @@ -33,6 +33,7 @@ export const clearAdminTx = ({ memo, client, onTxSucceed, + catchTxError, }: ClearAdminTxParams): Observable => { return pipe( sendingTx(fee), @@ -40,7 +41,6 @@ export const clearAdminTx = ({ postFn: () => client.clearAdmin(address, contractAddress, fee, memo), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(); const txFee = txInfo.events.find((e) => e.type === "tx")?.attributes[0] .value; diff --git a/src/lib/app-fns/tx/common/catchTxError.tsx b/src/lib/app-fns/tx/common/catchTxError.tsx index b59d5e76c..fc1dd2684 100644 --- a/src/lib/app-fns/tx/common/catchTxError.tsx +++ b/src/lib/app-fns/tx/common/catchTxError.tsx @@ -3,7 +3,6 @@ import { catchError } from "rxjs"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ActionVariant, ReceiptInfo, @@ -39,15 +38,17 @@ const getActionVariant = (isRejected: boolean): ActionVariant => isRejected ? "rejected" : "failed"; export const catchTxError = ( + trackTxFailed: () => void, + trackTxRejected: () => void, onTxFailed?: () => void ): OperatorFunction => { return catchError((error: Error) => { const txHash = error.message.match("(?:tx )(.*?)(?= at)")?.at(1); - AmpTrack( - error.message === "Request rejected" - ? AmpEvent.TX_REJECTED - : AmpEvent.TX_FAILED - ); + if (error.message === "Request rejected") { + trackTxRejected(); + } else { + trackTxFailed(); + } onTxFailed?.(); return Promise.resolve({ value: null, diff --git a/src/lib/app-fns/tx/execute.tsx b/src/lib/app-fns/tx/execute.tsx index 5d645881b..dd6c1f0f1 100644 --- a/src/lib/app-fns/tx/execute.tsx +++ b/src/lib/app-fns/tx/execute.tsx @@ -6,15 +6,15 @@ import type { Coin, StdFee } from "@cosmjs/stargate"; import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { Activity } from "lib/stores/contract"; import type { ContractAddr, HumanAddr, TxResultRendering } from "lib/types"; import { TxStreamPhase } from "lib/types"; import { encode, formatUFee, getCurrentDate } from "lib/utils"; -import { catchTxError, postTx, sendingTx } from "./common"; +import { postTx, sendingTx } from "./common"; interface ExecuteTxParams { address: HumanAddr; @@ -23,6 +23,7 @@ interface ExecuteTxParams { msg: object; funds: Coin[]; client: SigningCosmWasmClient; + catchTxError: CatchTxError; onTxSucceed?: (activity: Activity) => void; onTxFailed?: () => void; } @@ -34,6 +35,7 @@ export const executeContractTx = ({ msg, funds, client, + catchTxError, onTxSucceed, onTxFailed, }: ExecuteTxParams): Observable => { @@ -44,7 +46,6 @@ export const executeContractTx = ({ client.execute(address, contractAddress, msg, fee, undefined, funds), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.({ type: "execute", action: Object.keys(msg)[0], diff --git a/src/lib/app-fns/tx/instantiate.tsx b/src/lib/app-fns/tx/instantiate.tsx index 83dda08a4..21af59ad9 100644 --- a/src/lib/app-fns/tx/instantiate.tsx +++ b/src/lib/app-fns/tx/instantiate.tsx @@ -6,10 +6,9 @@ import type { Coin, StdFee } from "@cosmjs/stargate"; import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import type { TxResultRendering } from "lib/types"; -import { catchTxError } from "./common/catchTxError"; import { postTx } from "./common/post"; import { sendingTx } from "./common/sending"; @@ -22,6 +21,7 @@ interface InstantiateTxParams { admin: string; funds: Coin[]; client: SigningCosmWasmClient; + catchTxError: CatchTxError; onTxSucceed?: (txInfo: InstantiateResult, contractLabel: string) => void; onTxFailed?: () => void; } @@ -35,6 +35,7 @@ export const instantiateContractTx = ({ admin, funds, client, + catchTxError, onTxSucceed, onTxFailed, }: InstantiateTxParams): Observable => { @@ -48,7 +49,6 @@ export const instantiateContractTx = ({ }), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(txInfo, label); // TODO: this is type hack return null as unknown as TxResultRendering; diff --git a/src/lib/app-fns/tx/migrate.tsx b/src/lib/app-fns/tx/migrate.tsx index 773363a9e..0b7080a64 100644 --- a/src/lib/app-fns/tx/migrate.tsx +++ b/src/lib/app-fns/tx/migrate.tsx @@ -6,14 +6,13 @@ import type { StdFee } from "@cosmjs/stargate"; import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractAddr, HumanAddr, TxResultRendering } from "lib/types"; import { TxStreamPhase } from "lib/types"; import { formatUFee } from "lib/utils"; -import { catchTxError } from "./common/catchTxError"; import { postTx } from "./common/post"; import { sendingTx } from "./common/sending"; @@ -24,6 +23,7 @@ interface MigrateTxParams { migrateMsg: object; fee: StdFee; client: SigningCosmWasmClient; + catchTxError: CatchTxError; onTxSucceed?: (txHash: string) => void; onTxFailed?: () => void; } @@ -35,6 +35,7 @@ export const migrateContractTx = ({ migrateMsg, fee, client, + catchTxError, onTxSucceed, onTxFailed, }: MigrateTxParams): Observable => { @@ -52,7 +53,6 @@ export const migrateContractTx = ({ ), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(txInfo.transactionHash); const txFee = txInfo.events.find((e) => e.type === "tx")?.attributes[0] .value; diff --git a/src/lib/app-fns/tx/resend.tsx b/src/lib/app-fns/tx/resend.tsx index aba1356f0..9463ecad2 100644 --- a/src/lib/app-fns/tx/resend.tsx +++ b/src/lib/app-fns/tx/resend.tsx @@ -5,31 +5,33 @@ import type { StdFee } from "@cosmjs/stargate"; import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { HumanAddr, TxResultRendering } from "lib/types"; import { TxStreamPhase } from "lib/types"; import { formatUFee } from "lib/utils"; -import { catchTxError, postTx, sendingTx } from "./common"; +import { postTx, sendingTx } from "./common"; interface ResendTxParams { address: HumanAddr; client: SigningCosmWasmClient; - onTxSucceed?: (txHash: string) => void; - onTxFailed?: () => void; fee: StdFee; messages: EncodeObject[]; + catchTxError: CatchTxError; + onTxSucceed?: (txHash: string) => void; + onTxFailed?: () => void; } export const resendTx = ({ address, client, - onTxSucceed, - onTxFailed, fee, messages, + catchTxError, + onTxSucceed, + onTxFailed, }: ResendTxParams): Observable => { return pipe( sendingTx(fee), @@ -37,7 +39,6 @@ export const resendTx = ({ postFn: () => client.signAndBroadcast(address, messages, fee), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(txInfo.transactionHash); const txFee = txInfo.events.find((e) => e.type === "tx")?.attributes[0] .value; diff --git a/src/lib/app-fns/tx/submitProposal.tsx b/src/lib/app-fns/tx/submitProposal.tsx index d1b5d3699..1b134e4d9 100644 --- a/src/lib/app-fns/tx/submitProposal.tsx +++ b/src/lib/app-fns/tx/submitProposal.tsx @@ -5,35 +5,37 @@ import { pipe } from "@rx-stream/pipe"; import { capitalize } from "lodash"; import type { Observable } from "rxjs"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { HumanAddr, TxResultRendering } from "lib/types"; import { TxStreamPhase } from "lib/types"; import { findAttr, formatUFee } from "lib/utils"; -import { catchTxError, postTx, sendingTx } from "./common"; +import { postTx, sendingTx } from "./common"; interface SubmitWhitelistProposalTxParams { address: HumanAddr; client: SigningCosmWasmClient; - onTxSucceed?: () => void; - onTxFailed?: () => void; fee: StdFee; messages: EncodeObject[]; whitelistNumber: number; amountToVote: string | null; + catchTxError: CatchTxError; + onTxSucceed?: () => void; + onTxFailed?: () => void; } export const submitWhitelistProposalTx = ({ address, client, - onTxSucceed, - onTxFailed, fee, messages, whitelistNumber, amountToVote, + catchTxError, + onTxSucceed, + onTxFailed, }: SubmitWhitelistProposalTxParams): Observable => { return pipe( sendingTx(fee), @@ -41,7 +43,6 @@ export const submitWhitelistProposalTx = ({ postFn: () => client.signAndBroadcast(address, messages, fee), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(); const mimicLog: logs.Log = { msg_index: 0, @@ -109,6 +110,7 @@ interface SubmitStoreCodeProposalTxParams { wasmFileName: string; messages: EncodeObject[]; amountToVote: string | null; + catchTxError: CatchTxError; onTxSucceed?: () => void; onTxFailed?: () => void; } @@ -121,6 +123,7 @@ export const submitStoreCodeProposalTx = ({ wasmFileName, messages, amountToVote, + catchTxError, onTxSucceed, onTxFailed, }: SubmitStoreCodeProposalTxParams): Observable => { @@ -130,7 +133,6 @@ export const submitStoreCodeProposalTx = ({ postFn: () => client.signAndBroadcast(address, messages, fee), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(); const mimicLog: logs.Log = { msg_index: 0, diff --git a/src/lib/app-fns/tx/updateAdmin.tsx b/src/lib/app-fns/tx/updateAdmin.tsx index c17d9b23e..13cc11326 100644 --- a/src/lib/app-fns/tx/updateAdmin.tsx +++ b/src/lib/app-fns/tx/updateAdmin.tsx @@ -6,9 +6,9 @@ import type { StdFee } from "@cosmjs/stargate"; import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { Addr, ContractAddr, @@ -18,7 +18,7 @@ import type { import { TxStreamPhase } from "lib/types"; import { formatUFee } from "lib/utils"; -import { catchTxError, postTx, sendingTx } from "./common"; +import { postTx, sendingTx } from "./common"; interface UpdateAdminTxParams { address: HumanAddr; @@ -26,6 +26,7 @@ interface UpdateAdminTxParams { newAdmin: Addr; fee: StdFee; client: SigningCosmWasmClient; + catchTxError: CatchTxError; onTxSucceed?: () => void; onTxFailed?: () => void; } @@ -36,6 +37,7 @@ export const updateAdminTx = ({ newAdmin, fee, client, + catchTxError, onTxSucceed, onTxFailed, }: UpdateAdminTxParams): Observable => { @@ -46,7 +48,6 @@ export const updateAdminTx = ({ client.updateAdmin(address, contractAddress, newAdmin, fee, undefined), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); onTxSucceed?.(); const txFee = txInfo.events.find((e) => e.type === "tx")?.attributes[0] .value; diff --git a/src/lib/app-fns/tx/upload.tsx b/src/lib/app-fns/tx/upload.tsx index 35a7f7c68..31ba50340 100644 --- a/src/lib/app-fns/tx/upload.tsx +++ b/src/lib/app-fns/tx/upload.tsx @@ -4,15 +4,14 @@ import { pipe } from "@rx-stream/pipe"; import type { Observable } from "rxjs"; import type { UploadSucceedCallback } from "lib/app-provider"; +import type { CatchTxError } from "lib/app-provider/tx/catchTxError"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { TxStreamPhase } from "lib/types"; import type { HumanAddr, TxResultRendering, ComposedMsg } from "lib/types"; import { findAttr } from "lib/utils"; import { formatUFee } from "lib/utils/formatter/denom"; -import { catchTxError } from "./common/catchTxError"; import { postTx } from "./common/post"; import { sendingTx } from "./common/sending"; @@ -24,8 +23,9 @@ interface UploadTxParams { fee: StdFee; memo?: string; client: SigningCosmWasmClient; - onTxSucceed: UploadSucceedCallback; isMigrate: boolean; + catchTxError: CatchTxError; + onTxSucceed: UploadSucceedCallback; } export const uploadContractTx = ({ @@ -36,8 +36,9 @@ export const uploadContractTx = ({ fee, memo, client, - onTxSucceed, isMigrate, + catchTxError, + onTxSucceed, }: UploadTxParams): Observable => { return pipe( sendingTx(fee), @@ -45,7 +46,6 @@ export const uploadContractTx = ({ postFn: () => client.signAndBroadcast(address, messages, fee, memo), }), ({ value: txInfo }) => { - AmpTrack(AmpEvent.TX_SUCCEED); const mimicLog: logs.Log = { msg_index: 0, log: "", diff --git a/src/lib/app-provider/contexts/nav.tsx b/src/lib/app-provider/contexts/nav.tsx index 8eea19795..47d0974f2 100644 --- a/src/lib/app-provider/contexts/nav.tsx +++ b/src/lib/app-provider/contexts/nav.tsx @@ -1,18 +1,21 @@ import type { Dispatch, ReactNode, SetStateAction } from "react"; import { useContext, createContext, useMemo } from "react"; +import { usePreviousPathname } from "../hooks/usePreviousPathname"; import { useLocalStorage } from "lib/hooks/useLocalStorage"; import type { Option } from "lib/types"; interface NavContextInterface { isExpand: boolean; isDevMode: Option; + prevPathname: string | null; setIsExpand: Dispatch>; setIsDevMode: Dispatch>>; } const NavContext = createContext({ isExpand: false, isDevMode: undefined, + prevPathname: null, setIsExpand: () => {}, setIsDevMode: () => {}, }); @@ -23,15 +26,17 @@ export const NavProvider = ({ children }: { children: ReactNode }) => { undefined ); const [isExpand, setIsExpand] = useLocalStorage("navbar", false); + const prevPathname = usePreviousPathname(); const states = useMemo( () => ({ isDevMode, isExpand, + prevPathname, setIsDevMode, setIsExpand, }), - [isDevMode, isExpand, setIsDevMode, setIsExpand] + [isDevMode, isExpand, prevPathname, setIsDevMode, setIsExpand] ); return {children}; diff --git a/src/lib/app-provider/hooks/index.ts b/src/lib/app-provider/hooks/index.ts index 76a5b2de0..2058471c1 100644 --- a/src/lib/app-provider/hooks/index.ts +++ b/src/lib/app-provider/hooks/index.ts @@ -1,5 +1,4 @@ export * from "./useAddress"; -export * from "./useAmplitude"; export * from "./useDummyWallet"; export * from "./useExampleAddresses"; export * from "./useFabricateFee"; @@ -13,3 +12,4 @@ export * from "./useBaseApiRoute"; export * from "./useRPCEndpoint"; export * from "./useConfig"; export * from "./useCurrentChain"; +export * from "./usePreviousPathname"; diff --git a/src/lib/app-provider/hooks/useAmplitude.ts b/src/lib/app-provider/hooks/useAmplitude.ts deleted file mode 100644 index 95cc5d226..000000000 --- a/src/lib/app-provider/hooks/useAmplitude.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { init, setDeviceId, setUserId } from "@amplitude/analytics-browser"; -import { useChain } from "@cosmos-kit/react"; -import { createHash } from "crypto"; -import { useEffect } from "react"; -import * as uuid from "uuid"; - -import type { Option } from "lib/types"; - -export const useAmplitude = (chainName: Option) => { - /** - * @remarks Revisit default chain name - */ - const { address } = useChain(chainName ?? "osmosis"); - if (typeof window !== "undefined") { - init(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY ?? "", undefined, { - trackingOptions: { - deviceManufacturer: false, - deviceModel: false, - ipAddress: false, - language: false, - osName: false, - osVersion: false, - platform: false, - }, - serverUrl: "/amplitude", - }); - - let deviceId = localStorage.getItem("deviceId"); - if (!deviceId) { - deviceId = uuid.v4(); - localStorage.setItem("deviceId", deviceId); - } - setDeviceId(deviceId); - } - - useEffect(() => { - const timeoutId = setTimeout(() => { - if (chainName) { - const userId = address - ? createHash("sha256").update(address).digest("hex") - : undefined; - setUserId(`${chainName}/${userId}`); - } - }, 300); - return () => clearTimeout(timeoutId); - }, [address, chainName]); -}; diff --git a/src/lib/app-provider/hooks/usePreviousPathname.ts b/src/lib/app-provider/hooks/usePreviousPathname.ts new file mode 100644 index 000000000..ae623af42 --- /dev/null +++ b/src/lib/app-provider/hooks/usePreviousPathname.ts @@ -0,0 +1,14 @@ +import { useRouter } from "next/router"; +import { useEffect, useRef } from "react"; + +export const usePreviousPathname = () => { + const { asPath } = useRouter(); + + const ref = useRef(null); + + useEffect(() => { + ref.current = asPath; + }, [asPath]); + + return ref.current; +}; diff --git a/src/lib/app-provider/tx/catchTxError.ts b/src/lib/app-provider/tx/catchTxError.ts new file mode 100644 index 000000000..fe0409ea3 --- /dev/null +++ b/src/lib/app-provider/tx/catchTxError.ts @@ -0,0 +1,17 @@ +import { useCallback } from "react"; + +import { useTrack } from "lib/amplitude"; +import { catchTxError } from "lib/app-fns/tx/common"; + +// HACK: This shouldn't be here. It should be in src/lib/app-fns/tx/common/catchTxError.ts +export type CatchTxError = ReturnType; + +export const useCatchTxError = () => { + const { trackTxFailed, trackTxRejected } = useTrack(); + + return useCallback( + (onTxFailed?: () => void) => + catchTxError(trackTxFailed, trackTxRejected, onTxFailed), + [trackTxFailed, trackTxRejected] + ); +}; diff --git a/src/lib/app-provider/tx/clearAdmin.ts b/src/lib/app-provider/tx/clearAdmin.ts index 13c360257..1f9cbbcf2 100644 --- a/src/lib/app-provider/tx/clearAdmin.ts +++ b/src/lib/app-provider/tx/clearAdmin.ts @@ -1,10 +1,14 @@ import { useQueryClient } from "@tanstack/react-query"; import { useCallback } from "react"; +import { CELATONE_QUERY_KEYS } from "../env"; import { useCurrentChain, useFabricateFee, useWasmConfig } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { clearAdminTx } from "lib/app-fns/tx/clearAdmin"; import type { ContractAddr, HumanAddr } from "lib/types"; +import { useCatchTxError } from "./catchTxError"; + export interface ClearAdminStreamParams { onTxSucceed?: () => void; } @@ -14,6 +18,8 @@ export const useClearAdminTx = (contractAddress: ContractAddr) => { const queryClient = useQueryClient(); const fabricateFee = useFabricateFee(); const wasm = useWasmConfig({ shouldRedirect: false }); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); return useCallback( async ({ onTxSucceed }: ClearAdminStreamParams) => { @@ -34,20 +40,24 @@ export const useClearAdminTx = (contractAddress: ContractAddr) => { fee: clearAdminFee, client, onTxSucceed: () => { + trackTxSucceed(); onTxSucceed?.(); Promise.all([ queryClient.invalidateQueries({ - queryKey: ["admin_by_contracts"], + queryKey: [CELATONE_QUERY_KEYS.ADMINS_BY_CONTRACTS], }), queryClient.invalidateQueries({ - queryKey: ["query", "instantiate_info"], + queryKey: [CELATONE_QUERY_KEYS.CONTRACT_INSTANTIATE_DETAIL], }), ]); }, + catchTxError, }); }, [ getSigningCosmWasmClient, + trackTxSucceed, + catchTxError, address, wasm, fabricateFee, diff --git a/src/lib/app-provider/tx/execute.ts b/src/lib/app-provider/tx/execute.ts index 257b0b94b..c39c4df81 100644 --- a/src/lib/app-provider/tx/execute.ts +++ b/src/lib/app-provider/tx/execute.ts @@ -2,10 +2,13 @@ import type { Coin, StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { executeContractTx } from "lib/app-fns/tx/execute"; import type { Activity } from "lib/stores/contract"; import type { ContractAddr, HumanAddr } from "lib/types"; +import { useCatchTxError } from "./catchTxError"; + export interface ExecuteStreamParams { onTxSucceed?: (activity: Activity) => void; onTxFailed?: () => void; @@ -17,6 +20,8 @@ export interface ExecuteStreamParams { export const useExecuteContractTx = () => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); return useCallback( async ({ @@ -39,10 +44,14 @@ export const useExecuteContractTx = () => { msg, funds, client, - onTxSucceed, + catchTxError, + onTxSucceed: (activity) => { + trackTxSucceed(); + onTxSucceed?.(activity); + }, onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningCosmWasmClient, trackTxSucceed, catchTxError] ); }; diff --git a/src/lib/app-provider/tx/instantiate.ts b/src/lib/app-provider/tx/instantiate.ts index ef3935eef..34370e0b1 100644 --- a/src/lib/app-provider/tx/instantiate.ts +++ b/src/lib/app-provider/tx/instantiate.ts @@ -3,8 +3,11 @@ import type { Coin, StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { instantiateContractTx } from "lib/app-fns/tx/instantiate"; +import { useCatchTxError } from "./catchTxError"; + export interface InstantiateStreamParams { estimatedFee: StdFee | undefined; codeId: number; @@ -18,6 +21,8 @@ export interface InstantiateStreamParams { export const useInstantiateTx = () => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); return useCallback( async ({ @@ -44,10 +49,14 @@ export const useInstantiateTx = () => { admin, funds, client, - onTxSucceed, + catchTxError, + onTxSucceed: (txResult, contractLabel) => { + trackTxSucceed(); + onTxSucceed?.(txResult, contractLabel); + }, onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningCosmWasmClient, trackTxSucceed, catchTxError] ); }; diff --git a/src/lib/app-provider/tx/migrate.ts b/src/lib/app-provider/tx/migrate.ts index 804f83f74..ba65ebb90 100644 --- a/src/lib/app-provider/tx/migrate.ts +++ b/src/lib/app-provider/tx/migrate.ts @@ -2,9 +2,12 @@ import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { migrateContractTx } from "lib/app-fns/tx/migrate"; import type { ContractAddr, HumanAddr, Option } from "lib/types"; +import { useCatchTxError } from "./catchTxError"; + export interface MigrateStreamParams { contractAddress: ContractAddr; codeId: number; @@ -16,6 +19,9 @@ export interface MigrateStreamParams { export const useMigrateTx = () => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); + return useCallback( async ({ contractAddress, @@ -37,10 +43,14 @@ export const useMigrateTx = () => { migrateMsg, fee: estimatedFee, client, - onTxSucceed, + catchTxError, + onTxSucceed: (txHash) => { + trackTxSucceed(); + onTxSucceed?.(txHash); + }, onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningCosmWasmClient, trackTxSucceed, catchTxError] ); }; diff --git a/src/lib/app-provider/tx/resend.ts b/src/lib/app-provider/tx/resend.ts index faa19911d..a2f623484 100644 --- a/src/lib/app-provider/tx/resend.ts +++ b/src/lib/app-provider/tx/resend.ts @@ -3,9 +3,12 @@ import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { resendTx } from "lib/app-fns/tx/resend"; import type { HumanAddr } from "lib/types"; +import { useCatchTxError } from "./catchTxError"; + export interface ResendStreamParams { onTxSucceed?: (txHash: string) => void; onTxFailed?: () => void; @@ -15,6 +18,9 @@ export interface ResendStreamParams { export const useResendTx = () => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); + return useCallback( async ({ onTxSucceed, @@ -29,12 +35,16 @@ export const useResendTx = () => { return resendTx({ address: address as HumanAddr, client, - onTxSucceed, - onTxFailed, fee: estimatedFee, messages, + catchTxError, + onTxSucceed: (txHash) => { + trackTxSucceed(); + onTxSucceed?.(txHash); + }, + onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningCosmWasmClient, trackTxSucceed, catchTxError] ); }; diff --git a/src/lib/app-provider/tx/submitProposal.ts b/src/lib/app-provider/tx/submitProposal.ts index 7083b0083..7c771b6c2 100644 --- a/src/lib/app-provider/tx/submitProposal.ts +++ b/src/lib/app-provider/tx/submitProposal.ts @@ -3,23 +3,28 @@ import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { submitStoreCodeProposalTx, submitWhitelistProposalTx, } from "lib/app-fns/tx/submitProposal"; import type { HumanAddr } from "lib/types"; +import { useCatchTxError } from "./catchTxError"; + export interface SubmitWhitelistProposalStreamParams { - onTxSucceed?: () => void; - onTxFailed?: () => void; estimatedFee?: StdFee; messages: EncodeObject[]; whitelistNumber: number; amountToVote: string | null; + onTxSucceed?: () => void; + onTxFailed?: () => void; } export const useSubmitWhitelistProposalTx = () => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); return useCallback( async ({ @@ -37,15 +42,19 @@ export const useSubmitWhitelistProposalTx = () => { return submitWhitelistProposalTx({ address: address as HumanAddr, client, - onTxSucceed, - onTxFailed, fee: estimatedFee, messages, whitelistNumber, amountToVote, + catchTxError, + onTxSucceed: () => { + trackTxSucceed(); + onTxSucceed?.(); + }, + onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningCosmWasmClient, trackTxSucceed, catchTxError] ); }; @@ -60,6 +69,9 @@ interface SubmitStoreCodeProposalStreamParams { export const useSubmitStoreCodeProposalTx = () => { const { address, getSigningCosmWasmClient, chain } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); + return useCallback( async ({ estimatedFee, @@ -77,14 +89,24 @@ export const useSubmitStoreCodeProposalTx = () => { address: address as HumanAddr, chainName: chain.chain_name, client, - onTxSucceed, - onTxFailed, fee: estimatedFee, messages, wasmFileName, amountToVote, + catchTxError, + onTxSucceed: () => { + trackTxSucceed(); + onTxSucceed?.(); + }, + onTxFailed, }); }, - [address, chain.chain_name, getSigningCosmWasmClient] + [ + address, + chain.chain_name, + getSigningCosmWasmClient, + trackTxSucceed, + catchTxError, + ] ); }; diff --git a/src/lib/app-provider/tx/updateAdmin.ts b/src/lib/app-provider/tx/updateAdmin.ts index 861e8aaad..567534040 100644 --- a/src/lib/app-provider/tx/updateAdmin.ts +++ b/src/lib/app-provider/tx/updateAdmin.ts @@ -2,9 +2,12 @@ import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { updateAdminTx } from "lib/app-fns/tx/updateAdmin"; import type { Addr, ContractAddr, HumanAddr, Option } from "lib/types"; +import { useCatchTxError } from "./catchTxError"; + export interface UpdateAdminStreamParams { contractAddress: ContractAddr; newAdmin: Addr; @@ -15,6 +18,8 @@ export interface UpdateAdminStreamParams { export const useUpdateAdminTx = () => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); return useCallback( async ({ @@ -35,10 +40,14 @@ export const useUpdateAdminTx = () => { newAdmin, fee: estimatedFee, client, - onTxSucceed, + catchTxError, + onTxSucceed: () => { + trackTxSucceed(); + onTxSucceed?.(); + }, onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningCosmWasmClient, trackTxSucceed, catchTxError] ); }; diff --git a/src/lib/app-provider/tx/upload.ts b/src/lib/app-provider/tx/upload.ts index 9718212f0..4f11e8a8b 100644 --- a/src/lib/app-provider/tx/upload.ts +++ b/src/lib/app-provider/tx/upload.ts @@ -3,10 +3,13 @@ import { gzip } from "node-gzip"; import { useCallback } from "react"; import { useCurrentChain } from "../hooks"; +import { useTrack } from "lib/amplitude"; import { uploadContractTx } from "lib/app-fns/tx/upload"; import type { AccessType, Addr, HumanAddr, Option } from "lib/types"; import { composeStoreCodeMsg } from "lib/utils"; +import { useCatchTxError } from "./catchTxError"; + export interface UploadTxInternalResult { codeDisplayName: string; codeId: string; @@ -29,6 +32,8 @@ export interface UploadStreamParams { export const useUploadContractTx = (isMigrate: boolean) => { const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { trackTxSucceed } = useTrack(); + const catchTxError = useCatchTxError(); return useCallback( async ({ @@ -59,10 +64,14 @@ export const useUploadContractTx = (isMigrate: boolean) => { wasmFileName, fee: estimatedFee, client, - onTxSucceed, isMigrate, + catchTxError, + onTxSucceed: (txResult) => { + trackTxSucceed(); + onTxSucceed(txResult); + }, }); }, - [address, getSigningCosmWasmClient, isMigrate] + [address, getSigningCosmWasmClient, isMigrate, trackTxSucceed, catchTxError] ); }; diff --git a/src/lib/components/ConnectWalletAlert.tsx b/src/lib/components/ConnectWalletAlert.tsx index 4b4a07b82..5aa209dcd 100644 --- a/src/lib/components/ConnectWalletAlert.tsx +++ b/src/lib/components/ConnectWalletAlert.tsx @@ -9,27 +9,26 @@ import { import type { AlertProps } from "@chakra-ui/react"; import type { MouseEventHandler } from "react"; +import { useTrack } from "lib/amplitude"; import { useCurrentChain } from "lib/app-provider"; -import { AmpTrackUseClickWallet } from "lib/services/amplitude"; import { CustomIcon } from "./icon"; interface ConnectWalletAlertProps extends AlertProps { title?: string; subtitle?: string; - page?: string; } export const ConnectWalletAlert = ({ title, subtitle, - page, ...alertProps }: ConnectWalletAlertProps) => { const { address, connect } = useCurrentChain(); + const { trackUseClickWallet } = useTrack(); const onClickConnect: MouseEventHandler = async (e) => { - AmpTrackUseClickWallet(page, "alert"); + trackUseClickWallet("alert"); e.preventDefault(); await connect(); }; diff --git a/src/lib/components/CopyLink.tsx b/src/lib/components/CopyLink.tsx index 16530a7c7..896ead015 100644 --- a/src/lib/components/CopyLink.tsx +++ b/src/lib/components/CopyLink.tsx @@ -2,8 +2,8 @@ import type { FlexProps, IconProps } from "@chakra-ui/react"; import { Flex, Text, useClipboard } from "@chakra-ui/react"; import { useMemo, useState } from "react"; +import { useTrack } from "lib/amplitude"; import { useCurrentChain } from "lib/app-provider"; -import { AmpTrackCopier } from "lib/services/amplitude"; import { CustomIcon } from "./icon"; import { Tooltip } from "./Tooltip"; @@ -25,6 +25,7 @@ export const CopyLink = ({ ...flexProps }: CopyLinkProps) => { const { address } = useCurrentChain(); + const { trackUseCopier } = useTrack(); const { onCopy, hasCopied } = useClipboard(value); const [isHover, setIsHover] = useState(false); @@ -49,7 +50,7 @@ export const CopyLink = ({ align="center" display={{ base: "inline", md: "flex" }} onClick={() => { - AmpTrackCopier(amptrackSection, type); + trackUseCopier(type, amptrackSection); onCopy(); }} _hover={{ diff --git a/src/lib/components/ExplorerLink.tsx b/src/lib/components/ExplorerLink.tsx index 00c277d5e..142d4ad2d 100644 --- a/src/lib/components/ExplorerLink.tsx +++ b/src/lib/components/ExplorerLink.tsx @@ -2,12 +2,12 @@ import type { BoxProps, TextProps } from "@chakra-ui/react"; import { Box, Text, Flex } from "@chakra-ui/react"; import type { ExplorerConfig } from "config/chain/types"; +import { useTrack } from "lib/amplitude"; import type { AddressReturnType } from "lib/app-provider"; import { useCelatoneApp } from "lib/app-provider/contexts"; import { useBaseApiRoute } from "lib/app-provider/hooks/useBaseApiRoute"; import { useCurrentChain } from "lib/app-provider/hooks/useCurrentChain"; import { useMobile } from "lib/app-provider/hooks/useMediaQuery"; -import { AmpTrackMintscan } from "lib/services/amplitude"; import type { Option } from "lib/types"; import { truncate } from "lib/utils"; @@ -118,6 +118,7 @@ const LinkRender = ({ textVariant: TextProps["variant"]; openNewTab: Option; }) => { + const { trackMintScan } = useTrack(); const { currentChainId } = useCelatoneApp(); const textElement = ( { - AmpTrackMintscan(type); + trackMintScan(type); e.stopPropagation(); }} > diff --git a/src/lib/components/InputWithIcon.tsx b/src/lib/components/InputWithIcon.tsx index d2d118850..1a7f027fb 100644 --- a/src/lib/components/InputWithIcon.tsx +++ b/src/lib/components/InputWithIcon.tsx @@ -2,7 +2,7 @@ import type { InputProps } from "@chakra-ui/react"; import { Input, InputGroup, InputRightElement } from "@chakra-ui/react"; import type { ChangeEvent } from "react"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { CustomIcon } from "./icon"; @@ -22,24 +22,28 @@ const InputWithIcon = ({ action, autoFocus = false, onChange, -}: InputWithIconProps) => ( - - AmpTrack(AmpEvent.USE_SEARCH_INPUT) : undefined} - /> - - - - -); +}: InputWithIconProps) => { + const { track } = useTrack(); + + return ( + + track(AmpEvent.USE_SEARCH_INPUT) : undefined} + /> + + + + + ); +}; export default InputWithIcon; diff --git a/src/lib/components/StickySidebar.tsx b/src/lib/components/StickySidebar.tsx index 7af7fe432..c275fbc41 100644 --- a/src/lib/components/StickySidebar.tsx +++ b/src/lib/components/StickySidebar.tsx @@ -12,8 +12,8 @@ import { } from "@chakra-ui/react"; import Link from "next/link"; +import { useTrack } from "lib/amplitude"; import { useInternalNavigate } from "lib/app-provider"; -import { AmpTrackUseRightHelperPanel } from "lib/services/amplitude"; import { CustomIcon } from "./icon"; @@ -63,6 +63,7 @@ export const StickySidebar = ({ hasForumAlert = false, ...boxProps }: StickySidebarProps) => { + const { trackUseRightHelperPanel } = useTrack(); const navigate = useInternalNavigate(); const { title, description, toPagePath, toPageTitle, toPage, page } = metadata; @@ -131,7 +132,7 @@ export const StickySidebar = ({ {toPage && toPagePath && toPageTitle && ( { - AmpTrackUseRightHelperPanel(page, `to-${toPagePath}`); + trackUseRightHelperPanel(page, `to-${toPagePath}`); navigate({ pathname: toPagePath }); }} title={toPageTitle} diff --git a/src/lib/components/ViewPermissionAddresses.tsx b/src/lib/components/ViewPermissionAddresses.tsx index 144e89d88..99cdf9ad5 100644 --- a/src/lib/components/ViewPermissionAddresses.tsx +++ b/src/lib/components/ViewPermissionAddresses.tsx @@ -1,8 +1,8 @@ import { Button } from "@chakra-ui/react"; import { useState } from "react"; +import { useTrack } from "lib/amplitude"; import { useGetAddressType } from "lib/app-provider"; -import { AmpTrackExpand } from "lib/services/amplitude"; import type { PermissionAddresses } from "lib/types"; import { ExplorerLink } from "./ExplorerLink"; @@ -17,6 +17,7 @@ export const ViewPermissionAddresses = ({ }) => { const [viewAll, setViewAll] = useState(false); const getAddressType = useGetAddressType(); + const { trackUseExpand } = useTrack(); const showAddressses = viewAll || (typeof permissionAddresses === "object" && @@ -37,7 +38,7 @@ export const ViewPermissionAddresses = ({ - } - /> -); +}: CopyButtonProps) => { + const { track } = useTrack(); + + return ( + + track(AmpEvent.USE_COPY_BUTTON, { section: amptrackSection }) + } + leftIcon={ + hasIcon ? : undefined + } + {...buttonProps} + > + {buttonText} + + } + /> + ); +}; diff --git a/src/lib/components/json/JsonReadOnly.tsx b/src/lib/components/json/JsonReadOnly.tsx index 59693895c..2e5fa367f 100644 --- a/src/lib/components/json/JsonReadOnly.tsx +++ b/src/lib/components/json/JsonReadOnly.tsx @@ -3,7 +3,7 @@ import dynamic from "next/dynamic"; import { useMemo, useState } from "react"; import { CopyButton } from "../copy"; -import { AmpTrackExpand } from "lib/services/amplitude"; +import { useTrack } from "lib/amplitude"; import { jsonLineCount, jsonValidate } from "lib/utils"; import { ViewFullMsgButton } from "./ViewFullMsgButton"; @@ -36,6 +36,7 @@ const JsonReadOnly = ({ amptrackSection, }: JsonReadOnlyProps) => { const [viewFull, setViewFull] = useState(false); + const { trackUseExpand } = useTrack(); const isJsonValid = useMemo(() => { return jsonValidate(text) === null || text.length === 0; @@ -93,7 +94,7 @@ const JsonReadOnly = ({ {showExpandButton && ( { - AmpTrackExpand({ + trackUseExpand({ action: viewFull ? "collapse" : "expand", component: "json", section: amptrackSection, diff --git a/src/lib/components/links/GitHubLink.tsx b/src/lib/components/links/GitHubLink.tsx index ce88e2552..3ecf7324f 100644 --- a/src/lib/components/links/GitHubLink.tsx +++ b/src/lib/components/links/GitHubLink.tsx @@ -1,13 +1,14 @@ import { Flex, Text } from "@chakra-ui/react"; import { CustomIcon } from "../icon"; -import { AmpTrackSocial } from "lib/services/amplitude"; +import { useTrack } from "lib/amplitude"; interface GitHubLinkProps { github: string; } export const GitHubLink = ({ github }: GitHubLinkProps) => { + const { trackSocial } = useTrack(); const [, , , org, repo] = github.split("/"); return ( @@ -16,7 +17,7 @@ export const GitHubLink = ({ github }: GitHubLinkProps) => { AmpTrackSocial(github)} + onClick={() => trackSocial(github)} target="_blank" rel="noreferrer noopener" style={{ display: "flex" }} diff --git a/src/lib/components/modal/ActionModal.tsx b/src/lib/components/modal/ActionModal.tsx index f28ac630d..3b671ee47 100644 --- a/src/lib/components/modal/ActionModal.tsx +++ b/src/lib/components/modal/ActionModal.tsx @@ -18,7 +18,7 @@ import { useCallback } from "react"; import type { IconKeys } from "../icon"; import { CustomIcon } from "../icon"; -import { AmpTrackUseOtherModal } from "lib/services/amplitude"; +import { useTrack } from "lib/amplitude"; export interface ActionModalProps { icon?: IconKeys; @@ -57,6 +57,7 @@ export function ActionModal({ closeOnOverlayClick = true, }: ActionModalProps) { const { isOpen, onOpen, onClose } = useDisclosure(); + const { trackUseOtherModal } = useTrack(); const handleOnMain = useCallback(() => { mainAction(); @@ -72,7 +73,7 @@ export function ActionModal({ { e.stopPropagation(); - AmpTrackUseOtherModal(title); + trackUseOtherModal(title); onOpen(); }} > diff --git a/src/lib/components/modal/CodeSnippet.tsx b/src/lib/components/modal/CodeSnippet.tsx index 2adfb4bf7..24f0e74f4 100644 --- a/src/lib/components/modal/CodeSnippet.tsx +++ b/src/lib/components/modal/CodeSnippet.tsx @@ -21,6 +21,7 @@ import AceEditor from "react-ace"; import { CopyButton } from "../copy"; import { CustomIcon } from "../icon"; import { CURR_THEME } from "env"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useCurrentChain, @@ -28,7 +29,6 @@ import { useRPCEndpoint, } from "lib/app-provider"; import { CustomTab } from "lib/components/CustomTab"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractAddr } from "lib/types"; import { coinsToStr, jsonPrettify } from "lib/utils"; @@ -59,6 +59,7 @@ const CodeSnippet = ({ const { chain: { chain_name: chainName, daemon_name: daemonName }, } = useCurrentChain(); + const { track } = useTrack(); const isDisabled = !contractAddress || !message.length; const lcdEndpoint = useLCDEndpoint(); const rpcEndpoint = useRPCEndpoint(); @@ -211,7 +212,7 @@ execute(); ml={ml} gap={1} onClick={() => { - AmpTrack(AmpEvent.USE_CONTRACT_SNIPPET); + track(AmpEvent.USE_CONTRACT_SNIPPET); onOpen(); }} > diff --git a/src/lib/components/modal/UnsupportedTokensModal.tsx b/src/lib/components/modal/UnsupportedTokensModal.tsx index ec29de7cf..c69f80a68 100644 --- a/src/lib/components/modal/UnsupportedTokensModal.tsx +++ b/src/lib/components/modal/UnsupportedTokensModal.tsx @@ -18,10 +18,10 @@ import { ExplorerLink } from "../ExplorerLink"; import type { IconKeys } from "../icon"; import { CustomIcon } from "../icon"; import { Tooltip } from "../Tooltip"; +import { useTrack } from "lib/amplitude"; import { useGetAddressType, useGetAddressTypeByLength } from "lib/app-provider"; import type { AddressReturnType } from "lib/app-provider"; import { Copier } from "lib/components/copy"; -import { AmpTrackUnsupportedToken } from "lib/services/amplitude"; import type { BalanceWithAssetInfo, Balance, Token, U, Addr } from "lib/types"; import { getTokenType, @@ -147,6 +147,7 @@ export const UnsupportedTokensModal = ({ }: UnsupportedTokensModalProps) => { const { isOpen, onOpen, onClose } = useDisclosure(); const getAddressTypeByLength = useGetAddressTypeByLength(); + const { trackUseUnsupportedToken } = useTrack(); if (unsupportedAssets.length === 0) return null; @@ -161,7 +162,7 @@ export const UnsupportedTokensModal = ({ size="sm" {...buttonProps} onClick={() => { - AmpTrackUnsupportedToken(amptrackSection); + trackUseUnsupportedToken(amptrackSection); onOpen(); }} > diff --git a/src/lib/components/modal/code/CodeDetailsTemplate.tsx b/src/lib/components/modal/code/CodeDetailsTemplate.tsx index 6ac389fed..85a41e362 100644 --- a/src/lib/components/modal/code/CodeDetailsTemplate.tsx +++ b/src/lib/components/modal/code/CodeDetailsTemplate.tsx @@ -2,6 +2,7 @@ import { Flex, Text, useToast } from "@chakra-ui/react"; import { useCallback, useEffect, useState } from "react"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useGetAddressType } from "lib/app-provider"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { TextInput } from "lib/components/forms"; @@ -9,7 +10,6 @@ import type { IconKeys } from "lib/components/icon"; import { CustomIcon } from "lib/components/icon"; import { PermissionChip } from "lib/components/PermissionChip"; import { useCodeStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { Addr, CodeInfo } from "lib/types"; interface CodeDetailsTemplateModalProps { @@ -31,6 +31,7 @@ export const CodeDetailsTemplateModal = ({ triggerElement, icon = "bookmark-solid", }: CodeDetailsTemplateModalProps) => { + const { track } = useTrack(); const { constants } = useCelatoneApp(); const { saveNewCode, updateCodeInfo } = useCodeStore(); const toast = useToast(); @@ -42,10 +43,10 @@ export const CodeDetailsTemplateModal = ({ const handleAction = useCallback(() => { if (isNewCode) { - AmpTrack(AmpEvent.CODE_SAVE); + track(AmpEvent.CODE_SAVE); saveNewCode(codeInfo.id); } else { - AmpTrack(AmpEvent.CODE_EDIT); + track(AmpEvent.CODE_EDIT); } updateCodeInfo(codeInfo.id, codeInfo.uploader as Addr, name); @@ -68,6 +69,7 @@ export const CodeDetailsTemplateModal = ({ title, toast, updateCodeInfo, + track, ]); // fix prefilling blank space problem (e.g. name saved as " ") diff --git a/src/lib/components/modal/code/RemoveCode.tsx b/src/lib/components/modal/code/RemoveCode.tsx index 644f44da8..68b2cdb73 100644 --- a/src/lib/components/modal/code/RemoveCode.tsx +++ b/src/lib/components/modal/code/RemoveCode.tsx @@ -2,9 +2,9 @@ import { useToast, Text, chakra, IconButton } from "@chakra-ui/react"; import { useCallback } from "react"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { CustomIcon } from "lib/components/icon"; import { useCodeStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { getNameAndDescriptionDefault, shortenName } from "lib/utils"; const StyledIconButton = chakra(IconButton, { @@ -33,10 +33,11 @@ export function RemoveCodeModal({ ), }: RemoveCodeModalProps) { const { removeSavedCode } = useCodeStore(); + const { track } = useTrack(); const toast = useToast(); const handleRemove = useCallback(() => { - AmpTrack(AmpEvent.CODE_REMOVE); + track(AmpEvent.CODE_REMOVE); removeSavedCode(codeId); @@ -50,7 +51,7 @@ export function RemoveCodeModal({ position: "bottom-right", icon: , }); - }, [codeId, name, removeSavedCode, toast]); + }, [codeId, name, removeSavedCode, toast, track]); return ( { - AmpTrack(AmpEvent.CODE_SAVE); + track(AmpEvent.CODE_SAVE); const id = Number(codeId); saveNewCode(id); diff --git a/src/lib/components/modal/contract/AddToOtherList.tsx b/src/lib/components/modal/contract/AddToOtherList.tsx index bc0e96c59..d2cd30257 100644 --- a/src/lib/components/modal/contract/AddToOtherList.tsx +++ b/src/lib/components/modal/contract/AddToOtherList.tsx @@ -3,10 +3,10 @@ import { observer } from "mobx-react-lite"; import { useEffect, useState } from "react"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { ListSelection } from "lib/components/ListSelection"; import { useHandleContractSave } from "lib/hooks/useHandleSave"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { LVPair } from "lib/types"; @@ -18,6 +18,7 @@ interface AddToOtherListModalProps { export const AddToOtherListModal = observer( ({ contractLocalInfo, triggerElement }: AddToOtherListModalProps) => { const [contractLists, setContractLists] = useState([]); + const { track } = useTrack(); const handleSave = useHandleContractSave({ title: "Action Complete!", @@ -25,7 +26,7 @@ export const AddToOtherListModal = observer( instantiator: contractLocalInfo.instantiator, label: contractLocalInfo.label, lists: contractLists, - actions: () => AmpTrack(AmpEvent.CONTRACT_EDIT_LISTS), + actions: () => track(AmpEvent.CONTRACT_EDIT_LISTS), }); useEffect(() => { diff --git a/src/lib/components/modal/contract/ClearAdmin.tsx b/src/lib/components/modal/contract/ClearAdmin.tsx index fdbeb8e93..c1ea80fbd 100644 --- a/src/lib/components/modal/contract/ClearAdmin.tsx +++ b/src/lib/components/modal/contract/ClearAdmin.tsx @@ -2,9 +2,9 @@ import { Text } from "@chakra-ui/react"; import { useCallback } from "react"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useClearAdminTx } from "lib/app-provider"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractAddr } from "lib/types"; interface ClearAdminModalProps { @@ -16,14 +16,15 @@ export const ClearAdminModal = ({ contractAddress, triggerElement, }: ClearAdminModalProps) => { + const { track } = useTrack(); const { broadcast } = useTxBroadcast(); const clearAdminTx = useClearAdminTx(contractAddress); const proceed = useCallback(async () => { - AmpTrack(AmpEvent.ACTION_ADMIN_CLEAR); + track(AmpEvent.ACTION_ADMIN_CLEAR); const stream = await clearAdminTx({}); if (stream) broadcast(stream); - }, [broadcast, clearAdminTx]); + }, [broadcast, clearAdminTx, track]); return ( { + const { track } = useTrack(); const defaultValues = useMemo(() => { return { name: contractLocalInfo.name ?? "", @@ -86,7 +87,7 @@ export const ContractDetailsTemplateModal = ({ tags: offchainState.tags, lists: offchainState.lists, actions: () => - AmpTrack(isSave ? AmpEvent.CONTRACT_SAVE : AmpEvent.CONTRACT_EDIT), + track(isSave ? AmpEvent.CONTRACT_SAVE : AmpEvent.CONTRACT_EDIT), }); return ( diff --git a/src/lib/components/modal/contract/RemoveContract.tsx b/src/lib/components/modal/contract/RemoveContract.tsx index e5913b7f1..14ec4b3b9 100644 --- a/src/lib/components/modal/contract/RemoveContract.tsx +++ b/src/lib/components/modal/contract/RemoveContract.tsx @@ -1,9 +1,9 @@ import type { MenuItemProps } from "@chakra-ui/react"; import { MenuItem, Text, Highlight } from "@chakra-ui/react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { ActionModal } from "lib/components/modal/ActionModal"; import { useHandleContractSave } from "lib/hooks/useHandleSave"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { LVPair } from "lib/types"; import { truncate } from "lib/utils"; @@ -19,6 +19,7 @@ export function RemoveContractModal({ contractRemovalInfo, menuItemProps, }: RemoveContractModalProps) { + const { track } = useTrack(); const displayName = contractLocalInfo.name ? contractLocalInfo.name : truncate(contractLocalInfo.contractAddress); @@ -31,7 +32,7 @@ export function RemoveContractModal({ lists: contractLocalInfo.lists?.filter( (item) => item.value !== contractRemovalInfo.value ), - actions: () => AmpTrack(AmpEvent.CONTRACT_REMOVE), + actions: () => track(AmpEvent.CONTRACT_REMOVE), }); return ( diff --git a/src/lib/components/modal/contract/SaveNewContract.tsx b/src/lib/components/modal/contract/SaveNewContract.tsx index 6040a031a..88d2c5d6c 100644 --- a/src/lib/components/modal/contract/SaveNewContract.tsx +++ b/src/lib/components/modal/contract/SaveNewContract.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from "react"; import { useForm } from "react-hook-form"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useExampleAddresses, useValidateAddress } from "lib/app-provider"; import type { FormStatus } from "lib/components/forms"; import { ControllerInput } from "lib/components/forms"; @@ -12,7 +13,6 @@ import { OffChainForm } from "lib/components/OffChainForm"; import { INSTANTIATED_LIST_NAME } from "lib/data"; import { useHandleContractSave } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useContractDetailByContractAddress } from "lib/services/contractService"; import type { Addr, ContractAddr, LVPair } from "lib/types"; import { @@ -35,6 +35,7 @@ export function SaveNewContractModal({ list, buttonProps, }: SaveNewContractModalProps) { + const { track } = useTrack(); const { getContractLocalInfo } = useContractStore(); const { validateContractAddress } = useValidateAddress(); @@ -157,7 +158,7 @@ export function SaveNewContractModal({ tags: offchainState.tags, lists: offchainState.lists, actions: () => { - AmpTrack(AmpEvent.CONTRACT_SAVE); + track(AmpEvent.CONTRACT_SAVE); resetForm(); }, }); diff --git a/src/lib/components/modal/list/CreateNewList.tsx b/src/lib/components/modal/list/CreateNewList.tsx index 74ff1aa0d..b554d1a56 100644 --- a/src/lib/components/modal/list/CreateNewList.tsx +++ b/src/lib/components/modal/list/CreateNewList.tsx @@ -4,13 +4,13 @@ import { useCallback, useEffect, useState } from "react"; import type { ReactNode } from "react"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp } from "lib/app-provider"; import type { FormStatus } from "lib/components/forms"; import { TextInput } from "lib/components/forms"; import { CustomIcon } from "lib/components/icon"; import { useGetMaxLengthError, useUserKey } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { shortenName } from "lib/utils"; interface CreateNewListModalProps { @@ -28,6 +28,7 @@ export function CreateNewListModal({ onCreate, onClose, }: CreateNewListModalProps) { + const { track } = useTrack(); const { constants } = useCelatoneApp(); const getMaxLengthError = useGetMaxLengthError(); const userKey = useUserKey(); @@ -70,7 +71,7 @@ export function CreateNewListModal({ onCreate?.(listName); onClose?.(); - AmpTrack(AmpEvent.LIST_CREATE); + track(AmpEvent.LIST_CREATE); toast({ title: `Create ${shortenName(listName)} successfully`, @@ -85,6 +86,7 @@ export function CreateNewListModal({ userKey, listName, resetListName, + track, onCreate, onClose, toast, diff --git a/src/lib/components/modal/list/EditListName.tsx b/src/lib/components/modal/list/EditListName.tsx index 94bddd8a2..a72caee42 100644 --- a/src/lib/components/modal/list/EditListName.tsx +++ b/src/lib/components/modal/list/EditListName.tsx @@ -2,6 +2,7 @@ import type { MenuItemProps } from "@chakra-ui/react"; import { MenuItem, useToast } from "@chakra-ui/react"; import { useEffect, useState } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useInternalNavigate } from "lib/app-provider"; import type { FormStatus } from "lib/components/forms"; import { TextInput } from "lib/components/forms/TextInput"; @@ -9,7 +10,6 @@ import { CustomIcon } from "lib/components/icon"; import { ActionModal } from "lib/components/modal/ActionModal"; import { useGetMaxLengthError, useUserKey } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { LVPair } from "lib/types"; import { formatSlugName, shortenName } from "lib/utils"; @@ -23,11 +23,13 @@ export function EditListNameModal({ menuItemProps, reroute = false, }: EditListNameModalProps) { + const { track } = useTrack(); const { constants } = useCelatoneApp(); const getMaxLengthError = useGetMaxLengthError(); const userKey = useUserKey(); const { renameList, isContractListExist } = useContractStore(); const navigate = useInternalNavigate(); + const toast = useToast(); const [listName, setListName] = useState(list.label); const [status, setStatus] = useState({ state: "init" }); @@ -57,9 +59,8 @@ export function EditListNameModal({ userKey, ]); - const toast = useToast(); const handleSave = () => { - AmpTrack(AmpEvent.LIST_EDIT); + track(AmpEvent.LIST_EDIT); // TODO: check list name and different toast status renameList(userKey, list.value, listName); toast({ diff --git a/src/lib/components/modal/list/RemoveList.tsx b/src/lib/components/modal/list/RemoveList.tsx index 0f1bd115e..4b2aa2751 100644 --- a/src/lib/components/modal/list/RemoveList.tsx +++ b/src/lib/components/modal/list/RemoveList.tsx @@ -2,11 +2,11 @@ import type { MenuItemProps } from "@chakra-ui/react"; import { MenuItem, useToast, Text } from "@chakra-ui/react"; import { ActionModal } from "../ActionModal"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useInternalNavigate } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { useUserKey } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { LVPair } from "lib/types"; import { shortenName } from "lib/utils"; @@ -18,11 +18,11 @@ interface RemoveListModalProps { export function RemoveListModal({ list, menuItemProps }: RemoveListModalProps) { const userKey = useUserKey(); const { removeList } = useContractStore(); - + const { track } = useTrack(); const toast = useToast(); const navigate = useInternalNavigate(); const handleRemove = () => { - AmpTrack(AmpEvent.LIST_REMOVE); + track(AmpEvent.LIST_REMOVE); removeList(userKey, list.value); navigate({ pathname: "/contract-lists" }); // TODO: show toast after removed and redirect to /contract-lists diff --git a/src/lib/components/pagination/Next.tsx b/src/lib/components/pagination/Next.tsx index 0024f06a3..5e529cf70 100644 --- a/src/lib/components/pagination/Next.tsx +++ b/src/lib/components/pagination/Next.tsx @@ -2,7 +2,7 @@ import type { ButtonProps } from "@chakra-ui/react"; import { Button } from "@chakra-ui/react"; import { useContext } from "react"; -import { AmpTrackPaginationNavigate } from "lib/services/amplitude"; +import { useTrack } from "lib/amplitude"; import { PaginatorContext } from "./PaginatorProvider"; @@ -13,6 +13,7 @@ interface NextProps extends ButtonProps { export const Next = ({ children, pageSize, ...buttonProps }: NextProps) => { const { actions, state } = useContext(PaginatorContext); + const { trackUsePaginationNavigate } = useTrack(); const { changePage } = actions; const { currentPage, pagesQuantity, isDisabled } = state; @@ -21,7 +22,7 @@ export const Next = ({ children, pageSize, ...buttonProps }: NextProps) => { const handleNextClick = () => { const currPage = currentPage + 1; if (!isLast) changePage(currPage); - AmpTrackPaginationNavigate("next", pageSize, currPage); + trackUsePaginationNavigate("next", pageSize, currPage); }; return ( diff --git a/src/lib/components/pagination/PageDetail.tsx b/src/lib/components/pagination/PageDetail.tsx index a652679c8..21df09d1d 100644 --- a/src/lib/components/pagination/PageDetail.tsx +++ b/src/lib/components/pagination/PageDetail.tsx @@ -2,7 +2,7 @@ import { Flex, Select, Text } from "@chakra-ui/react"; import type { ChangeEvent } from "react"; import { CustomIcon } from "../icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; +import { AmpEvent, useTrack } from "lib/amplitude"; interface PageDetailProps { pageSize: number; @@ -18,42 +18,46 @@ export const PageDetail = ({ lastDataInPage, totalData, onPageSizeChange, -}: PageDetailProps) => ( - - - - Items per page: +}: PageDetailProps) => { + const { track } = useTrack(); + + return ( + + + + Items per page: + + + + + {`${offsetData.toLocaleString()} - ${lastDataInPage.toLocaleString()} of ${totalData.toLocaleString()} items`} - - - {`${offsetData.toLocaleString()} - ${lastDataInPage.toLocaleString()} of ${totalData.toLocaleString()} items`} - - -); + ); +}; diff --git a/src/lib/components/pagination/Previous.tsx b/src/lib/components/pagination/Previous.tsx index 747d26a60..3a86bf611 100644 --- a/src/lib/components/pagination/Previous.tsx +++ b/src/lib/components/pagination/Previous.tsx @@ -3,7 +3,7 @@ import { Button } from "@chakra-ui/react"; import { useContext } from "react"; import type { ReactNode } from "react"; -import { AmpTrackPaginationNavigate } from "lib/services/amplitude"; +import { useTrack } from "lib/amplitude"; import { PaginatorContext } from "./PaginatorProvider"; @@ -17,6 +17,7 @@ export const Previous = ({ ...buttonProps }: PreviousProps) => { const { actions, state } = useContext(PaginatorContext); + const { trackUsePaginationNavigate } = useTrack(); const { changePage } = actions; const { currentPage, isDisabled } = state; @@ -25,7 +26,7 @@ export const Previous = ({ const handlePreviousClick = () => { const currPage = currentPage - 1; if (!isFirst) changePage(currPage); - AmpTrackPaginationNavigate("previous", pageSize, currPage); + trackUsePaginationNavigate("previous", pageSize, currPage); }; return ( diff --git a/src/lib/components/select-code/CodeSelectDrawerButton.tsx b/src/lib/components/select-code/CodeSelectDrawerButton.tsx index 29998829f..2918c9104 100644 --- a/src/lib/components/select-code/CodeSelectDrawerButton.tsx +++ b/src/lib/components/select-code/CodeSelectDrawerButton.tsx @@ -22,9 +22,9 @@ import { FilterByPermission } from "../forms"; import { CustomIcon } from "../icon"; import InputWithIcon from "../InputWithIcon"; import { MySavedCodesTable, MyStoredCodesTable } from "../table"; +import { AmpEvent, useTrack } from "lib/amplitude"; import type { PermissionFilterValue } from "lib/hooks"; import { useMyCodesData } from "lib/model/code"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; interface CodeFilterState { keyword: string; @@ -55,6 +55,7 @@ export const CodeSelectDrawerButton = ({ // ------------------------------------------// // ---------------DEPENDENCIES---------------// // ------------------------------------------// + const { track } = useTrack(); const { storedCodesCount, storedCodes: stored, @@ -78,7 +79,7 @@ export const CodeSelectDrawerButton = ({ ml="auto" w="120px" onClick={() => { - AmpTrack(AmpEvent.USE_CODE_MODAL); + track(AmpEvent.USE_CODE_MODAL); onOpen(); }} > diff --git a/src/lib/components/select-code/CodeSelectSection.tsx b/src/lib/components/select-code/CodeSelectSection.tsx index e91d63c89..55167e8b0 100644 --- a/src/lib/components/select-code/CodeSelectSection.tsx +++ b/src/lib/components/select-code/CodeSelectSection.tsx @@ -2,9 +2,9 @@ import { Flex, Radio, RadioGroup } from "@chakra-ui/react"; import { useState } from "react"; import type { Control, FieldPath, FieldValues } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { ControllerInput } from "lib/components/forms"; import type { FormStatus } from "lib/components/forms"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { LCDCodeInfoSuccessCallback } from "lib/services/codeService"; import type { Option } from "lib/types"; @@ -29,6 +29,7 @@ export const CodeSelectSection = ({ setCodeHash, status, }: CodeSelectSectionProps) => { + const { track } = useTrack(); const [method, setMethod] = useState<"select-existing" | "fill-manually">( "select-existing" ); @@ -37,7 +38,7 @@ export const CodeSelectSection = ({ <> { - AmpTrack( + track( nextVal === "fill-manually" ? AmpEvent.USE_CODE_FILL : AmpEvent.USE_CODE_SELECT diff --git a/src/lib/components/select-contract/SelectContractAdmin.tsx b/src/lib/components/select-contract/SelectContractAdmin.tsx index e11945ea1..494727bbd 100644 --- a/src/lib/components/select-contract/SelectContractAdmin.tsx +++ b/src/lib/components/select-contract/SelectContractAdmin.tsx @@ -10,11 +10,11 @@ import { DrawerBody, } from "@chakra-ui/react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCurrentChain } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { ADMIN_SPECIAL_SLUG } from "lib/data"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useContractListByAdmin } from "lib/services/contractService"; import type { ContractListInfo, ContractLocalInfo } from "lib/stores/contract"; import type { ContractAddr, HumanAddr } from "lib/types"; @@ -31,6 +31,7 @@ export const SelectContractAdmin = ({ notSelected, onContractSelect, }: SelectContractAdminProps) => { + const { track } = useTrack(); const { isOpen, onOpen, onClose } = useDisclosure(); const { address } = useCurrentChain(); const { getContractLocalInfo } = useContractStore(); @@ -63,7 +64,7 @@ export const SelectContractAdmin = ({ px={4} size="sm" onClick={() => { - AmpTrack(AmpEvent.USE_CONTRACT_MODAL); + track(AmpEvent.USE_CONTRACT_MODAL); onOpen(); }} leftIcon={ diff --git a/src/lib/components/select-contract/SelectContractInstantiator.tsx b/src/lib/components/select-contract/SelectContractInstantiator.tsx index 87d1bd051..26c66aa49 100644 --- a/src/lib/components/select-contract/SelectContractInstantiator.tsx +++ b/src/lib/components/select-contract/SelectContractInstantiator.tsx @@ -19,6 +19,7 @@ import { useState } from "react"; import type { KeyboardEvent } from "react"; import { CustomIcon } from "../icon"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { CELATONE_QUERY_KEYS, useBaseApiRoute, @@ -29,7 +30,6 @@ import { import { DEFAULT_RPC_ERROR } from "lib/data"; import { useInstantiatedByMe } from "lib/model/contract"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { queryContract } from "lib/services/contract"; import type { ContractAddr, RpcQueryError } from "lib/types"; @@ -49,6 +49,7 @@ export const SelectContractInstantiator = ({ const { isOpen, onOpen, onClose } = useDisclosure(); const [listSlug, setListSlug] = useState(""); const { validateContractAddress } = useValidateAddress(); + const { track } = useTrack(); const [searchContract, setSearchContract] = useState( "" as ContractAddr @@ -74,7 +75,7 @@ export const SelectContractInstantiator = ({ }; const onSelectThenClose = (contract: ContractAddr) => { - AmpTrack(AmpEvent.USE_CONTRACT_MODAL_LISTS); + track(AmpEvent.USE_CONTRACT_MODAL_LISTS); onContractSelect(contract); resetOnClose(); }; @@ -105,7 +106,7 @@ export const SelectContractInstantiator = ({ const err = validateContractAddress(searchContract); if (err !== null) setInvalid(err); else { - AmpTrack(AmpEvent.USE_CONTRACT_MODAL_SEARCH); + track(AmpEvent.USE_CONTRACT_MODAL_SEARCH); refetch(); } }; @@ -124,7 +125,7 @@ export const SelectContractInstantiator = ({ size="sm" px={4} onClick={() => { - AmpTrack(AmpEvent.USE_CONTRACT_MODAL); + track(AmpEvent.USE_CONTRACT_MODAL); onOpen(); }} leftIcon={ diff --git a/src/lib/components/state/InvalidState.tsx b/src/lib/components/state/InvalidState.tsx index f0fd84ee0..240af3544 100644 --- a/src/lib/components/state/InvalidState.tsx +++ b/src/lib/components/state/InvalidState.tsx @@ -2,7 +2,7 @@ import { Flex, Heading, Text } from "@chakra-ui/react"; import { useRouter } from "next/router"; import { useEffect } from "react"; -import { AmpTrackInvalidState } from "lib/services/amplitude"; +import { useTrack } from "lib/amplitude"; import { StateImage } from "./StateImage"; @@ -12,9 +12,11 @@ interface InvalidStateProps { export const InvalidState = ({ title }: InvalidStateProps) => { const router = useRouter(); + const { trackInvalidState } = useTrack(); + useEffect(() => { - if (router.isReady) AmpTrackInvalidState(title); - }, [router.isReady, title]); + if (router.isReady) trackInvalidState(title); + }, [router.isReady, title, trackInvalidState]); return ( void; } -export const ViewMore = ({ onClick }: ViewMoreProps) => ( - - - -); +export const ViewMore = ({ onClick }: ViewMoreProps) => { + const { track } = useTrack(); + return ( + + + + ); +}; diff --git a/src/lib/components/table/codes/CodeNameCell.tsx b/src/lib/components/table/codes/CodeNameCell.tsx index 27ac8ff99..03304e0f8 100644 --- a/src/lib/components/table/codes/CodeNameCell.tsx +++ b/src/lib/components/table/codes/CodeNameCell.tsx @@ -1,10 +1,10 @@ import { useToast } from "@chakra-ui/react"; import { EditableCell } from "../EditableCell"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { useCodeStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { CodeLocalInfo } from "lib/stores/code"; interface CodeNameCellProps { @@ -16,12 +16,13 @@ export const CodeNameCell = ({ code, isReadOnly = false, }: CodeNameCellProps) => { + const { track } = useTrack(); const { constants } = useCelatoneApp(); const toast = useToast(); const { updateCodeInfo } = useCodeStore(); const onSave = (inputValue?: string) => { - AmpTrack(AmpEvent.USE_QUICK_EDIT_CODE); + track(AmpEvent.USE_QUICK_EDIT_CODE); updateCodeInfo(code.id, code.uploader, inputValue); toast({ title: "New Code Name Saved", diff --git a/src/lib/components/table/contracts/ContractNameCell.tsx b/src/lib/components/table/contracts/ContractNameCell.tsx index db44b80b3..b10fba429 100644 --- a/src/lib/components/table/contracts/ContractNameCell.tsx +++ b/src/lib/components/table/contracts/ContractNameCell.tsx @@ -1,7 +1,7 @@ import { EditableCell } from "../EditableCell"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp } from "lib/app-provider"; import { useHandleContractSave } from "lib/hooks/useHandleSave"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractLocalInfo } from "lib/stores/contract"; interface ContractNameCellProps { @@ -13,13 +13,14 @@ export const ContractNameCell = ({ contractLocalInfo, isReadOnly = false, }: ContractNameCellProps) => { + const { track } = useTrack(); const { constants } = useCelatoneApp(); const onSave = useHandleContractSave({ title: "Changed name successfully!", contractAddress: contractLocalInfo.contractAddress, instantiator: contractLocalInfo.instantiator, label: contractLocalInfo.label, - actions: () => AmpTrack(AmpEvent.USE_QUICK_EDIT_CONTRACT), + actions: () => track(AmpEvent.USE_QUICK_EDIT_CONTRACT), }); return ( ( getTagsDefault(contractLocalInfo.tags) ); @@ -24,7 +25,7 @@ export function EditTags({ contractLocalInfo }: EditTagsProps) { instantiator: contractLocalInfo.instantiator, label: contractLocalInfo.label, tags: tagResult, - actions: () => AmpTrack(AmpEvent.CONTRACT_EDIT_TAGS), + actions: () => track(AmpEvent.CONTRACT_EDIT_TAGS), }); return ( diff --git a/src/lib/components/table/proposals/ProposalsTableRow.tsx b/src/lib/components/table/proposals/ProposalsTableRow.tsx index 7eb5ebdd0..fd4d71829 100644 --- a/src/lib/components/table/proposals/ProposalsTableRow.tsx +++ b/src/lib/components/table/proposals/ProposalsTableRow.tsx @@ -2,11 +2,11 @@ import type { DividerProps, GridProps } from "@chakra-ui/react"; import { Grid } from "@chakra-ui/react"; import { TableRow, TableRowFreeze } from "../tableComponents"; +import { useTrack } from "lib/amplitude"; import { useBaseApiRoute, useCelatoneApp } from "lib/app-provider"; import { ExplorerLink, getNavigationUrl } from "lib/components/ExplorerLink"; import { StopPropagationBox } from "lib/components/StopPropagationBox"; import { Proposer } from "lib/components/table/proposals/Proposer"; -import { AmpTrackMintscan } from "lib/services/amplitude"; import type { Option, Proposal } from "lib/types"; import { ProposalStatus } from "lib/types"; import { openNewTab } from "lib/utils"; @@ -31,6 +31,7 @@ export const ProposalsTableRow = ({ chainConfig: { explorerLink }, } = useCelatoneApp(); const lcdEndpoint = useBaseApiRoute("rest"); + const { trackMintScan } = useTrack(); // TODO - Revisit split columnsWidth const columnsWidth = templateColumns?.toString().split(" "); @@ -53,7 +54,7 @@ export const ProposalsTableRow = ({ onClick={ !isDepositFailed ? () => { - AmpTrackMintscan("proposal-detail", { + trackMintScan("proposal-detail", { type: proposal.type, status: proposal.status, }); diff --git a/src/lib/components/upload/InstantiatePermissionRadio.tsx b/src/lib/components/upload/InstantiatePermissionRadio.tsx index d562dc1cc..b9001f1b5 100644 --- a/src/lib/components/upload/InstantiatePermissionRadio.tsx +++ b/src/lib/components/upload/InstantiatePermissionRadio.tsx @@ -5,13 +5,9 @@ import { useController, useFieldArray, useWatch } from "react-hook-form"; import { AddressInput } from "../AddressInput"; import { AssignMe } from "../AssignMe"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useCurrentChain } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; -import { - AmpEvent, - AmpTrack, - AmpTrackUseInstantiatePermission, -} from "lib/services/amplitude"; import type { Addr, UploadSectionState } from "lib/types"; import { AccessType } from "lib/types"; @@ -19,7 +15,6 @@ interface InstantiatePermissionRadioProps { control: Control; setValue: UseFormSetValue; trigger: UseFormTrigger; - page?: string; } interface PermissionRadioProps { @@ -38,8 +33,8 @@ export const InstantiatePermissionRadio = ({ control, setValue, trigger, - page, }: InstantiatePermissionRadioProps) => { + const { track, trackUseInstantiatePermission } = useTrack(); const { address: walletAddress } = useCurrentChain(); const { chainConfig: { @@ -68,17 +63,14 @@ export const InstantiatePermissionRadio = ({ const emptyAddressesLength = addresses.filter( (addr) => addr.address.trim().length === 0 ).length; - if (page) { - AmpTrackUseInstantiatePermission( - page, - AccessType[permission], - emptyAddressesLength, - addresses.length - emptyAddressesLength - ); - } + trackUseInstantiatePermission( + AccessType[permission], + emptyAddressesLength, + addresses.length - emptyAddressesLength + ); // Run this effect only when the amount of address input or selected permission changes // eslint-disable-next-line react-hooks/exhaustive-deps - }, [addresses.length, page, permission]); + }, [addresses.length, permission]); return ( { - AmpTrack(AmpEvent.USE_ASSIGN_ME); + track(AmpEvent.USE_ASSIGN_ME); setValue( `addresses.${idx}.address`, walletAddress as Addr diff --git a/src/lib/components/upload/UploadSection.tsx b/src/lib/components/upload/UploadSection.tsx index 7356d3a75..586837231 100644 --- a/src/lib/components/upload/UploadSection.tsx +++ b/src/lib/components/upload/UploadSection.tsx @@ -5,6 +5,7 @@ import { useForm } from "react-hook-form"; import { DropZone } from "../dropzone"; import { ControllerInput } from "../forms"; +import { AmpEvent, useTrack } from "lib/amplitude"; import type { UploadSucceedCallback, UploadTxInternalResult, @@ -22,7 +23,6 @@ import { CustomIcon } from "lib/components/icon"; import { useGetMaxLengthError } from "lib/hooks"; import { useCodeStore } from "lib/providers/store"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { Addr, HumanAddr, @@ -48,6 +48,7 @@ export const UploadSection = ({ onComplete, isMigrate = false, }: UploadSectionProps) => { + const { track } = useTrack(); const { constants, chainConfig: { @@ -148,7 +149,7 @@ export const UploadSection = ({ const proceed = useCallback(async () => { if (address) { - AmpTrack(AmpEvent.ACTION_UPLOAD); + track(AmpEvent.ACTION_UPLOAD); const stream = await postUploadTx({ wasmFileName: wasmFile?.name, wasmCode: wasmFile?.arrayBuffer(), @@ -174,6 +175,7 @@ export const UploadSection = ({ }, [ address, postUploadTx, + track, wasmFile, addresses, permission, diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts index cd025d8c0..0864e2797 100644 --- a/src/lib/hooks/index.ts +++ b/src/lib/hooks/index.ts @@ -9,3 +9,4 @@ export * from "./useUserKey"; export * from "./useOpenTab"; export * from "./useIsCurrentPage"; export * from "./useGetMaxLengthError"; +export * from "./useLocalStorage"; diff --git a/src/lib/hooks/useLocalStorage.ts b/src/lib/hooks/useLocalStorage.ts index 6f83fa03f..5e4e10954 100644 --- a/src/lib/hooks/useLocalStorage.ts +++ b/src/lib/hooks/useLocalStorage.ts @@ -8,9 +8,10 @@ export const useLocalStorage = ( key: string, defaultValue: T ): PersistedState => { + const storageKey = `celatone-${key}`; const [storedValue, setStoredValue] = useState(() => { try { - const value = window.localStorage.getItem(key); + const value = window.localStorage.getItem(storageKey); return value ? (JSON.parse(value) as T) : defaultValue; } catch (e) { return defaultValue as T; @@ -19,11 +20,11 @@ export const useLocalStorage = ( useEffect(() => { try { - window.localStorage.setItem(key, JSON.stringify(storedValue)); + window.localStorage.setItem(storageKey, JSON.stringify(storedValue)); } catch (e) { // I want application to not crush, but don't care about the message } - }, [key, storedValue]); + }, [storageKey, storedValue]); return [storedValue, setStoredValue]; }; diff --git a/src/lib/layout/Footer.tsx b/src/lib/layout/Footer.tsx index 1874e4202..8756dfa14 100644 --- a/src/lib/layout/Footer.tsx +++ b/src/lib/layout/Footer.tsx @@ -3,9 +3,9 @@ import { Flex, Text, Image } from "@chakra-ui/react"; import Link from "next/link"; import { CURR_THEME } from "env"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { CustomIcon } from "lib/components/icon"; import type { IconKeys } from "lib/components/icon"; -import { AmpEvent, AmpTrack, AmpTrackSocial } from "lib/services/amplitude"; interface SocialMenuType { url: string; @@ -65,53 +65,60 @@ const SocialMenuRender = ({ }: { isThemed?: boolean; iconSize: IconProps["boxSize"]; -}) => ( - <> - {(isThemed ? themedSocial : socialMenu).map((item) => ( - { - AmpTrackSocial(item.url); - }} - > - { + const { trackSocial } = useTrack(); + return ( + <> + {(isThemed ? themedSocial : socialMenu).map((item) => ( + { + trackSocial(item.url); + }} > - - - - ))} - -); + + + + + ))} + + ); +}; -const AllesFeedback = () => ( - AmpTrack(AmpEvent.FEEDBACK)} - > - { + const { track } = useTrack(); + + return ( + track(AmpEvent.FEEDBACK)} > - - - Feedback - - - -); + + + + Feedback + + + + ); +}; const IconLink = ({ href, @@ -123,43 +130,47 @@ const IconLink = ({ icon: IconKeys; text1: string; text2: string; -}) => ( - AmpTrack(AmpEvent.ALLESLABS)} - > - { + const { track } = useTrack(); + + return ( + track(AmpEvent.ALLESLABS)} > - - - {text1} - - {text2} + + + + {text1} + + {text2} + - - - -); + + + ); +}; const Footer = () => { const isThemed = CURR_THEME.footer; diff --git a/src/lib/layout/Header.tsx b/src/lib/layout/Header.tsx index deb98929a..166dba493 100644 --- a/src/lib/layout/Header.tsx +++ b/src/lib/layout/Header.tsx @@ -10,18 +10,20 @@ import { import { CHAIN_CONFIGS } from "config/chain"; import { CURR_THEME } from "env"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useSelectChain } from "lib/app-provider"; import { AppLink } from "lib/components/AppLink"; import { FaucetBtn } from "lib/components/button"; import { CustomIcon } from "lib/components/icon"; import { WalletSection } from "lib/components/Wallet"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import Searchbar from "./Searchbar"; const Header = () => { + const { track } = useTrack(); const { availableChainIds, currentChainId } = useCelatoneApp(); const selectChain = useSelectChain(); + return ( { - AmpTrack(AmpEvent.USE_SELECT_NETWORK)}> + track(AmpEvent.USE_SELECT_NETWORK)}> { const [displayResults, setDisplayResults] = useState(false); const [isTyping, setIsTyping] = useState(false); const [cursor, setCursor] = useState(); + const { trackUseMainSearch } = useTrack(); const { chainConfig: { @@ -255,7 +256,7 @@ const Searchbar = () => { const handleSelectResult = useCallback( (type?: SearchResultType, isClick = false) => { - AmpTrackUseMainSearch(isClick); + trackUseMainSearch(isClick); const routeOptions = getRouteOptions(type); if (routeOptions) { navigate({ @@ -266,7 +267,7 @@ const Searchbar = () => { setKeyword(""); } }, - [metadata.icns.address, keyword, navigate] + [trackUseMainSearch, navigate, metadata.icns.address, keyword] ); const handleOnKeyEnter = useCallback( diff --git a/src/lib/layout/mobile/NavDrawer.tsx b/src/lib/layout/mobile/NavDrawer.tsx index ef0c182d4..5f8cff36f 100644 --- a/src/lib/layout/mobile/NavDrawer.tsx +++ b/src/lib/layout/mobile/NavDrawer.tsx @@ -19,6 +19,7 @@ import { import type { MenuInfo } from "../navbar/type"; import { CHAIN_CONFIGS } from "config/chain"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, usePublicProjectConfig, @@ -28,9 +29,9 @@ import { AppLink } from "lib/components/AppLink"; import { CustomIcon } from "lib/components/icon"; import { useIsCurrentPage } from "lib/hooks"; import { usePublicProjectStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; export const NavDrawer = () => { + const { track } = useTrack(); const { isOpen, onOpen, onClose } = useDisclosure(); const { currentChainId, availableChainIds } = useCelatoneApp(); const isCurrentPage = useIsCurrentPage(); @@ -100,7 +101,7 @@ export const NavDrawer = () => { - AmpTrack(AmpEvent.USE_SELECT_NETWORK)}> + track(AmpEvent.USE_SELECT_NETWORK)}> { href={submenu.slug} key={submenu.slug} onClick={() => { - AmpTrack(AmpEvent.USE_SIDEBAR); + track(AmpEvent.USE_SIDEBAR); onClose(); }} > diff --git a/src/lib/layout/navbar/Collapse.tsx b/src/lib/layout/navbar/Collapse.tsx index b93e6a95a..98d18da81 100644 --- a/src/lib/layout/navbar/Collapse.tsx +++ b/src/lib/layout/navbar/Collapse.tsx @@ -1,10 +1,10 @@ import { Box, Flex, IconButton, Image } from "@chakra-ui/react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useMobile } from "lib/app-provider"; import { AppLink } from "lib/components/AppLink"; import { CustomIcon } from "lib/components/icon"; import { Tooltip } from "lib/components/Tooltip"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { NavMenuProps, SubmenuInfo } from "./type"; @@ -59,6 +59,7 @@ export const CollapseNavMenu = ({ setIsExpand, }: NavMenuProps) => { const isMobile = useMobile(); + const { track } = useTrack(); return ( @@ -105,7 +106,7 @@ export const CollapseNavMenu = ({ AmpTrack(AmpEvent.USE_SIDEBAR)} + onClick={() => track(AmpEvent.USE_SIDEBAR)} > ( - - {navMenu.map((item) => ( - - - - {item.category} - - {item.category === "Your Account" && ( - - )} - - {item.submenu.map((submenu) => - submenu.isDisable ? ( - -
+}: NavMenuProps) => { + const { track } = useTrack(); + + return ( + + {navMenu.map((item) => ( + + + + {item.category} + + {item.category === "Your Account" && ( + + )} + + {item.submenu.map((submenu) => + submenu.isDisable ? ( + +
+ +
+
+ ) : ( + track(AmpEvent.USE_SIDEBAR)} + > -
-
- ) : ( - AmpTrack(AmpEvent.USE_SIDEBAR)} - > - - - ) - )} -
- ))} -
-); +
+ ) + )} +
+ ))} + + ); +}; diff --git a/src/lib/pages/account-details/components/asset/index.tsx b/src/lib/pages/account-details/components/asset/index.tsx index 4150029b9..3ae441814 100644 --- a/src/lib/pages/account-details/components/asset/index.tsx +++ b/src/lib/pages/account-details/components/asset/index.tsx @@ -2,6 +2,7 @@ import { Flex, Grid, Text, Button } from "@chakra-ui/react"; import type { Big } from "big.js"; import big from "big.js"; +import { useTrack } from "lib/amplitude"; import { useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { Loading } from "lib/components/Loading"; @@ -10,7 +11,6 @@ import { TableTitle, ViewMore } from "lib/components/table"; import { TokenCard } from "lib/components/TokenCard"; import { useOpenAssetTab } from "lib/hooks"; import { useUserAssetInfos } from "lib/pages/account-details/data"; -import { AmpTrackViewJson } from "lib/services/amplitude"; import type { BalanceWithAssetInfo, HumanAddr, Option, USD } from "lib/types"; import { calTotalValue, formatPrice } from "lib/utils"; @@ -70,6 +70,7 @@ const AssetTitle = ({ const AssetCta = ({ walletAddress, totalAsset }: AssetCtaProps) => { const { unsupportedAssets } = useUserAssetInfos(walletAddress); const openAssetTab = useOpenAssetTab(); + const { trackUseViewJSON } = useTrack(); return ( { size="sm" rightIcon={} onClick={() => { - AmpTrackViewJson("account_details_page_assets"); + trackUseViewJSON("account_details_page_assets"); openAssetTab(walletAddress); }} > diff --git a/src/lib/pages/account-details/components/delegations/DelegationInfo.tsx b/src/lib/pages/account-details/components/delegations/DelegationInfo.tsx index 033fb4fc8..944ebd472 100644 --- a/src/lib/pages/account-details/components/delegations/DelegationInfo.tsx +++ b/src/lib/pages/account-details/components/delegations/DelegationInfo.tsx @@ -2,9 +2,9 @@ import { Button, Flex, Heading } from "@chakra-ui/react"; import type { MouseEventHandler, ReactNode } from "react"; import { MobileDelegationTitle } from "../mobile/MobileDelegationTitle"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; interface DelegationInfoProps { onViewMore?: () => void; @@ -40,6 +40,7 @@ const DelegationsTitle = ({ ); }; + export const DelegationInfo = ({ onViewMore, onClickToggle, @@ -47,13 +48,16 @@ export const DelegationInfo = ({ infoCards, TotalBondedCard, }: DelegationInfoProps) => { + const { track } = useTrack(); const isMobile = useMobile(); + let isMobileDetail = null; if (isMobile && onViewMore) { isMobileDetail = false; } else { isMobileDetail = true; } + return ( <> } onClick={() => { - AmpTrack(AmpEvent.USE_VIEW_MORE); + track(AmpEvent.USE_VIEW_MORE); onViewMore(); }} > diff --git a/src/lib/pages/account-details/components/delegations/DelegationsBody.tsx b/src/lib/pages/account-details/components/delegations/DelegationsBody.tsx index 2398d6752..acca90752 100644 --- a/src/lib/pages/account-details/components/delegations/DelegationsBody.tsx +++ b/src/lib/pages/account-details/components/delegations/DelegationsBody.tsx @@ -1,8 +1,8 @@ import { Flex, RadioGroup, Stack } from "@chakra-ui/react"; import { useState } from "react"; +import { useTrack } from "lib/amplitude"; import type { Delegation, Unbonding } from "lib/pages/account-details/data"; -import { AmpTrackUseRadio } from "lib/services/amplitude"; import type { Option, TokenWithValue } from "lib/types"; import { DelegationsTab } from "./DelegationsTab"; @@ -32,11 +32,12 @@ export const DelegationsBody = ({ }: DelegationsBodyProps) => { // NOTE: set between "Delegated" and "Unbonding" const [value, setValue] = useState("Delegated"); + const { trackUseRadio } = useTrack(); return ( { - AmpTrackUseRadio(newValue.toLocaleLowerCase()); + trackUseRadio(newValue.toLocaleLowerCase()); setValue(newValue); }} value={value} diff --git a/src/lib/pages/account-details/components/delegations/index.tsx b/src/lib/pages/account-details/components/delegations/index.tsx index 5cac6d4de..3bdded78c 100644 --- a/src/lib/pages/account-details/components/delegations/index.tsx +++ b/src/lib/pages/account-details/components/delegations/index.tsx @@ -2,10 +2,10 @@ import { Flex, useDisclosure } from "@chakra-ui/react"; import type Big from "big.js"; import big from "big.js"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { Loading } from "lib/components/Loading"; import { EmptyState } from "lib/components/state"; import { useUserDelegationInfos } from "lib/pages/account-details/data"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { HumanAddr, Option, @@ -37,6 +37,7 @@ export const DelegationsSection = ({ walletAddress, onViewMore, }: DelegationsSectionProps) => { + const { track } = useTrack(); const { isOpen, onToggle } = useDisclosure(); const { stakingParams, @@ -134,7 +135,7 @@ export const DelegationsSection = ({ onViewMore={onViewMore} redelegationCount={redelegationCount} onClickToggle={() => { - AmpTrack(AmpEvent.USE_SEE_REDELEGATIONS); + track(AmpEvent.USE_SEE_REDELEGATIONS); onToggle(); }} /> diff --git a/src/lib/pages/account-details/index.tsx b/src/lib/pages/account-details/index.tsx index bc69af68d..67dd6cb4c 100644 --- a/src/lib/pages/account-details/index.tsx +++ b/src/lib/pages/account-details/index.tsx @@ -9,6 +9,7 @@ import { import { useRouter } from "next/router"; import { useCallback, useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useInternalNavigate, useValidateAddress, @@ -22,7 +23,6 @@ import PageContainer from "lib/components/PageContainer"; import { InvalidState } from "lib/components/state"; import { useAccountDetailsTableCounts } from "lib/model/account"; import { useAccountId } from "lib/services/accountService"; -import { AmpEvent, AmpTrack, AmpTrackUseTab } from "lib/services/amplitude"; import { useICNSNamesByAddress } from "lib/services/nameService"; import { usePublicProjectByAccountAddress, @@ -66,6 +66,7 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { const wasm = useWasmConfig({ shouldRedirect: false }); const navigate = useInternalNavigate(); const router = useRouter(); + const { trackUseTab } = useTrack(); // TODO: remove assertion later const tab = getFirstQueryParam(router.query.tab) as TabIndex; const { data: publicInfo } = usePublicProjectByAccountAddress(accountAddress); @@ -86,7 +87,7 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { const handleTabChange = useCallback( (nextTab: TabIndex) => () => { if (nextTab === tab) return; - AmpTrackUseTab(nextTab); + trackUseTab(nextTab); navigate({ pathname: "/accounts/[accountAddress]/[tab]", query: { @@ -98,7 +99,7 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { }, }); }, - [accountAddress, tab, navigate] + [tab, trackUseTab, navigate, accountAddress] ); useEffect(() => { @@ -331,6 +332,7 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { const AccountDetails = () => { const router = useRouter(); + const { track } = useTrack(); const { validateUserAddress, validateContractAddress } = useValidateAddress(); // TODO: change to `Addr` for correctness (i.e. interchain account) const accountAddressParam = getFirstQueryParam( @@ -340,8 +342,9 @@ const AccountDetails = () => { useEffect(() => { if (router.isReady) - AmpTrack(AmpEvent.TO_ACCOUNT_DETAIL, { ...(tab && { tab }) }); - }, [router.isReady, tab]); + // TODO + track(AmpEvent.TO_ACCOUNT_DETAIL, { ...(tab && { tab }) }); + }, [router.isReady, tab, track]); if (!router.isReady) return ; diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index 989490056..c01e4bca5 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -3,6 +3,7 @@ import type { StdFee } from "@cosmjs/stargate"; import { useRouter } from "next/router"; import { useCallback, useEffect, useState } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useFabricateFee, useInternalNavigate, @@ -21,11 +22,6 @@ import type { FormStatus } from "lib/components/forms"; import { TextInput } from "lib/components/forms"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { - AmpEvent, - AmpTrack, - AmpTrackToAdminUpdate, -} from "lib/services/amplitude"; import { useContractDetailByContractAddress } from "lib/services/contractService"; import type { Addr, ContractAddr, HumanAddr } from "lib/types"; import { MsgType } from "lib/types"; @@ -34,7 +30,9 @@ import { composeMsg, getFirstQueryParam } from "lib/utils"; const UpdateAdmin = () => { useWasmConfig({ shouldRedirect: true }); const router = useRouter(); + const { track } = useTrack(); const { address } = useCurrentChain(); + const { trackToAdminUpdate } = useTrack(); const { validateContractAddress, validateUserAddress } = useValidateAddress(); const getAddressType = useGetAddressType(); const navigate = useInternalNavigate(); @@ -89,7 +87,7 @@ const UpdateAdmin = () => { }); const proceed = useCallback(async () => { - AmpTrack(AmpEvent.ACTION_ADMIN_UPDATE); + track(AmpEvent.ACTION_ADMIN_UPDATE); const stream = await updateAdminTx({ contractAddress: contractAddressParam, newAdmin: adminAddress as Addr, @@ -102,6 +100,7 @@ const UpdateAdmin = () => { contractAddressParam, updateAdminTx, broadcast, + track, estimatedFee, ]); @@ -166,8 +165,8 @@ const UpdateAdmin = () => { ]); useEffect(() => { - if (router.isReady) AmpTrackToAdminUpdate(!!contractAddressParam); - }, [router.isReady, contractAddressParam]); + if (router.isReady) trackToAdminUpdate(!!contractAddressParam); + }, [contractAddressParam, router.isReady, trackToAdminUpdate]); return ( diff --git a/src/lib/pages/block-details/index.tsx b/src/lib/pages/block-details/index.tsx index 070681206..2e25fd1bb 100644 --- a/src/lib/pages/block-details/index.tsx +++ b/src/lib/pages/block-details/index.tsx @@ -1,24 +1,25 @@ import { useRouter } from "next/router"; import { useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { Breadcrumb } from "lib/components/Breadcrumb"; import { Loading } from "lib/components/Loading"; import PageContainer from "lib/components/PageContainer"; import { EmptyState } from "lib/components/state"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useBlockInfoQuery } from "lib/services/blockService"; import { getFirstQueryParam } from "lib/utils"; import { BlockDetailsTop, BlockInfo, BlockTxsTable } from "./components"; const BlockDetail = () => { + const { track } = useTrack(); const router = useRouter(); const heightParam = getFirstQueryParam(router.query.height); const { data: blockData, isLoading } = useBlockInfoQuery(heightParam); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_BLOCK_DETAIL); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_BLOCK_DETAIL); + }, [router.isReady, track]); if (isLoading) return ; diff --git a/src/lib/pages/blocks/index.tsx b/src/lib/pages/blocks/index.tsx index 2222e9afc..2775fc7fb 100644 --- a/src/lib/pages/blocks/index.tsx +++ b/src/lib/pages/blocks/index.tsx @@ -2,16 +2,18 @@ import { Heading, Text } from "@chakra-ui/react"; import { useRouter } from "next/router"; import { useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import PageContainer from "lib/components/PageContainer"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { BlocksTable } from "./components/BlocksTable"; const BlocksPage = () => { const router = useRouter(); + const { track } = useTrack(); + useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_BLOCKS); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_BLOCKS); + }, [router.isReady, track]); return ( diff --git a/src/lib/pages/code-details/index.tsx b/src/lib/pages/code-details/index.tsx index e6458564e..199ffc928 100644 --- a/src/lib/pages/code-details/index.tsx +++ b/src/lib/pages/code-details/index.tsx @@ -13,6 +13,7 @@ import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useWasmConfig, useMobile } from "lib/app-provider"; import { Breadcrumb } from "lib/components/Breadcrumb"; import { CopyLink } from "lib/components/CopyLink"; @@ -26,7 +27,6 @@ import { InvalidState } from "lib/components/state"; import type { CodeDataState } from "lib/model/code"; import { useCodeData } from "lib/model/code"; import { useCodeStore, useSchemaStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { Option } from "lib/types"; import { AccessConfigPermission } from "lib/types"; import { getCw2Info, getFirstQueryParam, isCodeId } from "lib/utils"; @@ -260,13 +260,14 @@ const CodeDetailsBody = observer( const CodeDetails = observer(() => { useWasmConfig({ shouldRedirect: true }); + const { track } = useTrack(); const router = useRouter(); const codeIdParam = getFirstQueryParam(router.query.codeId); const data = useCodeData(codeIdParam); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_CODE_DETAIL); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_CODE_DETAIL); + }, [router.isReady, track]); if (data.isLoading) return ; return ( diff --git a/src/lib/pages/codes/index.tsx b/src/lib/pages/codes/index.tsx index ec67d18c5..b6cb79961 100644 --- a/src/lib/pages/codes/index.tsx +++ b/src/lib/pages/codes/index.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useInternalNavigate, useWasmConfig, @@ -16,7 +17,6 @@ import PageContainer from "lib/components/PageContainer"; import { EmptyState } from "lib/components/state"; import { CodesTable } from "lib/components/table"; import type { PermissionFilterValue } from "lib/hooks"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useRecentCodesData } from "./data"; @@ -27,6 +27,7 @@ interface RecentCodesState { const RecentCodes = observer(() => { useWasmConfig({ shouldRedirect: true }); + const { track } = useTrack(); const router = useRouter(); const navigate = useInternalNavigate(); const onRowSelect = (codeId: number) => @@ -48,8 +49,8 @@ const RecentCodes = observer(() => { ); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_RECENT_CODES); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_RECENT_CODES); + }, [router.isReady, track]); const emptyState = ( { useWasmConfig({ shouldRedirect: true }); + const { track } = useTrack(); const router = useRouter(); const { validateContractAddress } = useValidateAddress(); const contractAddressParam = getFirstQueryParam( @@ -172,8 +173,8 @@ const ContractDetails = observer(() => { const contractData = useContractData(contractAddressParam); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_CONTRACT_DETAIL); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_CONTRACT_DETAIL); + }, [router.isReady, track]); if (contractData.isContractDetailLoading) return ; return ( diff --git a/src/lib/pages/contract-list/index.tsx b/src/lib/pages/contract-list/index.tsx index db872083f..ebf1cde50 100644 --- a/src/lib/pages/contract-list/index.tsx +++ b/src/lib/pages/contract-list/index.tsx @@ -3,6 +3,7 @@ import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import { useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useInternalNavigate, useWasmConfig } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { CreateNewListModal } from "lib/components/modal"; @@ -10,10 +11,10 @@ import PageContainer from "lib/components/PageContainer"; import { AllContractLists } from "lib/components/select-contract"; import { useInstantiatedMockInfoByMe } from "lib/model/contract"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; const AllContractListsPage = observer(() => { useWasmConfig({ shouldRedirect: true }); + const { track } = useTrack(); const router = useRouter(); const navigate = useInternalNavigate(); const { getContractLists } = useContractStore(); @@ -24,8 +25,8 @@ const AllContractListsPage = observer(() => { }; useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_ALL_LISTS); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_ALL_LISTS); + }, [router.isReady, track]); return ( diff --git a/src/lib/pages/contract-list/slug.tsx b/src/lib/pages/contract-list/slug.tsx index 1399831bb..c03a43ce2 100644 --- a/src/lib/pages/contract-list/slug.tsx +++ b/src/lib/pages/contract-list/slug.tsx @@ -10,6 +10,7 @@ import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import { useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useInternalNavigate, useWasmConfig } from "lib/app-provider"; import { Breadcrumb } from "lib/components/Breadcrumb"; import { CustomIcon } from "lib/components/icon"; @@ -23,12 +24,12 @@ import { ContractListDetail } from "lib/components/select-contract"; import { INSTANTIATED_LIST_NAME, SAVED_LIST_NAME } from "lib/data"; import { useInstantiatedByMe } from "lib/model/contract"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractAddr } from "lib/types"; import { formatSlugName, getFirstQueryParam } from "lib/utils"; const ContractsByList = observer(() => { useWasmConfig({ shouldRedirect: true }); + const { track } = useTrack(); const router = useRouter(); const navigate = useInternalNavigate(); const listSlug = getFirstQueryParam(router.query.slug); @@ -63,16 +64,16 @@ const ContractsByList = observer(() => { if (router.isReady) { switch (listSlug) { case formatSlugName(INSTANTIATED_LIST_NAME): - AmpTrack(AmpEvent.TO_LIST_BY_ME); + track(AmpEvent.TO_INSTANTIATED_BY_ME); break; case formatSlugName(SAVED_LIST_NAME): - AmpTrack(AmpEvent.TO_LIST_SAVED); + track(AmpEvent.TO_SAVED_CONTRACT); break; default: - AmpTrack(AmpEvent.TO_LIST_OTHERS); + track(AmpEvent.TO_LIST_OTHERS); } } - }, [router.isReady, listSlug]); + }, [router.isReady, listSlug, track]); if (!contractListInfo) return null; diff --git a/src/lib/pages/contracts/index.tsx b/src/lib/pages/contracts/index.tsx index 074a8fd31..584e97bc3 100644 --- a/src/lib/pages/contracts/index.tsx +++ b/src/lib/pages/contracts/index.tsx @@ -1,6 +1,9 @@ import { Heading, Box, Flex, Text } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; +import { useRouter } from "next/router"; +import { useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useInternalNavigate, useWasmConfig, @@ -17,7 +20,10 @@ import { useRecentContractsData } from "./data"; const RecentContracts = observer(() => { useWasmConfig({ shouldRedirect: true }); + const { track } = useTrack(); + const router = useRouter(); const navigate = useInternalNavigate(); + const isMobile = useMobile(); const onRowSelect = (contract: ContractAddr) => navigate({ pathname: "/contracts/[contract]", @@ -26,6 +32,10 @@ const RecentContracts = observer(() => { const { recentContracts, isLoading } = useRecentContractsData(""); + useEffect(() => { + if (router.isReady) track(AmpEvent.TO_RECENT_CONTRACT); + }, [router.isReady, track]); + const emptyState = ( { withBorder /> ); - const isMobile = useMobile(); const MobileSection = () => { if (isLoading) return ; if (!recentContracts?.length) return emptyState; @@ -48,6 +57,7 @@ const RecentContracts = observer(() => { ); }; + return ( diff --git a/src/lib/pages/deploy/index.tsx b/src/lib/pages/deploy/index.tsx index 52eb8cd7c..5ab32c65b 100644 --- a/src/lib/pages/deploy/index.tsx +++ b/src/lib/pages/deploy/index.tsx @@ -10,6 +10,7 @@ import { import { useRouter } from "next/router"; import { useEffect } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useCurrentChain, @@ -22,7 +23,6 @@ import { CustomIcon } from "lib/components/icon"; import { Loading } from "lib/components/Loading"; import { Stepper } from "lib/components/stepper"; import WasmPageContainer from "lib/components/WasmPageContainer"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useUploadAccessParams } from "lib/services/proposalService"; import type { HumanAddr } from "lib/types"; import { AccessConfigPermission } from "lib/types"; @@ -56,6 +56,7 @@ const getAlertContent = ( }; const Deploy = () => { + const { track } = useTrack(); const router = useRouter(); const navigate = useInternalNavigate(); const { address } = useCurrentChain(); @@ -74,8 +75,8 @@ const Deploy = () => { useWasmConfig({ shouldRedirect: true }); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_DEPLOY); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_DEPLOY); + }, [router.isReady, track]); if (isFetching) return ; diff --git a/src/lib/pages/execute/components/JsonExecute.tsx b/src/lib/pages/execute/components/JsonExecute.tsx index 682402c2d..6705ed146 100644 --- a/src/lib/pages/execute/components/JsonExecute.tsx +++ b/src/lib/pages/execute/components/JsonExecute.tsx @@ -4,6 +4,7 @@ import dynamic from "next/dynamic"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useForm, useFormState } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useFabricateFee, useExecuteContractTx, @@ -30,7 +31,6 @@ import { LoadingOverlay } from "lib/components/LoadingOverlay"; import { useExecuteCmds } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { AmpEvent, AmpTrackAction } from "lib/services/amplitude"; import type { Activity } from "lib/stores/contract"; import type { ComposedMsg, ContractAddr, HumanAddr } from "lib/types"; import { MsgType } from "lib/types"; @@ -69,6 +69,7 @@ export const JsonExecute = ({ const { broadcast } = useTxBroadcast(); const { addActivity } = useContractStore(); const getAttachFunds = useAttachFunds(); + const { trackAction } = useTrack(); // ------------------------------------------// // ------------------STATES------------------// @@ -152,7 +153,7 @@ export const JsonExecute = ({ assetsJsonStr, assetsSelect ); - AmpTrackAction(AmpEvent.ACTION_EXECUTE, funds.length, attachFundsOption); + trackAction(AmpEvent.ACTION_EXECUTE, funds.length, attachFundsOption); const stream = await executeTx({ onTxSucceed: (activity: Activity) => { addActivity(activity); @@ -175,6 +176,7 @@ export const JsonExecute = ({ contractAddress, msg, getAttachFunds, + trackAction, assetsJsonStr, assetsSelect, addActivity, diff --git a/src/lib/pages/execute/components/MsgSuggestion.tsx b/src/lib/pages/execute/components/MsgSuggestion.tsx index 0ed57c46f..fb7bebadf 100644 --- a/src/lib/pages/execute/components/MsgSuggestion.tsx +++ b/src/lib/pages/execute/components/MsgSuggestion.tsx @@ -1,7 +1,7 @@ import { Box, Text, ButtonGroup } from "@chakra-ui/react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { ContractCmdButton } from "lib/components/ContractCmdButton"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractAddr } from "lib/types"; import { jsonPrettify } from "lib/utils"; @@ -16,6 +16,8 @@ export const MsgSuggestion = ({ cmds, setMsg, }: MsgSuggestionProps) => { + const { track } = useTrack(); + return ( {contractAddress && ( @@ -39,7 +41,7 @@ export const MsgSuggestion = ({ key={`query-cmd-${cmd}`} cmd={cmd} onClickCmd={() => { - AmpTrack(AmpEvent.USE_CMD_EXECUTE); + track(AmpEvent.USE_CMD_EXECUTE); setMsg(jsonPrettify(queryMsg)); }} /> diff --git a/src/lib/pages/execute/components/schema-execute/ExecuteBox.tsx b/src/lib/pages/execute/components/schema-execute/ExecuteBox.tsx index d8656f493..0214423c6 100644 --- a/src/lib/pages/execute/components/schema-execute/ExecuteBox.tsx +++ b/src/lib/pages/execute/components/schema-execute/ExecuteBox.tsx @@ -18,6 +18,7 @@ import dynamic from "next/dynamic"; import { useCallback, useEffect, useState, useMemo } from "react"; import { useForm, useFormState } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCurrentChain, useExecuteContractTx, @@ -40,7 +41,6 @@ import { CustomIcon } from "lib/components/icon"; import { JsonSchemaForm } from "lib/components/json-schema"; import { useContractStore } from "lib/providers/store"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { AmpEvent, AmpTrackAction } from "lib/services/amplitude"; import type { Activity } from "lib/stores/contract"; import type { SchemaInfo } from "lib/stores/schema"; import type { @@ -92,6 +92,7 @@ export const ExecuteBox = ({ const { broadcast } = useTxBroadcast(); const { addActivity } = useContractStore(); const getAttachFunds = useAttachFunds(); + const { trackAction } = useTrack(); // ------------------------------------------// // ------------------STATES------------------// @@ -188,7 +189,7 @@ export const ExecuteBox = ({ assetsJsonStr, assetsSelect ); - AmpTrackAction(AmpEvent.ACTION_EXECUTE, funds.length, attachFundsOption); + trackAction(AmpEvent.ACTION_EXECUTE, funds.length, attachFundsOption); const stream = await executeTx({ onTxSucceed: (activity: Activity) => { addActivity(activity); @@ -211,6 +212,7 @@ export const ExecuteBox = ({ contractAddress, msg, getAttachFunds, + trackAction, assetsJsonStr, assetsSelect, addActivity, diff --git a/src/lib/pages/execute/components/schema-execute/index.tsx b/src/lib/pages/execute/components/schema-execute/index.tsx index 7eebfc9a1..b25dc5246 100644 --- a/src/lib/pages/execute/components/schema-execute/index.tsx +++ b/src/lib/pages/execute/components/schema-execute/index.tsx @@ -2,10 +2,10 @@ import { Accordion, Button, Flex } from "@chakra-ui/react"; import type { Coin } from "@cosmjs/stargate"; import { useEffect, useMemo, useRef, useState } from "react"; +import { useTrack } from "lib/amplitude"; import { CustomIcon } from "lib/components/icon"; import InputWithIcon from "lib/components/InputWithIcon"; import { EmptyState } from "lib/components/state"; -import { AmpTrackExpandAll } from "lib/services/amplitude"; import type { ExecuteSchema } from "lib/stores/schema"; import type { ContractAddr } from "lib/types"; import { getDefaultMsg, resolveInitialMsg } from "lib/utils"; @@ -19,7 +19,6 @@ interface SchemaExecuteProps { initialFunds: Coin[]; } -// TODO: add initialMsg and initialFunds export const SchemaExecute = ({ contractAddress, schema, @@ -27,7 +26,12 @@ export const SchemaExecute = ({ initialFunds, }: SchemaExecuteProps) => { // ------------------------------------------// - // --------------------REF-------------------// + // ---------------DEPENDENCIES---------------// + // ------------------------------------------// + const { trackUseExpandAll } = useTrack(); + + // ------------------------------------------// + // -----------------REFERENCE----------------// // ------------------------------------------// const accordionRef = useRef(null); @@ -92,7 +96,7 @@ export const SchemaExecute = ({ } minH="40px" onClick={() => { - AmpTrackExpandAll(expandedIndexes.length ? "collapse" : "expand"); + trackUseExpandAll(expandedIndexes.length ? "collapse" : "expand"); setExpandedIndexes((prev) => !prev.length ? Array.from(Array(schema.length).keys()) : [] ); diff --git a/src/lib/pages/execute/index.tsx b/src/lib/pages/execute/index.tsx index aa5742c35..44901e4df 100644 --- a/src/lib/pages/execute/index.tsx +++ b/src/lib/pages/execute/index.tsx @@ -3,12 +3,12 @@ import type { Coin } from "@cosmjs/stargate"; import { useRouter } from "next/router"; import { useCallback, useEffect, useState } from "react"; +import { useTrack } from "lib/amplitude"; import { useInternalNavigate, useWasmConfig } from "lib/app-provider"; import { ConnectWalletAlert } from "lib/components/ConnectWalletAlert"; import { ContractSelectSection } from "lib/components/ContractSelectSection"; import { CustomIcon } from "lib/components/icon"; import PageContainer from "lib/components/PageContainer"; -import { AmpTrackToExecute } from "lib/services/amplitude"; import type { ContractDetail } from "lib/services/contractService"; import type { ContractAddr } from "lib/types"; import { @@ -37,6 +37,7 @@ const Execute = () => { const [initialFunds, setInitialFunds] = useState([]); const [codeHash, setCodeHash] = useState(""); const [codeId, setCodeId] = useState(""); + const { trackToExecute } = useTrack(); // ------------------------------------------// // ----------------CALLBACKS-----------------// @@ -65,12 +66,11 @@ const Execute = () => { // ---------------SIDE EFFECTS---------------// // ------------------------------------------// useEffect(() => { - const msgParam = getFirstQueryParam(router.query.msg); if (router.isReady) { const contractAddressParam = getFirstQueryParam( router.query.contract ) as ContractAddr; - + const msgParam = getFirstQueryParam(router.query.msg); if (!msgParam.length) { setInitialMsg(""); setInitialFunds([]); @@ -89,9 +89,9 @@ const Execute = () => { } setContractAddress(contractAddressParam); - AmpTrackToExecute(!!contractAddressParam, !!msgParam); + trackToExecute(!!contractAddressParam, !!msgParam); } - }, [router, onContractSelect]); + }, [router, onContractSelect, trackToExecute]); return ( diff --git a/src/lib/pages/faucet/index.tsx b/src/lib/pages/faucet/index.tsx index 08c8c41ec..30868a04c 100644 --- a/src/lib/pages/faucet/index.tsx +++ b/src/lib/pages/faucet/index.tsx @@ -11,6 +11,7 @@ import axios from "axios"; import { useRouter } from "next/router"; import { useEffect, useMemo, useState } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useCurrentChain, @@ -24,7 +25,6 @@ import { CustomIcon } from "lib/components/icon"; import type { IconKeys } from "lib/components/icon"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useOpenTxTab } from "lib/hooks"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useFaucetInfo } from "lib/services/faucetService"; type ResultStatus = "success" | "error" | "warning"; @@ -42,6 +42,7 @@ const STATUS_ICONS: Record = { }; const Faucet = () => { + const { track } = useTrack(); const { address: walletAddress = "" } = useCurrentChain(); const [address, setAddress] = useState(""); const [isLoading, setIsLoading] = useState(false); @@ -76,8 +77,8 @@ const Faucet = () => { }, [faucet.enabled, faucetInfo?.formattedAmount, faucetInfo?.formattedDenom]); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_FAUCET); - }, [router]); + if (router.isReady) track(AmpEvent.TO_FAUCET); + }, [router, track]); useEffect(() => { if (address) { @@ -92,7 +93,7 @@ const Faucet = () => { const onSubmit = async () => { setIsLoading(true); - AmpTrack(AmpEvent.ACTION_FAUCET); + track(AmpEvent.ACTION_FAUCET); if (!faucetUrl) { setResult({ status: "error", message: "Faucet URL not set" }); @@ -122,7 +123,7 @@ const Faucet = () => { ), }); - AmpTrack(AmpEvent.TX_SUCCEED); + track(AmpEvent.TX_SUCCEED); setIsLoading(false); setResult({ status: "success", @@ -155,7 +156,7 @@ const Faucet = () => { }); } - AmpTrack(AmpEvent.TX_FAILED); + track(AmpEvent.TX_FAILED); setIsLoading(false); }); }; @@ -182,7 +183,7 @@ const Faucet = () => { helperAction={ { - AmpTrack(AmpEvent.USE_ASSIGN_ME); + track(AmpEvent.USE_ASSIGN_ME); setAddress(walletAddress); }} isDisable={address === walletAddress} diff --git a/src/lib/pages/home/index.tsx b/src/lib/pages/home/index.tsx index a32d569cf..e5e219936 100644 --- a/src/lib/pages/home/index.tsx +++ b/src/lib/pages/home/index.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { useEffect } from "react"; import { CURR_THEME } from "env"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCelatoneApp, useInternalNavigate, @@ -16,13 +17,12 @@ import { ViewMore } from "lib/components/table"; import { Tooltip } from "lib/components/Tooltip"; import { BlocksTable } from "lib/pages/blocks/components/BlocksTable"; import { TxsTable } from "lib/pages/txs/components/TxsTable"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useAverageBlockTime, useLatestBlockInfo, } from "lib/services/blockService"; import { useTxsCount } from "lib/services/txService"; -import type { Option } from "lib/types"; +import { type Option } from "lib/types"; import { dateFromNow, formatUTC } from "lib/utils"; import { DevShortcut, TopDecorations } from "./components"; @@ -121,6 +121,7 @@ const Home = () => { isLoading: isLoadingLatestBlockInfo, error: latestBlockInfoError, } = useLatestBlockInfo(); + const { track } = useTrack(); const { data } = useAverageBlockTime(); const averageBlockTime = calculateAverageBlockTime( data?.latest, @@ -139,8 +140,8 @@ const Home = () => { }); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_NETWORK_OVERVIEW); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_OVERVIEW); + }, [track, router.isReady]); return ( diff --git a/src/lib/pages/instantiate/component/InstantiateOffchainForm.tsx b/src/lib/pages/instantiate/component/InstantiateOffchainForm.tsx index f1632657b..50f281c2e 100644 --- a/src/lib/pages/instantiate/component/InstantiateOffchainForm.tsx +++ b/src/lib/pages/instantiate/component/InstantiateOffchainForm.tsx @@ -2,13 +2,13 @@ import { Text, Flex, Heading, Button } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; import { useForm } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCurrentChain, useInternalNavigate } from "lib/app-provider"; import { OffChainForm } from "lib/components/OffChainForm"; import type { OffchainDetail } from "lib/components/OffChainForm"; import { INSTANTIATED_LIST_NAME } from "lib/data"; import { useUserKey } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { ContractAddr, HumanAddr, LVPair } from "lib/types"; import { formatSlugName } from "lib/utils"; @@ -28,6 +28,7 @@ export const InstantiateOffChainForm = observer( contractAddress, contractLabel, }: InstantiateOffChainFormProps) => { + const { track } = useTrack(); const { address = "" } = useCurrentChain(); const navigate = useInternalNavigate(); const { updateContractLocalInfo } = useContractStore(); @@ -65,7 +66,7 @@ export const InstantiateOffChainForm = observer( const saveContract = () => { handleSubmit((data) => { - AmpTrack(AmpEvent.CONTRACT_SAVE_AFTER_INIT); + track(AmpEvent.CONTRACT_SAVE_AFTER_INIT); updateContractLocalInfo( userKey, contractAddress, diff --git a/src/lib/pages/instantiate/instantiate.tsx b/src/lib/pages/instantiate/instantiate.tsx index 4d4b481b6..5a12aad77 100644 --- a/src/lib/pages/instantiate/instantiate.tsx +++ b/src/lib/pages/instantiate/instantiate.tsx @@ -7,6 +7,7 @@ import { useRouter } from "next/router"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useFabricateFee, useInstantiateTx, @@ -40,12 +41,6 @@ import { Stepper } from "lib/components/stepper"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useSchemaStore } from "lib/providers/store"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { - AmpEvent, - AmpTrack, - AmpTrackAction, - AmpTrackToInstantiate, -} from "lib/services/amplitude"; import type { CodeIdInfoResponse } from "lib/services/code"; import { useLCDCodeInfo } from "lib/services/codeService"; import type { ComposedMsg, HumanAddr } from "lib/types"; @@ -92,6 +87,7 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { const { validateUserAddress, validateContractAddress } = useValidateAddress(); const getAttachFunds = useAttachFunds(); const { getSchemaByCodeHash } = useSchemaStore(); + const { track, trackAction, trackToInstantiate } = useTrack(); // ------------------------------------------// // ------------------STATES------------------// @@ -242,7 +238,7 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { ); const proceed = useCallback(async () => { - AmpTrackAction(AmpEvent.ACTION_EXECUTE, funds.length, attachFundsOption); + trackAction(AmpEvent.ACTION_INSTANTIATE, funds.length, attachFundsOption); const stream = await postInstantiateTx({ codeId: Number(codeId), initMsg: JSON.parse(currentInput), @@ -265,6 +261,7 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { funds, attachFundsOption, postInstantiateTx, + trackAction, codeId, currentInput, label, @@ -366,8 +363,8 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { }, [jsonSchema, setValue]); useEffect(() => { - if (router.isReady) AmpTrackToInstantiate(!!msgQuery, !!codeIdQuery); - }, [router.isReady, msgQuery, codeIdQuery]); + if (router.isReady) trackToInstantiate(!!msgQuery, !!codeIdQuery); + }, [router.isReady, msgQuery, codeIdQuery, trackToInstantiate]); return ( <> @@ -420,7 +417,7 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { helperAction={ { - AmpTrack(AmpEvent.USE_ASSIGN_ME); + track(AmpEvent.USE_ASSIGN_ME); setValue("adminAddress", address); }} isDisable={adminAddress === address} diff --git a/src/lib/pages/migrate/components/MigrateContract.tsx b/src/lib/pages/migrate/components/MigrateContract.tsx index 0d267c193..b542f8648 100644 --- a/src/lib/pages/migrate/components/MigrateContract.tsx +++ b/src/lib/pages/migrate/components/MigrateContract.tsx @@ -5,6 +5,7 @@ import Long from "long"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useFabricateFee, useSimulateFeeQuery, @@ -26,7 +27,6 @@ import JsonInput from "lib/components/json/JsonInput"; import { CodeSelectSection } from "lib/components/select-code"; import { useSchemaStore } from "lib/providers/store"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import type { CodeIdInfoResponse } from "lib/services/code"; import { useLCDCodeInfo } from "lib/services/codeService"; import type { ComposedMsg, ContractAddr, HumanAddr } from "lib/types"; @@ -57,6 +57,7 @@ export const MigrateContract = ({ const migrateTx = useMigrateTx(); const fabricateFee = useFabricateFee(); const { getSchemaByCodeHash } = useSchemaStore(); + const { track } = useTrack(); // ------------------------------------------// // ----------------FORM HOOKS----------------// @@ -172,7 +173,7 @@ export const MigrateContract = ({ ); const proceed = useCallback(async () => { - AmpTrack(AmpEvent.ACTION_MIGRATE); + track(AmpEvent.ACTION_MIGRATE); const stream = await migrateTx({ contractAddress, codeId: Number(codeId), @@ -188,6 +189,7 @@ export const MigrateContract = ({ } }, [ migrateTx, + track, contractAddress, codeId, currentInput, diff --git a/src/lib/pages/migrate/index.tsx b/src/lib/pages/migrate/index.tsx index 945be1608..e62337124 100644 --- a/src/lib/pages/migrate/index.tsx +++ b/src/lib/pages/migrate/index.tsx @@ -3,6 +3,7 @@ import { useRouter } from "next/router"; import { useCallback, useEffect } from "react"; import { useForm } from "react-hook-form"; +import { useTrack } from "lib/amplitude"; import { useCurrentChain, useInternalNavigate, @@ -13,7 +14,6 @@ import { ContractSelectSection } from "lib/components/ContractSelectSection"; import { Loading } from "lib/components/Loading"; import { Stepper } from "lib/components/stepper"; import WasmPageContainer from "lib/components/WasmPageContainer"; -import { AmpTrackToMigrate } from "lib/services/amplitude"; import { useContractDetailByContractAddress } from "lib/services/contractService"; import { useUploadAccessParams } from "lib/services/proposalService"; import type { ContractAddr } from "lib/types"; @@ -36,6 +36,7 @@ const Migrate = () => { const router = useRouter(); const navigate = useInternalNavigate(); const { data: uploadAccess, isFetching } = useUploadAccessParams(); + const { trackToMigrate } = useTrack(); const { address = "" } = useCurrentChain(); @@ -92,9 +93,8 @@ const Migrate = () => { }, [codeIdParam, contractAddressParam, setValue]); useEffect(() => { - if (router.isReady) - AmpTrackToMigrate(!!contractAddressParam, !!codeIdParam); - }, [router.isReady, codeIdParam, contractAddressParam]); + if (router.isReady) trackToMigrate(!!contractAddressParam, !!codeIdParam); + }, [router.isReady, codeIdParam, contractAddressParam, trackToMigrate]); const renderBody = () => { switch (migrateStep) { diff --git a/src/lib/pages/not-found/index.tsx b/src/lib/pages/not-found/index.tsx index 54ff364b1..c28c4ff4e 100644 --- a/src/lib/pages/not-found/index.tsx +++ b/src/lib/pages/not-found/index.tsx @@ -3,15 +3,16 @@ import { useRouter } from "next/router"; import { useEffect } from "react"; import { CURR_THEME } from "env"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { BackButton } from "lib/components/button"; import PageContainer from "lib/components/PageContainer"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; const NotFoundPage = () => { + const { track } = useTrack(); const router = useRouter(); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_NOT_FOUND); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_NOT_FOUND); + }, [router.isReady, track]); return ( diff --git a/src/lib/pages/past-txs/index.tsx b/src/lib/pages/past-txs/index.tsx index f9373b92d..2712e8540 100644 --- a/src/lib/pages/past-txs/index.tsx +++ b/src/lib/pages/past-txs/index.tsx @@ -10,6 +10,7 @@ import type { ChangeEvent } from "react"; import { useEffect, useMemo } from "react"; import { useForm } from "react-hook-form"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useCurrentChain } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import PageContainer from "lib/components/PageContainer"; @@ -21,7 +22,6 @@ import { TxFilterSelection } from "lib/components/TxFilterSelection"; import { TxRelationSelection } from "lib/components/TxRelationSelection"; import { DEFAULT_TX_FILTERS } from "lib/data"; import { useAccountId } from "lib/services/accountService"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; import { useTxsByAddressPagination, useTxsCountByAddress, @@ -35,6 +35,7 @@ interface PastTxsState { } const PastTxs = () => { + const { track } = useTrack(); const router = useRouter(); const { address, @@ -120,8 +121,8 @@ const PastTxs = () => { }, [pastTxsState]); useEffect(() => { - if (router.isReady) AmpTrack(AmpEvent.TO_PAST_TXS); - }, [router.isReady]); + if (router.isReady) track(AmpEvent.TO_PAST_TXS); + }, [router.isReady, track]); useEffect(() => { setPageSize(10); diff --git a/src/lib/pages/pools/components/FilterByPoolType.tsx b/src/lib/pages/pools/components/FilterByPoolType.tsx index 236967b21..0053e9dd1 100644 --- a/src/lib/pages/pools/components/FilterByPoolType.tsx +++ b/src/lib/pages/pools/components/FilterByPoolType.tsx @@ -6,8 +6,8 @@ import { STABLESWAP_ICON, CLP_ICON, } from "../constant"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { SelectInput } from "lib/components/forms"; -import { AmpEvent, AmpTrackUseFilter } from "lib/services/amplitude"; import type { PoolTypeFilter } from "lib/types"; import { PoolType } from "lib/types"; @@ -60,17 +60,20 @@ export const FilterByPoolType = ({ setPoolTypeValue, initialSelected, labelBgColor = "background.main", -}: FilterByPoolTypeProps) => ( - - - formLabel="Filter by Pool Type" - options={options} - onChange={(newVal) => { - AmpTrackUseFilter(AmpEvent.USE_FILTER_POOL_TYPE, [newVal], newVal); - setPoolTypeValue(newVal); - }} - initialSelected={initialSelected} - labelBgColor={labelBgColor} - /> - -); +}: FilterByPoolTypeProps) => { + const { trackUseFilter } = useTrack(); + return ( + + + formLabel="Filter by Pool Type" + options={options} + onChange={(newVal) => { + trackUseFilter(AmpEvent.USE_FILTER_POOL_TYPE, [newVal], newVal); + setPoolTypeValue(newVal); + }} + initialSelected={initialSelected} + labelBgColor={labelBgColor} + /> + + ); +}; diff --git a/src/lib/pages/pools/components/pool-details/JsonModalButton.tsx b/src/lib/pages/pools/components/pool-details/JsonModalButton.tsx index ae9fe4829..16e720ab2 100644 --- a/src/lib/pages/pools/components/pool-details/JsonModalButton.tsx +++ b/src/lib/pages/pools/components/pool-details/JsonModalButton.tsx @@ -11,9 +11,9 @@ import { Box, } from "@chakra-ui/react"; +import { useTrack } from "lib/amplitude"; import { CustomIcon } from "lib/components/icon"; import JsonReadOnly from "lib/components/json/JsonReadOnly"; -import { AmpTrackViewJson } from "lib/services/amplitude"; import { jsonPrettify } from "lib/utils"; interface JsonModalButtonProps { @@ -26,6 +26,7 @@ export const JsonModalButton = ({ modalHeader, }: JsonModalButtonProps) => { const { isOpen, onOpen, onClose } = useDisclosure(); + const { trackUseViewJSON } = useTrack(); return ( <> - - ) : ( - - - {item.category} - - - - )} - {item.category === "Your Account" ? ( - - ) : ( - - - {item.subSection && ( + return ( + + + + + {menuInfo.category} + + + + + + {menuInfo.subSection && ( + + {menuInfo.subSection.map((subitem) => ( +
- {item.subSection.map((subitem) => ( -
- - {subitem.category} - - {subitem.submenu.map((submenu) => - submenu.isDisable ? ( - -
- -
-
- ) : ( - track(AmpEvent.USE_SIDEBAR)} - > - - - ) - )} -
- ))} + {subitem.category}
- )} - - )} - - + {subitem.submenu.map((submenu) => + submenu.isDisable ? ( + +
+ +
+
+ ) : ( + track(AmpEvent.USE_SIDEBAR)} + > + + + ) + )} +
+ ))} +
+ )} +
+
+
+ ); +}; + +export const ExpandNavMenu = ({ + navMenu, + isCurrentPage, + setIsExpand, +}: NavMenuProps) => { + const yourAccountMenu = navMenu[0]; + const restNavMenu = navMenu.slice(1); + + return ( + + + + {yourAccountMenu.category} + + + + + + + {restNavMenu.map((item) => ( + ))} ); diff --git a/src/lib/layout/navbar/index.tsx b/src/lib/layout/navbar/index.tsx index 028d714bb..79178471a 100644 --- a/src/lib/layout/navbar/index.tsx +++ b/src/lib/layout/navbar/index.tsx @@ -8,7 +8,7 @@ import { useWasmConfig, } from "lib/app-provider"; import type { IconKeys } from "lib/components/icon"; -import { INSTANTIATED_LIST_NAME, SAVED_LIST_NAME } from "lib/data"; +import { INSTANTIATED_LIST_NAME, SAVED_LIST_NAME, StorageKeys } from "lib/data"; import { useIsCurrentPage } from "lib/hooks"; import { usePublicProjectStore } from "lib/providers/store"; import { formatSlugName, getListIcon } from "lib/utils"; @@ -33,6 +33,7 @@ const Navbar = observer(({ isExpand, setIsExpand }: NavbarProps) => { const navMenu: MenuInfo[] = [ { category: "Your Account", + slug: "your-account", submenu: [ { name: "Past Transactions", @@ -53,6 +54,7 @@ const Navbar = observer(({ isExpand, setIsExpand }: NavbarProps) => { ? [ { category: "Public Projects", + slug: StorageKeys.PublicProjects, submenu: [ ...getSavedPublicProjects().map((list) => ({ name: list.name, @@ -72,6 +74,7 @@ const Navbar = observer(({ isExpand, setIsExpand }: NavbarProps) => { ? [ { category: "Developer Tools", + slug: StorageKeys.DeveloperTools, submenu: [ { name: "Deploy Contract", diff --git a/src/lib/layout/navbar/type.ts b/src/lib/layout/navbar/type.ts index 52a4e0681..abeb65c4c 100644 --- a/src/lib/layout/navbar/type.ts +++ b/src/lib/layout/navbar/type.ts @@ -16,6 +16,7 @@ export interface SubSection { export interface MenuInfo { category: string; + slug: string; submenu: SubmenuInfo[]; subSection?: SubSection[]; } diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 3cadf1100..a9ba93cfc 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -31,3 +31,4 @@ export * from "./window"; export * from "./number"; export * from "./math"; export * from "./sha256"; +export * from "./storage"; diff --git a/src/lib/utils/storage.ts b/src/lib/utils/storage.ts new file mode 100644 index 000000000..9174e4d6b --- /dev/null +++ b/src/lib/utils/storage.ts @@ -0,0 +1,24 @@ +export const getStorageKey = (key: string): string => `celatone-${key}`; + +export const setItem = (key: string, value: T): void => { + try { + window.localStorage.setItem(getStorageKey(key), JSON.stringify(value)); + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error setting localStorage item:", error); + } +}; + +export const getItem = (key: string, defaultValue: T): T => { + try { + const value = window.localStorage.getItem(getStorageKey(key)); + if (value) { + return JSON.parse(value) as T; + } + return defaultValue; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error getting localStorage item:", error); + return defaultValue; + } +}; From 93d4584b30c760bde8543450dce935caf17adec4 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Sep 2023 11:13:37 +0700 Subject: [PATCH 11/15] fix: rename properties --- src/lib/amplitude/track-event/useMandatoryProperties.ts | 5 +++-- src/lib/amplitude/useAmplitudeInit.ts | 7 ++++--- src/lib/app-provider/contexts/nav.tsx | 2 +- src/lib/data/constant.ts | 6 +++--- src/lib/layout/navbar/Expand.tsx | 2 +- src/lib/layout/navbar/index.tsx | 4 ++-- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/lib/amplitude/track-event/useMandatoryProperties.ts b/src/lib/amplitude/track-event/useMandatoryProperties.ts index 35252cef5..8a8e2e907 100644 --- a/src/lib/amplitude/track-event/useMandatoryProperties.ts +++ b/src/lib/amplitude/track-event/useMandatoryProperties.ts @@ -26,8 +26,9 @@ export const useMandatoryProperties = () => { rawAddressHash, chain: currentChainId, mobile: isMobile, - navbar: getItem(StorageKeys.Navbar, ""), - developerTools: getItem(StorageKeys.DeveloperTools, ""), + navSidebar: getItem(StorageKeys.NavSidebar, ""), + devSidebar: getItem(StorageKeys.DevSidebar, ""), + projectSidebar: getItem(StorageKeys.ProjectSidebar, ""), }), [currentChainId, isMobile, prevPathname, router.pathname, rawAddressHash] ); diff --git a/src/lib/amplitude/useAmplitudeInit.ts b/src/lib/amplitude/useAmplitudeInit.ts index a502d1163..8d9ad0f5a 100644 --- a/src/lib/amplitude/useAmplitudeInit.ts +++ b/src/lib/amplitude/useAmplitudeInit.ts @@ -46,10 +46,11 @@ export const useAmplitudeInit = () => { const identifyEvent = new Identify(); identifyEvent.set("Wallets", wallets); identifyEvent.set("Wallets Count", wallets.length); - identifyEvent.set("Navbar", getItem(StorageKeys.Navbar, "")); + identifyEvent.set("Nav Sidebar", getItem(StorageKeys.NavSidebar, "")); + identifyEvent.set("Dev Sidebar", getItem(StorageKeys.DevSidebar, "")); identifyEvent.set( - "Developer Tools", - getItem(StorageKeys.DeveloperTools, "") + "Project Sidebar", + getItem(StorageKeys.ProjectSidebar, "") ); identifyEvent.set("Networks", networks); identifyEvent.set("Networks Count", networks.length); diff --git a/src/lib/app-provider/contexts/nav.tsx b/src/lib/app-provider/contexts/nav.tsx index 5af276848..1a42305f4 100644 --- a/src/lib/app-provider/contexts/nav.tsx +++ b/src/lib/app-provider/contexts/nav.tsx @@ -17,7 +17,7 @@ const NavContext = createContext({ }); export const NavProvider = ({ children }: { children: ReactNode }) => { - const [isExpand, setIsExpand] = useLocalStorage(StorageKeys.Navbar, false); + const [isExpand, setIsExpand] = useLocalStorage(StorageKeys.NavSidebar, true); const prevPathname = usePreviousPathname(); const states = useMemo( diff --git a/src/lib/data/constant.ts b/src/lib/data/constant.ts index 5c919b09a..047ba327d 100644 --- a/src/lib/data/constant.ts +++ b/src/lib/data/constant.ts @@ -46,9 +46,9 @@ export const DEFAULT_TX_FILTERS = { export const UPPERBOUND_COUNT = 10000; export enum StorageKeys { - Navbar = "navbar", - DeveloperTools = "developer-tools", + NavSidebar = "nav-sidebar", + DevSidebar = "dev-sidebar", + ProjectSidebar = "project-sidebar", Wallets = "wallets", Networks = "networks", - PublicProjects = "public-projects", } diff --git a/src/lib/layout/navbar/Expand.tsx b/src/lib/layout/navbar/Expand.tsx index 8e0afb887..7d45bd0d5 100644 --- a/src/lib/layout/navbar/Expand.tsx +++ b/src/lib/layout/navbar/Expand.tsx @@ -105,7 +105,7 @@ interface NavbarRenderProps { const NavbarRender = ({ menuInfo, isCurrentPage }: NavbarRenderProps) => { const { track } = useTrack(); - const [isExpand, setIsExpand] = useLocalStorage(menuInfo.slug, false); + const [isExpand, setIsExpand] = useLocalStorage(menuInfo.slug, true); const defaultIndex = isExpand ? [0] : []; const handleChange = (index: number[]) => { diff --git a/src/lib/layout/navbar/index.tsx b/src/lib/layout/navbar/index.tsx index 79178471a..6b5722a6a 100644 --- a/src/lib/layout/navbar/index.tsx +++ b/src/lib/layout/navbar/index.tsx @@ -54,7 +54,7 @@ const Navbar = observer(({ isExpand, setIsExpand }: NavbarProps) => { ? [ { category: "Public Projects", - slug: StorageKeys.PublicProjects, + slug: StorageKeys.ProjectSidebar, submenu: [ ...getSavedPublicProjects().map((list) => ({ name: list.name, @@ -74,7 +74,7 @@ const Navbar = observer(({ isExpand, setIsExpand }: NavbarProps) => { ? [ { category: "Developer Tools", - slug: StorageKeys.DeveloperTools, + slug: StorageKeys.DevSidebar, submenu: [ { name: "Deploy Contract", From 85c7410092b08573054158c8c92e5e9129e5b3df Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Sep 2023 11:37:05 +0700 Subject: [PATCH 12/15] feat: add USE_TO_YOUR_ACCOUNT --- src/lib/amplitude/types.ts | 2 +- src/lib/layout/navbar/Expand.tsx | 5 +- src/lib/layout/navbar/index.tsx | 238 ++++++++++++++++--------------- src/lib/layout/navbar/type.ts | 1 + 4 files changed, 132 insertions(+), 114 deletions(-) diff --git a/src/lib/amplitude/types.ts b/src/lib/amplitude/types.ts index 386f09bdc..7037c8c04 100644 --- a/src/lib/amplitude/types.ts +++ b/src/lib/amplitude/types.ts @@ -44,7 +44,6 @@ export enum AmpEvent { TO_ALL_LISTS = "To All Lists", TO_ALL_PROJECTS = "To All Public Projects", TO_ACCOUNT_DETAIL = "To Account Detail", - TO_YOUR_ACCOUNT = "To Your Account", TO_CONTRACT_DETAIL = "To Contract Detail", TO_CODE_DETAIL = "To Code Detail", TO_PROJECT_DETAIL = "To Public Project Detail", @@ -125,6 +124,7 @@ export enum AmpEvent { USE_TOGGLE = "Use Toggle", USE_SCHEMA_TOGGLE = "Use Schema Toggle", USE_JSON_QUERY_AGAIN = "Use Json Query Again", + USE_TO_YOUR_ACCOUNT = "Use To Your Account", // TX TX_SUCCEED = "Tx Succeed", TX_FAILED = "Tx Failed", diff --git a/src/lib/layout/navbar/Expand.tsx b/src/lib/layout/navbar/Expand.tsx index 7d45bd0d5..e9e38f267 100644 --- a/src/lib/layout/navbar/Expand.tsx +++ b/src/lib/layout/navbar/Expand.tsx @@ -88,7 +88,10 @@ const SubMenuRender = ({ isCurrentPage, submenu }: SubMenuProps) => { track(AmpEvent.USE_SIDEBAR)} + onClick={() => { + track(AmpEvent.USE_SIDEBAR); + subitem.trackEvent?.(); + }} > diff --git a/src/lib/layout/navbar/index.tsx b/src/lib/layout/navbar/index.tsx index 6b5722a6a..c9ad548a2 100644 --- a/src/lib/layout/navbar/index.tsx +++ b/src/lib/layout/navbar/index.tsx @@ -1,7 +1,8 @@ import { Flex } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; -import type { Dispatch, SetStateAction } from "react"; +import { useMemo, type Dispatch, type SetStateAction } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { usePublicProjectConfig, useCurrentChain, @@ -27,124 +28,137 @@ const Navbar = observer(({ isExpand, setIsExpand }: NavbarProps) => { const publicProject = usePublicProjectConfig({ shouldRedirect: false }); const isCurrentPage = useIsCurrentPage(); const wasm = useWasmConfig({ shouldRedirect: false }); + const { track } = useTrack(); const { address } = useCurrentChain(); - const navMenu: MenuInfo[] = [ - { - category: "Your Account", - slug: "your-account", - submenu: [ - { - name: "Past Transactions", - slug: "/past-txs", - icon: "history" as IconKeys, - }, - { - name: "Your Account Details", - slug: `/accounts/${address}`, - icon: "admin" as IconKeys, - isDisable: !address, - tooltipText: - "You need to connect wallet to view your account details.", - }, - ], - }, - ...(publicProject.enabled - ? [ + const navMenu: MenuInfo[] = useMemo( + () => [ + { + category: "Your Account", + slug: "your-account", + submenu: [ { - category: "Public Projects", - slug: StorageKeys.ProjectSidebar, - submenu: [ - ...getSavedPublicProjects().map((list) => ({ - name: list.name, - slug: `/projects/${list.slug}`, - logo: list.logo as IconKeys, - })), - { - name: "View All Projects", - slug: "/projects", - icon: "public-project" as IconKeys, - }, - ], + name: "Past Transactions", + slug: "/past-txs", + icon: "history" as IconKeys, }, - ] - : []), - ...(wasm.enabled - ? [ { - category: "Developer Tools", - slug: StorageKeys.DevSidebar, - submenu: [ - { - name: "Deploy Contract", - slug: "/deploy", - icon: "add-new" as IconKeys, - }, - { - name: "Query", - slug: "/query", - icon: "query" as IconKeys, - }, - { - name: "Execute", - slug: "/execute", - icon: "execute" as IconKeys, - }, - { - name: "Migrate", - slug: "/migrate", - icon: "migrate" as IconKeys, - }, - // { - // name: "Recent Activities", - // slug: "/", - // icon: "list" as IconKeys, - // }, - ], - subSection: [ - { - category: "This Wallet", - submenu: [ - { - name: "My Stored Codes", - slug: "/stored-codes", - icon: "code" as IconKeys, - }, - { - name: INSTANTIATED_LIST_NAME, - slug: `/contract-lists/${formatSlugName( - INSTANTIATED_LIST_NAME - )}`, - icon: getListIcon(INSTANTIATED_LIST_NAME), - }, - ], - }, - { - category: "This Device", - submenu: [ - { - name: "Saved Codes", - slug: "/saved-codes", - icon: "code" as IconKeys, - }, - { - name: SAVED_LIST_NAME, - slug: `/contract-lists/${formatSlugName(SAVED_LIST_NAME)}`, - icon: "contract-address" as IconKeys, - }, - { - name: "View All Contract List", - slug: "/contract-lists", - icon: "more" as IconKeys, - }, - ], - }, - ], + name: "Your Account Details", + slug: `/accounts/${address}`, + icon: "admin" as IconKeys, + isDisable: !address, + tooltipText: + "You need to connect wallet to view your account details.", + trackEvent: () => track(AmpEvent.USE_TO_YOUR_ACCOUNT), }, - ] - : []), - ]; + ], + }, + ...(publicProject.enabled + ? [ + { + category: "Public Projects", + slug: StorageKeys.ProjectSidebar, + submenu: [ + ...getSavedPublicProjects().map((list) => ({ + name: list.name, + slug: `/projects/${list.slug}`, + logo: list.logo as IconKeys, + })), + { + name: "View All Projects", + slug: "/projects", + icon: "public-project" as IconKeys, + }, + ], + }, + ] + : []), + ...(wasm.enabled + ? [ + { + category: "Developer Tools", + slug: StorageKeys.DevSidebar, + submenu: [ + { + name: "Deploy Contract", + slug: "/deploy", + icon: "add-new" as IconKeys, + }, + { + name: "Query", + slug: "/query", + icon: "query" as IconKeys, + }, + { + name: "Execute", + slug: "/execute", + icon: "execute" as IconKeys, + }, + { + name: "Migrate", + slug: "/migrate", + icon: "migrate" as IconKeys, + }, + // { + // name: "Recent Activities", + // slug: "/", + // icon: "list" as IconKeys, + // }, + ], + subSection: [ + { + category: "This Wallet", + submenu: [ + { + name: "My Stored Codes", + slug: "/stored-codes", + icon: "code" as IconKeys, + }, + { + name: INSTANTIATED_LIST_NAME, + slug: `/contract-lists/${formatSlugName( + INSTANTIATED_LIST_NAME + )}`, + icon: getListIcon(INSTANTIATED_LIST_NAME), + }, + ], + }, + { + category: "This Device", + submenu: [ + { + name: "Saved Codes", + slug: "/saved-codes", + icon: "code" as IconKeys, + }, + { + name: SAVED_LIST_NAME, + slug: `/contract-lists/${formatSlugName( + SAVED_LIST_NAME + )}`, + icon: "contract-address" as IconKeys, + }, + { + name: "View All Contract List", + slug: "/contract-lists", + icon: "more" as IconKeys, + }, + ], + }, + ], + }, + ] + : []), + ], + [ + address, + getSavedPublicProjects, + publicProject.enabled, + track, + wasm.enabled, + ] + ); return ( diff --git a/src/lib/layout/navbar/type.ts b/src/lib/layout/navbar/type.ts index abeb65c4c..8f01065ae 100644 --- a/src/lib/layout/navbar/type.ts +++ b/src/lib/layout/navbar/type.ts @@ -7,6 +7,7 @@ export interface SubmenuInfo { logo?: string; isDisable?: boolean; tooltipText?: string; + trackEvent?: () => void; } export interface SubSection { From d52c4f86c32c711e8d5ac79ecaf81e5957e573f3 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Sep 2023 11:44:00 +0700 Subject: [PATCH 13/15] fix: prevent track when tab is undefined --- src/lib/pages/account-details/index.tsx | 5 ++--- src/lib/pages/public-project/slug.tsx | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/pages/account-details/index.tsx b/src/lib/pages/account-details/index.tsx index 67dd6cb4c..0bdd77312 100644 --- a/src/lib/pages/account-details/index.tsx +++ b/src/lib/pages/account-details/index.tsx @@ -338,12 +338,11 @@ const AccountDetails = () => { const accountAddressParam = getFirstQueryParam( router.query.accountAddress ).toLowerCase() as HumanAddr; + // TODO: fix assertion later const tab = getFirstQueryParam(router.query.tab) as TabIndex; useEffect(() => { - if (router.isReady) - // TODO - track(AmpEvent.TO_ACCOUNT_DETAIL, { ...(tab && { tab }) }); + if (router.isReady && tab) track(AmpEvent.TO_ACCOUNT_DETAIL, { tab }); }, [router.isReady, tab, track]); if (!router.isReady) return ; diff --git a/src/lib/pages/public-project/slug.tsx b/src/lib/pages/public-project/slug.tsx index 1ae15c1a6..b8cb5dbab 100644 --- a/src/lib/pages/public-project/slug.tsx +++ b/src/lib/pages/public-project/slug.tsx @@ -60,6 +60,8 @@ const ProjectDetail = () => { useEffect(() => { if (router.isReady) { + if (tab) track(AmpEvent.TO_PROJECT_DETAIL, { tab }); + if (!tab || !Object.values(TabIndex).includes(tab)) { navigate({ pathname: "/projects/[slug]/[tab]", @@ -72,7 +74,6 @@ const ProjectDetail = () => { }, }); } - track(AmpEvent.TO_PROJECT_DETAIL, { ...(tab && { tab }) }); } }, [router.isReady, tab, slug, navigate, track]); From 42b408bd1cebf9a684439cbb8edb0f4f91c515bc Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Sep 2023 13:07:43 +0700 Subject: [PATCH 14/15] feat: add view, reattach --- .../json-schema/AttachSchemaCard.tsx | 21 +++++++++++++++---- .../section/SchemaInputSection.tsx | 12 +++++++++-- .../json-schema/upload/UploadSchema.tsx | 2 +- .../json-schema/view/ViewSchemaModal.tsx | 6 +++--- .../components/code-info/CodeInfoSection.tsx | 16 +++++++++++++- 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/lib/components/json-schema/AttachSchemaCard.tsx b/src/lib/components/json-schema/AttachSchemaCard.tsx index 6510f91bb..b14949d35 100644 --- a/src/lib/components/json-schema/AttachSchemaCard.tsx +++ b/src/lib/components/json-schema/AttachSchemaCard.tsx @@ -1,5 +1,7 @@ import { Button, Flex, IconButton, Text } from "@chakra-ui/react"; +import { useCallback } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { CustomIcon } from "lib/components/icon"; import { useSchemaStore } from "lib/providers/store"; import type { CodeSchema } from "lib/stores/schema"; @@ -12,7 +14,7 @@ interface AttachSchemaCardProps { codeId: string; codeHash: string; schema: Option; - openDrawer: () => void; + openModal: () => void; } export const AttachSchemaCard = ({ @@ -20,9 +22,20 @@ export const AttachSchemaCard = ({ codeId, codeHash, schema, - openDrawer, + openModal, }: AttachSchemaCardProps) => { const { deleteSchema } = useSchemaStore(); + const { track } = useTrack(); + + const handleAttach = useCallback(() => { + openModal(); + track(AmpEvent.USE_ATTACHED_JSON_MODAL); + }, [track, openModal]); + + const handleReattach = useCallback(() => { + openModal(); + track(AmpEvent.USE_EDIT_ATTACHED_JSON); + }, [track, openModal]); return ( Attach JSON Schema - @@ -55,7 +68,7 @@ export const AttachSchemaCard = ({ - { const { isOpen, onClose, onOpen } = useDisclosure(); + const { track } = useTrack(); const msgSchema = jsonSchema?.[type]; const prettyType = capitalize(type); + const handleReattach = useCallback(() => { + onOpen(); + track(AmpEvent.USE_EDIT_ATTACHED_JSON); + }, [onOpen, track]); + return ( <> )} @@ -122,7 +130,7 @@ export const SchemaInputSection = observer( - diff --git a/src/lib/components/json-schema/upload/UploadSchema.tsx b/src/lib/components/json-schema/upload/UploadSchema.tsx index 179d7d5ba..6fcd80af9 100644 --- a/src/lib/components/json-schema/upload/UploadSchema.tsx +++ b/src/lib/components/json-schema/upload/UploadSchema.tsx @@ -26,7 +26,7 @@ export const UploadSchema = ({ codeId={codeId} codeHash={codeHash} schema={schema} - openDrawer={onOpen} + openModal={onOpen} /> { + const handleView = useCallback(() => { onOpen(); track(AmpEvent.USE_VIEW_ATTACHED_JSON, { tab: ALL_TABS[0] }); }, [onOpen, track]); @@ -93,14 +93,14 @@ export const ViewSchemaModal = ({ } aria-label="view schema" /> ) : ( - )} diff --git a/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx b/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx index 984b00337..2b6adbe59 100644 --- a/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx +++ b/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx @@ -8,7 +8,9 @@ import { useDisclosure, Spinner, } from "@chakra-ui/react"; +import { useCallback } from "react"; +import { AmpEvent, useTrack } from "lib/amplitude"; import { useGetAddressType, useMobile } from "lib/app-provider"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; @@ -132,6 +134,18 @@ export const CodeInfoSection = ({ ); const uploaderType = getAddressType(uploader); const isMobile = useMobile(); + const { track } = useTrack(); + + const handleView = useCallback(() => { + toJsonSchemaTab(); + track(AmpEvent.USE_VIEW_ATTACHED_JSON); + }, [toJsonSchemaTab, track]); + + const handleAttach = useCallback(() => { + onOpen(); + track(AmpEvent.USE_ATTACHED_JSON_MODAL); + }, [onOpen, track]); + return ( @@ -195,7 +209,7 @@ export const CodeInfoSection = ({ ) } - onClick={attached ? toJsonSchemaTab : onOpen} + onClick={attached ? handleView : handleAttach} > {attached ? "View Schema" : "Attach"} From 8ff104e2b6bf211c50ecf13483f6f46825c9a95a Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Sep 2023 13:46:22 +0700 Subject: [PATCH 15/15] fix: add remove json modal --- .../json-schema/AttachSchemaCard.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/lib/components/json-schema/AttachSchemaCard.tsx b/src/lib/components/json-schema/AttachSchemaCard.tsx index b14949d35..8d0dc0863 100644 --- a/src/lib/components/json-schema/AttachSchemaCard.tsx +++ b/src/lib/components/json-schema/AttachSchemaCard.tsx @@ -1,9 +1,9 @@ import { Button, Flex, IconButton, Text } from "@chakra-ui/react"; import { useCallback } from "react"; +import { RemoveSchemaModal } from "../modal/RemoveSchemaModal"; import { AmpEvent, useTrack } from "lib/amplitude"; import { CustomIcon } from "lib/components/icon"; -import { useSchemaStore } from "lib/providers/store"; import type { CodeSchema } from "lib/stores/schema"; import type { Option } from "lib/types"; @@ -24,7 +24,6 @@ export const AttachSchemaCard = ({ schema, openModal, }: AttachSchemaCardProps) => { - const { deleteSchema } = useSchemaStore(); const { track } = useTrack(); const handleAttach = useCallback(() => { @@ -71,12 +70,18 @@ export const AttachSchemaCard = ({ - deleteSchema(codeHash)} - icon={} + } + aria-label="delete schema" + /> + } />