Skip to content

Commit

Permalink
Israel.5490.governance mixpanel (#6604)
Browse files Browse the repository at this point in the history
* committing to change branches

* mixpanel metrics added to snapshot voting, and governance proposals and voiting

* added mixpanel events to the governance proposal votes

* made requested code changes

* changed import types and tested to confirm proposal call reached mixpanel dashboard
  • Loading branch information
Israellund authored Feb 21, 2024
1 parent 3c46b1f commit 5cf5fc4
Show file tree
Hide file tree
Showing 9 changed files with 399 additions and 316 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ import React, { useEffect, useState } from 'react';

import 'components/ProposalCard/ProposalCard.scss';
import AaveProposal from 'controllers/chain/ethereum/aave/proposal';
import { isNotNil } from 'helpers/typeGuards';
import { getProposalUrlPath } from 'identifiers';
import type { AnyProposal } from '../../../models/types';

import { useCommonNavigate } from 'navigation/helpers';
import app from 'state';
import {
useCosmosProposalMetadataQuery,
useCosmosProposalTallyQuery,
} from 'state/api/proposals';
import { slugify } from 'utils';
import { CWCard } from '../component_kit/cw_card';
import { CWDivider } from '../component_kit/cw_divider';
import { CWText } from '../component_kit/cw_text';
import { getPrimaryTagText, getStatusClass, getStatusText } from './helpers';
import { ProposalTag } from './ProposalTag';
import { useCommonNavigate } from 'navigation/helpers';
import {
useCosmosProposalTallyQuery,
useCosmosProposalMetadataQuery,
} from 'state/api/proposals';
import { getPrimaryTagText, getStatusClass, getStatusText } from './helpers';

type ProposalCardProps = {
injectedContent?: React.ReactNode;
Expand All @@ -30,7 +29,7 @@ export const ProposalCard = ({
}: ProposalCardProps) => {
const navigate = useCommonNavigate();
const [title, setTitle] = useState(
proposal.title || `Proposal ${proposal.identifier}`
proposal.title || `Proposal ${proposal.identifier}`,
);
const { data: metadata } = useCosmosProposalMetadataQuery(proposal);
const { isFetching: isFetchingTally } = useCosmosProposalTallyQuery(proposal);
Expand All @@ -48,15 +47,6 @@ export const ProposalCard = ({
}
}, [proposal]);

useEffect(() => {
if (proposal instanceof AaveProposal) {
proposal.ipfsDataReady.once('ready', () => {
// triggers render of shortDescription too
setTitle(proposal?.ipfsData.title);
});
}
}, [proposal]);

return (
<CWCard
elevation="elevation-2"
Expand All @@ -72,7 +62,7 @@ export const ProposalCard = ({
const path = getProposalUrlPath(
proposal.slug,
`${proposal.identifier}-${slugify(proposal.title)}`,
true
true,
);

navigate(path); // avoid resetting scroll point
Expand Down Expand Up @@ -102,7 +92,7 @@ export const ProposalCard = ({
fontWeight="medium"
className={`proposal-status-text ${getStatusClass(
proposal,
isFetchingTally
isFetchingTally,
)}`}
>
{getStatusText(proposal, isFetchingTally)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React, { useEffect, useState } from 'react';

import 'components/proposals/voting_actions.scss';
import { notifyError } from 'controllers/app/notifications';
import type CosmosAccount from 'controllers/chain/cosmos/account';
Expand All @@ -24,8 +22,11 @@ import {
NearSputnikVote,
NearSputnikVoteString,
} from 'controllers/chain/near/sputnik/types';
import React, { useEffect, useState } from 'react';
import type { AnyProposal } from '../../../models/types';
import { VotingType } from '../../../models/types';
import { MixpanelGovernanceEvents } from '/analytics/types';
import { useBrowserAnalyticsTrack } from '/hooks/useBrowserAnalyticsTrack';

import app from 'state';

Expand All @@ -51,6 +52,8 @@ export const VotingActions = (props: VotingActionsProps) => {
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(app.isLoggedIn());
const [, setConviction] = useState<number>();

const { trackAnalytics } = useBrowserAnalyticsTrack({ onAction: true });

useEffect(() => {
app.loginStateEmitter.once('redraw', () => {
setIsLoggedIn(app.isLoggedIn());
Expand Down Expand Up @@ -103,71 +106,115 @@ export const VotingActions = (props: VotingActionsProps) => {
const chain = app.chain as Cosmos;
const depositAmountInMinimalDenom = parseInt(
naturalDenomToMinimal(amount, chain.meta?.decimals),
10
10,
);

proposal
.submitDepositTx(user, chain.chain.coins(depositAmountInMinimalDenom))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
} else {
proposal
.voteTx(new CosmosVote(user, 'Yes'))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.voteTx(new CosmosVote(user, 'Yes'));
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.COSMOS_VOTE_OCCURRED,
});
} catch (error) {
notifyError(error.toString());
}
}
} else if (proposal instanceof CompoundProposal) {
proposal
.submitVoteWebTx(new CompoundProposalVote(user, BravoVote.YES))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.submitVoteWebTx(
new CompoundProposalVote(user, BravoVote.YES),
);
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.COMPOUND_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else if (proposal instanceof AaveProposal) {
proposal
.submitVoteWebTx(new AaveProposalVote(user, true))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.submitVoteWebTx(new AaveProposalVote(user, true));
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.AAVE_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else if (proposal instanceof NearSputnikProposal) {
proposal
.submitVoteWebTx(
new NearSputnikVote(user, NearSputnikVoteString.Approve)
)
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.submitVoteWebTx(
new NearSputnikVote(user, NearSputnikVoteString.Approve),
);
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.SPUTNIK_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else {
toggleVotingModal(false);
return notifyError('Invalid proposal type');
}
};

const voteNo = (e) => {
const voteNo = async (e) => {
e.preventDefault();
toggleVotingModal(true);

if (
proposal instanceof CosmosProposal ||
proposal instanceof CosmosProposalV1
) {
proposal
.voteTx(new CosmosVote(user, 'No'))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.voteTx(new CosmosVote(user, 'No'));
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.COSMOS_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else if (proposal instanceof CompoundProposal) {
proposal
.submitVoteWebTx(new CompoundProposalVote(user, BravoVote.NO))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.submitVoteWebTx(
new CompoundProposalVote(user, BravoVote.NO),
);
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.COMPOUND_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else if (proposal instanceof AaveProposal) {
proposal
.submitVoteWebTx(new AaveProposalVote(user, false))
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.submitVoteWebTx(new AaveProposalVote(user, false));
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.AAVE_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else if (proposal instanceof NearSputnikProposal) {
proposal
.submitVoteWebTx(
new NearSputnikVote(user, NearSputnikVoteString.Reject)
)
.then(emitRedraw)
.catch((err) => notifyError(err.toString()));
try {
await proposal.submitVoteWebTx(
new NearSputnikVote(user, NearSputnikVoteString.Reject),
);
emitRedraw();
trackAnalytics({
event: MixpanelGovernanceEvents.SPUTNIK_VOTE_OCCURRED,
});
} catch (err) {
notifyError(err.toString());
}
} else {
toggleVotingModal(false);
return notifyError('Invalid proposal type');
Expand Down Expand Up @@ -225,7 +272,7 @@ export const VotingActions = (props: VotingActionsProps) => {
if (proposal instanceof NearSputnikProposal) {
proposal
.submitVoteWebTx(
new NearSputnikVote(user, NearSputnikVoteString.Remove)
new NearSputnikVote(user, NearSputnikVoteString.Remove),
)
.then(() => {
onModalClose();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import React from 'react';

import type { SnapshotProposal, SnapshotSpace } from 'helpers/snapshot_utils';
import app from '../../state';
import { useBrowserAnalyticsTrack } from 'hooks/useBrowserAnalyticsTrack';
import '../../../styles/modals/confirm_snapshot_vote_modal.scss';
import { notifyError } from '../../controllers/app/notifications';
import { castVote } from '../../helpers/snapshot_utils';
import { formatNumberShort } from '../../../../shared/adapters/currency';
import { CWButton } from '../components/component_kit/new_designs/cw_button';
import app from '../../state';
import { CWText } from '../components/component_kit/cw_text';
import {
CWModalBody,
CWModalFooter,
CWModalHeader,
} from '../components/component_kit/new_designs/CWModal';

import '../../../styles/modals/confirm_snapshot_vote_modal.scss';
import { CWButton } from '../components/component_kit/new_designs/cw_button';
import { formatNumberShort } from '/adapters/currency';
import { MixpanelSnapshotEvents } from '/analytics/types';

type ConfirmSnapshotVoteModalProps = {
id: string;
Expand All @@ -28,7 +29,7 @@ type ConfirmSnapshotVoteModalProps = {
};

export const ConfirmSnapshotVoteModal = (
props: ConfirmSnapshotVoteModalProps
props: ConfirmSnapshotVoteModalProps,
) => {
const {
id,
Expand All @@ -44,6 +45,35 @@ export const ConfirmSnapshotVoteModal = (

const [isSaving, setIsSaving] = React.useState<boolean>(false);

const { trackAnalytics } = useBrowserAnalyticsTrack({ onAction: true });

const handleVote = async (e) => {
e.preventDefault();
setIsSaving(true);
const votePayload = {
space: space.id,
proposal: id,
type: 'single-choice',
choice: parseInt(selectedChoice) + 1,
metadata: JSON.stringify({}),
};
try {
castVote(author.address, votePayload).then(async () => {
await app.snapshot.refreshProposals();
onModalClose();
successCallback();
});
trackAnalytics({
event: MixpanelSnapshotEvents.SNAPSHOT_VOTE_OCCURRED,
});
} catch (err) {
console.log(err);
const errorMessage = err.message;
notifyError(errorMessage);
}
setIsSaving(false);
};

return (
<div className="ConfirmSnapshotVoteModal">
<CWModalHeader label="Confirm vote" onModalClose={onModalClose} />
Expand Down Expand Up @@ -83,29 +113,7 @@ export const ConfirmSnapshotVoteModal = (
buttonType="primary"
buttonHeight="sm"
disabled={isSaving}
onClick={async (e) => {
e.preventDefault();
setIsSaving(true);
const votePayload = {
space: space.id,
proposal: id,
type: 'single-choice',
choice: parseInt(selectedChoice) + 1,
metadata: JSON.stringify({}),
};
try {
castVote(author.address, votePayload).then(async () => {
await app.snapshot.refreshProposals();
onModalClose();
successCallback();
});
} catch (err) {
console.log(err);
const errorMessage = err.message;
notifyError(errorMessage);
}
setIsSaving(false);
}}
onClick={handleVote}
/>
</CWModalFooter>
</div>
Expand Down
Loading

0 comments on commit 5cf5fc4

Please sign in to comment.