Commit dc28dff6 authored by Max Alekseenko's avatar Max Alekseenko

add mixpanel events

parent 64e52b9e
...@@ -20,6 +20,7 @@ export enum EventTypes { ...@@ -20,6 +20,7 @@ export enum EventTypes {
FILTERS = 'Filters', FILTERS = 'Filters',
BUTTON_CLICK = 'Button click', BUTTON_CLICK = 'Button click',
PROMO_BANNER = 'Promo banner', PROMO_BANNER = 'Promo banner',
APP_FEEDBACK = 'App feedback',
} }
/* eslint-disable @typescript-eslint/indent */ /* eslint-disable @typescript-eslint/indent */
...@@ -135,5 +136,11 @@ Type extends EventTypes.PROMO_BANNER ? { ...@@ -135,5 +136,11 @@ Type extends EventTypes.PROMO_BANNER ? {
'Source': 'Marketplace'; 'Source': 'Marketplace';
'Link': string; 'Link': string;
} : } :
Type extends EventTypes.APP_FEEDBACK ? {
'Action': 'Rating';
'Source': 'Discovery' | 'App modal' | 'App page';
'AppId': string;
'Score': number;
} :
undefined; undefined;
/* eslint-enable @typescript-eslint/indent */ /* eslint-enable @typescript-eslint/indent */
...@@ -5,6 +5,7 @@ import React, { useCallback } from 'react'; ...@@ -5,6 +5,7 @@ import React, { useCallback } from 'react';
import type { MarketplaceAppWithSecurityReport, ContractListTypes } from 'types/client/marketplace'; import type { MarketplaceAppWithSecurityReport, ContractListTypes } from 'types/client/marketplace';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import AppSecurityReport from './AppSecurityReport'; import AppSecurityReport from './AppSecurityReport';
...@@ -21,7 +22,7 @@ interface Props extends MarketplaceAppWithSecurityReport { ...@@ -21,7 +22,7 @@ interface Props extends MarketplaceAppWithSecurityReport {
className?: string; className?: string;
showContractList: (id: string, type: ContractListTypes) => void; showContractList: (id: string, type: ContractListTypes) => void;
userRating: number | undefined; userRating: number | undefined;
rateApp: (appId: string, recordId: string | undefined, rating: number) => void; rateApp: (appId: string, recordId: string | undefined, rating: number, source: EventPayload<EventTypes.APP_FEEDBACK>['Source']) => void;
isSendingRating: boolean; isSendingRating: boolean;
isRatingLoading: boolean; isRatingLoading: boolean;
canRate: boolean | undefined; canRate: boolean | undefined;
...@@ -180,6 +181,7 @@ const MarketplaceAppCard = ({ ...@@ -180,6 +181,7 @@ const MarketplaceAppCard = ({
isSending={ isSendingRating } isSending={ isSendingRating }
isLoading={ isRatingLoading } isLoading={ isRatingLoading }
canRate={ canRate } canRate={ canRate }
source="Discovery"
/> />
<IconButton <IconButton
aria-label="Mark as favorite" aria-label="Mark as favorite"
......
...@@ -10,6 +10,7 @@ import { ContractListTypes } from 'types/client/marketplace'; ...@@ -10,6 +10,7 @@ import { ContractListTypes } from 'types/client/marketplace';
import config from 'configs/app'; import config from 'configs/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import type { IconName } from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -28,7 +29,7 @@ type Props = { ...@@ -28,7 +29,7 @@ type Props = {
data: MarketplaceAppWithSecurityReport; data: MarketplaceAppWithSecurityReport;
showContractList: (id: string, type: ContractListTypes, hasPreviousStep: boolean) => void; showContractList: (id: string, type: ContractListTypes, hasPreviousStep: boolean) => void;
userRating: number | undefined; userRating: number | undefined;
rateApp: (appId: string, recordId: string | undefined, rating: number) => void; rateApp: (appId: string, recordId: string | undefined, rating: number, source: EventPayload<EventTypes.APP_FEEDBACK>['Source']) => void;
isSendingRating: boolean; isSendingRating: boolean;
isRatingLoading: boolean; isRatingLoading: boolean;
canRate: boolean | undefined; canRate: boolean | undefined;
...@@ -181,6 +182,7 @@ const MarketplaceAppModal = ({ ...@@ -181,6 +182,7 @@ const MarketplaceAppModal = ({
isLoading={ isRatingLoading } isLoading={ isRatingLoading }
fullView fullView
canRate={ canRate } canRate={ canRate }
source="App modal"
/> />
</Box> </Box>
) } ) }
......
...@@ -96,6 +96,7 @@ const MarketplaceAppTopBar = ({ appId, data, isLoading, securityReport }: Props) ...@@ -96,6 +96,7 @@ const MarketplaceAppTopBar = ({ appId, data, isLoading, securityReport }: Props)
isSending={ isSendingRating } isSending={ isSendingRating }
isLoading={ isRatingLoading } isLoading={ isRatingLoading }
canRate={ canRate } canRate={ canRate }
source="App page"
/> />
{ !isMobile && ( { !isMobile && (
<Flex flex="1" justifyContent="flex-end"> <Flex flex="1" justifyContent="flex-end">
......
...@@ -4,6 +4,7 @@ import type { MouseEvent } from 'react'; ...@@ -4,6 +4,7 @@ import type { MouseEvent } from 'react';
import type { MarketplaceAppWithSecurityReport, ContractListTypes, UserRatings } from 'types/client/marketplace'; import type { MarketplaceAppWithSecurityReport, ContractListTypes, UserRatings } from 'types/client/marketplace';
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import EmptySearchResult from './EmptySearchResult'; import EmptySearchResult from './EmptySearchResult';
...@@ -19,7 +20,7 @@ type Props = { ...@@ -19,7 +20,7 @@ type Props = {
onAppClick: (event: MouseEvent, id: string) => void; onAppClick: (event: MouseEvent, id: string) => void;
showContractList: (id: string, type: ContractListTypes) => void; showContractList: (id: string, type: ContractListTypes) => void;
userRatings: UserRatings; userRatings: UserRatings;
rateApp: (appId: string, recordId: string, rating: number) => void; rateApp: (appId: string, recordId: string, rating: number, source: EventPayload<EventTypes.APP_FEEDBACK>['Source']) => void;
isSendingRating: boolean; isSendingRating: boolean;
isRatingLoading: boolean; isRatingLoading: boolean;
canRate: boolean | undefined; canRate: boolean | undefined;
......
...@@ -2,6 +2,7 @@ import { Text, Flex, Spinner, Button } from '@chakra-ui/react'; ...@@ -2,6 +2,7 @@ import { Text, Flex, Spinner, Button } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { mdash } from 'lib/html-entities'; import { mdash } from 'lib/html-entities';
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import Stars from './Stars'; import Stars from './Stars';
...@@ -12,11 +13,12 @@ type Props = { ...@@ -12,11 +13,12 @@ type Props = {
appId: string; appId: string;
recordId?: string; recordId?: string;
userRating: number | undefined; userRating: number | undefined;
rate: (appId: string, recordId: string | undefined, rating: number) => void; rate: (appId: string, recordId: string | undefined, rating: number, source: EventPayload<EventTypes.APP_FEEDBACK>['Source']) => void;
isSending?: boolean; isSending?: boolean;
source: EventPayload<EventTypes.APP_FEEDBACK>['Source'];
}; };
const PopoverContent = ({ appId, recordId, userRating, rate, isSending }: Props) => { const PopoverContent = ({ appId, recordId, userRating, rate, isSending, source }: Props) => {
const [ hovered, setHovered ] = React.useState(-1); const [ hovered, setHovered ] = React.useState(-1);
const [ selected, setSelected ] = React.useState(-1); const [ selected, setSelected ] = React.useState(-1);
...@@ -36,8 +38,8 @@ const PopoverContent = ({ appId, recordId, userRating, rate, isSending }: Props) ...@@ -36,8 +38,8 @@ const PopoverContent = ({ appId, recordId, userRating, rate, isSending }: Props)
if (selected < 0) { if (selected < 0) {
return; return;
} }
rate(appId, recordId, selected + 1); rate(appId, recordId, selected + 1, source);
}, [ appId, recordId, selected, rate ]); }, [ appId, recordId, selected, rate, source ]);
if (userRating) { if (userRating) {
return ( return (
......
...@@ -2,6 +2,7 @@ import { Text, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure, Skele ...@@ -2,6 +2,7 @@ import { Text, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure, Skele
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
import Popover from 'ui/shared/chakra/Popover'; import Popover from 'ui/shared/chakra/Popover';
import Content from './PopoverContent'; import Content from './PopoverContent';
...@@ -16,16 +17,17 @@ type Props = { ...@@ -16,16 +17,17 @@ type Props = {
rating?: number; rating?: number;
recordId?: string; recordId?: string;
userRating: number | undefined; userRating: number | undefined;
rate: (appId: string, recordId: string | undefined, rating: number) => void; rate: (appId: string, recordId: string | undefined, rating: number, source: EventPayload<EventTypes.APP_FEEDBACK>['Source']) => void;
isSending?: boolean; isSending?: boolean;
isLoading?: boolean; isLoading?: boolean;
fullView?: boolean; fullView?: boolean;
canRate: boolean | undefined; canRate: boolean | undefined;
source: EventPayload<EventTypes.APP_FEEDBACK>['Source'];
}; };
const Rating = ({ const Rating = ({
appId, rating, recordId, userRating, rate, appId, rating, recordId, userRating, rate,
isSending, isLoading, fullView, canRate, isSending, isLoading, fullView, canRate, source,
}: Props) => { }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
// have to implement this solution because popover loses focus on button click inside it (issue: https://github.com/chakra-ui/chakra-ui/issues/7359) // have to implement this solution because popover loses focus on button click inside it (issue: https://github.com/chakra-ui/chakra-ui/issues/7359)
...@@ -68,6 +70,7 @@ const Rating = ({ ...@@ -68,6 +70,7 @@ const Rating = ({
userRating={ userRating } userRating={ userRating }
rate={ rate } rate={ rate }
isSending={ isSending } isSending={ isSending }
source={ source }
/> />
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
......
...@@ -7,6 +7,8 @@ import type { UserRatings, AppRatings } from 'types/client/marketplace'; ...@@ -7,6 +7,8 @@ import type { UserRatings, AppRatings } from 'types/client/marketplace';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
import * as mixpanel from 'lib/mixpanel/index';
import { ADDRESS_COUNTERS } from 'stubs/address'; import { ADDRESS_COUNTERS } from 'stubs/address';
const feature = config.features.marketplace; const feature = config.features.marketplace;
...@@ -89,7 +91,12 @@ export default function useRatings() { ...@@ -89,7 +91,12 @@ export default function useRatings() {
setCanRate(canRate); setCanRate(canRate);
}, [ address, addressCountersQuery ]); }, [ address, addressCountersQuery ]);
const rateApp = useCallback(async(appId: string, recordId: string | undefined, rating: number) => { const rateApp = useCallback(async(
appId: string,
recordId: string | undefined,
rating: number,
source: EventPayload<EventTypes.APP_FEEDBACK>['Source'],
) => {
setIsSending(true); setIsSending(true);
try { try {
if (!address || !base) { if (!address || !base) {
...@@ -119,6 +126,10 @@ export default function useRatings() { ...@@ -119,6 +126,10 @@ export default function useRatings() {
description: 'Your rating improves the service', description: 'Your rating improves the service',
}); });
fetchRatings(); fetchRatings();
mixpanel.logEvent(
mixpanel.EventTypes.APP_FEEDBACK,
{ Action: 'Rating', Source: source, AppId: appId, Score: rating },
);
} catch (error) { } catch (error) {
toast({ toast({
status: 'error', status: 'error',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment