import type { FC } from '../../../lib/teact/teact'; import React, { memo, useEffect, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiChannelStatistics, ApiGroupStatistics, ApiMessage, StatisticsGraph, StatisticsRecentMessage as StatisticsRecentMessageType, } from '../../../api/types'; import { selectChat, selectChatFullInfo, selectStatistics } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { callApi } from '../../../api/gramjs'; import useForceUpdate from '../../../hooks/useForceUpdate'; import useLang from '../../../hooks/useLang'; import Loading from '../../ui/Loading'; import StatisticsOverview from './StatisticsOverview'; import StatisticsRecentMessage from './StatisticsRecentMessage'; import './Statistics.scss'; type ILovelyChart = { create: Function }; let lovelyChartPromise: Promise; let LovelyChart: ILovelyChart; async function ensureLovelyChart() { if (!lovelyChartPromise) { lovelyChartPromise = import('../../../lib/lovely-chart/LovelyChart') as Promise; LovelyChart = await lovelyChartPromise; } return lovelyChartPromise; } const CHANNEL_GRAPHS_TITLES = { growthGraph: 'ChannelStats.Graph.Growth', followersGraph: 'ChannelStats.Graph.Followers', muteGraph: 'ChannelStats.Graph.Notifications', topHoursGraph: 'ChannelStats.Graph.ViewsByHours', viewsBySourceGraph: 'ChannelStats.Graph.ViewsBySource', newFollowersBySourceGraph: 'ChannelStats.Graph.NewFollowersBySource', languagesGraph: 'ChannelStats.Graph.Language', interactionsGraph: 'ChannelStats.Graph.Interactions', }; const CHANNEL_GRAPHS = Object.keys(CHANNEL_GRAPHS_TITLES) as (keyof ApiChannelStatistics)[]; const GROUP_GRAPHS_TITLES = { growthGraph: 'Stats.GroupGrowthTitle', membersGraph: 'Stats.GroupMembersTitle', languagesGraph: 'Stats.GroupLanguagesTitle', messagesGraph: 'Stats.GroupMessagesTitle', actionsGraph: 'Stats.GroupActionsTitle', topHoursGraph: 'Stats.GroupTopHoursTitle', }; const GROUP_GRAPHS = Object.keys(GROUP_GRAPHS_TITLES) as (keyof ApiGroupStatistics)[]; export type OwnProps = { chatId: string; }; export type StateProps = { statistics: ApiChannelStatistics | ApiGroupStatistics; dcId?: number; isGroup: boolean; }; const Statistics: FC = ({ chatId, statistics, dcId, isGroup, }) => { const lang = useLang(); // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); const [isReady, setIsReady] = useState(false); const loadedCharts = useRef([]); const { loadStatistics, loadStatisticsAsyncGraph } = getActions(); const forceUpdate = useForceUpdate(); useEffect(() => { loadStatistics({ chatId, isGroup }); }, [chatId, loadStatistics, isGroup]); const graphs = useMemo(() => { return isGroup ? GROUP_GRAPHS : CHANNEL_GRAPHS; }, [isGroup]); const graphTitles = useMemo(() => { return isGroup ? GROUP_GRAPHS_TITLES : CHANNEL_GRAPHS_TITLES; }, [isGroup]); // Load async graphs useEffect(() => { if (!statistics) { return; } graphs.forEach((name) => { const graph = statistics[name as keyof typeof statistics]; const isAsync = typeof graph === 'string'; if (isAsync) { loadStatisticsAsyncGraph({ name, chatId, token: graph, // Hardcode percentage for languages graph, since API does not return `percentage` flag isPercentage: name === 'languagesGraph', }); } }); }, [graphs, chatId, statistics, loadStatisticsAsyncGraph]); useEffect(() => { (async () => { await ensureLovelyChart(); if (!isReady) { setIsReady(true); return; } if (!statistics || !containerRef.current) { return; } graphs.forEach((name, index: number) => { const graph = statistics[name as keyof typeof statistics]; const isAsync = typeof graph === 'string'; if (isAsync || loadedCharts.current.includes(name)) { return; } if (!graph) { loadedCharts.current.push(name); return; } const { zoomToken } = graph; LovelyChart.create( containerRef.current!.children[index], { title: lang((graphTitles as Record)[name]), ...zoomToken ? { onZoom: (x: number) => callApi('fetchStatisticsAsyncGraph', { token: zoomToken, x, dcId }), zoomOutLabel: lang('Graph.ZoomOut'), } : {}, ...graph as StatisticsGraph, }, ); loadedCharts.current.push(name); containerRef.current!.children[index].classList.remove('hidden'); }); forceUpdate(); })(); }, [ graphs, graphTitles, isReady, statistics, lang, chatId, loadStatisticsAsyncGraph, dcId, forceUpdate, ]); if (!isReady || !statistics) { return ; } return (
{!loadedCharts.current.length && }
{graphs.map((graph) => (
))}
{Boolean((statistics as ApiChannelStatistics).recentTopMessages?.length) && (

{lang('ChannelStats.Recent.Header')}

{(statistics as ApiChannelStatistics).recentTopMessages.map((message) => ( ))}
)}
); }; export default memo(withGlobal( (global, { chatId }): StateProps => { const statistics = selectStatistics(global, chatId); const chat = selectChat(global, chatId); const dcId = selectChatFullInfo(global, chatId)?.statisticsDcId; const isGroup = chat?.type === 'chatTypeSuperGroup'; return { statistics, dcId, isGroup, }; }, )(Statistics));