Skip to content

Commit cdb8065

Browse files
authored
feat: analytics events for credits/awards (#4291)
1 parent 95dba71 commit cdb8065

File tree

11 files changed

+272
-41
lines changed

11 files changed

+272
-41
lines changed

packages/shared/src/components/award/AwardButton.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import type {
2020
} from '../../contexts/GiveAwardModalContext';
2121
import { useRequestProtocol } from '../../hooks/useRequestProtocol';
2222
import { getCompanionWrapper } from '../../lib/extension';
23+
import type { Post } from '../../graphql/posts';
2324

2425
type AwardButtonProps = {
2526
appendTo?: 'parent' | Element | ((ref: Element) => Element);
2627
type: AwardTypes;
2728
className?: string;
2829
entity: AwardEntity;
30+
post?: Post;
2931
} & Pick<ButtonProps<'button'>, 'pressed' | 'variant'>;
3032
export const AwardButton = ({
3133
appendTo: appendToProps,
@@ -34,6 +36,7 @@ export const AwardButton = ({
3436
entity,
3537
pressed,
3638
variant = ButtonVariant.Tertiary,
39+
post,
3740
}: AwardButtonProps): ReactElement => {
3841
const { isCompanion } = useRequestProtocol();
3942
const { user, showLogin } = useAuthContext();
@@ -49,6 +52,7 @@ export const AwardButton = ({
4952
props: {
5053
type,
5154
entity,
55+
post,
5256
},
5357
});
5458
};

packages/shared/src/components/comments/CommentActionButtons.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ export default function CommentActionButtons({
365365
numAwards: comment.numAwards,
366366
}}
367367
pressed={!!comment.userState?.awarded}
368+
post={post}
368369
/>
369370
{!!comment.numAwards && (
370371
<Typography

packages/shared/src/components/cores/CoreOptionButton.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
TypographyColor,
1111
TypographyType,
1212
} from '../typography/Typography';
13+
import { LogEvent } from '../../lib/log';
14+
import { useLogContext } from '../../contexts/LogContext';
1315

1416
type CoreOptionButtonProps = {
1517
id: string;
@@ -18,14 +20,20 @@ export const CoreOptionButton = ({
1820
id,
1921
}: CoreOptionButtonProps): ReactElement => {
2022
const isMobile = useViewSize(ViewSize.MobileL);
21-
const { selectedProduct, setSelectedProduct, openCheckout } =
23+
const { logEvent } = useLogContext();
24+
const { selectedProduct, setSelectedProduct, openCheckout, origin } =
2225
useBuyCoresContext();
2326
const onSelect = useCallback(() => {
27+
// TODO: Amount should be deducted from selected product entity
28+
logEvent({
29+
event_name: LogEvent.SelectCreditsQuantity,
30+
extra: JSON.stringify({ origin, amount: id }),
31+
});
2432
setSelectedProduct(id);
2533
if (!isMobile) {
2634
openCheckout({ priceId: id });
2735
}
28-
}, [id, isMobile, openCheckout, setSelectedProduct]);
36+
}, [id, isMobile, logEvent, openCheckout, origin, setSelectedProduct]);
2937
return (
3038
<Button
3139
className={classNames(

packages/shared/src/components/credit/BuyCreditsButton.tsx

+12-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import Link from '../utilities/Link';
77
import { webappUrl } from '../../lib/constants';
88
import { anchorDefaultRel } from '../../lib/strings';
99
import { isIOSNative } from '../../lib/func';
10+
import { LogEvent, Origin } from '../../lib/log';
11+
import { useLogContext } from '../../contexts/LogContext';
1012

1113
type BuyCreditsButtonProps = {
1214
onPlusClick: () => void;
@@ -17,6 +19,15 @@ export const BuyCreditsButton = ({
1719
hideBuyButton,
1820
}: BuyCreditsButtonProps): ReactElement => {
1921
const renderBuyButton = !isIOSNative() && !hideBuyButton;
22+
const { logEvent } = useLogContext();
23+
const trackBuyCredits = () => {
24+
logEvent({
25+
event_name: LogEvent.StartBuyingCredits,
26+
extra: JSON.stringify({ origin: Origin.Award }),
27+
});
28+
onPlusClick();
29+
};
30+
2031
return (
2132
<div className="flex items-center rounded-10 bg-surface-float">
2233
<Link href={`${webappUrl}/earnings`} passHref>
@@ -38,7 +49,7 @@ export const BuyCreditsButton = ({
3849
variant={ButtonVariant.Tertiary}
3950
icon={<PlusIcon />}
4051
size={ButtonSize.Small}
41-
onClick={onPlusClick}
52+
onClick={trackBuyCredits}
4253
/>
4354
</>
4455
) : null}

packages/shared/src/components/modals/award/BuyCoresModal.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { IconSize } from '../../Icon';
2424
import useDebounceFn from '../../../hooks/useDebounceFn';
2525
import { CoreOptionList } from '../../cores/CoreOptionList';
2626
import { CoreAmountNeeded } from '../../cores/CoreAmountNeeded';
27+
import type { Origin } from '../../../lib/log';
2728

2829
const CoreOptions = () => {
2930
return (
@@ -182,14 +183,20 @@ const ModalRender = ({ ...props }: ModalProps) => {
182183
};
183184

184185
type BuyCoresModalProps = ModalProps & {
186+
origin: Origin;
185187
onCompletion?: () => void;
186188
};
187189
export const BuyCoresModal = ({
190+
origin,
188191
onCompletion,
189192
...props
190193
}: BuyCoresModalProps): ReactElement => {
191194
return (
192-
<BuyCoresContextProvider amountNeeded={40} onCompletion={onCompletion}>
195+
<BuyCoresContextProvider
196+
amountNeeded={40}
197+
onCompletion={onCompletion}
198+
origin={origin}
199+
>
193200
<ModalRender {...props} />
194201
</BuyCoresContextProvider>
195202
);

packages/shared/src/components/modals/award/GiveAwardModal.tsx

+52-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ReactElement } from 'react';
2-
import React, { useCallback, useState } from 'react';
2+
import React, { useCallback, useEffect, useRef, useState } from 'react';
33

44
import classNames from 'classnames';
55
import { useMutation, useQuery } from '@tanstack/react-query';
@@ -38,15 +38,22 @@ import { labels } from '../../../lib';
3838
import type { ApiErrorResult } from '../../../graphql/common';
3939
import { generateQueryKey, RequestKey } from '../../../lib/query';
4040
import { useAuthContext } from '../../../contexts/AuthContext';
41+
import { Origin } from '../../../lib/log';
42+
import type { Post } from '../../../graphql/posts';
4143

4244
const AwardItem = ({ item }: { item: Product }) => {
43-
const { setActiveStep } = useGiveAwardModalContext();
45+
const { setActiveStep, logAwardEvent } = useGiveAwardModalContext();
46+
47+
const onClick = useCallback(() => {
48+
logAwardEvent({ awardEvent: 'PICK', extra: { award: item.value } });
49+
setActiveStep({ screen: 'COMMENT', product: item });
50+
}, [item, logAwardEvent, setActiveStep]);
4451

4552
return (
4653
<Button
4754
variant={ButtonVariant.Float}
4855
className="flex !h-auto flex-col items-center justify-center gap-2 rounded-14 bg-surface-float !p-1"
49-
onClick={() => setActiveStep({ screen: 'COMMENT', product: item })}
56+
onClick={onClick}
5057
>
5158
<Image src={item.image} alt={item.name} className="size-20" />
5259
<div className="flex items-center justify-center">
@@ -133,8 +140,14 @@ const IntroScreen = () => {
133140

134141
const CommentScreen = () => {
135142
const { updateUser, user } = useAuthContext();
136-
const { setActiveStep, type, entity, product, onRequestClose } =
137-
useGiveAwardModalContext();
143+
const {
144+
setActiveStep,
145+
type,
146+
entity,
147+
product,
148+
onRequestClose,
149+
logAwardEvent,
150+
} = useGiveAwardModalContext();
138151
const isMobile = useViewSize(ViewSize.MobileL);
139152
const { displayToast } = useToastNotification();
140153
const [note, setNote] = useState('');
@@ -163,6 +176,24 @@ const CommentScreen = () => {
163176
},
164177
});
165178

179+
const onAwardClick = useCallback(() => {
180+
logAwardEvent({ awardEvent: 'AWARD', extra: { award: product.value } });
181+
awardMutation({
182+
productId: product.id,
183+
type,
184+
entityId: entity.id,
185+
note,
186+
});
187+
}, [
188+
awardMutation,
189+
entity.id,
190+
logAwardEvent,
191+
note,
192+
product.id,
193+
product.value,
194+
type,
195+
]);
196+
166197
return (
167198
<>
168199
<Modal.Header title="Give an Award" showCloseButton={!isMobile}>
@@ -223,14 +254,7 @@ const CommentScreen = () => {
223254
loading={isPending}
224255
className="w-full"
225256
variant={ButtonVariant.Primary}
226-
onClick={() => {
227-
awardMutation({
228-
productId: product.id,
229-
type,
230-
entityId: entity.id,
231-
note,
232-
});
233-
}}
257+
onClick={onAwardClick}
234258
>
235259
Send Award for <CoinIcon />{' '}
236260
{product.value === 0 ? 'Free' : product.value}
@@ -269,7 +293,15 @@ const ModalBody = () => {
269293

270294
const ModalRender = ({ ...props }: ModalProps) => {
271295
const isMobile = useViewSize(ViewSize.MobileL);
272-
const { activeModal, setActiveModal } = useGiveAwardModalContext();
296+
const { activeModal, setActiveModal, logAwardEvent } =
297+
useGiveAwardModalContext();
298+
const trackingRef = useRef(false);
299+
useEffect(() => {
300+
if (!trackingRef.current) {
301+
trackingRef.current = true;
302+
logAwardEvent({ awardEvent: 'START' });
303+
}
304+
}, [logAwardEvent]);
273305

274306
const onCompletion = useCallback(() => {
275307
setActiveModal('AWARD');
@@ -288,7 +320,11 @@ const ModalRender = ({ ...props }: ModalProps) => {
288320
</Modal>
289321
) : null}
290322
{activeModal === 'BUY_CORES' ? (
291-
<BuyCoresModal {...props} onCompletion={onCompletion} />
323+
<BuyCoresModal
324+
{...props}
325+
onCompletion={onCompletion}
326+
origin={Origin.Award}
327+
/>
292328
) : null}
293329
</>
294330
);
@@ -297,6 +333,7 @@ const ModalRender = ({ ...props }: ModalProps) => {
297333
type GiveAwardModalProps = ModalProps & {
298334
type: AwardTypes;
299335
entity: AwardEntity;
336+
post?: Post;
300337
};
301338
const GiveAwardModal = (props: GiveAwardModalProps): ReactElement => {
302339
return (

packages/shared/src/contexts/BuyCoresContext.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { checkIsExtension } from '../lib/func';
1616
import type { OpenCheckoutFn } from './payment/context';
1717
import { useAuthContext } from './AuthContext';
18+
import type { Origin } from '../lib/log';
1819

1920
const SCREENS = {
2021
INTRO: 'INTRO',
@@ -32,19 +33,22 @@ export type BuyCoresContextData = {
3233
setSelectedProduct: (product: string) => void;
3334
activeStep: Screens;
3435
setActiveStep: (step: Screens) => void;
36+
origin?: Origin;
3537
};
3638

3739
const BuyCoresContext = React.createContext<BuyCoresContextData>(undefined);
3840
export default BuyCoresContext;
3941

4042
export type BuyCoresContextProviderProps = {
4143
children?: ReactNode;
44+
origin: Origin;
4245
amountNeeded?: number;
4346
onCompletion?: () => void;
4447
};
4548

4649
export const BuyCoresContextProvider = ({
4750
onCompletion,
51+
origin,
4852
amountNeeded,
4953
children,
5054
}: BuyCoresContextProviderProps): ReactElement => {
@@ -121,12 +125,14 @@ export const BuyCoresContextProvider = ({
121125
selectedProduct,
122126
setSelectedProduct,
123127
openCheckout,
128+
origin,
124129
}),
125130
[
126131
activeStep,
127132
amountNeeded,
128133
onCompletion,
129134
openCheckout,
135+
origin,
130136
paddle,
131137
selectedProduct,
132138
],

0 commit comments

Comments
 (0)