Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: analytics events for credits/awards #4291

Merged
merged 5 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/shared/src/components/award/AwardButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import type {
} from '../../contexts/GiveAwardModalContext';
import { useRequestProtocol } from '../../hooks/useRequestProtocol';
import { getCompanionWrapper } from '../../lib/extension';
import type { Post } from '../../graphql/posts';

type AwardButtonProps = {
appendTo?: 'parent' | Element | ((ref: Element) => Element);
type: AwardTypes;
className?: string;
entity: AwardEntity;
post?: Post;
} & Pick<ButtonProps<'button'>, 'pressed' | 'variant'>;
export const AwardButton = ({
appendTo: appendToProps,
Expand All @@ -34,6 +36,7 @@ export const AwardButton = ({
entity,
pressed,
variant = ButtonVariant.Tertiary,
post,
}: AwardButtonProps): ReactElement => {
const { isCompanion } = useRequestProtocol();
const { user, showLogin } = useAuthContext();
Expand All @@ -49,6 +52,7 @@ export const AwardButton = ({
props: {
type,
entity,
post,
},
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ export default function CommentActionButtons({
numAwards: comment.numAwards,
}}
pressed={!!comment.userState?.awarded}
post={post}
/>
{!!comment.numAwards && (
<Typography
Expand Down
10 changes: 9 additions & 1 deletion packages/shared/src/components/cores/CoreOptionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
TypographyColor,
TypographyType,
} from '../typography/Typography';
import { LogEvent } from '../../lib/log';
import { useLogContext } from '../../contexts/LogContext';

type CoreOptionButtonProps = {
id: string;
Expand All @@ -18,9 +20,15 @@ export const CoreOptionButton = ({
id,
}: CoreOptionButtonProps): ReactElement => {
const isMobile = useViewSize(ViewSize.MobileL);
const { selectedProduct, setSelectedProduct, openCheckout } =
const { logEvent } = useLogContext();
const { selectedProduct, setSelectedProduct, openCheckout, origin } =
useBuyCoresContext();
const onSelect = useCallback(() => {
// TODO: Amount should be deducted from selected product entity
logEvent({
event_name: LogEvent.SelectCreditsQuantity,
extra: JSON.stringify({ origin, amount: id }),
});
setSelectedProduct(id);
if (!isMobile) {
openCheckout({ priceId: id });
Expand Down
13 changes: 12 additions & 1 deletion packages/shared/src/components/credit/BuyCreditsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Link from '../utilities/Link';
import { webappUrl } from '../../lib/constants';
import { anchorDefaultRel } from '../../lib/strings';
import { isIOSNative } from '../../lib/func';
import { LogEvent, Origin } from '../../lib/log';
import { useLogContext } from '../../contexts/LogContext';

type BuyCreditsButtonProps = {
onPlusClick: () => void;
Expand All @@ -17,6 +19,15 @@ export const BuyCreditsButton = ({
hideBuyButton,
}: BuyCreditsButtonProps): ReactElement => {
const renderBuyButton = !isIOSNative() && !hideBuyButton;
const { logEvent } = useLogContext();
const trackBuyCredits = () => {
logEvent({
event_name: LogEvent.StartBuyingCredits,
extra: JSON.stringify({ origin: Origin.Award }),
});
onPlusClick();
};

return (
<div className="flex items-center rounded-10 bg-surface-float">
<Link href={`${webappUrl}/earnings`} passHref>
Expand All @@ -38,7 +49,7 @@ export const BuyCreditsButton = ({
variant={ButtonVariant.Tertiary}
icon={<PlusIcon />}
size={ButtonSize.Small}
onClick={onPlusClick}
onClick={trackBuyCredits}
/>
</>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { IconSize } from '../../Icon';
import useDebounceFn from '../../../hooks/useDebounceFn';
import { CoreOptionList } from '../../cores/CoreOptionList';
import { CoreAmountNeeded } from '../../cores/CoreAmountNeeded';
import type { Origin } from '../../../lib/log';

const CoreOptions = () => {
return (
Expand Down Expand Up @@ -182,14 +183,20 @@ const ModalRender = ({ ...props }: ModalProps) => {
};

type BuyCoresModalProps = ModalProps & {
origin: typeof Origin;
onCompletion?: () => void;
};
export const BuyCoresModal = ({
origin,
onCompletion,
...props
}: BuyCoresModalProps): ReactElement => {
return (
<BuyCoresContextProvider amountNeeded={40} onCompletion={onCompletion}>
<BuyCoresContextProvider
amountNeeded={40}
onCompletion={onCompletion}
origin={origin}
>
<ModalRender {...props} />
</BuyCoresContextProvider>
);
Expand Down
67 changes: 52 additions & 15 deletions packages/shared/src/components/modals/award/GiveAwardModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ReactElement } from 'react';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';

import classNames from 'classnames';
import { useMutation, useQuery } from '@tanstack/react-query';
Expand Down Expand Up @@ -38,15 +38,22 @@ import { labels } from '../../../lib';
import type { ApiErrorResult } from '../../../graphql/common';
import { generateQueryKey, RequestKey } from '../../../lib/query';
import { useAuthContext } from '../../../contexts/AuthContext';
import { Origin } from '../../../lib/log';
import type { Post } from '../../../graphql/posts';

const AwardItem = ({ item }: { item: Product }) => {
const { setActiveStep } = useGiveAwardModalContext();
const { setActiveStep, logAwardEvent } = useGiveAwardModalContext();

const onClick = useCallback(() => {
logAwardEvent({ awardEvent: 'PICK', extra: { award: item.value } });
setActiveStep({ screen: 'COMMENT', product: item });
}, [item, logAwardEvent, setActiveStep]);

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

const CommentScreen = () => {
const { updateUser, user } = useAuthContext();
const { setActiveStep, type, entity, product, onRequestClose } =
useGiveAwardModalContext();
const {
setActiveStep,
type,
entity,
product,
onRequestClose,
logAwardEvent,
} = useGiveAwardModalContext();
const isMobile = useViewSize(ViewSize.MobileL);
const { displayToast } = useToastNotification();
const [note, setNote] = useState('');
Expand Down Expand Up @@ -163,6 +176,24 @@ const CommentScreen = () => {
},
});

const onAwardClick = useCallback(() => {
logAwardEvent({ awardEvent: 'AWARD', extra: { award: product.value } });
awardMutation({
productId: product.id,
type,
entityId: entity.id,
note,
});
}, [
awardMutation,
entity.id,
logAwardEvent,
note,
product.id,
product.value,
type,
]);

return (
<>
<Modal.Header title="Give an Award" showCloseButton={!isMobile}>
Expand Down Expand Up @@ -223,14 +254,7 @@ const CommentScreen = () => {
loading={isPending}
className="w-full"
variant={ButtonVariant.Primary}
onClick={() => {
awardMutation({
productId: product.id,
type,
entityId: entity.id,
note,
});
}}
onClick={onAwardClick}
>
Send Award for <CoinIcon />{' '}
{product.value === 0 ? 'Free' : product.value}
Expand Down Expand Up @@ -269,7 +293,15 @@ const ModalBody = () => {

const ModalRender = ({ ...props }: ModalProps) => {
const isMobile = useViewSize(ViewSize.MobileL);
const { activeModal, setActiveModal } = useGiveAwardModalContext();
const { activeModal, setActiveModal, logAwardEvent } =
useGiveAwardModalContext();
const trackingRef = useRef(false);
useEffect(() => {
if (!trackingRef.current) {
trackingRef.current = true;
logAwardEvent({ awardEvent: 'START' });
}
}, [logAwardEvent]);

const onCompletion = useCallback(() => {
setActiveModal('AWARD');
Expand All @@ -288,7 +320,11 @@ const ModalRender = ({ ...props }: ModalProps) => {
</Modal>
) : null}
{activeModal === 'BUY_CORES' ? (
<BuyCoresModal {...props} onCompletion={onCompletion} />
<BuyCoresModal
{...props}
onCompletion={onCompletion}
origin={Origin.Award}
/>
) : null}
</>
);
Expand All @@ -297,6 +333,7 @@ const ModalRender = ({ ...props }: ModalProps) => {
type GiveAwardModalProps = ModalProps & {
type: AwardTypes;
entity: AwardEntity;
post?: Post;
};
const GiveAwardModal = (props: GiveAwardModalProps): ReactElement => {
return (
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/src/contexts/BuyCoresContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { checkIsExtension } from '../lib/func';
import type { OpenCheckoutFn } from './PaymentContext';
import { useAuthContext } from './AuthContext';
import type { Origin } from '../lib/log';

const SCREENS = {
INTRO: 'INTRO',
Expand All @@ -32,19 +33,22 @@ export type BuyCoresContextData = {
setSelectedProduct: (product: string) => void;
activeStep: Screens;
setActiveStep: (step: Screens) => void;
origin?: typeof Origin;
};

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

export type BuyCoresContextProviderProps = {
children?: ReactNode;
origin: typeof Origin;
amountNeeded?: number;
onCompletion?: () => void;
};

export const BuyCoresContextProvider = ({
onCompletion,
origin,
amountNeeded,
children,
}: BuyCoresContextProviderProps): ReactElement => {
Expand Down Expand Up @@ -121,12 +125,14 @@ export const BuyCoresContextProvider = ({
selectedProduct,
setSelectedProduct,
openCheckout,
origin,
}),
[
activeStep,
amountNeeded,
onCompletion,
openCheckout,
origin,
paddle,
selectedProduct,
],
Expand Down
Loading