From fb448617269a43665739971ee64959a791cc036c Mon Sep 17 00:00:00 2001 From: Anton Vikulov Date: Wed, 18 Oct 2023 11:44:30 +0500 Subject: [PATCH] feat(ChangelogDialog): metrica --- .../ChangelogDialog/ChangelogDialog.tsx | 57 ++++++++++++++++++- .../__stories__/ChangelogDialog.stories.tsx | 6 ++ .../ChangelogDialog/components/Item/Item.tsx | 24 +++++--- src/components/ChangelogDialog/constants.ts | 6 ++ src/types.ts | 3 + 5 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 src/components/ChangelogDialog/constants.ts create mode 100644 src/types.ts diff --git a/src/components/ChangelogDialog/ChangelogDialog.tsx b/src/components/ChangelogDialog/ChangelogDialog.tsx index 9bf778ad..5e1f1f7c 100644 --- a/src/components/ChangelogDialog/ChangelogDialog.tsx +++ b/src/components/ChangelogDialog/ChangelogDialog.tsx @@ -1,12 +1,14 @@ -import React from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; import {ArrowUpRightFromSquare} from '@gravity-ui/icons'; -import {Dialog, Icon, Link} from '@gravity-ui/uikit'; import type {DialogProps} from '@gravity-ui/uikit'; +import {Dialog, Icon, Link} from '@gravity-ui/uikit'; +import {Metrica} from '../../types'; import {block} from '../utils/cn'; import {Item} from './components/Item/Item'; +import {Goals} from './constants'; import i18n from './i18n'; import type {ChangelogItem} from './types'; @@ -23,6 +25,8 @@ export interface ChangelogDialogProps { disableOutsideClick?: boolean; onClose: DialogProps['onClose']; onStoryClick?: (storyId: string) => void; + service?: string; + metrica?: Metrica; } let nextId = 1; @@ -39,10 +43,56 @@ export function ChangelogDialog({ disableOutsideClick, onClose, onStoryClick, + service, + metrica, }: ChangelogDialogProps) { const idRef = React.useRef(); idRef.current = idRef.current || getNextId(); const dialogCaptionId = `changelog-dialog-title-${idRef.current}`; + const refActionCount = useRef(0); + + useEffect(() => { + if (!service || !metrica || !open) return () => {}; + refActionCount.current = 0; + + metrica.reachGoal(Goals.Show, {service}); + + let fired = false; + const timeoutId = window.setTimeout(() => { + fired = true; + }, 3000); + + return () => { + clearTimeout(timeoutId); + if (!fired && !refActionCount.current) { + metrica.reachGoal(Goals.Bounce, {service}); + } + metrica.reachGoal(Goals.Hide, {service}); + }; + }, [open, service, metrica]); + + const handleStoryClick = useCallback( + (storyId: string) => { + refActionCount.current++; + if (service && metrica) { + metrica.reachGoal(Goals.Click, {service, action: storyId}); + } + if (onStoryClick) { + onStoryClick(storyId); + } + }, + [onStoryClick, service, metrica], + ); + + const handleLinkClick = useCallback( + (link: string) => { + refActionCount.current++; + if (service && metrica) { + metrica.reachGoal(Goals.Click, {service, action: link}); + } + }, + [service, metrica], + ); return ( )) ) : ( diff --git a/src/components/ChangelogDialog/__stories__/ChangelogDialog.stories.tsx b/src/components/ChangelogDialog/__stories__/ChangelogDialog.stories.tsx index f5099481..59a13844 100644 --- a/src/components/ChangelogDialog/__stories__/ChangelogDialog.stories.tsx +++ b/src/components/ChangelogDialog/__stories__/ChangelogDialog.stories.tsx @@ -115,4 +115,10 @@ Default.args = { // eslint-disable-next-line no-console console.log(storyId); }, + service: 'storybook', + metrica: { + reachGoal: (target: string, params: Record) => { + console.log('reachGoal', target, params); + }, + }, }; diff --git a/src/components/ChangelogDialog/components/Item/Item.tsx b/src/components/ChangelogDialog/components/Item/Item.tsx index 6ac0b2e6..e9bbf000 100644 --- a/src/components/ChangelogDialog/components/Item/Item.tsx +++ b/src/components/ChangelogDialog/components/Item/Item.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {CirclePlay} from '@gravity-ui/icons'; import {Button, Icon, Label} from '@gravity-ui/uikit'; @@ -16,9 +16,22 @@ export interface ItemProps { className?: string; data: ChangelogItem; onStoryClick?: (storyId: string) => void; + onLinkClick?: (link: string) => void; } -export function Item({className, data, onStoryClick}: ItemProps) { +export function Item({className, data, onStoryClick, onLinkClick}: ItemProps) { + const handleLinkClick = useCallback(() => { + if (onLinkClick && data.link) { + onLinkClick(data.link); + } + }, [data.link, onLinkClick]); + + const handleStoryClick = useCallback(() => { + if (onStoryClick && data.storyId) { + onStoryClick(data.storyId); + } + }, [data.storyId, onStoryClick]); + return (
@@ -48,6 +61,7 @@ export function Item({className, data, onStoryClick}: ItemProps) { view="outlined" href={data.link} target={'_blank'} + onClick={handleLinkClick} > {i18n('action_read-more')} @@ -56,11 +70,7 @@ export function Item({className, data, onStoryClick}: ItemProps) {