347 lines
10 KiB
JavaScript
347 lines
10 KiB
JavaScript
// src/views/EventDetail/hooks/useEventDetailEvents.js
|
||
// 事件详情页面事件追踪 Hook
|
||
|
||
import { useCallback, useEffect } from 'react';
|
||
import { usePostHogTrack } from '../../../hooks/usePostHogRedux';
|
||
import { RETENTION_EVENTS } from '../../../lib/constants';
|
||
import { logger } from '../../../utils/logger';
|
||
|
||
/**
|
||
* 事件详情(EventDetail)事件追踪 Hook
|
||
* @param {Object} options - 配置选项
|
||
* @param {Object} options.event - 事件对象
|
||
* @param {number} options.event.id - 事件ID
|
||
* @param {string} options.event.title - 事件标题
|
||
* @param {string} options.event.importance - 重要性等级
|
||
* @param {Function} options.navigate - 路由导航函数
|
||
* @returns {Object} 事件追踪处理函数集合
|
||
*/
|
||
export const useEventDetailEvents = ({ event, navigate } = {}) => {
|
||
const { track } = usePostHogTrack();
|
||
|
||
// 🎯 页面浏览事件 - 页面加载时触发
|
||
useEffect(() => {
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for page view tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.EVENT_DETAIL_VIEWED, {
|
||
event_id: event.id,
|
||
event_title: event.title || '',
|
||
importance: event.importance || 'unknown',
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '📄 Event Detail Page Viewed', {
|
||
eventId: event.id,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪事件分析内容查看
|
||
* @param {Object} analysisData - 分析数据
|
||
* @param {string} analysisData.type - 分析类型 ('market_impact' | 'stock_correlation' | 'timeline')
|
||
* @param {number} analysisData.relatedStockCount - 相关股票数量
|
||
* @param {number} analysisData.timelineEventCount - 时间线事件数量
|
||
*/
|
||
const trackEventAnalysisViewed = useCallback((analysisData = {}) => {
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for analysis tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.EVENT_ANALYSIS_VIEWED, {
|
||
event_id: event.id,
|
||
analysis_type: analysisData.type || 'overview',
|
||
related_stock_count: analysisData.relatedStockCount || 0,
|
||
timeline_event_count: analysisData.timelineEventCount || 0,
|
||
has_market_impact: Boolean(analysisData.marketImpact),
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '📊 Event Analysis Viewed', {
|
||
eventId: event.id,
|
||
analysisType: analysisData.type,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪事件时间线点击
|
||
* @param {Object} timelineItem - 时间线项目
|
||
* @param {string} timelineItem.id - 时间线项目ID
|
||
* @param {string} timelineItem.date - 时间线日期
|
||
* @param {string} timelineItem.title - 时间线标题
|
||
* @param {number} position - 在时间线中的位置
|
||
*/
|
||
const trackEventTimelineClicked = useCallback((timelineItem, position = 0) => {
|
||
if (!timelineItem || !timelineItem.id) {
|
||
logger.warn('useEventDetailEvents', 'Timeline item is required');
|
||
return;
|
||
}
|
||
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for timeline tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.EVENT_TIMELINE_CLICKED, {
|
||
event_id: event.id,
|
||
timeline_item_id: timelineItem.id,
|
||
timeline_date: timelineItem.date || '',
|
||
timeline_title: timelineItem.title || '',
|
||
position,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '⏰ Event Timeline Clicked', {
|
||
eventId: event.id,
|
||
timelineItemId: timelineItem.id,
|
||
position,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪相关股票点击(从事件详情)
|
||
* @param {Object} stock - 股票对象
|
||
* @param {string} stock.code - 股票代码
|
||
* @param {string} stock.name - 股票名称
|
||
* @param {number} position - 在列表中的位置
|
||
*/
|
||
const trackRelatedStockClicked = useCallback((stock, position = 0) => {
|
||
if (!stock || !stock.code) {
|
||
logger.warn('useEventDetailEvents', 'Stock object is required');
|
||
return;
|
||
}
|
||
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for stock tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.STOCK_CLICKED, {
|
||
stock_code: stock.code,
|
||
stock_name: stock.name || '',
|
||
source: 'event_detail_related_stocks',
|
||
event_id: event.id,
|
||
position,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '🎯 Related Stock Clicked', {
|
||
stockCode: stock.code,
|
||
eventId: event.id,
|
||
position,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪相关概念点击(从事件详情)
|
||
* @param {Object} concept - 概念对象
|
||
* @param {string} concept.code - 概念代码
|
||
* @param {string} concept.name - 概念名称
|
||
* @param {number} position - 在列表中的位置
|
||
*/
|
||
const trackRelatedConceptClicked = useCallback((concept, position = 0) => {
|
||
if (!concept || !concept.code) {
|
||
logger.warn('useEventDetailEvents', 'Concept object is required');
|
||
return;
|
||
}
|
||
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for concept tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.CONCEPT_CLICKED, {
|
||
concept_code: concept.code,
|
||
concept_name: concept.name || '',
|
||
source: 'event_detail_related_concepts',
|
||
event_id: event.id,
|
||
position,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '🏷️ Related Concept Clicked', {
|
||
conceptCode: concept.code,
|
||
eventId: event.id,
|
||
position,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪标签页切换
|
||
* @param {string} tabName - 标签名称 ('overview' | 'related_stocks' | 'related_concepts' | 'timeline')
|
||
*/
|
||
const trackTabClicked = useCallback((tabName) => {
|
||
if (!tabName) {
|
||
logger.warn('useEventDetailEvents', 'Tab name is required');
|
||
return;
|
||
}
|
||
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for tab tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.NEWS_TAB_CLICKED, {
|
||
tab_name: tabName,
|
||
event_id: event.id,
|
||
context: 'event_detail',
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '📑 Tab Clicked', {
|
||
tabName,
|
||
eventId: event.id,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪事件收藏/取消收藏
|
||
* @param {boolean} isFavorited - 是否收藏
|
||
*/
|
||
const trackEventFavoriteToggled = useCallback((isFavorited) => {
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for favorite tracking');
|
||
return;
|
||
}
|
||
|
||
const eventName = isFavorited ? 'Event Favorited' : 'Event Unfavorited';
|
||
|
||
track(eventName, {
|
||
event_id: event.id,
|
||
event_title: event.title || '',
|
||
action: isFavorited ? 'add' : 'remove',
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', `${isFavorited ? '⭐' : '☆'} Event Favorite Toggled`, {
|
||
eventId: event.id,
|
||
isFavorited,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪事件分享
|
||
* @param {string} shareMethod - 分享方式 ('wechat' | 'link' | 'qrcode')
|
||
*/
|
||
const trackEventShared = useCallback((shareMethod) => {
|
||
if (!shareMethod) {
|
||
logger.warn('useEventDetailEvents', 'Share method is required');
|
||
return;
|
||
}
|
||
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for share tracking');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.CONTENT_SHARED, {
|
||
content_type: 'event',
|
||
content_id: event.id,
|
||
content_title: event.title || '',
|
||
share_method: shareMethod,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '📤 Event Shared', {
|
||
eventId: event.id,
|
||
shareMethod,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪评论点赞/取消点赞
|
||
* @param {string} commentId - 评论ID
|
||
* @param {boolean} isLiked - 是否点赞
|
||
*/
|
||
const trackCommentLiked = useCallback((commentId, isLiked) => {
|
||
if (!commentId) {
|
||
logger.warn('useEventDetailEvents', 'Comment ID is required');
|
||
return;
|
||
}
|
||
|
||
track(isLiked ? 'Comment Liked' : 'Comment Unliked', {
|
||
comment_id: commentId,
|
||
event_id: event?.id,
|
||
action: isLiked ? 'like' : 'unlike',
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', `${isLiked ? '❤️' : '🤍'} Comment ${isLiked ? 'Liked' : 'Unliked'}`, {
|
||
commentId,
|
||
eventId: event?.id,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪添加评论
|
||
* @param {string} commentId - 评论ID
|
||
* @param {number} contentLength - 评论内容长度
|
||
*/
|
||
const trackCommentAdded = useCallback((commentId, contentLength = 0) => {
|
||
if (!event || !event.id) {
|
||
logger.warn('useEventDetailEvents', 'Event object is required for comment tracking');
|
||
return;
|
||
}
|
||
|
||
track('Comment Added', {
|
||
comment_id: commentId,
|
||
event_id: event.id,
|
||
content_length: contentLength,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '💬 Comment Added', {
|
||
commentId,
|
||
eventId: event.id,
|
||
contentLength,
|
||
});
|
||
}, [track, event]);
|
||
|
||
/**
|
||
* 追踪删除评论
|
||
* @param {string} commentId - 评论ID
|
||
*/
|
||
const trackCommentDeleted = useCallback((commentId) => {
|
||
if (!commentId) {
|
||
logger.warn('useEventDetailEvents', 'Comment ID is required');
|
||
return;
|
||
}
|
||
|
||
track('Comment Deleted', {
|
||
comment_id: commentId,
|
||
event_id: event?.id,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useEventDetailEvents', '🗑️ Comment Deleted', {
|
||
commentId,
|
||
eventId: event?.id,
|
||
});
|
||
}, [track, event]);
|
||
|
||
return {
|
||
// 页面级事件
|
||
trackEventAnalysisViewed,
|
||
|
||
// 交互事件
|
||
trackEventTimelineClicked,
|
||
trackRelatedStockClicked,
|
||
trackRelatedConceptClicked,
|
||
trackTabClicked,
|
||
|
||
// 用户行为事件
|
||
trackEventFavoriteToggled,
|
||
trackEventShared,
|
||
|
||
// 社交互动事件
|
||
trackCommentLiked,
|
||
trackCommentAdded,
|
||
trackCommentDeleted,
|
||
};
|
||
};
|
||
|
||
export default useEventDetailEvents;
|