Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Correctly handle Room.timeline events which have a nullable Room #7635

Merged
merged 2 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions src/actions/MatrixActionCreators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";

import dis from "../dispatcher/dispatcher";
import { ActionPayload } from "../dispatcher/payloads";
Expand Down Expand Up @@ -160,7 +160,7 @@ function createRoomReceiptAction(matrixClient: MatrixClient, event: MatrixEvent,
}

/**
* @typedef RoomTimelineAction
* @typedef IRoomTimelineActionPayload
* @type {Object}
* @property {string} action 'MatrixActions.Room.timeline'.
* @property {boolean} isLiveEvent whether the event was attached to a
Expand All @@ -169,6 +169,13 @@ function createRoomReceiptAction(matrixClient: MatrixClient, event: MatrixEvent,
* event was attached to a timeline in the set of unfiltered timelines.
* @property {Room} room the Room whose tags changed.
*/
export interface IRoomTimelineActionPayload extends Pick<ActionPayload, "action"> {
action: 'MatrixActions.Room.timeline';
event: MatrixEvent;
room: Room | null;
isLiveEvent?: boolean;
isLiveUnfilteredRoomTimelineEvent: boolean;
}

/**
* Create a MatrixActions.Room.timeline action that represents a
Expand All @@ -177,7 +184,7 @@ function createRoomReceiptAction(matrixClient: MatrixClient, event: MatrixEvent,
*
* @param {MatrixClient} matrixClient the matrix client.
* @param {MatrixEvent} timelineEvent the event that was added/removed.
* @param {Room} room the Room that was stored.
* @param {?Room} room the Room that was stored.
* @param {boolean} toStartOfTimeline whether the event is being added
* to the start (and not the end) of the timeline.
* @param {boolean} removed whether the event was removed from the
Expand All @@ -186,25 +193,22 @@ function createRoomReceiptAction(matrixClient: MatrixClient, event: MatrixEvent,
* @param {boolean} data.liveEvent whether the event is a live event,
* belonging to a live timeline.
* @param {EventTimeline} data.timeline the timeline being altered.
* @returns {RoomTimelineAction} an action of type `MatrixActions.Room.timeline`.
* @returns {IRoomTimelineActionPayload} an action of type `MatrixActions.Room.timeline`.
*/
function createRoomTimelineAction(
matrixClient: MatrixClient,
timelineEvent: MatrixEvent,
room: Room,
room: Room | null,
toStartOfTimeline: boolean,
removed: boolean,
data: {
liveEvent: boolean;
timeline: EventTimeline;
},
): ActionPayload {
data: IRoomTimelineData,
): IRoomTimelineActionPayload {
return {
action: 'MatrixActions.Room.timeline',
event: timelineEvent,
isLiveEvent: data.liveEvent,
isLiveUnfilteredRoomTimelineEvent:
room && data.timeline.getTimelineSet() === room.getUnfilteredTimelineSet(),
isLiveUnfilteredRoomTimelineEvent: room && data.timeline.getTimelineSet() === room.getUnfilteredTimelineSet(),
room,
};
}

Expand Down
11 changes: 3 additions & 8 deletions src/autocomplete/UserProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { RoomState } from "matrix-js-sdk/src/models/room-state";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";

import { MatrixClientPeg } from '../MatrixClientPeg';
import QueryMatcher from './QueryMatcher';
Expand All @@ -42,11 +42,6 @@ const USER_REGEX = /\B@\S*/g;
// to allow you to tab-complete /mat into /(matthew)
const FORCED_USER_REGEX = /[^/,:; \t\n]\S*/g;

interface IRoomTimelineData {
timeline: EventTimeline;
liveEvent?: boolean;
}

export default class UserProvider extends AutocompleteProvider {
matcher: QueryMatcher<RoomMember>;
users: RoomMember[];
Expand Down Expand Up @@ -78,12 +73,12 @@ export default class UserProvider extends AutocompleteProvider {

private onRoomTimeline = (
ev: MatrixEvent,
room: Room,
room: Room | null,
toStartOfTimeline: boolean,
removed: boolean,
data: IRoomTimelineData,
) => {
if (!room) return;
if (!room) return; // notification timeline, we'll get this event again with a room specific timeline
if (removed) return;
if (room.roomId !== this.room.roomId) return;

Expand Down
10 changes: 8 additions & 2 deletions src/components/structures/FilePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.

import React from 'react';
import { Filter } from 'matrix-js-sdk/src/filter';
import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set";
import { EventTimelineSet, IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";
import { Direction } from "matrix-js-sdk/src/models/event-timeline";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from 'matrix-js-sdk/src/models/room';
Expand Down Expand Up @@ -62,7 +62,13 @@ class FilePanel extends React.Component<IProps, IState> {
timelineSet: null,
};

private onRoomTimeline = (ev: MatrixEvent, room: Room, toStartOfTimeline: true, removed: true, data: any): void => {
private onRoomTimeline = (
ev: MatrixEvent,
room: Room | null,
toStartOfTimeline: boolean,
removed: boolean,
data: IRoomTimelineData,
): void => {
if (room?.roomId !== this.props?.roomId) return;
if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return;

Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -934,10 +934,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
};

private onRoomTimeline = (ev: MatrixEvent, room: Room, toStartOfTimeline: boolean, removed, data) => {
private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data) => {
if (this.unmounted) return;

// ignore events for other rooms
// ignore events for other rooms or the notification timeline set
if (!room || room.roomId !== this.state.room?.roomId) return;

// ignore events from filtered timelines
Expand Down
11 changes: 4 additions & 7 deletions src/components/structures/TimelinePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import React, { createRef, ReactNode, SyntheticEvent } from 'react';
import ReactDOM from "react-dom";
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set";
import { EventTimelineSet, IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";
import { Direction, EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { TimelineWindow } from "matrix-js-sdk/src/timeline-window";
import { EventType, RelationType } from 'matrix-js-sdk/src/@types/event';
import { SyncState } from 'matrix-js-sdk/src/sync';
import { RoomMember } from 'matrix-js-sdk';
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { debounce } from 'lodash';
import { logger } from "matrix-js-sdk/src/logger";

Expand Down Expand Up @@ -526,13 +526,10 @@ class TimelinePanel extends React.Component<IProps, IState> {

private onRoomTimeline = (
ev: MatrixEvent,
room: Room,
room: Room | null,
toStartOfTimeline: boolean,
removed: boolean,
data: {
timeline: EventTimeline;
liveEvent?: boolean;
},
data: IRoomTimelineData,
): void => {
// ignore events for other timeline sets
if (data.timeline.getTimelineSet() !== this.props.timelineSet) return;
Expand Down
15 changes: 7 additions & 8 deletions src/components/views/avatars/DecoratedRoomAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room";
import { User } from "matrix-js-sdk/src/models/user";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { JoinRule } from "matrix-js-sdk/src/@types/partials";

import RoomAvatar from "./RoomAvatar";
import NotificationBadge from '../rooms/NotificationBadge';
Expand Down Expand Up @@ -92,9 +94,9 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
}

private get isPublicRoom(): boolean {
const joinRules = this.props.room.currentState.getStateEvents("m.room.join_rules", "");
const joinRules = this.props.room.currentState.getStateEvents(EventType.RoomJoinRules, "");
const joinRule = joinRules && joinRules.getContent().join_rule;
return joinRule === 'public';
return joinRule === JoinRule.Public;
}

private get dmUser(): User {
Expand All @@ -114,14 +116,11 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
}
}

private onRoomTimeline = (ev: MatrixEvent, room: Room) => {
private onRoomTimeline = (ev: MatrixEvent, room: Room | null) => {
if (this.isUnmounted) return;
if (this.props.room.roomId !== room?.roomId) return;

// apparently these can happen?
if (!room) return;
if (this.props.room.roomId !== room.roomId) return;

if (ev.getType() === 'm.room.join_rules' || ev.getType() === 'm.room.member') {
if (ev.getType() === EventType.RoomJoinRules || ev.getType() === EventType.RoomMember) {
const newIcon = this.calculateIcon();
if (newIcon !== this.state.icon) {
this.setState({ icon: newIcon });
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/rooms/WhoIsTypingTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
return WhoIsTypingTile.isVisible(this.state);
};

private onRoomTimeline = (event: MatrixEvent, room: Room): void => {
if (room?.roomId === this.props.room?.roomId) {
private onRoomTimeline = (event: MatrixEvent, room: Room | null): void => {
if (room?.roomId === this.props.room.roomId) {
const userId = event.getSender();
// remove user from usersTyping
const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId);
Expand Down
12 changes: 6 additions & 6 deletions src/indexing/EventIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { Direction, EventTimeline } from 'matrix-js-sdk/src/models/event-timeline';
import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set';
import { EventTimelineSet, IRoomTimelineData } from 'matrix-js-sdk/src/models/event-timeline-set';
import { RoomState } from 'matrix-js-sdk/src/models/room-state';
import { TimelineIndex, TimelineWindow } from 'matrix-js-sdk/src/timeline-window';
import { sleep } from "matrix-js-sdk/src/utils";
Expand Down Expand Up @@ -187,17 +187,17 @@ export default class EventIndex extends EventEmitter {
*/
private onRoomTimeline = async (
ev: MatrixEvent,
room: Room,
room: Room | null,
toStartOfTimeline: boolean,
removed: boolean,
data: {
liveEvent: boolean;
},
data: IRoomTimelineData,
) => {
if (!room) return; // notification timeline, we'll get this event again with a room specific timeline

const client = MatrixClientPeg.get();

// We only index encrypted rooms locally.
if (!client.isRoomEncrypted(room.roomId)) return;
if (!client.isRoomEncrypted(ev.getRoomId())) return;

if (ev.isRedaction()) {
return this.redactEvent(ev);
Expand Down
6 changes: 2 additions & 4 deletions src/stores/notifications/RoomNotificationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@ export class RoomNotificationState extends NotificationState implements IDestroy
this.updateNotificationState();
};

private handleRoomEventUpdate = (event: MatrixEvent) => {
const roomId = event.getRoomId();

if (roomId !== this.room.roomId) return; // ignore - not for us
private handleRoomEventUpdate = (event: MatrixEvent, room: Room | null) => {
if (room?.roomId !== this.room.roomId) return; // ignore - not for us or notifications timeline
this.updateNotificationState();
};

Expand Down
16 changes: 12 additions & 4 deletions src/stores/room-list/RoomListStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { SpaceWatcher } from "./SpaceWatcher";
import SpaceStore from "../spaces/SpaceStore";
import { Action } from "../../dispatcher/actions";
import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload";
import { IRoomTimelineActionPayload } from "../../actions/MatrixActionCreators";

interface IState {
tagsEnabled?: boolean;
Expand Down Expand Up @@ -239,15 +240,22 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange);
this.updateFn.trigger();
} else if (payload.action === 'MatrixActions.Room.timeline') {
const eventPayload = (<any>payload); // TODO: Type out the dispatcher types
const eventPayload = <IRoomTimelineActionPayload>payload;

// Ignore non-live events (backfill)
if (!eventPayload.isLiveEvent || !payload.isLiveUnfilteredRoomTimelineEvent) return;
// Ignore non-live events (backfill) and notification timeline set events (without a room)
if (!eventPayload.isLiveEvent ||
!eventPayload.isLiveUnfilteredRoomTimelineEvent ||
!eventPayload.room
) {
return;
}

const roomId = eventPayload.event.getRoomId();
const room = this.matrixClient.getRoom(roomId);
const tryUpdate = async (updatedRoom: Room) => {
if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') {
if (eventPayload.event.getType() === EventType.RoomTombstone &&
eventPayload.event.getStateKey() === ''
) {
const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']);
if (newRoom) {
// If we have the new room, then the new room check will have seen the predecessor
Expand Down