diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f7d614c..95beeeee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#321](https://github.com/alleslabs/celatone-frontend/pull/321) Add amplitude to proposal to store code page - [#274](https://github.com/alleslabs/celatone-frontend/pull/274) Add proposal to store code page - [#279](https://github.com/alleslabs/celatone-frontend/pull/279) Add instantiate permission to msg store code, change error display design, and upgrade cosmjs to version 0.30.1 diff --git a/src/lib/components/ConnectWalletAlert.tsx b/src/lib/components/ConnectWalletAlert.tsx index a52ac6d89..eaee57c51 100644 --- a/src/lib/components/ConnectWalletAlert.tsx +++ b/src/lib/components/ConnectWalletAlert.tsx @@ -11,24 +11,26 @@ import type { AlertProps } from "@chakra-ui/react"; import { useWallet } from "@cosmos-kit/react"; import type { MouseEventHandler } from "react"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; +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 } = useWallet(); const onClickConnect: MouseEventHandler = async (e) => { - AmpTrack(AmpEvent.USE_CLICK_WALLET); + AmpTrackUseClickWallet(page, "alert"); e.preventDefault(); await connect(); }; diff --git a/src/lib/components/StickySidebar.tsx b/src/lib/components/StickySidebar.tsx index 033efdb44..7af79ba7a 100644 --- a/src/lib/components/StickySidebar.tsx +++ b/src/lib/components/StickySidebar.tsx @@ -17,10 +17,12 @@ import { } from "lib/app-provider"; import type { Network } from "lib/data"; import { getChainNameByNetwork } from "lib/data"; +import { AmpTrackUseRightHelperPanel } from "lib/services/amplitude"; import { CustomIcon } from "./icon"; export interface SidebarMetadata { + page: string; title: string; description: React.ReactElement; toNetwork?: boolean; @@ -70,8 +72,15 @@ export const StickySidebar = ({ const selectChain = useSelectChain(); const { isMainnet } = useCurrentNetwork(); const { network } = useCurrentNetwork(); - const { title, description, toNetwork, toPagePath, toPageTitle, toPage } = - metadata[network]; + const { + title, + description, + toNetwork, + toPagePath, + toPageTitle, + toPage, + page, + } = metadata[network]; const hasAction = toPage; return ( @@ -105,17 +114,21 @@ export const StickySidebar = ({ {toNetwork && ( + onClick={() => { + AmpTrackUseRightHelperPanel(page, "change-network"); selectChain( getChainNameByNetwork(isMainnet ? "testnet" : "mainnet") - ) - } + ); + }} title={isMainnet ? "Switch To Testnet" : "Switch To Mainnet"} /> )} {toPage && toPagePath && toPageTitle && ( navigate({ pathname: toPagePath })} + onClick={() => { + AmpTrackUseRightHelperPanel(page, `to-${toPagePath}`); + navigate({ pathname: toPagePath }); + }} title={toPageTitle} /> )} diff --git a/src/lib/components/upload/InstantiatePermissionRadio.tsx b/src/lib/components/upload/InstantiatePermissionRadio.tsx index b7ddbd780..17d37b68b 100644 --- a/src/lib/components/upload/InstantiatePermissionRadio.tsx +++ b/src/lib/components/upload/InstantiatePermissionRadio.tsx @@ -1,12 +1,17 @@ import { Text, Box, Radio, RadioGroup, Button, Flex } from "@chakra-ui/react"; import { useWallet } from "@cosmos-kit/react"; +import { useEffect } from "react"; import type { Control, UseFormSetValue, UseFormTrigger } from "react-hook-form"; import { useController, useFieldArray, useWatch } from "react-hook-form"; import { AddressInput } from "../AddressInput"; import { AssignMe } from "../AssignMe"; import { CustomIcon } from "lib/components/icon"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; +import { + AmpEvent, + AmpTrack, + AmpTrackUseInstantiatePermission, +} from "lib/services/amplitude"; import type { Addr, UploadSectionState } from "lib/types"; import { AccessType } from "lib/types"; @@ -14,6 +19,7 @@ interface InstantiatePermissionRadioProps { control: Control; setValue: UseFormSetValue; trigger: UseFormTrigger; + page?: string; } interface PermissionRadioProps { @@ -32,6 +38,7 @@ export const InstantiatePermissionRadio = ({ control, setValue, trigger, + page, }: InstantiatePermissionRadioProps) => { const { address: walletAddress } = useWallet(); @@ -52,6 +59,19 @@ export const InstantiatePermissionRadio = ({ name: "addresses", }); + useEffect(() => { + const emptyAddressesLength = addresses.filter( + (addr) => addr.address.trim().length === 0 + ).length; + if (page) + AmpTrackUseInstantiatePermission( + page, + permission, + emptyAddressesLength, + addresses.length - emptyAddressesLength + ); + }, [addresses, page, permission]); + return ( @@ -24,6 +28,7 @@ export const SIDEBAR_WHITELIST_DETAILS: SidebarDetails = { toPageTitle: "Submit Proposal To Store Code", }, testnet: { + page: whitelistPage, title: "Do I need to open proposal to whitelisting in testnet?", description: ( @@ -40,13 +45,16 @@ export const SIDEBAR_WHITELIST_DETAILS: SidebarDetails = { }, // TODO: fill localnet information localnet: { + page: whitelistPage, title: "", description: , }, }; +const storeCodePage = "proposal-store-code"; export const SIDEBAR_STORE_CODE_DETAILS: SidebarDetails = { mainnet: { + page: storeCodePage, title: "Why do I need to submit proposal?", description: ( @@ -64,14 +72,22 @@ export const SIDEBAR_STORE_CODE_DETAILS: SidebarDetails = { toNetwork: true, }, testnet: { + page: storeCodePage, title: "Do I need to submit Proposal to store code in testnet?", description: ( On the Osmosis testnet, you can store code without submitting a proposal by using - + + AmpTrackUseRightHelperPanel(storeCodePage, "to-/deploy") + } + > {" "} - Deploy Contract + + Deploy Contract +

@@ -89,6 +105,7 @@ export const SIDEBAR_STORE_CODE_DETAILS: SidebarDetails = { }, // TODO: fill localnet information localnet: { + page: storeCodePage, title: "", description: , }, diff --git a/src/lib/pages/proposal/store-code/index.tsx b/src/lib/pages/proposal/store-code/index.tsx index 6957e3e65..cb1eba6cb 100644 --- a/src/lib/pages/proposal/store-code/index.tsx +++ b/src/lib/pages/proposal/store-code/index.tsx @@ -12,6 +12,7 @@ import { import { Sha256 } from "@cosmjs/crypto"; import type { Coin, StdFee } from "@cosmjs/stargate"; import { useWallet } from "@cosmos-kit/react"; +import { useRouter } from "next/router"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; @@ -46,7 +47,13 @@ import { MAX_PROPOSAL_TITLE_LENGTH, } from "lib/data"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; -import { AmpEvent, AmpTrack } from "lib/services/amplitude"; +import { + AmpEvent, + AmpTrack, + AmpTrackUseDepositFill, + AmpTrackUseSubmitProposal, + AmpTrackUseUnpin, +} from "lib/services/amplitude"; import { useGovParams } from "lib/services/proposalService"; import type { Addr, @@ -79,7 +86,8 @@ const defaultValues: StoreCodeProposalState = { codeHash: "", }; -// TODO - add amp track +const page = "proposal-store-code"; + const StoreCodeProposal = () => { const { appHumanAddress: { example: exampleHumanAddress }, @@ -137,6 +145,12 @@ const StoreCodeProposal = () => { } = watch(); const { wasmFile, permission, addresses } = uploadSectionWatch(); + // Amp + const router = useRouter(); + useEffect(() => { + if (router.isReady) AmpTrack(AmpEvent.TO_PROPOSAL_TO_STORE_CODE); + }, [router.isReady]); + const { variant, description, icon } = getAlert( initialDeposit.amount, minDeposit?.formattedAmount, @@ -251,6 +265,14 @@ const StoreCodeProposal = () => { const proceed = useCallback(async () => { if (!wasmFile) return null; + + AmpTrackUseSubmitProposal(page, { + initialDeposit: initialDeposit.amount, + minDeposit: minDeposit?.formattedAmount, + addressesCount: addresses.length, + permission: AccessType[permission], + }); + const submitStoreCodeProposalMsg = async () => { return composeStoreCodeProposalMsg({ proposer: walletAddress as HumanAddr, @@ -437,7 +459,10 @@ const StoreCodeProposal = () => { {/* Unpin code */} setValue("unpinCode", e.target.checked)} + onChange={(e) => { + AmpTrackUseUnpin(page, e.target.checked); + setValue("unpinCode", e.target.checked); + }} > {PROPOSAL_STORE_CODE_TEXT.unpinLabel} @@ -501,6 +526,7 @@ const StoreCodeProposal = () => { control={uploadSectionControl} setValue={uploadSectionSetValue} trigger={uploadSectionTrigger} + page={page} /> {/* Deposit */} @@ -531,6 +557,10 @@ const StoreCodeProposal = () => { color="honeydew.main" onClick={() => { if (!minDeposit) return; + AmpTrackUseDepositFill( + page, + minDeposit.formattedAmount + ); setValue( "initialDeposit.amount", minDeposit.formattedAmount diff --git a/src/lib/services/amplitude.tsx b/src/lib/services/amplitude.tsx index abb845856..921e71c86 100644 --- a/src/lib/services/amplitude.tsx +++ b/src/lib/services/amplitude.tsx @@ -1,7 +1,8 @@ import { track } from "@amplitude/analytics-browser"; +import big from "big.js"; import type { AttachFundsType } from "lib/components/fund/types"; -import type { Option } from "lib/types"; +import type { Option, Token, AccessType } from "lib/types"; export enum AmpEvent { INVALID_STATE = "To Invalid State", @@ -54,6 +55,7 @@ export enum AmpEvent { TO_TRANSACTION_DETAIL = "To Transaction Detail", TO_NOT_FOUND = "To 404 Not Found", TO_FAUCET = "To Faucet", + TO_PROPOSAL_TO_STORE_CODE = "To Proposal To Store Code", // ACTIONS ACTION_UPLOAD = "Act Upload", ACTION_INSTANTIATE = "Action Instantiate", @@ -96,6 +98,11 @@ export enum AmpEvent { USE_UNSUPPORTED_ASSETS = "Use Unsupported Assets", USE_TX_MSG_EXPAND = "Use Transaction Message Expand", USE_EXPAND = "Use General Expand", + USE_RIGHT_HELPER_PANEL = "Use Right Helper Panel", // Sticky panel + USE_UNPIN = "Use Unpin", + USE_INSTANTIATE_PERMISSION = "Use Instantiate Permission", + USE_DEPOSIT_FILL = "Use Deposit Fill", + USE_SUBMIT_PROPOSAL = "Use Submit Proposal", // TX TX_SUCCEED = "Tx Succeed", TX_FAILED = "Tx Failed", @@ -131,7 +138,11 @@ type SpecialAmpEvent = | AmpEvent.USE_VIEW_JSON | AmpEvent.USE_UNSUPPORTED_ASSETS | AmpEvent.USE_COPIER - | AmpEvent.USE_EXPAND; + | AmpEvent.USE_EXPAND + | AmpEvent.USE_RIGHT_HELPER_PANEL + | AmpEvent.USE_UNPIN + | AmpEvent.USE_INSTANTIATE_PERMISSION + | AmpEvent.USE_DEPOSIT_FILL; export const AmpTrackInvalidState = (title: string) => track(AmpEvent.INVALID_STATE, { title }); @@ -199,3 +210,45 @@ export const AmpTrackExpand = ( component: "assets" | "json" | "permission_address" | "event_box", section: Option ) => track(AmpEvent.USE_EXPAND, { action, component, section }); + +export const AmpTrackUseClickWallet = (page?: string, component?: string) => + track(AmpEvent.USE_CLICK_WALLET, { page, component }); + +export const AmpTrackUseRightHelperPanel = (page: string, action: string) => + track(AmpEvent.USE_RIGHT_HELPER_PANEL, { page, action }); + +export const AmpTrackUseUnpin = (page: string, check: boolean) => + track(AmpEvent.USE_UNPIN, { page, check }); + +export const AmpTrackUseInstantiatePermission = ( + page: string, + type: AccessType, + emptyAddressesLength: number, + addressesLength: number +) => + track(AmpEvent.USE_INSTANTIATE_PERMISSION, { + page, + type, + emptyAddressesLength, + addressesLength, + }); + +export const AmpTrackUseDepositFill = (page: string, amount: Token) => + track(AmpEvent.USE_DEPOSIT_FILL, { page, amount }); + +export const AmpTrackUseSubmitProposal = ( + page: string, + properties: { + initialDeposit: string; + minDeposit: Option; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; + } +) => { + const proposalPeriod = big(properties.initialDeposit).lt( + properties.minDeposit || "0" + ) + ? "Deposit" + : "Voting"; + track(AmpEvent.USE_SUBMIT_PROPOSAL, { page, proposalPeriod, ...properties }); +};