Statistics: Support for Public Shares, various fixes (#1889)

This commit is contained in:
Alexander Zinchuk 2022-05-31 20:58:39 +04:00
parent 017170c2e2
commit 2dfac887cd
17 changed files with 176 additions and 18 deletions

View File

@ -3,11 +3,14 @@ import type {
ApiChannelStatistics,
ApiGroupStatistics,
ApiMessageStatistics,
ApiMessagePublicForward,
StatisticsGraph,
StatisticsOverviewItem,
StatisticsOverviewPercentage,
StatisticsOverviewPeriod,
} from '../../types';
import { buildAvatarHash } from './chats';
import { buildApiPeerId } from './peers';
export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiChannelStatistics {
return {
@ -61,6 +64,31 @@ export function buildMessageStatistics(stats: GramJs.stats.MessageStats): ApiMes
};
}
export function buildMessagePublicForwards(
result: GramJs.messages.TypeMessages,
): ApiMessagePublicForward[] | undefined {
if (!result || !('messages' in result)) {
return undefined;
}
return result.messages.map((message) => {
const peerId = buildApiPeerId((message.peerId as GramJs.PeerChannel).channelId, 'channel');
const channel = result.chats.find((p) => buildApiPeerId(p.id, 'channel') === peerId);
return {
messageId: message.id,
views: (message as GramJs.Message).views,
title: (channel as GramJs.Channel).title,
chat: {
id: peerId,
type: 'chatTypeChannel',
username: (channel as GramJs.Channel).username,
avatarHash: buildAvatarHash((channel as GramJs.Channel).photo),
},
};
});
}
export function buildGraph(
result: GramJs.TypeStatsGraph, isPercentage?: boolean,
): StatisticsGraph | undefined {

View File

@ -85,7 +85,8 @@ export {
} from './reactions';
export {
fetchChannelStatistics, fetchGroupStatistics, fetchMessageStatistics, fetchStatisticsAsyncGraph,
fetchChannelStatistics, fetchGroupStatistics, fetchMessageStatistics,
fetchMessagePublicForwards, fetchStatisticsAsyncGraph,
} from './statistics';
export {

View File

@ -2,13 +2,14 @@ import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs';
import type {
ApiChat, ApiChannelStatistics, ApiGroupStatistics, ApiMessageStatistics, StatisticsGraph,
ApiChat, ApiChannelStatistics, ApiGroupStatistics, ApiMessageStatistics, ApiMessagePublicForward, StatisticsGraph,
} from '../../types';
import { invokeRequest } from './client';
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
import { buildInputEntity } from '../gramjsBuilders';
import {
buildChannelStatistics, buildGroupStatistics, buildMessageStatistics, buildGraph,
buildChannelStatistics, buildGroupStatistics, buildMessageStatistics, buildMessagePublicForwards, buildGraph,
} from '../apiBuilders/statistics';
export async function fetchChannelStatistics({
@ -58,6 +59,32 @@ export async function fetchMessageStatistics({
return buildMessageStatistics(result);
}
export async function fetchMessagePublicForwards({
chat,
messageId,
dcId,
}: {
chat: ApiChat;
messageId: number;
dcId?: number;
}): Promise<ApiMessagePublicForward[] | undefined> {
const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
msgId: messageId,
offsetPeer: new GramJs.InputPeerEmpty(),
}), undefined, undefined, undefined, dcId);
if (!result) {
return undefined;
}
if ('chats' in result) {
addEntitiesWithPhotosToLocalDb(result.chats);
}
return buildMessagePublicForwards(result);
}
export async function fetchStatisticsAsyncGraph({
token,
x,

View File

@ -1,4 +1,5 @@
import type { ApiMessage } from './messages';
import type { ApiChat } from './chats';
import type { ApiMessage, ApiPhoto } from './messages';
export interface ApiChannelStatistics {
growthGraph?: StatisticsGraph | string;
@ -34,6 +35,15 @@ export interface ApiMessageStatistics {
viewsGraph?: StatisticsGraph | string;
forwards?: number;
views?: number;
publicForwards?: number;
publicForwardsData?: ApiMessagePublicForward[];
}
export interface ApiMessagePublicForward {
messageId: number;
views?: number;
title?: string;
chat: ApiChat;
}
export interface StatisticsGraph {

View File

@ -439,6 +439,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|| contentKey === HeaderContent.SharedMedia
|| contentKey === HeaderContent.MemberList
|| contentKey === HeaderContent.AddingMembers
|| contentKey === HeaderContent.MessageStatistics
|| isManagement
);

View File

@ -5,7 +5,7 @@ import React, {
import { getActions, withGlobal } from '../../../global';
import { callApi } from '../../../api/gramjs';
import type { ApiMessageStatistics, StatisticsGraph } from '../../../api/types';
import type { ApiMessageStatistics, ApiMessagePublicForward, StatisticsGraph } from '../../../api/types';
import { selectChat } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
@ -14,6 +14,7 @@ import useForceUpdate from '../../../hooks/useForceUpdate';
import Loading from '../../ui/Loading';
import StatisticsOverview from './StatisticsOverview';
import StatisticsPublicForward from './StatisticsPublicForward';
import './Statistics.scss';
@ -156,6 +157,16 @@ const Statistics: FC<OwnProps & StateProps> = ({
<div className={buildClassName('Statistics__graph', !loadedCharts.current.includes(graph) && 'hidden')} />
))}
</div>
{Boolean(statistics.publicForwards) && (
<div className="Statistics__public-forwards">
<h2 className="Statistics__public-forwards-title">{lang('Stats.Message.PublicShares')}</h2>
{statistics.publicForwardsData!.map((item: ApiMessagePublicForward) => (
<StatisticsPublicForward data={item} />
))}
</div>
)}
</div>
);
};

View File

@ -3,8 +3,8 @@
overflow-x: hidden;
overflow-y: hidden;
&__messages {
padding: 1rem 0.25rem;
&__messages, &__public-forwards {
padding: 1rem 0;
border-top: 1px solid var(--color-borders);
&-title {
@ -38,6 +38,7 @@
&.hidden {
opacity: 0;
margin: 0;
}
}
@ -47,7 +48,7 @@
}
.lovely-chart--header {
margin: 0 1rem;
margin: 0 0.75rem;
}
.lovely-chart--header,

View File

@ -160,6 +160,8 @@ const Statistics: FC<OwnProps & StateProps> = ({
);
loadedCharts.current.push(name);
containerRef.current!.children[index].classList.remove('hidden');
});
forceUpdate();
@ -180,7 +182,7 @@ const Statistics: FC<OwnProps & StateProps> = ({
<div ref={containerRef}>
{graphs.map((graph) => (
<div className={buildClassName('Statistics__graph', !loadedCharts.current.includes(graph) && 'hidden')} />
<div key={graph} className="Statistics__graph hidden" />
))}
</div>

View File

@ -13,7 +13,6 @@
&__title {
margin-right: 2em;
padding-left: 0.25rem;
font-size: 16px;
color: var(--text-color);
line-height: 30px;

View File

@ -5,7 +5,7 @@ import type {
ApiChannelStatistics, ApiGroupStatistics, ApiMessageStatistics, StatisticsOverviewItem,
} from '../../../api/types';
import { formatIntegerCompact } from '../../../util/textFormat';
import { formatInteger, formatIntegerCompact } from '../../../util/textFormat';
import { formatFullDate } from '../../../util/dateFormat';
import buildClassName from '../../../util/buildClassName';
import useLang from '../../../hooks/useLang';
@ -44,11 +44,14 @@ const GROUP_OVERVIEW: OverviewCell[][] = [
const MESSAGE_OVERVIEW: OverviewCell[][] = [
[
{ name: 'views', title: 'StatisticViews', isPlain: true },
{ name: 'views', title: 'Stats.Message.Views', isPlain: true },
{
name: 'forwards', title: 'PrivateShares', isPlain: true, isApproximate: true,
name: 'forwards', title: 'Stats.Message.PrivateShares', isPlain: true, isApproximate: true,
},
],
[
{ name: 'publicForwards', title: 'Stats.Message.PublicShares', isPlain: true },
],
];
export type OwnProps = {
@ -103,7 +106,9 @@ const StatisticsOverview: FC<OwnProps> = ({ isGroup, isMessage, statistics }) =>
if (cell.isPlain) {
return (
<td className="StatisticsOverview__table-cell">
<b className="StatisticsOverview__table-value">{cell.isApproximate ? `${field}` : field}</b>
<b className="StatisticsOverview__table-value">
{cell.isApproximate ? `${formatInteger(field)}` : formatInteger(field)}
</b>
<h3 className="StatisticsOverview__table-heading">{lang(cell.title)}</h3>
</td>
);

View File

@ -0,0 +1,25 @@
.StatisticsPublicForward {
position: relative;
cursor: pointer;
padding: 0.5rem 0.75rem;
display: flex;
align-items: center;
&:hover, &:active {
background-color: var(--color-chat-hover);
}
.Avatar {
flex-shrink: 0;
margin-right: 0.5rem;
}
&__title {
line-height: 1.25rem;
}
&__views {
color: var(--color-text-meta);
font-size: 0.8125rem;
}
}

View File

@ -0,0 +1,41 @@
import type { FC } from '../../../lib/teact/teact';
import React, { memo, useCallback } from '../../../lib/teact/teact';
import useLang from '../../../hooks/useLang';
import { getActions } from '../../../global';
import type { ApiMessagePublicForward } from '../../../api/types';
import Avatar from '../../common/Avatar';
import './StatisticsPublicForward.scss';
export type OwnProps = {
data: ApiMessagePublicForward;
};
const StatisticsPublicForward: FC<OwnProps> = ({ data }) => {
const lang = useLang();
const { openChatByUsername } = getActions();
const handleClick = useCallback(() => {
openChatByUsername({ username: data.chat.username, messageId: data.messageId });
}, [data, openChatByUsername]);
return (
<div className="StatisticsPublicForward" onClick={handleClick}>
<Avatar size="medium" chat={data.chat} />
<div className="StatisticsPublicForward__info">
<div className="StatisticsPublicForward__title">
{data.title}
</div>
<div className="StatisticsPublicForward__views">
{lang('ChannelStats.ViewsCount', data.views, 'i')}
</div>
</div>
</div>
);
};
export default memo(StatisticsPublicForward);

View File

@ -1,8 +1,7 @@
.StatisticsRecentMessage {
position: relative;
cursor: pointer;
padding: 0.5rem;
border-radius: var(--border-radius-default-small);
padding: 0.5rem 0.75rem;
&:hover, &:active {
background-color: var(--color-chat-hover);

View File

@ -49,7 +49,7 @@ const StatisticsRecentMessage: FC<OwnProps> = ({ message }) => {
{renderSummary(lang, message, mediaBlobUrl || mediaThumbnail, isRoundVideo)}
</div>
<div className="StatisticsRecentMessage__meta">
{lang('ChannelStats.ViewsCount', message.views)}
{lang('ChannelStats.ViewsCount', message.views, 'i')}
</div>
</div>

View File

@ -44,10 +44,16 @@ addActionHandler('loadMessageStatistics', async (global, actions, payload) => {
global = getGlobal();
const { views, forwards } = selectChatMessages(global, chatId)[messageId];
result.views = views;
result.forwards = forwards;
const dcId = chat.fullInfo!.statisticsDcId;
const publicForwards = await callApi('fetchMessagePublicForwards', { chat, messageId, dcId });
result.publicForwards = publicForwards?.length;
result.publicForwardsData = publicForwards;
global = getGlobal();
setGlobal(updateMessageStatistics(global, result));
});

View File

@ -1226,4 +1226,5 @@ folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;`;

View File

@ -227,6 +227,7 @@
"stats.getBroadcastStats",
"stats.getMegagroupStats",
"stats.getMessageStats",
"stats.getMessagePublicForwards",
"stats.loadAsyncGraph",
"messages.getAttachMenuBots",
"messages.getAttachMenuBot",