Wire up bunch of interaction events into Posthog (#7707)

pull/21833/head
Michael Telatynski 2022-02-09 14:42:08 +00:00 committed by GitHub
parent 5620b83d34
commit 999e1b7421
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 120 additions and 26 deletions

View File

@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { PureComponent } from "react"; import { PureComponent, SyntheticEvent } from "react";
import { Screen as ScreenEvent } from "matrix-analytics-events/types/typescript/Screen"; import { Screen as ScreenEvent } from "matrix-analytics-events/types/typescript/Screen";
import { Interaction as InteractionEvent } from "matrix-analytics-events/types/typescript/Interaction";
import PageType from "./PageTypes"; import PageType from "./PageTypes";
import Views from "./Views"; import Views from "./Views";
@ -88,6 +89,21 @@ export default class PosthogTrackers {
this.override = null; this.override = null;
this.trackPage(); this.trackPage();
} }
public static trackInteraction(name: InteractionEvent["name"], ev?: SyntheticEvent): void {
let interactionType: InteractionEvent["interactionType"];
if (ev?.type === "click") {
interactionType = "Pointer";
} else if (ev?.type.startsWith("key")) {
interactionType = "Keyboard";
}
PosthogAnalytics.instance.trackEvent<InteractionEvent>({
eventName: "Interaction",
interactionType,
name,
});
}
} }
export class PosthogScreenTracker extends PureComponent<{ screenName: ScreenName }> { export class PosthogScreenTracker extends PureComponent<{ screenName: ScreenName }> {

View File

@ -25,6 +25,7 @@ import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers';
import { parseFragment as parseHtml, Element as ChildElement } from "parse5"; import { parseFragment as parseHtml, Element as ChildElement } from "parse5";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { IContent } from 'matrix-js-sdk/src/models/event'; import { IContent } from 'matrix-js-sdk/src/models/event';
import { SlashCommand as SlashCommandEvent } from "matrix-analytics-events/types/typescript/SlashCommand";
import { MatrixClientPeg } from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
@ -62,6 +63,7 @@ import { shouldShowComponent } from "./customisations/helpers/UIComponents";
import { TimelineRenderingType } from './contexts/RoomContext'; import { TimelineRenderingType } from './contexts/RoomContext';
import RoomViewStore from "./stores/RoomViewStore"; import RoomViewStore from "./stores/RoomViewStore";
import { XOR } from "./@types/common"; import { XOR } from "./@types/common";
import { PosthogAnalytics } from "./PosthogAnalytics";
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event { interface HTMLInputEvent extends Event {
@ -105,6 +107,7 @@ interface ICommandOpts {
aliases?: string[]; aliases?: string[];
args?: string; args?: string;
description: string; description: string;
analyticsName?: SlashCommandEvent["command"];
runFn?: RunFn; runFn?: RunFn;
category: string; category: string;
hideCompletionAfterSpace?: boolean; hideCompletionAfterSpace?: boolean;
@ -121,6 +124,7 @@ export class Command {
public readonly category: string; public readonly category: string;
public readonly hideCompletionAfterSpace: boolean; public readonly hideCompletionAfterSpace: boolean;
public readonly renderingTypes?: TimelineRenderingType[]; public readonly renderingTypes?: TimelineRenderingType[];
public readonly analyticsName?: SlashCommandEvent["command"];
private readonly _isEnabled?: () => boolean; private readonly _isEnabled?: () => boolean;
constructor(opts: ICommandOpts) { constructor(opts: ICommandOpts) {
@ -133,6 +137,7 @@ export class Command {
this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false; this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false;
this._isEnabled = opts.isEnabled; this._isEnabled = opts.isEnabled;
this.renderingTypes = opts.renderingTypes; this.renderingTypes = opts.renderingTypes;
this.analyticsName = opts.analyticsName;
} }
public getCommand() { public getCommand() {
@ -167,6 +172,13 @@ export class Command {
); );
} }
if (this.analyticsName) {
PosthogAnalytics.instance.trackEvent<SlashCommandEvent>({
eventName: "SlashCommand",
command: this.analyticsName,
});
}
return this.runFn.bind(this)(roomId, args); return this.runFn.bind(this)(roomId, args);
} }
@ -488,6 +500,7 @@ export const Commands = [
command: 'invite', command: 'invite',
args: '<user-id> [<reason>]', args: '<user-id> [<reason>]',
description: _td('Invites user with given id to current room'), description: _td('Invites user with given id to current room'),
analyticsName: "invite",
isEnabled: () => shouldShowComponent(UIComponent.InviteUsers), isEnabled: () => shouldShowComponent(UIComponent.InviteUsers),
runFn: function(roomId, args) { runFn: function(roomId, args) {
if (args) { if (args) {
@ -674,6 +687,7 @@ export const Commands = [
command: 'part', command: 'part',
args: '[<room-address>]', args: '[<room-address>]',
description: _td('Leave room'), description: _td('Leave room'),
analyticsName: "part",
runFn: function(roomId, args) { runFn: function(roomId, args) {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();

View File

@ -36,7 +36,6 @@ import { EchoChamber } from "../../../stores/local-echo/EchoChamber";
import { RoomNotifState } from "../../../RoomNotifs"; import { RoomNotifState } from "../../../RoomNotifs";
import Modal from "../../../Modal"; import Modal from "../../../Modal";
import ExportDialog from "../dialogs/ExportDialog"; import ExportDialog from "../dialogs/ExportDialog";
import { onRoomFilesClick, onRoomMembersClick } from "../right_panel/RoomSummaryCard";
import RoomViewStore from "../../../stores/RoomViewStore"; import RoomViewStore from "../../../stores/RoomViewStore";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases'; import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog"; import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog";
@ -44,6 +43,7 @@ import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import DMRoomMap from "../../../utils/DMRoomMap"; import DMRoomMap from "../../../utils/DMRoomMap";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import PosthogTrackers from "../../../PosthogTrackers";
interface IProps extends IContextMenuProps { interface IProps extends IContextMenuProps {
room: Room; room: Room;
@ -86,6 +86,8 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
room_id: room.roomId, room_id: room.roomId,
}); });
onFinished(); onFinished();
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuLeaveItem", ev);
}; };
leaveOption = <IconizedContextMenuOption leaveOption = <IconizedContextMenuOption
@ -109,6 +111,8 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
roomId: room.roomId, roomId: room.roomId,
}); });
onFinished(); onFinished();
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuInviteItem", ev);
}; };
inviteOption = <IconizedContextMenuOption inviteOption = <IconizedContextMenuOption
@ -124,7 +128,10 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
if (room.getMyMembership() === "join") { if (room.getMyMembership() === "join") {
const isFavorite = roomTags.includes(DefaultTagID.Favourite); const isFavorite = roomTags.includes(DefaultTagID.Favourite);
favouriteOption = <IconizedContextMenuCheckbox favouriteOption = <IconizedContextMenuCheckbox
onClick={(e) => onTagRoom(e, DefaultTagID.Favourite)} onClick={(e) => {
onTagRoom(e, DefaultTagID.Favourite);
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e);
}}
active={isFavorite} active={isFavorite}
label={isFavorite ? _t("Favourited") : _t("Favourite")} label={isFavorite ? _t("Favourited") : _t("Favourite")}
iconClassName="mx_RoomTile_iconStar" iconClassName="mx_RoomTile_iconStar"
@ -171,6 +178,8 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
initial_tab_id: ROOM_NOTIFICATIONS_TAB, initial_tab_id: ROOM_NOTIFICATIONS_TAB,
}); });
onFinished(); onFinished();
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev);
}} }}
label={_t("Notifications")} label={_t("Notifications")}
iconClassName={iconClassName} iconClassName={iconClassName}
@ -190,8 +199,9 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
ev.stopPropagation(); ev.stopPropagation();
ensureViewingRoom(); ensureViewingRoom();
onRoomMembersClick(false); RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, false);
onFinished(); onFinished();
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuPeopleItem", ev);
}} }}
label={_t("People")} label={_t("People")}
iconClassName="mx_RoomTile_iconPeople" iconClassName="mx_RoomTile_iconPeople"
@ -258,7 +268,7 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
ev.stopPropagation(); ev.stopPropagation();
ensureViewingRoom(); ensureViewingRoom();
onRoomFilesClick(false); RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, false);
onFinished(); onFinished();
}} }}
label={_t("Files")} label={_t("Files")}
@ -291,6 +301,7 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
room_id: room.roomId, room_id: room.roomId,
}); });
onFinished(); onFinished();
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuSettingsItem", ev);
}} }}
label={_t("Settings")} label={_t("Settings")}
iconClassName="mx_RoomTile_iconSettings" iconClassName="mx_RoomTile_iconSettings"

View File

@ -19,7 +19,7 @@ import classNames from 'classnames';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import RightPanelStore from '../../../stores/right-panel/RightPanelStore'; import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { backLabelForPhase } from '../../../stores/right-panel/RightPanelStorePhases'; import { backLabelForPhase } from '../../../stores/right-panel/RightPanelStorePhases';
@ -30,8 +30,9 @@ interface IProps {
className?: string; className?: string;
withoutScrollContainer?: boolean; withoutScrollContainer?: boolean;
closeLabel?: string; closeLabel?: string;
onClose?(): void; onClose?(ev: ButtonEvent): void;
cardState?; onBack?(ev: ButtonEvent): void;
cardState?: any;
} }
interface IGroupProps { interface IGroupProps {
@ -49,6 +50,7 @@ export const Group: React.FC<IGroupProps> = ({ className, title, children }) =>
const BaseCard: React.FC<IProps> = ({ const BaseCard: React.FC<IProps> = ({
closeLabel, closeLabel,
onClose, onClose,
onBack,
className, className,
header, header,
footer, footer,
@ -59,7 +61,8 @@ const BaseCard: React.FC<IProps> = ({
const cardHistory = RightPanelStore.instance.roomPhaseHistory; const cardHistory = RightPanelStore.instance.roomPhaseHistory;
if (cardHistory.length > 1) { if (cardHistory.length > 1) {
const prevCard = cardHistory[cardHistory.length - 2]; const prevCard = cardHistory[cardHistory.length - 2];
const onBackClick = () => { const onBackClick = (ev: ButtonEvent) => {
onBack?.(ev);
RightPanelStore.instance.popCard(); RightPanelStore.instance.popCard();
}; };
const label = backLabelForPhase(prevCard.phase) ?? _t("Back"); const label = backLabelForPhase(prevCard.phase) ?? _t("Back");

View File

@ -23,7 +23,7 @@ import { useIsEncrypted } from '../../../hooks/useIsEncrypted';
import BaseCard, { Group } from "./BaseCard"; import BaseCard, { Group } from "./BaseCard";
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import RoomAvatar from "../avatars/RoomAvatar"; import RoomAvatar from "../avatars/RoomAvatar";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import defaultDispatcher from "../../../dispatcher/dispatcher"; import defaultDispatcher from "../../../dispatcher/dispatcher";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases'; import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import Modal from "../../../Modal"; import Modal from "../../../Modal";
@ -47,6 +47,7 @@ import RoomName from "../elements/RoomName";
import UIStore from "../../../stores/UIStore"; import UIStore from "../../../stores/UIStore";
import ExportDialog from "../dialogs/ExportDialog"; import ExportDialog from "../dialogs/ExportDialog";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import PosthogTrackers from "../../../PosthogTrackers";
interface IProps { interface IProps {
room: Room; room: Room;
@ -59,7 +60,7 @@ interface IAppsSectionProps {
interface IButtonProps { interface IButtonProps {
className: string; className: string;
onClick(): void; onClick(ev: ButtonEvent): void;
} }
const Button: React.FC<IButtonProps> = ({ children, className, onClick }) => { const Button: React.FC<IButtonProps> = ({ children, className, onClick }) => {
@ -229,16 +230,18 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
</Group>; </Group>;
}; };
export const onRoomMembersClick = (allowClose = true) => { const onRoomMembersClick = (ev: ButtonEvent) => {
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, allowClose); RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, true);
PosthogTrackers.trackInteraction("WebRightPanelRoomInfoPeopleButton", ev);
}; };
export const onRoomFilesClick = (allowClose = true) => { const onRoomFilesClick = () => {
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, allowClose); RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, true);
}; };
const onRoomSettingsClick = () => { const onRoomSettingsClick = (ev: ButtonEvent) => {
defaultDispatcher.dispatch({ action: "open_room_settings" }); defaultDispatcher.dispatch({ action: "open_room_settings" });
PosthogTrackers.trackInteraction("WebRightPanelRoomInfoSettingsButton", ev);
}; };
const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => { const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {

View File

@ -34,7 +34,7 @@ import Modal from '../../../Modal';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import createRoom, { findDMForUser, privateShouldBeEncrypted } from '../../../createRoom'; import createRoom, { findDMForUser, privateShouldBeEncrypted } from '../../../createRoom';
import DMRoomMap from '../../../utils/DMRoomMap'; import DMRoomMap from '../../../utils/DMRoomMap';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import RoomViewStore from "../../../stores/RoomViewStore"; import RoomViewStore from "../../../stores/RoomViewStore";
import MultiInviter from "../../../utils/MultiInviter"; import MultiInviter from "../../../utils/MultiInviter";
@ -78,6 +78,7 @@ import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStoreIPanelState'; import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStoreIPanelState';
import { useUserStatusMessage } from "../../../hooks/useUserStatusMessage"; import { useUserStatusMessage } from "../../../hooks/useUserStatusMessage";
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier'; import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
import PosthogTrackers from "../../../PosthogTrackers";
export interface IDevice { export interface IDevice {
deviceId: string; deviceId: string;
@ -422,7 +423,7 @@ const UserOptionsSection: React.FC<{
if (canInvite && (member?.membership ?? 'leave') === 'leave' && shouldShowComponent(UIComponent.InviteUsers)) { if (canInvite && (member?.membership ?? 'leave') === 'leave' && shouldShowComponent(UIComponent.InviteUsers)) {
const roomId = member && member.roomId ? member.roomId : RoomViewStore.getRoomId(); const roomId = member && member.roomId ? member.roomId : RoomViewStore.getRoomId();
const onInviteUserButton = async () => { const onInviteUserButton = async (ev: ButtonEvent) => {
try { try {
// We use a MultiInviter to re-use the invite logic, even though // We use a MultiInviter to re-use the invite logic, even though
// we're only inviting one user. // we're only inviting one user.
@ -438,6 +439,8 @@ const UserOptionsSection: React.FC<{
description: ((err && err.message) ? err.message : _t("Operation failed")), description: ((err && err.message) ? err.message : _t("Operation failed")),
}); });
} }
PosthogTrackers.trackInteraction("WebRightPanelRoomUserInfoInviteButton", ev);
}; };
inviteUserButton = ( inviteUserButton = (
@ -1719,6 +1722,11 @@ const UserInfo: React.FC<IProps> = ({
onClose={onClose} onClose={onClose}
closeLabel={closeLabel} closeLabel={closeLabel}
cardState={cardState} cardState={cardState}
onBack={(ev: ButtonEvent) => {
if (RightPanelStore.instance.previousCard.phase === RightPanelPhases.RoomMemberList) {
PosthogTrackers.trackInteraction("WebRightPanelRoomUserInfoBackButton", ev);
}
}}
> >
{ content } { content }
</BaseCard>; </BaseCard>;

View File

@ -20,6 +20,7 @@ import { EventStatus, IContent, MatrixEvent } from 'matrix-js-sdk/src/models/eve
import { MsgType } from 'matrix-js-sdk/src/@types/event'; import { MsgType } from 'matrix-js-sdk/src/@types/event';
import { Room } from 'matrix-js-sdk/src/models/room'; import { Room } from 'matrix-js-sdk/src/models/room';
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { Composer as ComposerEvent } from "matrix-analytics-events/types/typescript/Composer";
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
@ -46,6 +47,7 @@ import RoomContext from '../../../contexts/RoomContext';
import { ComposerType } from "../../../dispatcher/payloads/ComposerInsertPayload"; import { ComposerType } from "../../../dispatcher/payloads/ComposerInsertPayload";
import { getSlashCommand, isSlashCommand, runSlashCommand, shouldSendAnyway } from "../../../editor/commands"; import { getSlashCommand, isSlashCommand, runSlashCommand, shouldSendAnyway } from "../../../editor/commands";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { PosthogAnalytics } from "../../../PosthogAnalytics";
function getHtmlReplyFallback(mxEvent: MatrixEvent): string { function getHtmlReplyFallback(mxEvent: MatrixEvent): string {
const html = mxEvent.getContent().formatted_body; const html = mxEvent.getContent().formatted_body;
@ -295,9 +297,17 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
private sendEdit = async (): Promise<void> => { private sendEdit = async (): Promise<void> => {
if (this.state.saveDisabled) return; if (this.state.saveDisabled) return;
const startTime = CountlyAnalytics.getTimestamp();
const editedEvent = this.props.editState.getEvent(); const editedEvent = this.props.editState.getEvent();
PosthogAnalytics.instance.trackEvent<ComposerEvent>({
eventName: "Composer",
isEditing: true,
inThread: !!editedEvent?.getThread(),
isReply: !!editedEvent.replyEventId,
});
const startTime = CountlyAnalytics.getTimestamp();
// Replace emoticon at the end of the message // Replace emoticon at the end of the message
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) { if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
const caret = this.editorRef.current?.getCaret(); const caret = this.editorRef.current?.getCaret();
@ -323,7 +333,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
if (!containsEmote(this.model) && isSlashCommand(this.model)) { if (!containsEmote(this.model) && isSlashCommand(this.model)) {
const [cmd, args, commandText] = getSlashCommand(this.model); const [cmd, args, commandText] = getSlashCommand(this.model);
if (cmd) { if (cmd) {
const threadId = this.props.editState?.getEvent()?.getThread()?.id || null; const threadId = editedEvent?.getThread()?.id || null;
if (cmd.category === CommandCategories.messages) { if (cmd.category === CommandCategories.messages) {
editContent["m.new_content"] = await runSlashCommand(cmd, args, roomId, threadId); editContent["m.new_content"] = await runSlashCommand(cmd, args, roomId, threadId);
if (!editContent["m.new_content"]) { if (!editContent["m.new_content"]) {

View File

@ -40,13 +40,14 @@ import SettingsStore from "../../../settings/SettingsStore";
import TruncatedList from '../elements/TruncatedList'; import TruncatedList from '../elements/TruncatedList';
import Spinner from "../elements/Spinner"; import Spinner from "../elements/Spinner";
import SearchBox from "../../structures/SearchBox"; import SearchBox from "../../structures/SearchBox";
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
import EntityTile from "./EntityTile"; import EntityTile from "./EntityTile";
import MemberTile from "./MemberTile"; import MemberTile from "./MemberTile";
import BaseAvatar from '../avatars/BaseAvatar'; import BaseAvatar from '../avatars/BaseAvatar';
import SpaceStore from "../../../stores/spaces/SpaceStore"; import SpaceStore from "../../../stores/spaces/SpaceStore";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../settings/UIFeature"; import { UIComponent } from "../../../settings/UIFeature";
import PosthogTrackers from "../../../PosthogTrackers";
const INITIAL_LOAD_NUM_MEMBERS = 30; const INITIAL_LOAD_NUM_MEMBERS = 30;
const INITIAL_LOAD_NUM_INVITED = 5; const INITIAL_LOAD_NUM_INVITED = 5;
@ -595,7 +596,9 @@ export default class MemberList extends React.Component<IProps, IState> {
</BaseCard>; </BaseCard>;
} }
onInviteButtonClick = (): void => { private onInviteButtonClick = (ev: ButtonEvent): void => {
PosthogTrackers.trackInteraction("WebRightPanelMemberListInviteButton", ev);
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({ action: 'require_registration' }); dis.dispatch({ action: 'require_registration' });
return; return;

View File

@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { createRef } from 'react'; import React, { createRef } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { MatrixEvent, IEventRelation } from "matrix-js-sdk/src/models/event"; import { MatrixEvent, IEventRelation } from "matrix-js-sdk/src/models/event";
@ -47,12 +48,13 @@ import UIStore, { UI_EVENTS } from '../../../stores/UIStore';
import RoomContext from '../../../contexts/RoomContext'; import RoomContext from '../../../contexts/RoomContext';
import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdatedPayload"; import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdatedPayload";
import MessageComposerButtons from './MessageComposerButtons'; import MessageComposerButtons from './MessageComposerButtons';
import { ButtonEvent } from '../elements/AccessibleButton';
let instanceCount = 0; let instanceCount = 0;
const NARROW_MODE_BREAKPOINT = 500; const NARROW_MODE_BREAKPOINT = 500;
interface ISendButtonProps { interface ISendButtonProps {
onClick: () => void; onClick: (ev: ButtonEvent) => void;
title?: string; // defaults to something generic title?: string; // defaults to something generic
} }

View File

@ -52,6 +52,7 @@ import IconizedContextMenu, {
} from "../context_menus/IconizedContextMenu"; } from "../context_menus/IconizedContextMenu";
import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore"; import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import PosthogTrackers from "../../../PosthogTrackers";
interface IProps { interface IProps {
room: Room; room: Room;
@ -255,6 +256,8 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
ev.stopPropagation(); ev.stopPropagation();
const target = ev.target as HTMLButtonElement; const target = ev.target as HTMLButtonElement;
this.setState({ notificationsMenuPosition: target.getBoundingClientRect() }); this.setState({ notificationsMenuPosition: target.getBoundingClientRect() });
PosthogTrackers.trackInteraction("WebRoomListRoomTileNotificationsMenu", ev);
}; };
private onCloseNotificationsMenu = () => { private onCloseNotificationsMenu = () => {
@ -322,6 +325,8 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
room_id: this.props.room.roomId, room_id: this.props.room.roomId,
}); });
this.setState({ generalMenuPosition: null }); // hide the menu this.setState({ generalMenuPosition: null }); // hide the menu
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuLeaveItem", ev);
}; };
private onForgetRoomClick = (ev: ButtonEvent) => { private onForgetRoomClick = (ev: ButtonEvent) => {
@ -344,6 +349,8 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
room_id: this.props.room.roomId, room_id: this.props.room.roomId,
}); });
this.setState({ generalMenuPosition: null }); // hide the menu this.setState({ generalMenuPosition: null }); // hide the menu
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuSettingsItem", ev);
}; };
private onCopyRoomClick = (ev: ButtonEvent) => { private onCopyRoomClick = (ev: ButtonEvent) => {
@ -366,6 +373,8 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
roomId: this.props.room.roomId, roomId: this.props.room.roomId,
}); });
this.setState({ generalMenuPosition: null }); // hide the menu this.setState({ generalMenuPosition: null }); // hide the menu
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuInviteItem", ev);
}; };
private async saveNotifState(ev: ButtonEvent, newState: RoomNotifState) { private async saveNotifState(ev: ButtonEvent, newState: RoomNotifState) {
@ -500,7 +509,10 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
> >
<IconizedContextMenuOptionList> <IconizedContextMenuOptionList>
<IconizedContextMenuCheckbox <IconizedContextMenuCheckbox
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)} onClick={(e) => {
this.onTagRoom(e, DefaultTagID.Favourite);
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuFavouriteToggle", e);
}}
active={isFavorite} active={isFavorite}
label={favouriteLabel} label={favouriteLabel}
iconClassName="mx_RoomTile_iconStar" iconClassName="mx_RoomTile_iconStar"

View File

@ -21,6 +21,7 @@ import { DebouncedFunc, throttle } from 'lodash';
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event"; import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { Room } from 'matrix-js-sdk/src/models/room'; import { Room } from 'matrix-js-sdk/src/models/room';
import { Composer as ComposerEvent } from "matrix-analytics-events/types/typescript/Composer";
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import EditorModel from '../../../editor/model'; import EditorModel from '../../../editor/model';
@ -57,6 +58,7 @@ import DocumentPosition from "../../../editor/position";
import { ComposerType } from "../../../dispatcher/payloads/ComposerInsertPayload"; import { ComposerType } from "../../../dispatcher/payloads/ComposerInsertPayload";
import { getSlashCommand, isSlashCommand, runSlashCommand, shouldSendAnyway } from "../../../editor/commands"; import { getSlashCommand, isSlashCommand, runSlashCommand, shouldSendAnyway } from "../../../editor/commands";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { PosthogAnalytics } from "../../../PosthogAnalytics";
interface IAddReplyOpts { interface IAddReplyOpts {
permalinkCreator?: RoomPermalinkCreator; permalinkCreator?: RoomPermalinkCreator;
@ -344,6 +346,13 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
return; return;
} }
PosthogAnalytics.instance.trackEvent<ComposerEvent>({
eventName: "Composer",
isEditing: false,
inThread: this.props.relation?.rel_type === RelationType.Thread,
isReply: !!this.props.replyToEvent,
});
// Replace emoticon at the end of the message // Replace emoticon at the end of the message
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) { if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
const indexOfLastPart = model.parts.length - 1; const indexOfLastPart = model.parts.length - 1;

View File

@ -18,7 +18,7 @@ import React, { ContextType } from 'react';
import { _t } from "../../../../../languageHandler"; import { _t } from "../../../../../languageHandler";
import RoomProfileSettings from "../../../room_settings/RoomProfileSettings"; import RoomProfileSettings from "../../../room_settings/RoomProfileSettings";
import AccessibleButton from "../../../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton";
import dis from "../../../../../dispatcher/dispatcher"; import dis from "../../../../../dispatcher/dispatcher";
import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
import SettingsStore from "../../../../../settings/SettingsStore"; import SettingsStore from "../../../../../settings/SettingsStore";
@ -27,6 +27,7 @@ import { replaceableComponent } from "../../../../../utils/replaceableComponent"
import UrlPreviewSettings from "../../../room_settings/UrlPreviewSettings"; import UrlPreviewSettings from "../../../room_settings/UrlPreviewSettings";
import RelatedGroupSettings from "../../../room_settings/RelatedGroupSettings"; import RelatedGroupSettings from "../../../room_settings/RelatedGroupSettings";
import AliasSettings from "../../../room_settings/AliasSettings"; import AliasSettings from "../../../room_settings/AliasSettings";
import PosthogTrackers from "../../../../../PosthogTrackers";
interface IProps { interface IProps {
roomId: string; roomId: string;
@ -49,11 +50,13 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
}; };
} }
private onLeaveClick = (): void => { private onLeaveClick = (ev: ButtonEvent): void => {
dis.dispatch({ dis.dispatch({
action: 'leave_room', action: 'leave_room',
room_id: this.props.roomId, room_id: this.props.roomId,
}); });
PosthogTrackers.trackInteraction("WebRoomSettingsLeaveButton", ev);
}; };
public render(): JSX.Element { public render(): JSX.Element {