Add analytics for the ViewRoom action (#7666)

This commit is contained in:
Michael Telatynski 2022-02-10 14:29:55 +00:00 committed by GitHub
parent 20e9d0c159
commit 0d6ef76605
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 332 additions and 159 deletions

View file

@ -31,6 +31,7 @@ import { Action } from "./dispatcher/actions";
import { hideToast as hideUpdateToast } from "./toasts/UpdateToast";
import { MatrixClientPeg } from "./MatrixClientPeg";
import { idbLoad, idbSave, idbDelete } from "./utils/StorageManager";
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
@ -185,9 +186,10 @@ export default abstract class BasePlatform {
const notification = new window.Notification(title, notifBody);
notification.onclick = () => {
const payload: ActionPayload = {
const payload: ViewRoomPayload = {
action: Action.ViewRoom,
room_id: room.roomId,
_trigger: "Notification",
};
if (ev.getThread()) {

View file

@ -56,6 +56,7 @@ import { getIncomingCallToastKey } from './toasts/IncomingCallToast';
import ToastStore from './stores/ToastStore';
import IncomingCallToast from "./toasts/IncomingCallToast";
import Resend from './Resend';
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
export const PROTOCOL_PSTN = 'm.protocol.pstn';
export const PROTOCOL_PSTN_PREFIXED = 'im.vector.protocol.pstn';
@ -890,9 +891,10 @@ export default class CallHandler extends EventEmitter {
call.answer();
this.setActiveCallRoomId(roomId);
CountlyAnalytics.instance.trackJoinCall(roomId, call.type === CallType.Video, false);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: "WebAcceptCall",
});
}
@ -927,9 +929,10 @@ export default class CallHandler extends EventEmitter {
const roomId = await ensureDMExists(MatrixClientPeg.get(), nativeUserId);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: "WebDialPad",
});
await this.placeMatrixCall(roomId, CallType.Voice, null);
@ -957,11 +960,12 @@ export default class CallHandler extends EventEmitter {
const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), destination);
this.placeCall(dmRoomId, call.type, call);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: dmRoomId,
should_peek: false,
joining: false,
_trigger: undefined, // other
});
} else {
try {

View file

@ -262,7 +262,7 @@ interface ICreateRoomEvent extends IEvent {
};
}
interface IJoinRoomEvent extends IEvent {
export interface IJoinRoomEvent extends IEvent {
key: Action.JoinRoom;
dur: number; // how long it took to join (until remote echo)
segmentation: {
@ -270,7 +270,7 @@ interface IJoinRoomEvent extends IEvent {
num_users: number;
is_encrypted: boolean;
is_public: boolean;
type: "room_directory" | "slash_command" | "link" | "invite";
type: "room_directory" | "slash_command" | "link" | "invite" | "tombstone";
};
}
/* eslint-enable camelcase */

View file

@ -64,6 +64,7 @@ import { TimelineRenderingType } from './contexts/RoomContext';
import RoomViewStore from "./stores/RoomViewStore";
import { XOR } from "./@types/common";
import { PosthogAnalytics } from "./PosthogAnalytics";
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event {
@ -344,11 +345,13 @@ export const Commands = [
logger.log(
`/timestamp_to_event: found ${eventId} (${originServerTs}) for timestamp=${unixTimestamp}`,
);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: eventId,
highlighted: true,
room_id: roomId,
_trigger: "SlashCommand",
_viaKeyboard: true,
});
})());
}
@ -608,26 +611,24 @@ export const Commands = [
roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_alias: roomAlias,
auto_join: true,
_type: "slash_command", // instrumentation
_trigger: "SlashCommand",
_viaKeyboard: true,
});
return success();
} else if (params[0][0] === '!') {
const [roomId, ...viaServers] = params;
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
opts: {
// These are passed down to the js-sdk's /join call
viaServers: viaServers,
},
via_servers: viaServers, // for the rejoin button
auto_join: true,
_type: "slash_command", // instrumentation
_trigger: "SlashCommand",
_viaKeyboard: true,
});
return success();
} else if (isPermalink) {
@ -649,10 +650,11 @@ export const Commands = [
const viaServers = permalinkParts.viaServers;
const eventId = permalinkParts.eventId;
const dispatch = {
const dispatch: ViewRoomPayload = {
action: Action.ViewRoom,
auto_join: true,
_type: "slash_command", // instrumentation
_trigger: "SlashCommand",
_viaKeyboard: true,
};
if (entity[0] === '!') dispatch["room_id"] = entity;
@ -1150,9 +1152,11 @@ export const Commands = [
const roomId = await ensureDMExists(MatrixClientPeg.get(), userId);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: "SlashCommand",
_viaKeyboard: true,
});
})());
},
@ -1172,9 +1176,11 @@ export const Commands = [
return success((async () => {
const cli = MatrixClientPeg.get();
const roomId = await ensureDMExists(cli, userId);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: "SlashCommand",
_viaKeyboard: true,
});
if (msg) {
cli.sendTextMessage(roomId, msg);

View file

@ -45,6 +45,7 @@ import { ROOM_SECURITY_TAB } from "./components/views/dialogs/RoomSettingsDialog
import AccessibleButton from './components/views/elements/AccessibleButton';
import RightPanelStore from './stores/right-panel/RightPanelStore';
import UserIdentifierCustomisations from './customisations/UserIdentifier';
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
export function getSenderName(event: MatrixEvent): string {
return event.sender?.name ?? event.getSender() ?? _t("Someone");
@ -522,11 +523,12 @@ function textForPowerEvent(event: MatrixEvent): () => string | null {
}
const onPinnedOrUnpinnedMessageClick = (messageId: string, roomId: string): void => {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: messageId,
highlighted: true,
room_id: roomId,
_trigger: undefined, // room doesn't change
});
};

View file

@ -119,6 +119,7 @@ import { ActionPayload } from "../../dispatcher/payloads";
import { SummarizedNotificationState } from "../../stores/notifications/SummarizedNotificationState";
import GenericToast from '../views/toasts/GenericToast';
import Views from '../../Views';
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
// legacy export
export { default as Views } from "../../Views";
@ -140,25 +141,6 @@ interface IScreen {
params?: QueryDict;
}
/* eslint-disable camelcase */
interface IRoomInfo {
room_id?: string;
room_alias?: string;
event_id?: string;
auto_join?: boolean;
highlighted?: boolean;
oob_data?: object;
via_servers?: string[];
threepid_invite?: IThreepidInvite;
justCreatedOpts?: IOpts;
// Whether or not to override default behaviour to end up at a timeline
forceTimeline?: boolean;
}
/* eslint-enable camelcase */
interface IProps { // TODO type things better
config: {
piwik: {
@ -675,7 +657,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// known to be in (eg. user clicks on a room in the recents panel), supply the ID
// If the user is clicking on a room in the context of the alias being presented
// to them, supply the room alias. If both are supplied, the room ID will be ignored.
const promise = this.viewRoom(payload as any);
const promise = this.viewRoom(payload as ViewRoomPayload);
if (payload.deferred_action) {
promise.then(() => {
dis.dispatch(payload.deferred_action);
@ -894,21 +876,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
// switch view to the given room
//
// @param {Object} roomInfo Object containing data about the room to be joined
// @param {string=} roomInfo.room_id ID of the room to join. One of room_id or room_alias must be given.
// @param {string=} roomInfo.room_alias Alias of the room to join. One of room_id or room_alias must be given.
// @param {boolean=} roomInfo.auto_join If true, automatically attempt to join the room if not already a member.
// @param {string=} roomInfo.event_id ID of the event in this room to show: this will cause a switch to the
// context of that particular event.
// @param {boolean=} roomInfo.highlighted If true, add event_id to the hash of the URL
// and alter the EventTile to appear highlighted.
// @param {Object=} roomInfo.threepid_invite Object containing data about the third party
// we received to join the room, if any.
// @param {Object=} roomInfo.oob_data Object of additional data about the room
// that has been passed out-of-band (eg.
// room name and avatar from an invite email)
private async viewRoom(roomInfo: IRoomInfo) {
private async viewRoom(roomInfo: ViewRoomPayload) {
this.focusComposer = true;
if (roomInfo.room_alias) {
@ -1120,9 +1088,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const dmRooms = dmRoomMap.getDMRoomsForUserId(userId);
if (dmRooms.length > 0) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: dmRooms[0],
_trigger: "MessageUser",
});
} else {
dis.dispatch({
@ -1395,9 +1364,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
private viewLastRoom() {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: localStorage.getItem('mx_last_room_id'),
_trigger: undefined, // other
});
}
@ -1823,7 +1793,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
else via = params.via;
}
const payload = {
const payload: ViewRoomPayload = {
action: Action.ViewRoom,
event_id: eventId,
via_servers: via,
@ -1842,6 +1812,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
},
room_alias: undefined,
room_id: undefined,
_trigger: undefined, // unknown or external trigger
};
if (roomString[0] === '#') {
payload.room_alias = roomString;

View file

@ -46,10 +46,10 @@ import BaseDialog from "../views/dialogs/BaseDialog";
import DirectorySearchBox from "../views/elements/DirectorySearchBox";
import ScrollPanel from "./ScrollPanel";
import Spinner from "../views/elements/Spinner";
import { ActionPayload } from "../../dispatcher/payloads";
import { getDisplayAliasForAliasSet } from "../../Rooms";
import { Action } from "../../dispatcher/actions";
import PosthogTrackers from "../../PosthogTrackers";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
const MAX_NAME_LENGTH = 80;
const MAX_TOPIC_LENGTH = 800;
@ -494,11 +494,11 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
private showRoom(room: IPublicRoomsChunkRoom, roomAlias?: string, autoJoin = false, shouldPeek = false) {
this.onFinished();
const payload: ActionPayload = {
const payload: ViewRoomPayload = {
action: Action.ViewRoom,
auto_join: autoJoin,
should_peek: shouldPeek,
_type: "room_directory", // instrumentation
_trigger: "RoomDirectory",
};
if (room) {
// Don't let the user view a room they won't be able to either
@ -524,9 +524,6 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
if (this.state.roomServer) {
payload.via_servers = [this.state.roomServer];
payload.opts = {
viaServers: [this.state.roomServer],
};
}
}
// It's not really possible to join Matrix rooms by ID because the HS has no way to know

View file

@ -101,6 +101,7 @@ import AppsDrawer from '../views/rooms/AppsDrawer';
import { RightPanelPhases } from '../../stores/right-panel/RightPanelStorePhases';
import { ActionPayload } from "../../dispatcher/payloads";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
const DEBUG = false;
let debuglog = function(msg: string) {};
@ -767,12 +768,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private onUserScroll = () => {
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.state.room.roomId,
event_id: this.state.initialEventId,
highlighted: false,
replyingToEvent: this.state.replyToEvent,
_trigger: undefined, // room doesn't change
});
}
};
@ -871,10 +873,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
setImmediate(() => {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
deferred_action: payload,
_trigger: "MessageSearch",
});
});
}
@ -1171,9 +1174,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (ev.getType() === EventType.RoomCanonicalAlias) {
// re-view the room so MatrixChat can manage the alias in the URL properly
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.state.room.roomId,
_trigger: undefined, // room doesn't change
});
return; // this event cannot affect permissions so bail
}
@ -1643,9 +1647,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// If we were viewing a highlighted event, firing view_room without
// an event will take care of both clearing the URL fragment and
// jumping to the bottom
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.state.room.roomId,
_trigger: undefined, // room doesn't change
});
} else {
// Otherwise we have to jump manually
@ -1778,9 +1783,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
onHiddenHighlightsClick = () => {
const oldRoom = this.getOldRoom();
if (!oldRoom) return;
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: oldRoom.roomId,
_trigger: "Predecessor",
});
};

View file

@ -62,6 +62,7 @@ import { useEventEmitterState } from "../../hooks/useEventEmitter";
import { IOOBData } from "../../stores/ThreepidInviteStore";
import { awaitRoomDownSync } from "../../utils/RoomUpgrade";
import RoomViewStore from "../../stores/RoomViewStore";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
space: Room;
@ -326,10 +327,9 @@ export const showRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: st
}
const roomAlias = getDisplayAliasForRoom(room) || undefined;
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
should_peek: true,
_type: "room_directory", // instrumentation
room_alias: roomAlias,
room_id: room.room_id,
via_servers: Array.from(hierarchy.viaMap.get(roomId) || []),
@ -339,6 +339,7 @@ export const showRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: st
name: room.name || roomAlias || _t("Unnamed room"),
roomType,
} as IOOBData,
_trigger: "RoomDirectory",
});
};

View file

@ -85,6 +85,7 @@ import { shouldShowComponent } from "../../customisations/helpers/UIComponents";
import { UIComponent } from "../../settings/UIFeature";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
import PosthogTrackers from "../../PosthogTrackers";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
space: Room;
@ -865,9 +866,10 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
private goToFirstRoom = async () => {
if (this.state.firstRoomId) {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.state.firstRoomId,
_trigger: undefined, // other
});
return;
}

View file

@ -46,6 +46,7 @@ import { _t } from '../../languageHandler';
import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu';
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
import SettingsStore from "../../settings/SettingsStore";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
room: Room;
@ -195,12 +196,13 @@ export default class ThreadView extends React.Component<IProps, IState> {
private onScroll = (): void => {
if (this.props.initialEvent && this.props.isInitialEventHighlighted) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.props.room.roomId,
event_id: this.props.initialEvent?.getId(),
highlighted: false,
replyingToEvent: this.state.replyToEvent,
_trigger: undefined, // room doesn't change
});
}
};

View file

@ -52,6 +52,7 @@ import Spinner from "../views/elements/Spinner";
import EditorStateTransfer from '../../utils/EditorStateTransfer';
import ErrorDialog from '../views/dialogs/ErrorDialog';
import CallEventGrouper from "./CallEventGrouper";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20;
@ -1191,9 +1192,10 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (eventId) {
onFinished = () => {
// go via the dispatcher so that the URL is updated
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.props.timelineSet.room.roomId,
_trigger: undefined, // room doesn't change
});
};
}

View file

@ -47,6 +47,7 @@ import { WidgetLayoutStore } from '../../../stores/widgets/WidgetLayoutStore';
import EndPollDialog from '../dialogs/EndPollDialog';
import { isPollEnded } from '../messages/MPollBody';
import { createMapSiteLink } from "../messages/MLocationBody";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
export function canCancel(status: EventStatus): boolean {
return status === EventStatus.QUEUED || status === EventStatus.NOT_SENT || status === EventStatus.ENCRYPTING;
@ -263,11 +264,12 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
}
private viewInRoom = () => {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
_trigger: undefined, // room doesn't change
});
this.closeMenu();
};

View file

@ -44,6 +44,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import DMRoomMap from "../../../utils/DMRoomMap";
import { Action } from "../../../dispatcher/actions";
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps extends IContextMenuProps {
room: Room;
@ -198,7 +199,7 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
ev.preventDefault();
ev.stopPropagation();
ensureViewingRoom();
ensureViewingRoom(ev);
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, false);
onFinished();
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuPeopleItem", ev);
@ -247,11 +248,13 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
}
};
const ensureViewingRoom = () => {
const ensureViewingRoom = (ev: ButtonEvent) => {
if (RoomViewStore.getRoomId() === room.roomId) return;
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
_trigger: "RoomList",
_viaKeyboard: ev.type !== "click",
}, true);
};
@ -267,7 +270,7 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
ev.preventDefault();
ev.stopPropagation();
ensureViewingRoom();
ensureViewingRoom(ev);
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, false);
onFinished();
}}
@ -280,7 +283,7 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
ev.preventDefault();
ev.stopPropagation();
ensureViewingRoom();
ensureViewingRoom(ev);
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary }, false);
onFinished();
}}

View file

@ -39,6 +39,7 @@ import { Action } from "../../../dispatcher/actions";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../settings/UIFeature";
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps extends IContextMenuProps {
space: Room;
@ -115,10 +116,11 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
ev.preventDefault();
ev.stopPropagation();
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: space.roomId,
forceTimeline: true,
_trigger: undefined, // room doesn't change
});
onFinished();
};
@ -192,9 +194,10 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
ev.preventDefault();
ev.stopPropagation();
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: space.roomId,
_trigger: undefined, // other
});
onFinished();
};

View file

@ -28,6 +28,7 @@ import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOpti
import { WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
mxEvent: MatrixEvent;
@ -75,11 +76,12 @@ const ThreadListContextMenu: React.FC<IExtendedProps> = ({
const viewInRoom = useCallback((evt: ButtonEvent): void => {
evt.preventDefault();
evt.stopPropagation();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: mxEvent.getId(),
highlighted: true,
room_id: mxEvent.getRoomId(),
_trigger: undefined, // room doesn't change
});
closeThreadOptions();
}, [mxEvent, closeThreadOptions]);

View file

@ -29,6 +29,7 @@ import { Action } from '../../../dispatcher/actions';
import { showCommunityRoomInviteDialog } from "../../../RoomInvite";
import GroupStore from "../../../stores/GroupStore";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps extends IDialogProps {
}
@ -100,9 +101,10 @@ export default class CreateCommunityPrototypeDialog extends React.PureComponent<
if (result.room_id) {
// Force the group store to update as it might have missed the general chat
await GroupStore.refreshGroupRooms(result.group_id);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: result.room_id,
_trigger: undefined, // Deprecated groups
});
showCommunityRoomInviteDialog(result.room_id, this.state.name);
} else {

View file

@ -42,6 +42,7 @@ import { UserTab } from "./UserSettingsDialog";
import TagOrderActions from "../../../actions/TagOrderActions";
import { inviteUsersToRoom } from "../../../RoomInvite";
import ProgressBar from "../elements/ProgressBar";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
matrixClient: MatrixClient;
@ -226,9 +227,10 @@ const CreateSpaceFromCommunityDialog: React.FC<IProps> = ({ matrixClient: cli, g
onFinished(roomId);
const onSpaceClick = () => {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: undefined, // other
});
};

View file

@ -46,6 +46,8 @@ import BaseAvatar from "../avatars/BaseAvatar";
import SpaceStore from "../../../stores/spaces/SpaceStore";
import { roomContextDetailsText } from "../../../Rooms";
import { Action } from "../../../dispatcher/actions";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { ButtonEvent } from "../elements/AccessibleButton";
const AVATAR_SIZE = 30;
@ -75,10 +77,12 @@ enum SendState {
const Entry: React.FC<IEntryProps> = ({ room, event, matrixClient: cli, onFinished }) => {
const [sendState, setSendState] = useState<SendState>(SendState.CanSend);
const jumpToRoom = () => {
dis.dispatch({
const jumpToRoom = (ev: ButtonEvent) => {
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
_trigger: "WebForwardShortcut",
_viaKeyboard: ev.type !== "click",
});
onFinished(true);
};

View file

@ -71,6 +71,7 @@ import CallHandler from "../../../CallHandler";
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
import CopyableText from "../elements/CopyableText";
import { ScreenName } from '../../../PosthogTrackers';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
/* eslint-disable camelcase */
@ -679,11 +680,12 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
existingRoom = DMRoomMap.shared().getDMRoomForIdentifiers(targetIds);
}
if (existingRoom) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: existingRoom.roomId,
should_peek: false,
joining: false,
_trigger: "MessageUser",
});
this.props.onFinished(true);
return;

View file

@ -68,6 +68,7 @@ import { BetaPill } from "../beta/BetaCard";
import { UserTab } from "./UserSettingsDialog";
import BetaFeedbackDialog from "./BetaFeedbackDialog";
import SdkConfig from "../../../SdkConfig";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
const MAX_RECENT_SEARCHES = 10;
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
@ -214,7 +215,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", onFinished }) =>
});
};
const viewRoom = (roomId: string, persist = false) => {
const viewRoom = (roomId: string, persist = false, viaKeyboard = false) => {
if (persist) {
const recents = new Set(SettingsStore.getValue("SpotlightSearch.recentSearches", null).reverse());
// remove & add the room to put it at the end
@ -229,9 +230,11 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", onFinished }) =>
);
}
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: "WebUnifiedSearch",
_viaKeyboard: viaKeyboard,
});
onFinished();
};
@ -249,8 +252,8 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", onFinished }) =>
<Option
id={`mx_SpotlightDialog_button_result_${room.roomId}`}
key={room.roomId}
onClick={() => {
viewRoom(room.roomId, true);
onClick={(ev) => {
viewRoom(room.roomId, true, ev.type !== "click");
}}
>
<DecoratedRoomAvatar room={room} avatarSize={20} tooltipProps={{ tabIndex: -1 }} />
@ -300,8 +303,8 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", onFinished }) =>
<Option
id={`mx_SpotlightDialog_button_result_${room.room_id}`}
key={room.room_id}
onClick={() => {
viewRoom(room.room_id, true);
onClick={(ev) => {
viewRoom(room.room_id, true, ev.type !== "click");
}}
>
<BaseAvatar
@ -389,8 +392,8 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", onFinished }) =>
<Option
id={`mx_SpotlightDialog_button_recentSearch_${room.roomId}`}
key={room.roomId}
onClick={() => {
viewRoom(room.roomId, true);
onClick={(ev) => {
viewRoom(room.roomId, true, ev.type !== "click");
}}
>
<DecoratedRoomAvatar room={room} avatarSize={20} tooltipProps={{ tabIndex: -1 }} />
@ -417,8 +420,8 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", onFinished }) =>
id={`mx_SpotlightDialog_button_recentlyViewed_${room.roomId}`}
title={room.name}
key={room.roomId}
onClick={() => {
viewRoom(room.roomId);
onClick={(ev) => {
viewRoom(room.roomId, false, ev.type !== "click");
}}
>
<DecoratedRoomAvatar room={room} avatarSize={32} tooltipProps={{ tabIndex: -1 }} />

View file

@ -37,6 +37,7 @@ import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
import { normalizeWheelEvent } from "../../../utils/Mouse";
import { IDialogProps } from '../dialogs/IDialogProps';
import UIStore from '../../../stores/UIStore';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
// Max scale to keep gaps around the image
const MAX_SCALE = 0.95;
@ -333,11 +334,12 @@ export default class ImageView extends React.Component<IProps, IState> {
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Element when clicked.
ev.preventDefault();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
_trigger: undefined, // room doesn't change
});
this.props.onFinished();
};

View file

@ -36,6 +36,7 @@ import IconizedContextMenu, {
IconizedContextMenuOptionList,
} from "../context_menus/IconizedContextMenu";
import JumpToDatePicker from './JumpToDatePicker';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
function getDaysArray(): string[] {
return [
@ -141,11 +142,12 @@ export default class DateSeparator extends React.Component<IProps, IState> {
`found ${eventId} (${originServerTs}) for timestamp=${unixTimestamp} (looking forward)`,
);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: eventId,
highlighted: true,
room_id: roomId,
_trigger: undefined, // room doesn't change
});
} catch (e) {
const code = e.errcode || e.statusCode;

View file

@ -25,6 +25,7 @@ import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import EventTileBubble from "./EventTileBubble";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
/* the MatrixEvent to show */
@ -39,11 +40,13 @@ export default class RoomCreate extends React.Component<IProps> {
const predecessor = this.props.mxEvent.getContent()['predecessor'];
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: predecessor['event_id'],
highlighted: true,
room_id: predecessor['room_id'],
_trigger: "Predecessor",
_viaKeyboard: e.type !== "click",
});
};

View file

@ -41,6 +41,7 @@ import ContentMessages from '../../../ContentMessages';
import UploadBar from '../../structures/UploadBar';
import SettingsStore from '../../../settings/SettingsStore';
import JumpToBottomButton from '../rooms/JumpToBottomButton';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
room: Room;
@ -144,12 +145,13 @@ export default class TimelineCard extends React.Component<IProps, IState> {
private onUserScroll = (): void => {
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.props.room.roomId,
event_id: this.state.initialEventId,
highlighted: false,
replyingToEvent: this.state.replyToEvent,
_trigger: undefined, // room doesn't change
});
}
};

View file

@ -79,6 +79,7 @@ import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStor
import { useUserStatusMessage } from "../../../hooks/useUserStatusMessage";
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
export interface IDevice {
deviceId: string;
@ -123,13 +124,15 @@ export const getE2EStatus = (cli: MatrixClient, userId: string, devices: IDevice
return anyDeviceUnverified ? E2EStatus.Warning : E2EStatus.Verified;
};
async function openDMForUser(matrixClient: MatrixClient, userId: string): Promise<void> {
async function openDMForUser(matrixClient: MatrixClient, userId: string, viaKeyboard = false): Promise<void> {
const lastActiveRoom = findDMForUser(matrixClient, userId);
if (lastActiveRoom) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: lastActiveRoom.roomId,
_trigger: "MessageUser",
_viaKeyboard: viaKeyboard,
});
return;
}
@ -327,10 +330,10 @@ const MessageButton = ({ userId }: { userId: string }) => {
return (
<AccessibleButton
onClick={async () => {
onClick={async (ev) => {
if (busy) return;
setBusy(true);
await openDMForUser(cli, userId);
await openDMForUser(cli, userId, ev.type !== "click");
setBusy(false);
}}
className="mx_UserInfo_field"
@ -389,11 +392,12 @@ const UserOptionsSection: React.FC<{
if (member.roomId && !isSpace) {
const onReadReceiptButton = function() {
const room = cli.getRoom(member.roomId);
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
highlighted: true,
event_id: room.getEventReadUpTo(member.userId),
room_id: member.roomId,
_trigger: undefined, // room doesn't change
});
};

View file

@ -77,6 +77,7 @@ import { CardContext } from '../right_panel/BaseCard';
import { copyPlaintext } from '../../../utils/strings';
import { DecryptionFailureTracker } from '../../../DecryptionFailureTracker';
import RedactedBody from '../messages/RedactedBody';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
const eventTileTypes = {
[EventType.RoomMessage]: 'messages.MessageEvent',
@ -714,11 +715,12 @@ export default class EventTile extends React.Component<IProps, IState> {
private viewInRoom = (evt: ButtonEvent): void => {
evt.preventDefault();
evt.stopPropagation();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
_trigger: undefined, // room doesn't change
});
};
@ -1016,11 +1018,12 @@ export default class EventTile extends React.Component<IProps, IState> {
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Element when clicked.
e.preventDefault();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
_trigger: this.props.timelineRenderingType === TimelineRenderingType.Search ? "MessageSearch" : undefined,
});
};

View file

@ -23,6 +23,7 @@ import {
} from "../../../accessibility/RovingTabIndex";
import NotificationBadge from "./NotificationBadge";
import { NotificationState } from "../../../stores/notifications/NotificationState";
import { ButtonEvent } from "../elements/AccessibleButton";
interface IProps {
isMinimized: boolean;
@ -30,7 +31,7 @@ interface IProps {
displayName: string;
avatar: React.ReactElement;
notificationState?: NotificationState;
onClick: () => void;
onClick: (ev: ButtonEvent) => void;
}
interface IState {

View file

@ -49,6 +49,7 @@ import RoomContext from '../../../contexts/RoomContext';
import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdatedPayload";
import MessageComposerButtons from './MessageComposerButtons';
import { ButtonEvent } from '../elements/AccessibleButton';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
let instanceCount = 0;
const NARROW_MODE_BREAKPOINT = 500;
@ -227,21 +228,17 @@ export default class MessageComposer extends React.Component<IProps, IState> {
}
const viaServers = [this.state.tombstone.getSender().split(':').slice(1).join(':')];
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
highlighted: true,
event_id: createEventId,
room_id: replacementRoomId,
auto_join: true,
_type: "tombstone", // instrumentation
// Try to join via the server that sent the event. This converts @something:example.org
// into a server domain by splitting on colons and ignoring the first entry ("@something").
via_servers: viaServers,
opts: {
// These are passed down to the js-sdk's /join call
viaServers: viaServers,
},
_trigger: "Tombstone",
_viaKeyboard: ev.type !== "click",
});
};

View file

@ -31,6 +31,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { TileShape } from "./EventTile";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
room: Room;
@ -45,11 +46,12 @@ export default class PinnedEventTile extends React.Component<IProps> {
public static contextType = MatrixClientContext;
private onTileClicked = () => {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.event.getId(),
highlighted: true,
room_id: this.props.event.getRoomId(),
_trigger: undefined, // room doesn't change
});
};

View file

@ -26,6 +26,7 @@ import InteractiveTooltip, { Direction } from "../elements/InteractiveTooltip";
import { roomContextDetailsText } from "../../../Rooms";
import { Action } from "../../../dispatcher/actions";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
const RecentlyViewedButton = () => {
const tooltipRef = useRef<InteractiveTooltip>();
@ -39,10 +40,12 @@ const RecentlyViewedButton = () => {
return <MenuItem
key={crumb.roomId}
onClick={() => {
dis.dispatch({
onClick={(ev) => {
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: crumb.roomId,
_trigger: "WebVerticalBreadcrumbs",
_viaKeyboard: ev.type !== "click",
});
tooltipRef.current?.hideTooltip();
}}

View file

@ -32,6 +32,7 @@ import { replaceableComponent } from '../../../utils/replaceableComponent';
import { getEventDisplayInfo, isVoiceMessage } from '../../../utils/EventUtils';
import MFileBody from "../messages/MFileBody";
import MVoiceMessageBody from "../messages/MVoiceMessageBody";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
mxEvent: MatrixEvent;
@ -94,11 +95,12 @@ export default class ReplyTile extends React.PureComponent<IProps> {
if (this.props.toggleExpandedQuote && e.shiftKey) {
this.props.toggleExpandedQuote();
} else {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
_trigger: undefined, // room doesn't change
});
}
}

View file

@ -29,6 +29,8 @@ import Toolbar from "../../../accessibility/Toolbar";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Action } from "../../../dispatcher/actions";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { ButtonEvent } from "../elements/AccessibleButton";
interface IProps {
}
@ -44,7 +46,7 @@ interface IState {
skipFirst: boolean;
}
const RoomBreadcrumbTile = ({ room, onClick }: { room: Room, onClick: () => void }) => {
const RoomBreadcrumbTile = ({ room, onClick }: { room: Room, onClick: (ev: ButtonEvent) => void }) => {
const [onFocus, isActive, ref] = useRovingTabIndex();
return (
@ -103,18 +105,24 @@ export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState>
setTimeout(() => this.setState({ doAnimation: true, skipFirst: false }), 0);
};
private viewRoom = (room: Room, index: number) => {
private viewRoom = (room: Room, index: number, viaKeyboard = false) => {
Analytics.trackEvent("Breadcrumbs", "click_node", String(index));
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
_trigger: "WebHorizontalBreadcrumbs",
_viaKeyboard: viaKeyboard,
});
};
public render(): React.ReactElement {
const tiles = BreadcrumbsStore.instance.rooms.map((r, i) => {
return <RoomBreadcrumbTile key={r.roomId} room={r} onClick={() => this.viewRoom(r, i)} />;
});
const tiles = BreadcrumbsStore.instance.rooms.map((r, i) => (
<RoomBreadcrumbTile
key={r.roomId}
room={r}
onClick={(ev: ButtonEvent) => this.viewRoom(r, i, ev.type !== "click")}
/>
));
if (tiles.length > 0) {
// NOTE: The CSSTransition timeout MUST match the timeout in our CSS!

View file

@ -23,6 +23,7 @@ import { Action } from '../../../dispatcher/actions';
import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import RoomDetailRow from "./RoomDetailRow";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
rooms?: Room[];
@ -39,10 +40,11 @@ export default class RoomDetailList extends React.Component<IProps> {
}
private onDetailsClick = (ev: React.MouseEvent, room: Room): void => {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
room_alias: room.getCanonicalAlias() || (room.getAltAliases() || [])[0],
_trigger: undefined, // Deprecated groups
});
};

View file

@ -65,6 +65,7 @@ import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../str
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import SettingsStore from "../../../settings/SettingsStore";
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
onKeyDown: (ev: React.KeyboardEvent, state: IRovingTabIndexState) => void;
@ -217,9 +218,10 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
e.preventDefault();
e.stopPropagation();
closeMenu();
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: activeSpace.roomId,
_trigger: undefined, // other
});
}}
/>
@ -417,10 +419,12 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
const currentRoomId = RoomViewStore.getRoomId();
const room = this.getRoomDelta(currentRoomId, viewRoomDeltaPayload.delta, viewRoomDeltaPayload.unread);
if (room) {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
show_room_tile: true, // to make sure the room gets scrolled into view
_trigger: "WebKeyboardShortcut",
_viaKeyboard: true,
});
}
} else if (payload.action === Action.PstnSupportUpdated) {
@ -501,9 +505,10 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
private onExplore = () => {
if (!isMetaSpace(this.props.activeSpace)) {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.props.activeSpace,
_trigger: undefined, // other
});
} else {
const initialText = RoomListStore.instance.getFirstNameFilterCondition()?.search;
@ -525,16 +530,18 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
resizeMethod="crop"
/>
);
const viewRoom = () => {
defaultDispatcher.dispatch({
const viewRoom = (ev) => {
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_alias: room.canonical_alias || room.aliases?.[0],
room_id: room.room_id,
via_servers: room.viaServers,
oobData: {
oob_data: {
avatarUrl: room.avatar_url,
name,
},
_trigger: "RoomList",
_viaKeyboard: ev.type !== "click",
});
};
return (

View file

@ -60,6 +60,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import TooltipTarget from "../elements/TooltipTarget";
import { BetaPill } from "../beta/BetaCard";
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
const contextMenuBelow = (elementRect: DOMRect) => {
// align the context menu's icons with the icon which opened the context menu
@ -104,9 +105,10 @@ const PrototypeCommunityContextMenu = (props: ComponentProps<typeof SpaceContext
// anyways.
const chat = CommunityPrototypeStore.instance.getSelectedCommunityGeneralChat();
if (chat) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: chat.roomId,
_trigger: undefined, // Deprecated groups
}, true);
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomMemberList }, undefined, chat.roomId);
} else {
@ -274,9 +276,10 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: activeSpace.roomId,
_trigger: undefined, // other
});
closePlusMenu();
}}

View file

@ -57,6 +57,7 @@ import { ListNotificationState } from "../../../stores/notifications/ListNotific
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS
const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS
@ -428,10 +429,12 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
if (room) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
show_room_tile: true, // to make sure the room gets scrolled into view
_trigger: "WebRoomListNotificationBadge",
_viaKeyboard: ev.type !== "click",
});
}
};

View file

@ -53,6 +53,7 @@ import IconizedContextMenu, {
import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
room: Room;
@ -239,11 +240,13 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
private onTileClick = (ev: React.KeyboardEvent) => {
ev.preventDefault();
ev.stopPropagation();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
show_room_tile: true, // make sure the room is visible in the list
room_id: this.props.room.roomId,
clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)),
_trigger: "RoomList",
_viaKeyboard: ev.type !== "click",
});
};

View file

@ -34,6 +34,7 @@ import { useLocalEcho } from "../../../hooks/useLocalEcho";
import dis from "../../../dispatcher/dispatcher";
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
import { Action } from "../../../dispatcher/actions";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
room: Room;
@ -267,9 +268,10 @@ const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeCh
closeSettingsFn();
// switch to the new room in the background
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: undefined, // other
});
// open new settings on this tab

View file

@ -27,6 +27,7 @@ import dis from "../../../../../dispatcher/dispatcher";
import { Action } from '../../../../../dispatcher/actions';
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
import CopyableText from "../../../elements/CopyableText";
import { ViewRoomPayload } from "../../../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
roomId: string;
@ -90,10 +91,12 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
e.preventDefault();
e.stopPropagation();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.state.oldRoomId,
event_id: this.state.oldEventId,
_trigger: "WebPredecessorSettings",
_viaKeyboard: e.type !== "click",
});
this.props.closeSettingsFn();
};

View file

@ -38,6 +38,7 @@ import Spinner from "../../../elements/Spinner";
import { UserTab } from "../../../dialogs/UserSettingsDialog";
import { OpenToTabPayload } from "../../../../../dispatcher/payloads/OpenToTabPayload";
import { Action } from "../../../../../dispatcher/actions";
import { ViewRoomPayload } from "../../../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
closeSettingsFn(success: boolean): void;
@ -113,9 +114,10 @@ const CommunityMigrator = ({ onFinished }) => {
kind="primary_outline"
onClick={() => {
if (community.spaceId) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: community.spaceId,
_trigger: undefined, // other
});
onFinished();
} else {

View file

@ -31,6 +31,7 @@ import { Action } from "../../../dispatcher/actions";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import VerificationRequestDialog from "../dialogs/VerificationRequestDialog";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps {
toastKey: string;
@ -110,10 +111,11 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
const cli = MatrixClientPeg.get();
try {
if (request.channel.roomId) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: request.channel.roomId,
should_peek: false,
_trigger: "VerificationRequest",
});
const member = cli.getUser(request.otherUserId);
RightPanelStore.instance.setCards(

View file

@ -22,6 +22,7 @@ import RoomAvatar from '../../avatars/RoomAvatar';
import dis from '../../../../dispatcher/dispatcher';
import { Action } from '../../../../dispatcher/actions';
import AccessibleTooltipButton from '../../elements/AccessibleTooltipButton';
import { ViewRoomPayload } from "../../../../dispatcher/payloads/ViewRoomPayload";
interface CallViewHeaderProps {
pipMode: boolean;
@ -37,9 +38,10 @@ const onFullscreenClick = () => {
};
const onExpandClick = (roomId: string) => {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
_trigger: "WebFloatingCallWindow",
});
};

View file

@ -36,6 +36,7 @@ import ActiveWidgetStore, { ActiveWidgetStoreEvent } from '../../../stores/Activ
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
const SHOW_CALL_IN_STATES = [
CallState.Connected,
@ -227,9 +228,10 @@ export default class PipView extends React.Component<IProps, IState> {
const callRoomId = this.state.primaryCall?.roomId;
const widgetRoomId = ActiveWidgetStore.instance.getRoomId(this.state.persistentWidgetId);
if (!!(callRoomId ?? widgetRoomId)) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: callRoomId ?? widgetRoomId,
_trigger: "WebFloatingCallWindow",
});
}
};

View file

@ -46,6 +46,7 @@ import { makeSpaceParentEvent } from "./utils/space";
import { Action } from "./dispatcher/actions";
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
import Spinner from "./components/views/elements/Spinner";
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
// we define a number of interfaces which take their names from the js-sdk
/* eslint-disable camelcase */
@ -257,7 +258,7 @@ export default async function createRoom(opts: IOpts): Promise<string | null> {
// state over multiple syncs so we can't atomically know when we have the
// entire thing.
if (opts.andView) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
should_peek: false,
@ -266,6 +267,7 @@ export default async function createRoom(opts: IOpts): Promise<string | null> {
// stream, if it hasn't already.
joining: true,
justCreatedOpts: opts,
_trigger: "Created",
});
}
CountlyAnalytics.instance.trackRoomCreate(startTime, roomId);

View file

@ -93,7 +93,7 @@ export enum Action {
UpdateSystemFont = "update_system_font",
/**
* Changes room based on payload parameters.
* Changes room based on payload parameters. Should be used with JoinRoomPayload.
*/
ViewRoom = "view_room",

View file

@ -0,0 +1,54 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { ViewRoom as ViewRoomEvent } from "matrix-analytics-events/types/typescript/ViewRoom";
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
import { IOpts } from "../../createRoom";
/* eslint-disable camelcase */
export interface ViewRoomPayload extends Pick<ActionPayload, "action"> {
action: Action.ViewRoom;
// either of room_id or room_alias must be specified
room_id?: string;
room_alias?: string;
event_id?: string; // the event to ensure is in view if any
highlighted?: boolean; // whether to highlight `event_id`
should_peek?: boolean; // whether we should peek the room if we are not yet joined
joining?: boolean; // whether we have already sent a join request for this room
via_servers?: string[]; // the list of servers to join via if no room_alias is provided
context_switch?: boolean; // whether this view room was a consequence of switching spaces
replyingToEvent?: MatrixEvent; // the event we are replying to in this room if any
auto_join?: boolean; // whether to automatically join the room if we are not already
threepid_invite?: IThreepidInvite; // details about any 3pid invite we have to this room
justCreatedOpts?: IOpts; // if this is a newly created room then this is a reference to the creation opts
oob_data?: IOOBData; // any out-of-band data about this room can be used to render some room details without peeking
forceTimeline?: boolean; // Whether to override default behaviour to end up at a timeline
show_room_tile?: boolean; // Whether to ensure that the room tile is visible in the room list
clear_search?: boolean; // Whether to clear the room list search
deferred_action?: ActionPayload; // Action to fire after MatrixChat handles this ViewRoom action
// additional parameters for the purpose of metrics & instrumentation
_trigger: ViewRoomEvent["trigger"];
_viaKeyboard?: ViewRoomEvent["viaKeyboard"];
}
/* eslint-enable camelcase */

View file

@ -30,6 +30,7 @@ import {
import dis from './dispatcher/dispatcher';
import { Action } from './dispatcher/actions';
import { ViewUserPayload } from './dispatcher/payloads/ViewUserPayload';
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
export enum Type {
URL = "url",
@ -116,9 +117,11 @@ function onUserClick(event: MouseEvent, userId: string) {
function onAliasClick(event: MouseEvent, roomAlias: string) {
event.preventDefault();
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_alias: roomAlias,
_trigger: "Timeline",
_viaKeyboard: false,
});
}

View file

@ -31,6 +31,7 @@ import FlairStore from "./FlairStore";
import GroupFilterOrderStore from "./GroupFilterOrderStore";
import GroupStore from "./GroupStore";
import dis from "../dispatcher/dispatcher";
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
interface IState {
// nothing of value - we use account data
@ -150,9 +151,10 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
// Automatically select the general chat when switching communities
const chat = this.getGeneralChat(payload.tag);
if (chat) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: chat.roomId,
_trigger: undefined, // Deprecated groups
});
}
}

View file

@ -20,6 +20,7 @@ import React, { ReactNode } from "react";
import { Store } from 'flux/utils';
import { MatrixError } from "matrix-js-sdk/src/http-api";
import { logger } from "matrix-js-sdk/src/logger";
import { ViewRoom as ViewRoomEvent } from "matrix-analytics-events/types/typescript/ViewRoom";
import dis from '../dispatcher/dispatcher';
import { MatrixClientPeg } from '../MatrixClientPeg';
@ -30,8 +31,10 @@ import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCach
import { ActionPayload } from "../dispatcher/payloads";
import { Action } from "../dispatcher/actions";
import { retry } from "../utils/promise";
import CountlyAnalytics from "../CountlyAnalytics";
import CountlyAnalytics, { IJoinRoomEvent } from "../CountlyAnalytics";
import { TimelineRenderingType } from "../contexts/RoomContext";
import { PosthogAnalytics } from "../PosthogAnalytics";
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
const NUM_JOIN_RETRY = 5;
@ -158,10 +161,11 @@ class RoomViewStore extends Store<ActionPayload> {
if (payload.context === TimelineRenderingType.Room) {
if (payload.event
&& payload.event.getRoomId() !== this.state.roomId) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: payload.event.getRoomId(),
replyingToEvent: payload.event,
_trigger: undefined, // room doesn't change
});
} else {
this.setState({
@ -182,8 +186,16 @@ class RoomViewStore extends Store<ActionPayload> {
}
}
private async viewRoom(payload: ActionPayload) {
private async viewRoom(payload: ViewRoomPayload): Promise<void> {
if (payload.room_id) {
if (payload._trigger !== null && payload.room_id !== this.state.roomId) {
PosthogAnalytics.instance.trackEvent<ViewRoomEvent>({
eventName: "ViewRoom",
trigger: payload._trigger,
viaKeyboard: payload._viaKeyboard,
});
}
const newState = {
roomId: payload.room_id,
roomAlias: payload.room_alias,
@ -204,7 +216,7 @@ class RoomViewStore extends Store<ActionPayload> {
};
// Allow being given an event to be replied to when switching rooms but sanity check its for this room
if (payload.replyingToEvent && payload.replyingToEvent.getRoomId() === payload.room_id) {
if (payload.replyingToEvent?.getRoomId() === payload.room_id) {
newState.replyingToEvent = payload.replyingToEvent;
}
@ -287,7 +299,20 @@ class RoomViewStore extends Store<ActionPayload> {
// if we received a Gateway timeout then retry
return err.httpStatus === 504;
});
CountlyAnalytics.instance.trackRoomJoin(startTime, roomId, payload._type);
let type: IJoinRoomEvent["segmentation"]["type"] = undefined;
switch ((payload as ViewRoomPayload)._trigger) {
case "SlashCommand":
type = "slash_command";
break;
case "Tombstone":
type = "tombstone";
break;
case "RoomDirectory":
type = "room_directory";
break;
}
CountlyAnalytics.instance.trackRoomJoin(startTime, roomId, type);
// We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
// have come down the sync stream yet, and that's the point at which we'd consider the user joined to the

View file

@ -54,6 +54,7 @@ import {
import { getCachedRoomIDForAlias } from "../../RoomAliasCache";
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
import { PosthogAnalytics } from "../../PosthogAnalytics";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
interface IState {}
@ -157,10 +158,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
if (space) {
const roomId = this.getNotificationState(space).getFirstRoomWithNotifications();
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
context_switch: true,
_trigger: "WebSpaceContextSwitch",
});
} else {
const lists = RoomListStore.instance.unfilteredLists;
@ -174,10 +176,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
}
});
if (unreadRoom) {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: unreadRoom.roomId,
context_switch: true,
_trigger: "WebSpaceContextSwitch",
});
break;
}
@ -222,16 +225,18 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.matrixClient.getRoom(roomId)?.getMyMembership() === "join" &&
this.isRoomInSpace(space, roomId)
) {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
context_switch: true,
_trigger: "WebSpaceContextSwitch",
});
} else if (cliSpace) {
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: space,
context_switch: true,
_trigger: "WebSpaceContextSwitch",
});
} else {
defaultDispatcher.dispatch({

View file

@ -59,6 +59,7 @@ import { ELEMENT_CLIENT_ID } from "../../identifiers";
import { getUserLanguage } from "../../languageHandler";
import { WidgetVariableCustomisations } from "../../customisations/WidgetVariables";
import { arrayFastClone } from "../../utils/arrays";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
// TODO: Destroy all of this code
@ -293,9 +294,10 @@ export class StopGapWidget extends EventEmitter {
}
// at this point we can change rooms, so do that
defaultDispatcher.dispatch({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: targetRoomId,
_trigger: "Widget",
});
// acknowledge so the widget doesn't freak out

View file

@ -29,6 +29,7 @@ import Spinner from "../components/views/elements/Spinner";
import { isMetaSpace } from "../stores/spaces";
import SpaceStore from "../stores/spaces/SpaceStore";
import { Action } from "../dispatcher/actions";
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
/**
* Approximation of a membership status for a given room.
@ -187,9 +188,10 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
SpaceStore.instance.activeSpace !== roomId &&
RoomViewStore.getRoomId() === roomId
) {
dis.dispatch({
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: SpaceStore.instance.activeSpace,
_trigger: undefined, // other
});
} else {
dis.dispatch({ action: 'view_home_page' });