Merge branch 'develop' into fix/call-search-areas
This commit is contained in:
commit
8a28611a9a
941 changed files with 29895 additions and 21237 deletions
|
@ -23,9 +23,10 @@ limitations under the License.
|
|||
|
||||
import React, { createRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { IRecommendedVersion, NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { EventSubscription } from "fbemitter";
|
||||
import { ISearchResults } from 'matrix-js-sdk/src/@types/search';
|
||||
|
||||
import shouldHideEvent from '../../shouldHideEvent';
|
||||
import { _t } from '../../languageHandler';
|
||||
|
@ -33,17 +34,14 @@ import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
|
|||
import ResizeNotifier from '../../utils/ResizeNotifier';
|
||||
import ContentMessages from '../../ContentMessages';
|
||||
import Modal from '../../Modal';
|
||||
import * as sdk from '../../index';
|
||||
import CallHandler, { PlaceCallType } from '../../CallHandler';
|
||||
import dis from '../../dispatcher/dispatcher';
|
||||
import Tinter from '../../Tinter';
|
||||
import rateLimitedFunc from '../../ratelimitedfunc';
|
||||
import * as Rooms from '../../Rooms';
|
||||
import eventSearch, { searchPagination } from '../../Searching';
|
||||
import MainSplit from './MainSplit';
|
||||
import RightPanel from './RightPanel';
|
||||
import RoomViewStore from '../../stores/RoomViewStore';
|
||||
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
|
||||
import RoomScrollStateStore, { ScrollState } from '../../stores/RoomScrollStateStore';
|
||||
import WidgetEchoStore from '../../stores/WidgetEchoStore';
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { Layout } from "../../settings/Layout";
|
||||
|
@ -59,13 +57,12 @@ import ScrollPanel from "./ScrollPanel";
|
|||
import TimelinePanel from "./TimelinePanel";
|
||||
import ErrorBoundary from "../views/elements/ErrorBoundary";
|
||||
import RoomPreviewBar from "../views/rooms/RoomPreviewBar";
|
||||
import ForwardMessage from "../views/rooms/ForwardMessage";
|
||||
import SearchBar from "../views/rooms/SearchBar";
|
||||
import SearchBar, { SearchScope } from "../views/rooms/SearchBar";
|
||||
import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar";
|
||||
import AuxPanel from "../views/rooms/AuxPanel";
|
||||
import RoomHeader from "../views/rooms/RoomHeader";
|
||||
import { XOR } from "../../@types/common";
|
||||
import { IThreepidInvite } from "../../stores/ThreepidInviteStore";
|
||||
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
|
||||
import EffectsOverlay from "../views/elements/EffectsOverlay";
|
||||
import { containsEmoji } from '../../effects/utils';
|
||||
import { CHAT_EFFECTS } from '../../effects';
|
||||
|
@ -81,8 +78,18 @@ import { objectHasDiff } from "../../utils/objects";
|
|||
import SpaceRoomView from "./SpaceRoomView";
|
||||
import { IOpts } from "../../createRoom";
|
||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||
import { omit } from 'lodash';
|
||||
import UIStore from "../../stores/UIStore";
|
||||
import EditorStateTransfer from "../../utils/EditorStateTransfer";
|
||||
import { throttle } from "lodash";
|
||||
import ErrorDialog from '../views/dialogs/ErrorDialog';
|
||||
import SearchResultTile from '../views/rooms/SearchResultTile';
|
||||
import Spinner from "../views/elements/Spinner";
|
||||
import UploadBar from './UploadBar';
|
||||
import RoomStatusBar from "./RoomStatusBar";
|
||||
import MessageComposer from '../views/rooms/MessageComposer';
|
||||
import JumpToBottomButton from "../views/rooms/JumpToBottomButton";
|
||||
import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar";
|
||||
import SpaceStore from "../../stores/SpaceStore";
|
||||
|
||||
const DEBUG = false;
|
||||
let debuglog = function(msg: string) {};
|
||||
|
@ -95,22 +102,8 @@ if (DEBUG) {
|
|||
}
|
||||
|
||||
interface IProps {
|
||||
threepidInvite: IThreepidInvite,
|
||||
|
||||
// Any data about the room that would normally come from the homeserver
|
||||
// but has been passed out-of-band, eg. the room name and avatar URL
|
||||
// from an email invite (a workaround for the fact that we can't
|
||||
// get this information from the HS using an email invite).
|
||||
// Fields:
|
||||
// * name (string) The room's name
|
||||
// * avatarUrl (string) The mxc:// avatar URL for the room
|
||||
// * inviterName (string) The display name of the person who
|
||||
// * invited us to the room
|
||||
oobData?: {
|
||||
name?: string;
|
||||
avatarUrl?: string;
|
||||
inviterName?: string;
|
||||
};
|
||||
threepidInvite: IThreepidInvite;
|
||||
oobData?: IOOBData;
|
||||
|
||||
resizeNotifier: ResizeNotifier;
|
||||
justCreatedOpts?: IOpts;
|
||||
|
@ -136,18 +129,12 @@ export interface IState {
|
|||
// Whether to highlight the event scrolled to
|
||||
isInitialEventHighlighted?: boolean;
|
||||
replyToEvent?: MatrixEvent;
|
||||
forwardingEvent?: MatrixEvent;
|
||||
numUnreadMessages: number;
|
||||
draggingFile: boolean;
|
||||
searching: boolean;
|
||||
searchTerm?: string;
|
||||
searchScope?: "All" | "Room";
|
||||
searchResults?: XOR<{}, {
|
||||
count: number;
|
||||
highlights: string[];
|
||||
results: MatrixEvent[];
|
||||
next_batch: string; // eslint-disable-line camelcase
|
||||
}>;
|
||||
searchScope?: SearchScope;
|
||||
searchResults?: XOR<{}, ISearchResults>;
|
||||
searchHighlights?: string[];
|
||||
searchInProgress?: boolean;
|
||||
callState?: CallState;
|
||||
|
@ -155,7 +142,6 @@ export interface IState {
|
|||
canPeek: boolean;
|
||||
showApps: boolean;
|
||||
isPeeking: boolean;
|
||||
showReadReceipts: boolean;
|
||||
showRightPanel: boolean;
|
||||
// error object, as from the matrix client/server API
|
||||
// If we failed to load information about the room,
|
||||
|
@ -175,14 +161,21 @@ export interface IState {
|
|||
// We load this later by asking the js-sdk to suggest a version for us.
|
||||
// This object is the result of Room#getRecommendedVersion()
|
||||
|
||||
upgradeRecommendation?: {
|
||||
version: string;
|
||||
needsUpgrade: boolean;
|
||||
urgent: boolean;
|
||||
};
|
||||
upgradeRecommendation?: IRecommendedVersion;
|
||||
canReact: boolean;
|
||||
canReply: boolean;
|
||||
layout: Layout;
|
||||
lowBandwidth: boolean;
|
||||
alwaysShowTimestamps: boolean;
|
||||
showTwelveHourTimestamps: boolean;
|
||||
readMarkerInViewThresholdMs: number;
|
||||
readMarkerOutOfViewThresholdMs: number;
|
||||
showHiddenEventsInTimeline: boolean;
|
||||
showReadReceipts: boolean;
|
||||
showRedactions: boolean;
|
||||
showJoinLeaves: boolean;
|
||||
showAvatarChanges: boolean;
|
||||
showDisplaynameChanges: boolean;
|
||||
matrixClientIsReady: boolean;
|
||||
showUrlPreview?: boolean;
|
||||
e2eStatus?: E2EStatus;
|
||||
|
@ -193,6 +186,7 @@ export interface IState {
|
|||
// whether or not a spaces context switch brought us here,
|
||||
// if it did we don't want the room to be marked as read as soon as it is loaded.
|
||||
wasContextSwitch?: boolean;
|
||||
editState?: EditorStateTransfer;
|
||||
}
|
||||
|
||||
@replaceableComponent("structures.RoomView")
|
||||
|
@ -200,8 +194,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
private readonly dispatcherRef: string;
|
||||
private readonly roomStoreToken: EventSubscription;
|
||||
private readonly rightPanelStoreToken: EventSubscription;
|
||||
private readonly showReadReceiptsWatchRef: string;
|
||||
private readonly layoutWatcherRef: string;
|
||||
private settingWatchers: string[];
|
||||
|
||||
private unmounted = false;
|
||||
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
|
||||
|
@ -232,7 +225,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
canPeek: false,
|
||||
showApps: false,
|
||||
isPeeking: false,
|
||||
showReadReceipts: true,
|
||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
|
||||
joining: false,
|
||||
atEndOfLiveTimeline: true,
|
||||
|
@ -242,6 +234,17 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
canReact: false,
|
||||
canReply: false,
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
lowBandwidth: SettingsStore.getValue("lowBandwidth"),
|
||||
alwaysShowTimestamps: SettingsStore.getValue("alwaysShowTimestamps"),
|
||||
showTwelveHourTimestamps: SettingsStore.getValue("showTwelveHourTimestamps"),
|
||||
readMarkerInViewThresholdMs: SettingsStore.getValue("readMarkerInViewThresholdMs"),
|
||||
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
|
||||
showHiddenEventsInTimeline: SettingsStore.getValue("showHiddenEventsInTimeline"),
|
||||
showReadReceipts: true,
|
||||
showRedactions: true,
|
||||
showJoinLeaves: true,
|
||||
showAvatarChanges: true,
|
||||
showDisplaynameChanges: true,
|
||||
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(),
|
||||
dragCounter: 0,
|
||||
};
|
||||
|
@ -260,7 +263,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
this.context.on("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||
this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
|
||||
this.context.on("Event.decrypted", this.onEventDecrypted);
|
||||
this.context.on("event", this.onEvent);
|
||||
// Start listening for RoomViewStore updates
|
||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||
this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate);
|
||||
|
@ -268,16 +270,36 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
|
||||
WidgetStore.instance.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
|
||||
|
||||
this.showReadReceiptsWatchRef = SettingsStore.watchSetting("showReadReceipts", null,
|
||||
this.onReadReceiptsChange);
|
||||
this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, this.onLayoutChange);
|
||||
this.settingWatchers = [
|
||||
SettingsStore.watchSetting("layout", null, (...[,,, value]) =>
|
||||
this.setState({ layout: value as Layout }),
|
||||
),
|
||||
SettingsStore.watchSetting("lowBandwidth", null, (...[,,, value]) =>
|
||||
this.setState({ lowBandwidth: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("alwaysShowTimestamps", null, (...[,,, value]) =>
|
||||
this.setState({ alwaysShowTimestamps: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("showTwelveHourTimestamps", null, (...[,,, value]) =>
|
||||
this.setState({ showTwelveHourTimestamps: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("readMarkerInViewThresholdMs", null, (...[,,, value]) =>
|
||||
this.setState({ readMarkerInViewThresholdMs: value as number }),
|
||||
),
|
||||
SettingsStore.watchSetting("readMarkerOutOfViewThresholdMs", null, (...[,,, value]) =>
|
||||
this.setState({ readMarkerOutOfViewThresholdMs: value as number }),
|
||||
),
|
||||
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, (...[,,, value]) =>
|
||||
this.setState({ showHiddenEventsInTimeline: value as boolean }),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private onWidgetStoreUpdate = () => {
|
||||
if (this.state.room) {
|
||||
this.checkWidgets(this.state.room);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private checkWidgets = (room) => {
|
||||
this.setState({
|
||||
|
@ -323,13 +345,35 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
initialEventId: RoomViewStore.getInitialEventId(),
|
||||
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||
replyToEvent: RoomViewStore.getQuotingEvent(),
|
||||
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
||||
// we should only peek once we have a ready client
|
||||
shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(),
|
||||
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
||||
showRedactions: SettingsStore.getValue("showRedactions", roomId),
|
||||
showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId),
|
||||
showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId),
|
||||
showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId),
|
||||
wasContextSwitch: RoomViewStore.getWasContextSwitch(),
|
||||
};
|
||||
|
||||
// Add watchers for each of the settings we just looked up
|
||||
this.settingWatchers = this.settingWatchers.concat([
|
||||
SettingsStore.watchSetting("showReadReceipts", roomId, (...[,,, value]) =>
|
||||
this.setState({ showReadReceipts: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("showRedactions", roomId, (...[,,, value]) =>
|
||||
this.setState({ showRedactions: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("showJoinLeaves", roomId, (...[,,, value]) =>
|
||||
this.setState({ showJoinLeaves: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("showAvatarChanges", roomId, (...[,,, value]) =>
|
||||
this.setState({ showAvatarChanges: value as boolean }),
|
||||
),
|
||||
SettingsStore.watchSetting("showDisplaynameChanges", roomId, (...[,,, value]) =>
|
||||
this.setState({ showDisplaynameChanges: value as boolean }),
|
||||
),
|
||||
]);
|
||||
|
||||
if (!initial && this.state.shouldPeek && !newState.shouldPeek) {
|
||||
// Stop peeking because we have joined this room now
|
||||
this.context.stopPeeking();
|
||||
|
@ -488,7 +532,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
} else if (room) {
|
||||
// Stop peeking because we have joined this room previously
|
||||
this.context.stopPeeking();
|
||||
this.setState({isPeeking: false});
|
||||
this.setState({ isPeeking: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -528,16 +572,12 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const hasPropsDiff = objectHasDiff(this.props, nextProps);
|
||||
|
||||
// React only shallow comparison and we only want to trigger
|
||||
// a component re-render if a room requires an upgrade
|
||||
const newUpgradeRecommendation = nextState.upgradeRecommendation || {}
|
||||
|
||||
const state = omit(this.state, ['upgradeRecommendation']);
|
||||
const newState = omit(nextState, ['upgradeRecommendation'])
|
||||
const { upgradeRecommendation, ...state } = this.state;
|
||||
const { upgradeRecommendation: newUpgradeRecommendation, ...newState } = nextState;
|
||||
|
||||
const hasStateDiff =
|
||||
objectHasDiff(state, newState) ||
|
||||
(newUpgradeRecommendation.needsUpgrade === true)
|
||||
newUpgradeRecommendation?.needsUpgrade !== upgradeRecommendation?.needsUpgrade ||
|
||||
objectHasDiff(state, newState);
|
||||
|
||||
return hasPropsDiff || hasStateDiff;
|
||||
}
|
||||
|
@ -611,7 +651,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||
this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
|
||||
this.context.removeListener("Event.decrypted", this.onEventDecrypted);
|
||||
this.context.removeListener("event", this.onEvent);
|
||||
}
|
||||
|
||||
window.removeEventListener('beforeunload', this.onPageUnload);
|
||||
|
@ -638,18 +677,12 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.showReadReceiptsWatchRef) {
|
||||
SettingsStore.unwatchSetting(this.showReadReceiptsWatchRef);
|
||||
// cancel any pending calls to the throttled updated
|
||||
this.updateRoomMembers.cancel();
|
||||
|
||||
for (const watcher of this.settingWatchers) {
|
||||
SettingsStore.unwatchSetting(watcher);
|
||||
}
|
||||
|
||||
// cancel any pending calls to the rate_limited_funcs
|
||||
this.updateRoomMembers.cancelPendingCall();
|
||||
|
||||
// no need to do this as Dir & Settings are now overlays. It just burnt CPU.
|
||||
// console.log("Tinter.tint from RoomView.unmount");
|
||||
// Tinter.tint(); // reset colourscheme
|
||||
|
||||
SettingsStore.unwatchSetting(this.layoutWatcherRef);
|
||||
}
|
||||
|
||||
private onUserScroll = () => {
|
||||
|
@ -659,14 +692,9 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
room_id: this.state.room.roomId,
|
||||
event_id: this.state.initialEventId,
|
||||
highlighted: false,
|
||||
replyingToEvent: this.state.replyToEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onLayoutChange = () => {
|
||||
this.setState({
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
});
|
||||
};
|
||||
|
||||
private onRightPanelStoreUpdate = () => {
|
||||
|
@ -786,6 +814,35 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
case 'focus_search':
|
||||
this.onSearchClick();
|
||||
break;
|
||||
|
||||
case "edit_event": {
|
||||
const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
|
||||
this.setState({ editState }, () => {
|
||||
if (payload.event) {
|
||||
this.messagePanel?.scrollToEventIfNeeded(payload.event.getId());
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case Action.ComposerInsert: {
|
||||
// re-dispatch to the correct composer
|
||||
dis.dispatch({
|
||||
...payload,
|
||||
action: this.state.editState ? "edit_composer_insert" : "send_composer_insert",
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case Action.FocusAComposer: {
|
||||
// re-dispatch to the correct composer
|
||||
dis.fire(this.state.editState ? Action.FocusEditMessageComposer : Action.FocusSendMessageComposer);
|
||||
break;
|
||||
}
|
||||
|
||||
case "scroll_to_bottom":
|
||||
this.messagePanel?.jumpToLiveTimeline();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -793,8 +850,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (!room) return;
|
||||
if (!this.state.room || room.roomId != this.state.room.roomId) return;
|
||||
if (!room || room.roomId !== this.state.room?.roomId) return;
|
||||
|
||||
// ignore events from filtered timelines
|
||||
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
|
||||
|
@ -815,38 +871,36 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
// we'll only be showing a spinner.
|
||||
if (this.state.joining) return;
|
||||
|
||||
if (!ev.isBeingDecrypted() && !ev.isDecryptionFailure()) {
|
||||
this.handleEffects(ev);
|
||||
}
|
||||
|
||||
if (ev.getSender() !== this.context.credentials.userId) {
|
||||
// update unread count when scrolled up
|
||||
if (!this.state.searchResults && this.state.atEndOfLiveTimeline) {
|
||||
// no change
|
||||
} else if (!shouldHideEvent(ev)) {
|
||||
} else if (!shouldHideEvent(ev, this.state)) {
|
||||
this.setState((state, props) => {
|
||||
return {numUnreadMessages: state.numUnreadMessages + 1};
|
||||
return { numUnreadMessages: state.numUnreadMessages + 1 };
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private onEventDecrypted = (ev) => {
|
||||
private onEventDecrypted = (ev: MatrixEvent) => {
|
||||
if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
|
||||
if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
|
||||
if (ev.isDecryptionFailure()) return;
|
||||
this.handleEffects(ev);
|
||||
};
|
||||
|
||||
private onEvent = (ev) => {
|
||||
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
|
||||
this.handleEffects(ev);
|
||||
};
|
||||
|
||||
private handleEffects = (ev) => {
|
||||
if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
|
||||
if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
|
||||
|
||||
private handleEffects = (ev: MatrixEvent) => {
|
||||
const notifState = RoomNotificationStateStore.instance.getRoomState(this.state.room);
|
||||
if (!notifState.isUnread) return;
|
||||
|
||||
CHAT_EFFECTS.forEach(effect => {
|
||||
if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
|
||||
dis.dispatch({action: `effects.${effect.command}`});
|
||||
dis.dispatch({ action: `effects.${effect.command}` });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -873,6 +927,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
// called when state.room is first initialised (either at initial load,
|
||||
// after a successful peek, or after we join the room).
|
||||
private onRoomLoaded = (room: Room) => {
|
||||
if (this.unmounted) return;
|
||||
// Attach a widget store listener only when we get a room
|
||||
WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
|
||||
this.onWidgetLayoutChange(); // provoke an update
|
||||
|
@ -887,9 +942,9 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private async calculateRecommendedVersion(room: Room) {
|
||||
this.setState({
|
||||
upgradeRecommendation: await room.getRecommendedVersion(),
|
||||
});
|
||||
const upgradeRecommendation = await room.getRecommendedVersion();
|
||||
if (this.unmounted) return;
|
||||
this.setState({ upgradeRecommendation });
|
||||
}
|
||||
|
||||
private async loadMembersIfJoined(room: Room) {
|
||||
|
@ -899,7 +954,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
try {
|
||||
await room.loadMembersIfNeeded();
|
||||
if (!this.unmounted) {
|
||||
this.setState({membersLoaded: true});
|
||||
this.setState({ membersLoaded: true });
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = `Fetching room members for ${room.roomId} failed.` +
|
||||
|
@ -927,7 +982,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private updatePreviewUrlVisibility({roomId}: Room) {
|
||||
private updatePreviewUrlVisibility({ roomId }: Room) {
|
||||
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
||||
const key = this.context.isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled';
|
||||
this.setState({
|
||||
|
@ -979,32 +1034,19 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private async updateE2EStatus(room: Room) {
|
||||
if (!this.context.isRoomEncrypted(room.roomId)) {
|
||||
return;
|
||||
}
|
||||
if (!this.context.isCryptoEnabled()) {
|
||||
// If crypto is not currently enabled, we aren't tracking devices at all,
|
||||
// so we don't know what the answer is. Let's error on the safe side and show
|
||||
// a warning for this case.
|
||||
this.setState({
|
||||
e2eStatus: E2EStatus.Warning,
|
||||
});
|
||||
return;
|
||||
if (!this.context.isRoomEncrypted(room.roomId)) return;
|
||||
|
||||
// If crypto is not currently enabled, we aren't tracking devices at all,
|
||||
// so we don't know what the answer is. Let's error on the safe side and show
|
||||
// a warning for this case.
|
||||
let e2eStatus = E2EStatus.Warning;
|
||||
if (this.context.isCryptoEnabled()) {
|
||||
/* At this point, the user has encryption on and cross-signing on */
|
||||
e2eStatus = await shieldStatusForRoom(this.context, room);
|
||||
}
|
||||
|
||||
/* At this point, the user has encryption on and cross-signing on */
|
||||
this.setState({
|
||||
e2eStatus: await shieldStatusForRoom(this.context, room),
|
||||
});
|
||||
}
|
||||
|
||||
private updateTint() {
|
||||
const room = this.state.room;
|
||||
if (!room) return;
|
||||
|
||||
console.log("Tinter.tint from updateTint");
|
||||
const colorScheme = SettingsStore.getValue("roomColor", room.roomId);
|
||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||
if (this.unmounted) return;
|
||||
this.setState({ e2eStatus });
|
||||
}
|
||||
|
||||
private onAccountData = (event: MatrixEvent) => {
|
||||
|
@ -1018,12 +1060,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
private onRoomAccountData = (event: MatrixEvent, room: Room) => {
|
||||
if (room.roomId == this.state.roomId) {
|
||||
const type = event.getType();
|
||||
if (type === "org.matrix.room.color_scheme") {
|
||||
const colorScheme = event.getContent();
|
||||
// XXX: we should validate the event
|
||||
console.log("Tinter.tint from onRoomAccountData");
|
||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||
} else if (type === "org.matrix.room.preview_urls" || type === "im.vector.web.settings") {
|
||||
if (type === "org.matrix.room.preview_urls" || type === "im.vector.web.settings") {
|
||||
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
||||
this.updatePreviewUrlVisibility(room);
|
||||
}
|
||||
|
@ -1050,7 +1087,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
return;
|
||||
}
|
||||
|
||||
this.updateRoomMembers(member);
|
||||
this.updateRoomMembers();
|
||||
};
|
||||
|
||||
private onMyMembership = (room: Room, membership: string, oldMembership: string) => {
|
||||
|
@ -1067,15 +1104,15 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
const canReact = room.getMyMembership() === "join" && room.currentState.maySendEvent("m.reaction", me);
|
||||
const canReply = room.maySendMessage();
|
||||
|
||||
this.setState({canReact, canReply});
|
||||
this.setState({ canReact, canReply });
|
||||
}
|
||||
}
|
||||
|
||||
// rate limited because a power level change will emit an event for every member in the room.
|
||||
private updateRoomMembers = rateLimitedFunc(() => {
|
||||
private updateRoomMembers = throttle(() => {
|
||||
this.updateDMState();
|
||||
this.updateE2EStatus(this.state.room);
|
||||
}, 500);
|
||||
}, 500, { leading: true, trailing: true });
|
||||
|
||||
private checkDesktopNotifications() {
|
||||
const memberCount = this.state.room.getJoinedMemberCount() + this.state.room.getInvitedMemberCount();
|
||||
|
@ -1096,14 +1133,14 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private onSearchResultsFillRequest = (backwards: boolean) => {
|
||||
private onSearchResultsFillRequest = (backwards: boolean): Promise<boolean> => {
|
||||
if (!backwards) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (this.state.searchResults.next_batch) {
|
||||
debuglog("requesting more search results");
|
||||
const searchPromise = searchPagination(this.state.searchResults);
|
||||
const searchPromise = searchPagination(this.state.searchResults as ISearchResults);
|
||||
return this.handleSearchResult(searchPromise);
|
||||
} else {
|
||||
debuglog("no more search results");
|
||||
|
@ -1131,7 +1168,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
room_id: this.getRoomId(),
|
||||
},
|
||||
});
|
||||
dis.dispatch({action: 'require_registration'});
|
||||
dis.dispatch({ action: 'require_registration' });
|
||||
} else {
|
||||
Promise.resolve().then(() => {
|
||||
const signUrl = this.props.threepidInvite?.signUrl;
|
||||
|
@ -1166,13 +1203,13 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
|
||||
// We always increment the counter no matter the types, because dragging is
|
||||
// still happening. If we didn't, the drag counter would get out of sync.
|
||||
this.setState({dragCounter: this.state.dragCounter + 1});
|
||||
this.setState({ dragCounter: this.state.dragCounter + 1 });
|
||||
|
||||
// See:
|
||||
// https://docs.w3cub.com/dom/datatransfer/types
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
|
||||
if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) {
|
||||
this.setState({draggingFile: true});
|
||||
this.setState({ draggingFile: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1211,7 +1248,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||
ev.dataTransfer.files, this.state.room.roomId, this.context,
|
||||
);
|
||||
dis.fire(Action.FocusComposer);
|
||||
dis.fire(Action.FocusSendMessageComposer);
|
||||
|
||||
this.setState({
|
||||
draggingFile: false,
|
||||
|
@ -1219,9 +1256,9 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
private injectSticker(url, info, text) {
|
||||
private injectSticker(url: string, info: object, text: string) {
|
||||
if (this.context.isGuest()) {
|
||||
dis.dispatch({action: 'require_registration'});
|
||||
dis.dispatch({ action: 'require_registration' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1234,7 +1271,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
});
|
||||
}
|
||||
|
||||
private onSearch = (term: string, scope) => {
|
||||
private onSearch = (term: string, scope: SearchScope) => {
|
||||
this.setState({
|
||||
searchTerm: term,
|
||||
searchScope: scope,
|
||||
|
@ -1255,14 +1292,14 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
this.searchId = new Date().getTime();
|
||||
|
||||
let roomId;
|
||||
if (scope === "Room") roomId = this.state.room.roomId;
|
||||
if (scope === SearchScope.Room) roomId = this.state.room.roomId;
|
||||
|
||||
debuglog("sending search request");
|
||||
const searchPromise = eventSearch(term, roomId);
|
||||
this.handleSearchResult(searchPromise);
|
||||
};
|
||||
|
||||
private handleSearchResult(searchPromise: Promise<any>) {
|
||||
private handleSearchResult(searchPromise: Promise<any>): Promise<boolean> {
|
||||
// keep a record of the current search id, so that if the search terms
|
||||
// change before we get a response, we can ignore the results.
|
||||
const localSearchId = this.searchId;
|
||||
|
@ -1275,7 +1312,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
debuglog("search complete");
|
||||
if (this.unmounted || !this.state.searching || this.searchId != localSearchId) {
|
||||
console.error("Discarding stale search results");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// postgres on synapse returns us precise details of the strings
|
||||
|
@ -1300,13 +1337,13 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
searchResults: results,
|
||||
});
|
||||
}, (error) => {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Search failed", error);
|
||||
Modal.createTrackedDialog('Search failed', '', ErrorDialog, {
|
||||
title: _t("Search failed"),
|
||||
description: ((error && error.message) ? error.message :
|
||||
_t("Server may be unavailable, overloaded, or search timed out :(")),
|
||||
});
|
||||
return false;
|
||||
}).finally(() => {
|
||||
this.setState({
|
||||
searchInProgress: false,
|
||||
|
@ -1315,9 +1352,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
private getSearchResultTiles() {
|
||||
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
// XXX: todo: merge overlapping results somehow?
|
||||
// XXX: why doesn't searching on name work?
|
||||
|
||||
|
@ -1369,7 +1403,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!haveTileForEvent(mxEv)) {
|
||||
if (!haveTileForEvent(mxEv, this.state.showHiddenEventsInTimeline)) {
|
||||
// XXX: can this ever happen? It will make the result count
|
||||
// not match the displayed count.
|
||||
continue;
|
||||
|
@ -1410,18 +1444,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
dis.dispatch({ action: "open_room_settings" });
|
||||
};
|
||||
|
||||
private onCancelClick = () => {
|
||||
console.log("updateTint from onCancelClick");
|
||||
this.updateTint();
|
||||
if (this.state.forwardingEvent) {
|
||||
dis.dispatch({
|
||||
action: 'forward_event',
|
||||
event: null,
|
||||
});
|
||||
}
|
||||
dis.fire(Action.FocusComposer);
|
||||
};
|
||||
|
||||
private onAppsClick = () => {
|
||||
dis.dispatch({
|
||||
action: "appsDrawer",
|
||||
|
@ -1429,13 +1451,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
private onLeaveClick = () => {
|
||||
dis.dispatch({
|
||||
action: 'leave_room',
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
};
|
||||
|
||||
private onForgetClick = () => {
|
||||
dis.dispatch({
|
||||
action: 'forget_room',
|
||||
|
@ -1456,7 +1471,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
console.error("Failed to reject invite: %s", error);
|
||||
|
||||
const msg = error.message ? error.message : JSON.stringify(error);
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
|
||||
title: _t("Failed to reject invite"),
|
||||
description: msg,
|
||||
|
@ -1490,7 +1504,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
console.error("Failed to reject invite: %s", error);
|
||||
|
||||
const msg = error.message ? error.message : JSON.stringify(error);
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
|
||||
title: _t("Failed to reject invite"),
|
||||
description: msg,
|
||||
|
@ -1526,10 +1539,19 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
|
||||
// jump down to the bottom of this room, where new events are arriving
|
||||
private jumpToLiveTimeline = () => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
|
||||
// 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({
|
||||
action: 'view_room',
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
} else {
|
||||
// Otherwise we have to jump manually
|
||||
this.messagePanel.jumpToLiveTimeline();
|
||||
dis.fire(Action.FocusSendMessageComposer);
|
||||
}
|
||||
};
|
||||
|
||||
// jump up to wherever our read marker is
|
||||
|
@ -1551,14 +1573,14 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
|
||||
const showBar = this.messagePanel.canJumpToReadMarker();
|
||||
if (this.state.showTopUnreadMessagesBar != showBar) {
|
||||
this.setState({showTopUnreadMessagesBar: showBar});
|
||||
this.setState({ showTopUnreadMessagesBar: showBar });
|
||||
}
|
||||
};
|
||||
|
||||
// get the current scroll position of the room, so that it can be
|
||||
// restored when we switch back to it.
|
||||
//
|
||||
private getScrollState() {
|
||||
private getScrollState(): ScrollState {
|
||||
const messagePanel = this.messagePanel;
|
||||
if (!messagePanel) return null;
|
||||
|
||||
|
@ -1605,29 +1627,45 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
let auxPanelMaxHeight = UIStore.instance.windowHeight -
|
||||
(54 + // height of RoomHeader
|
||||
36 + // height of the status area
|
||||
51 + // minimum height of the message compmoser
|
||||
51 + // minimum height of the message composer
|
||||
120); // amount of desired scrollback
|
||||
|
||||
// XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway
|
||||
// but it's better than the video going missing entirely
|
||||
if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50;
|
||||
|
||||
this.setState({auxPanelMaxHeight: auxPanelMaxHeight});
|
||||
if (this.state.auxPanelMaxHeight !== auxPanelMaxHeight) {
|
||||
this.setState({ auxPanelMaxHeight });
|
||||
}
|
||||
};
|
||||
|
||||
private onStatusBarVisible = () => {
|
||||
if (this.unmounted) return;
|
||||
this.setState({
|
||||
statusBarVisible: true,
|
||||
});
|
||||
if (this.unmounted || this.state.statusBarVisible) return;
|
||||
this.setState({ statusBarVisible: true });
|
||||
};
|
||||
|
||||
private onStatusBarHidden = () => {
|
||||
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
||||
if (this.unmounted) return;
|
||||
this.setState({
|
||||
statusBarVisible: false,
|
||||
});
|
||||
if (this.unmounted || !this.state.statusBarVisible) return;
|
||||
this.setState({ statusBarVisible: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* called by the parent component when PageUp/Down/etc is pressed.
|
||||
*
|
||||
* We pass it down to the scroll panel.
|
||||
*/
|
||||
private handleScrollKey = ev => {
|
||||
let panel;
|
||||
if (this.searchResultsPanel.current) {
|
||||
panel = this.searchResultsPanel.current;
|
||||
} else if (this.messagePanel) {
|
||||
panel = this.messagePanel;
|
||||
}
|
||||
|
||||
if (panel) {
|
||||
panel.handleScrollKey(ev);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1644,10 +1682,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
// otherwise react calls it with null on each update.
|
||||
private gatherTimelinePanelRef = r => {
|
||||
this.messagePanel = r;
|
||||
if (r) {
|
||||
console.log("updateTint from RoomView.gatherTimelinePanelRef");
|
||||
this.updateTint();
|
||||
}
|
||||
};
|
||||
|
||||
private getOldRoom() {
|
||||
|
@ -1666,7 +1700,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
onHiddenHighlightsClick = () => {
|
||||
const oldRoom = this.getOldRoom();
|
||||
if (!oldRoom) return;
|
||||
dis.dispatch({action: "view_room", room_id: oldRoom.roomId});
|
||||
dis.dispatch({ action: "view_room", room_id: oldRoom.roomId });
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -1722,10 +1756,8 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
const myMembership = this.state.room.getMyMembership();
|
||||
if (myMembership === "invite"
|
||||
// SpaceRoomView handles invites itself
|
||||
&& (!SettingsStore.getValue("feature_spaces") || !this.state.room.isSpaceRoom())
|
||||
) {
|
||||
// SpaceRoomView handles invites itself
|
||||
if (myMembership === "invite" && (!SpaceStore.spacesEnabled || !this.state.room.isSpaceRoom())) {
|
||||
if (this.state.joining || this.state.rejecting) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
|
@ -1803,10 +1835,8 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
let isStatusAreaExpanded = true;
|
||||
|
||||
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
||||
const UploadBar = sdk.getComponent('structures.UploadBar');
|
||||
statusBar = <UploadBar room={this.state.room} />;
|
||||
} else if (!this.state.searchResults) {
|
||||
const RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
|
||||
isStatusAreaExpanded = this.state.statusBarVisible;
|
||||
statusBar = <RoomStatusBar
|
||||
room={this.state.room}
|
||||
|
@ -1827,7 +1857,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
{statusBar}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
const roomVersionRecommendation = this.state.upgradeRecommendation;
|
||||
const showRoomUpgradeBar = (
|
||||
roomVersionRecommendation &&
|
||||
|
@ -1839,11 +1869,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
|
||||
let aux = null;
|
||||
let previewBar;
|
||||
let hideCancel = false;
|
||||
if (this.state.forwardingEvent) {
|
||||
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
|
||||
} else if (this.state.searching) {
|
||||
hideCancel = true; // has own cancel
|
||||
if (this.state.searching) {
|
||||
aux = <SearchBar
|
||||
searchInProgress={this.state.searchInProgress}
|
||||
onCancelClick={this.onCancelSearchClick}
|
||||
|
@ -1852,7 +1878,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
/>;
|
||||
} else if (showRoomUpgradeBar) {
|
||||
aux = <RoomUpgradeWarningBar room={this.state.room} recommendation={roomVersionRecommendation} />;
|
||||
hideCancel = true;
|
||||
} else if (myMembership !== "join") {
|
||||
// We do have a room object for this room, but we're not currently in it.
|
||||
// We may have a 3rd party invite to it.
|
||||
|
@ -1861,7 +1886,6 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
inviterName = this.props.oobData.inviterName;
|
||||
}
|
||||
const invitedEmail = this.props.threepidInvite?.toEmail;
|
||||
hideCancel = true;
|
||||
previewBar = (
|
||||
<RoomPreviewBar
|
||||
onJoinClick={this.onJoinButtonClicked}
|
||||
|
@ -1875,7 +1899,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
room={this.state.room}
|
||||
/>
|
||||
);
|
||||
if (!this.state.canPeek && (!SettingsStore.getValue("feature_spaces") || !this.state.room?.isSpaceRoom())) {
|
||||
if (!this.state.canPeek && (!SpaceStore.spacesEnabled || !this.state.room?.isSpaceRoom())) {
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
{ previewBar }
|
||||
|
@ -1889,10 +1913,10 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
className="mx_RoomView_auxPanel_hiddenHighlights"
|
||||
onClick={this.onHiddenHighlightsClick}
|
||||
>
|
||||
{_t(
|
||||
{ _t(
|
||||
"You have %(count)s unread notifications in a prior version of this room.",
|
||||
{count: hiddenHighlightCount},
|
||||
)}
|
||||
{ count: hiddenHighlightCount },
|
||||
) }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
@ -1929,12 +1953,9 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
myMembership === 'join' && !this.state.searchResults
|
||||
);
|
||||
if (canSpeak) {
|
||||
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
||||
messageComposer =
|
||||
<MessageComposer
|
||||
room={this.state.room}
|
||||
callState={this.state.callState}
|
||||
showApps={this.state.showApps}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
replyToEvent={this.state.replyToEvent}
|
||||
|
@ -1979,11 +2000,8 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
hideMessagePanel = true;
|
||||
}
|
||||
|
||||
const shouldHighlight = this.state.isInitialEventHighlighted;
|
||||
let highlightedEventId = null;
|
||||
if (this.state.forwardingEvent) {
|
||||
highlightedEventId = this.state.forwardingEvent.getId();
|
||||
} else if (shouldHighlight) {
|
||||
if (this.state.isInitialEventHighlighted) {
|
||||
highlightedEventId = this.state.initialEventId;
|
||||
}
|
||||
|
||||
|
@ -2010,19 +2028,19 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
onScroll={this.onMessageListScroll}
|
||||
onUserScroll={this.onUserScroll}
|
||||
onReadMarkerUpdated={this.updateTopUnreadMessagesBar}
|
||||
showUrlPreview = {this.state.showUrlPreview}
|
||||
showUrlPreview={this.state.showUrlPreview}
|
||||
className={messagePanelClassNames}
|
||||
membersLoaded={this.state.membersLoaded}
|
||||
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
showReactions={true}
|
||||
layout={this.state.layout}
|
||||
editState={this.state.editState}
|
||||
/>);
|
||||
|
||||
let topUnreadMessagesBar = null;
|
||||
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
||||
if (this.state.showTopUnreadMessagesBar && !this.state.searchResults) {
|
||||
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
|
||||
topUnreadMessagesBar = (
|
||||
<TopUnreadMessagesBar onScrollUpClick={this.jumpToReadMarker} onCloseClick={this.forgetReadMarker} />
|
||||
);
|
||||
|
@ -2030,9 +2048,8 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
let jumpToBottom;
|
||||
// Do not show JumpToBottomButton if we have search results showing, it makes no sense
|
||||
if (!this.state.atEndOfLiveTimeline && !this.state.searchResults) {
|
||||
const JumpToBottomButton = sdk.getComponent('rooms.JumpToBottomButton');
|
||||
jumpToBottom = (<JumpToBottomButton
|
||||
highlight={this.state.room.getUnreadNotificationCount('highlight') > 0}
|
||||
highlight={this.state.room.getUnreadNotificationCount(NotificationCountType.Highlight) > 0}
|
||||
numUnreadMessages={this.state.numUnreadMessages}
|
||||
onScrollToBottomClick={this.jumpToLiveTimeline}
|
||||
roomId={this.state.roomId}
|
||||
|
@ -2057,7 +2074,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
return (
|
||||
<RoomContext.Provider value={this.state}>
|
||||
<main className={mainClasses} ref={this.roomView} onKeyDown={this.onReactKeyDown}>
|
||||
{showChatEffects && this.roomView.current &&
|
||||
{ showChatEffects && this.roomView.current &&
|
||||
<EffectsOverlay roomWidth={this.roomView.current.offsetWidth} />
|
||||
}
|
||||
<ErrorBoundary>
|
||||
|
@ -2068,9 +2085,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
inRoom={myMembership === 'join'}
|
||||
onSearchClick={this.onSearchClick}
|
||||
onSettingsClick={this.onSettingsClick}
|
||||
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
||||
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
||||
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
onAppsClick={this.state.hasPinnedWidgets ? this.onAppsClick : null}
|
||||
appsShown={this.state.showApps}
|
||||
|
@ -2078,13 +2093,13 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
/>
|
||||
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||
<div className="mx_RoomView_body">
|
||||
{auxPanel}
|
||||
{ auxPanel }
|
||||
<div className={timelineClasses}>
|
||||
{fileDropTarget}
|
||||
{topUnreadMessagesBar}
|
||||
{jumpToBottom}
|
||||
{messagePanel}
|
||||
{searchResultsPanel}
|
||||
{ fileDropTarget }
|
||||
{ topUnreadMessagesBar }
|
||||
{ jumpToBottom }
|
||||
{ messagePanel }
|
||||
{ searchResultsPanel }
|
||||
</div>
|
||||
{statusBarArea}
|
||||
{previewBar}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue