Conform more of the codebase to strictNullChecks
(#10666)
* Conform more of the codebase to `strictNullChecks` * Iterate * Iterate * Iterate * Iterate
This commit is contained in:
parent
8867f1801e
commit
93b4ee654b
17 changed files with 126 additions and 114 deletions
|
@ -293,7 +293,7 @@ export default class AddThreepid {
|
||||||
const authClient = new IdentityAuthClient();
|
const authClient = new IdentityAuthClient();
|
||||||
const supportsSeparateAddAndBind = await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind();
|
const supportsSeparateAddAndBind = await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind();
|
||||||
|
|
||||||
let result;
|
let result: { success: boolean } | MatrixError;
|
||||||
if (this.submitUrl) {
|
if (this.submitUrl) {
|
||||||
result = await MatrixClientPeg.get().submitMsisdnTokenOtherUrl(
|
result = await MatrixClientPeg.get().submitMsisdnTokenOtherUrl(
|
||||||
this.submitUrl,
|
this.submitUrl,
|
||||||
|
@ -311,7 +311,7 @@ export default class AddThreepid {
|
||||||
} else {
|
} else {
|
||||||
throw new UserFriendlyError("The add / bind with MSISDN flow is misconfigured");
|
throw new UserFriendlyError("The add / bind with MSISDN flow is misconfigured");
|
||||||
}
|
}
|
||||||
if (result.errcode) {
|
if (result instanceof Error) {
|
||||||
throw result;
|
throw result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Optional } from "matrix-events-sdk";
|
||||||
|
|
||||||
import { _t } from "./languageHandler";
|
import { _t } from "./languageHandler";
|
||||||
|
|
||||||
function getDaysArray(): string[] {
|
function getDaysArray(): string[] {
|
||||||
|
@ -194,10 +196,7 @@ function withinCurrentYear(prevDate: Date, nextDate: Date): boolean {
|
||||||
return prevDate.getFullYear() === nextDate.getFullYear();
|
return prevDate.getFullYear() === nextDate.getFullYear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wantsDateSeparator(
|
export function wantsDateSeparator(prevEventDate: Optional<Date>, nextEventDate: Optional<Date>): boolean {
|
||||||
prevEventDate: Date | null | undefined,
|
|
||||||
nextEventDate: Date | null | undefined,
|
|
||||||
): boolean {
|
|
||||||
if (!nextEventDate || !prevEventDate) {
|
if (!nextEventDate || !prevEventDate) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,8 +288,8 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
this.play(AudioID.Ring);
|
this.play(AudioID.Ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isCallSilenced(callId: string): boolean {
|
public isCallSilenced(callId?: string): boolean {
|
||||||
return this.isForcedSilent() || this.silencedCalls.has(callId);
|
return this.isForcedSilent() || (!!callId && this.silencedCalls.has(callId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -395,6 +395,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappedRoomId = LegacyCallHandler.instance.roomIdForCall(call);
|
const mappedRoomId = LegacyCallHandler.instance.roomIdForCall(call);
|
||||||
|
if (!mappedRoomId) return;
|
||||||
if (this.getCallForRoom(mappedRoomId)) {
|
if (this.getCallForRoom(mappedRoomId)) {
|
||||||
logger.log(
|
logger.log(
|
||||||
"Got incoming call for room " + mappedRoomId + " but there's already a call for this room: ignoring",
|
"Got incoming call for room " + mappedRoomId + " but there's already a call for this room: ignoring",
|
||||||
|
@ -411,7 +412,8 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
// the call, we'll be ready to send. NB. This is the protocol-level room ID not
|
// the call, we'll be ready to send. NB. This is the protocol-level room ID not
|
||||||
// the mapped one: that's where we'll send the events.
|
// the mapped one: that's where we'll send the events.
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
cli.prepareToEncrypt(cli.getRoom(call.roomId));
|
const room = cli.getRoom(call.roomId);
|
||||||
|
if (room) cli.prepareToEncrypt(room);
|
||||||
};
|
};
|
||||||
|
|
||||||
public getCallById(callId: string): MatrixCall | null {
|
public getCallById(callId: string): MatrixCall | null {
|
||||||
|
@ -505,7 +507,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
if (this.audioPromises.has(audioId)) {
|
if (this.audioPromises.has(audioId)) {
|
||||||
this.audioPromises.set(
|
this.audioPromises.set(
|
||||||
audioId,
|
audioId,
|
||||||
this.audioPromises.get(audioId).then(() => {
|
this.audioPromises.get(audioId)!.then(() => {
|
||||||
audio.load();
|
audio.load();
|
||||||
return playAudio();
|
return playAudio();
|
||||||
}),
|
}),
|
||||||
|
@ -531,7 +533,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
};
|
};
|
||||||
if (audio) {
|
if (audio) {
|
||||||
if (this.audioPromises.has(audioId)) {
|
if (this.audioPromises.has(audioId)) {
|
||||||
this.audioPromises.set(audioId, this.audioPromises.get(audioId).then(pauseAudio));
|
this.audioPromises.set(audioId, this.audioPromises.get(audioId)!.then(pauseAudio));
|
||||||
} else {
|
} else {
|
||||||
pauseAudio();
|
pauseAudio();
|
||||||
}
|
}
|
||||||
|
@ -546,7 +548,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
// is the call we consider 'the' call for its room.
|
// is the call we consider 'the' call for its room.
|
||||||
const mappedRoomId = this.roomIdForCall(call);
|
const mappedRoomId = this.roomIdForCall(call);
|
||||||
|
|
||||||
const callForThisRoom = this.getCallForRoom(mappedRoomId);
|
const callForThisRoom = mappedRoomId ? this.getCallForRoom(mappedRoomId) : null;
|
||||||
return !!callForThisRoom && call.callId === callForThisRoom.callId;
|
return !!callForThisRoom && call.callId === callForThisRoom.callId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,7 +842,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
cancelButton: _t("OK"),
|
cancelButton: _t("OK"),
|
||||||
onFinished: (allow) => {
|
onFinished: (allow) => {
|
||||||
SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow);
|
SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow);
|
||||||
cli.setFallbackICEServerAllowed(allow);
|
cli.setFallbackICEServerAllowed(!!allow);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
@ -898,7 +900,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
// previous calls that are probably stale by now, so just cancel them.
|
// previous calls that are probably stale by now, so just cancel them.
|
||||||
if (mappedRoomId !== roomId) {
|
if (mappedRoomId !== roomId) {
|
||||||
const mappedRoom = MatrixClientPeg.get().getRoom(mappedRoomId);
|
const mappedRoom = MatrixClientPeg.get().getRoom(mappedRoomId);
|
||||||
if (mappedRoom.getPendingEvents().length > 0) {
|
if (mappedRoom?.getPendingEvents().length) {
|
||||||
Resend.cancelUnsentEvents(mappedRoom);
|
Resend.cancelUnsentEvents(mappedRoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -933,7 +935,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async placeCall(roomId: string, type?: CallType, transferee?: MatrixCall): Promise<void> {
|
public async placeCall(roomId: string, type: CallType, transferee?: MatrixCall): Promise<void> {
|
||||||
// Pause current broadcast, if any
|
// Pause current broadcast, if any
|
||||||
SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.pause();
|
SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.pause();
|
||||||
|
|
||||||
|
|
|
@ -273,7 +273,9 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
withoutScrollContainer
|
withoutScrollContainer
|
||||||
ref={this.card}
|
ref={this.card}
|
||||||
>
|
>
|
||||||
<Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />
|
{this.card.current && (
|
||||||
|
<Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />
|
||||||
|
)}
|
||||||
<SearchWarning isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
|
<SearchWarning isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
|
||||||
<TimelinePanel
|
<TimelinePanel
|
||||||
manageReadReceipts={false}
|
manageReadReceipts={false}
|
||||||
|
|
|
@ -153,6 +153,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private doStickyHeaders(list: HTMLDivElement): void {
|
private doStickyHeaders(list: HTMLDivElement): void {
|
||||||
|
if (!list.parentElement) return;
|
||||||
const topEdge = list.scrollTop;
|
const topEdge = list.scrollTop;
|
||||||
const bottomEdge = list.offsetHeight + list.scrollTop;
|
const bottomEdge = list.offsetHeight + list.scrollTop;
|
||||||
const sublists = list.querySelectorAll<HTMLDivElement>(".mx_RoomSublist:not(.mx_RoomSublist_hidden)");
|
const sublists = list.querySelectorAll<HTMLDivElement>(".mx_RoomSublist:not(.mx_RoomSublist_hidden)");
|
||||||
|
|
|
@ -123,8 +123,8 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get duration(): number | null {
|
public get duration(): number | null {
|
||||||
if (!this.hangup || !this.selectAnswer) return null;
|
if (!this.hangup?.getDate() || !this.selectAnswer?.getDate()) return null;
|
||||||
return this.hangup.getDate().getTime() - this.selectAnswer.getDate().getTime();
|
return this.hangup.getDate()!.getTime() - this.selectAnswer.getDate()!.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -169,7 +169,7 @@ interface IProps {
|
||||||
// the initial queryParams extracted from the hash-fragment of the URI
|
// the initial queryParams extracted from the hash-fragment of the URI
|
||||||
startingFragmentQueryParams?: QueryDict;
|
startingFragmentQueryParams?: QueryDict;
|
||||||
// called when we have completed a token login
|
// called when we have completed a token login
|
||||||
onTokenLoginCompleted?: () => void;
|
onTokenLoginCompleted: () => void;
|
||||||
// Represents the screen to display as a result of parsing the initial window.location
|
// Represents the screen to display as a result of parsing the initial window.location
|
||||||
initialScreenAfterLogin?: IScreen;
|
initialScreenAfterLogin?: IScreen;
|
||||||
// displayname, if any, to set on the device when logging in/registering.
|
// displayname, if any, to set on the device when logging in/registering.
|
||||||
|
@ -1200,7 +1200,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
// We have to manually update the room list because the forgotten room will not
|
// We have to manually update the room list because the forgotten room will not
|
||||||
// be notified to us, therefore the room list will have no other way of knowing
|
// be notified to us, therefore the room list will have no other way of knowing
|
||||||
// the room is forgotten.
|
// the room is forgotten.
|
||||||
RoomListStore.instance.manualRoomUpdate(room, RoomUpdateCause.RoomRemoved);
|
if (room) RoomListStore.instance.manualRoomUpdate(room, RoomUpdateCause.RoomRemoved);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
const errCode = err.errcode || _td("unknown error code");
|
const errCode = err.errcode || _td("unknown error code");
|
||||||
|
@ -2124,7 +2124,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
onForgotPasswordClick={showPasswordReset ? this.onForgotPasswordClick : undefined}
|
onForgotPasswordClick={showPasswordReset ? this.onForgotPasswordClick : undefined}
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
fragmentAfterLogin={fragmentAfterLogin}
|
fragmentAfterLogin={fragmentAfterLogin}
|
||||||
defaultUsername={this.props.startingFragmentQueryParams.defaultUsername as string}
|
defaultUsername={this.props.startingFragmentQueryParams?.defaultUsername as string | undefined}
|
||||||
{...this.getServerProperties()}
|
{...this.getServerProperties()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
|
import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
|
||||||
import { isSupportedReceiptType } from "matrix-js-sdk/src/utils";
|
import { isSupportedReceiptType } from "matrix-js-sdk/src/utils";
|
||||||
|
import { Optional } from "matrix-events-sdk";
|
||||||
|
|
||||||
import shouldHideEvent from "../../shouldHideEvent";
|
import shouldHideEvent from "../../shouldHideEvent";
|
||||||
import { wantsDateSeparator } from "../../DateUtils";
|
import { wantsDateSeparator } from "../../DateUtils";
|
||||||
|
@ -436,10 +437,8 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
||||||
* defaults to 0.
|
* defaults to 0.
|
||||||
*/
|
*/
|
||||||
public scrollToEvent(eventId: string, pixelOffset: number, offsetBase: number): void {
|
public scrollToEvent(eventId: string, pixelOffset?: number, offsetBase?: number): void {
|
||||||
if (this.scrollPanel.current) {
|
this.scrollPanel.current?.scrollToToken(eventId, pixelOffset, offsetBase);
|
||||||
this.scrollPanel.current.scrollToToken(eventId, pixelOffset, offsetBase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public scrollToEventIfNeeded(eventId: string): void {
|
public scrollToEventIfNeeded(eventId: string): void {
|
||||||
|
@ -590,16 +589,16 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
return { nextEventAndShouldShow, nextTile };
|
return { nextEventAndShouldShow, nextTile };
|
||||||
}
|
}
|
||||||
|
|
||||||
private get pendingEditItem(): string | undefined {
|
private get pendingEditItem(): string | null {
|
||||||
if (!this.props.room) {
|
if (!this.props.room) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return localStorage.getItem(editorRoomKey(this.props.room.roomId, this.context.timelineRenderingType));
|
return localStorage.getItem(editorRoomKey(this.props.room.roomId, this.context.timelineRenderingType));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,7 +814,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public wantsDateSeparator(prevEvent: MatrixEvent | null, nextEventDate: Date): boolean {
|
public wantsDateSeparator(prevEvent: MatrixEvent | null, nextEventDate: Optional<Date>): boolean {
|
||||||
if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) {
|
if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1174,7 +1173,7 @@ class CreationGrouper extends BaseGrouper {
|
||||||
const ts = createEvent.event.getTs();
|
const ts = createEvent.event.getTs();
|
||||||
ret.push(
|
ret.push(
|
||||||
<li key={ts + "~"}>
|
<li key={ts + "~"}>
|
||||||
<DateSeparator roomId={createEvent.event.getRoomId()} ts={ts} />
|
<DateSeparator roomId={createEvent.event.getRoomId()!} ts={ts} />
|
||||||
</li>,
|
</li>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1326,7 +1325,7 @@ class MainGrouper extends BaseGrouper {
|
||||||
const ts = this.events[0].getTs();
|
const ts = this.events[0].getTs();
|
||||||
ret.push(
|
ret.push(
|
||||||
<li key={ts + "~"}>
|
<li key={ts + "~"}>
|
||||||
<DateSeparator roomId={this.events[0].getRoomId()} ts={ts} />
|
<DateSeparator roomId={this.events[0].getRoomId()!} ts={ts} />
|
||||||
</li>,
|
</li>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,8 @@ interface IState {
|
||||||
secondaryCall: MatrixCall;
|
secondaryCall: MatrixCall;
|
||||||
|
|
||||||
// widget candidate to be displayed in the pip view.
|
// widget candidate to be displayed in the pip view.
|
||||||
persistentWidgetId: string;
|
persistentWidgetId: string | null;
|
||||||
persistentRoomId: string;
|
persistentRoomId: string | null;
|
||||||
showWidgetInPip: boolean;
|
showWidgetInPip: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
||||||
if (callRoomId ?? this.state.persistentRoomId) {
|
if (callRoomId ?? this.state.persistentRoomId) {
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
room_id: callRoomId ?? this.state.persistentRoomId,
|
room_id: callRoomId ?? this.state.persistentRoomId ?? undefined,
|
||||||
metricsTrigger: "WebFloatingCallWindow",
|
metricsTrigger: "WebFloatingCallWindow",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
||||||
pipContent.push(({ onStartMoving }) => (
|
pipContent.push(({ onStartMoving }) => (
|
||||||
<WidgetPip
|
<WidgetPip
|
||||||
widgetId={this.state.persistentWidgetId}
|
widgetId={this.state.persistentWidgetId}
|
||||||
room={MatrixClientPeg.get().getRoom(this.state.persistentRoomId)!}
|
room={MatrixClientPeg.get().getRoom(this.state.persistentRoomId ?? undefined)!}
|
||||||
viewingRoom={this.state.viewedRoomId === this.state.persistentRoomId}
|
viewingRoom={this.state.viewedRoomId === this.state.persistentRoomId}
|
||||||
onStartMoving={onStartMoving}
|
onStartMoving={onStartMoving}
|
||||||
movePersistedElement={this.props.movePersistedElement}
|
movePersistedElement={this.props.movePersistedElement}
|
||||||
|
|
|
@ -218,36 +218,40 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.Timeline:
|
case RightPanelPhases.Timeline:
|
||||||
card = (
|
if (this.props.room) {
|
||||||
<TimelineCard
|
card = (
|
||||||
classNames="mx_ThreadPanel mx_TimelineCard"
|
<TimelineCard
|
||||||
room={this.props.room}
|
classNames="mx_ThreadPanel mx_TimelineCard"
|
||||||
timelineSet={this.props.room.getUnfilteredTimelineSet()}
|
room={this.props.room}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
timelineSet={this.props.room.getUnfilteredTimelineSet()}
|
||||||
onClose={this.onClose}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
onClose={this.onClose}
|
||||||
e2eStatus={this.props.e2eStatus}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
/>
|
e2eStatus={this.props.e2eStatus}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.FilePanel:
|
case RightPanelPhases.FilePanel:
|
||||||
card = <FilePanel roomId={roomId} resizeNotifier={this.props.resizeNotifier} onClose={this.onClose} />;
|
card = <FilePanel roomId={roomId} resizeNotifier={this.props.resizeNotifier} onClose={this.onClose} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.ThreadView:
|
case RightPanelPhases.ThreadView:
|
||||||
card = (
|
if (this.props.room) {
|
||||||
<ThreadView
|
card = (
|
||||||
room={this.props.room}
|
<ThreadView
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
room={this.props.room}
|
||||||
onClose={this.onClose}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
mxEvent={cardState?.threadHeadEvent}
|
onClose={this.onClose}
|
||||||
initialEvent={cardState?.initialEvent}
|
mxEvent={cardState?.threadHeadEvent}
|
||||||
isInitialEventHighlighted={cardState?.isInitialEventHighlighted}
|
initialEvent={cardState?.initialEvent}
|
||||||
initialEventScrollIntoView={cardState?.initialEventScrollIntoView}
|
isInitialEventHighlighted={cardState?.isInitialEventHighlighted}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
initialEventScrollIntoView={cardState?.initialEventScrollIntoView}
|
||||||
e2eStatus={this.props.e2eStatus}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
/>
|
e2eStatus={this.props.e2eStatus}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.ThreadPanel:
|
case RightPanelPhases.ThreadPanel:
|
||||||
|
@ -262,18 +266,22 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.RoomSummary:
|
case RightPanelPhases.RoomSummary:
|
||||||
card = (
|
if (this.props.room) {
|
||||||
<RoomSummaryCard
|
card = (
|
||||||
room={this.props.room}
|
<RoomSummaryCard
|
||||||
onClose={this.onClose}
|
room={this.props.room}
|
||||||
// whenever RightPanel is passed a room it is passed a permalinkcreator
|
onClose={this.onClose}
|
||||||
permalinkCreator={this.props.permalinkCreator!}
|
// whenever RightPanel is passed a room it is passed a permalinkcreator
|
||||||
/>
|
permalinkCreator={this.props.permalinkCreator!}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.Widget:
|
case RightPanelPhases.Widget:
|
||||||
card = <WidgetCard room={this.props.room} widgetId={cardState?.widgetId} onClose={this.onClose} />;
|
if (this.props.room) {
|
||||||
|
card = <WidgetCard room={this.props.room} widgetId={cardState?.widgetId} onClose={this.onClose} />;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
// and the root event.
|
// and the root event.
|
||||||
// The rest will be lost for now, until the aggregation API on the server
|
// The rest will be lost for now, until the aggregation API on the server
|
||||||
// becomes available to fetch a whole thread
|
// becomes available to fetch a whole thread
|
||||||
if (!initialEvent && this.context.client) {
|
if (!initialEvent && this.context.client && roomId) {
|
||||||
initialEvent = (await fetchInitialEvent(this.context.client, roomId, initialEventId)) ?? undefined;
|
initialEvent = (await fetchInitialEvent(this.context.client, roomId, initialEventId)) ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,7 +741,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
// If an event ID wasn't specified, default to the one saved for this room
|
// If an event ID wasn't specified, default to the one saved for this room
|
||||||
// in the scroll state store. Assume initialEventPixelOffset should be set.
|
// in the scroll state store. Assume initialEventPixelOffset should be set.
|
||||||
if (!newState.initialEventId) {
|
if (!newState.initialEventId && newState.roomId) {
|
||||||
const roomScrollState = RoomScrollStateStore.getScrollState(newState.roomId);
|
const roomScrollState = RoomScrollStateStore.getScrollState(newState.roomId);
|
||||||
if (roomScrollState) {
|
if (roomScrollState) {
|
||||||
newState.initialEventId = roomScrollState.focussedEvent;
|
newState.initialEventId = roomScrollState.focussedEvent;
|
||||||
|
@ -770,7 +770,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
// callback because this would prevent the setStates from being batched,
|
// callback because this would prevent the setStates from being batched,
|
||||||
// ie. cause it to render RoomView twice rather than the once that is necessary.
|
// ie. cause it to render RoomView twice rather than the once that is necessary.
|
||||||
if (initial) {
|
if (initial) {
|
||||||
this.setupRoom(newState.room, newState.roomId, newState.joining, newState.shouldPeek);
|
this.setupRoom(newState.room, newState.roomId, !!newState.joining, !!newState.shouldPeek);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -794,13 +794,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.setState({ activeCall });
|
this.setState({ activeCall });
|
||||||
};
|
};
|
||||||
|
|
||||||
private getRoomId = (): string => {
|
private getRoomId = (): string | undefined => {
|
||||||
// According to `onRoomViewStoreUpdate`, `state.roomId` can be null
|
// According to `onRoomViewStoreUpdate`, `state.roomId` can be null
|
||||||
// if we have a room alias we haven't resolved yet. To work around this,
|
// if we have a room alias we haven't resolved yet. To work around this,
|
||||||
// first we'll try the room object if it's there, and then fallback to
|
// first we'll try the room object if it's there, and then fallback to
|
||||||
// the bare room ID. (We may want to update `state.roomId` after
|
// the bare room ID. (We may want to update `state.roomId` after
|
||||||
// resolving aliases, so we could always trust it.)
|
// resolving aliases, so we could always trust it.)
|
||||||
return this.state.room ? this.state.room.roomId : this.state.roomId;
|
return this.state.room?.roomId ?? this.state.roomId;
|
||||||
};
|
};
|
||||||
|
|
||||||
private getPermalinkCreatorForRoom(room: Room): RoomPermalinkCreator {
|
private getPermalinkCreatorForRoom(room: Room): RoomPermalinkCreator {
|
||||||
|
@ -1008,7 +1008,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
SettingsStore.unwatchSetting(watcher);
|
SettingsStore.unwatchSetting(watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.viewsLocalRoom) {
|
if (this.viewsLocalRoom && this.state.room) {
|
||||||
// clean up if this was a local room
|
// clean up if this was a local room
|
||||||
this.context.client?.store.removeRoom(this.state.room.roomId);
|
this.context.client?.store.removeRoom(this.state.room.roomId);
|
||||||
}
|
}
|
||||||
|
@ -1188,16 +1188,16 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onLocalRoomEvent(roomId: string): void {
|
private onLocalRoomEvent(roomId: string): void {
|
||||||
if (roomId !== this.state.room.roomId) return;
|
if (!this.context.client || !this.state.room || roomId !== this.state.room.roomId) return;
|
||||||
createRoomFromLocalRoom(this.context.client, this.state.room as LocalRoom);
|
createRoomFromLocalRoom(this.context.client, this.state.room as LocalRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoomTimeline = (
|
private onRoomTimeline = (
|
||||||
ev: MatrixEvent,
|
ev: MatrixEvent,
|
||||||
room: Room | undefined,
|
room: Room | undefined,
|
||||||
toStartOfTimeline: boolean,
|
toStartOfTimeline: boolean | undefined,
|
||||||
removed: boolean,
|
removed: boolean,
|
||||||
data?: IRoomTimelineData,
|
data: IRoomTimelineData,
|
||||||
): void => {
|
): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
|
@ -1249,6 +1249,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleEffects = (ev: MatrixEvent): void => {
|
private handleEffects = (ev: MatrixEvent): void => {
|
||||||
|
if (!this.state.room) return;
|
||||||
const notifState = this.context.roomNotificationStateStore.getRoomState(this.state.room);
|
const notifState = this.context.roomNotificationStateStore.getRoomState(this.state.room);
|
||||||
if (!notifState.isUnread) return;
|
if (!notifState.isUnread) return;
|
||||||
|
|
||||||
|
@ -1362,7 +1363,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
private updatePreviewUrlVisibility({ roomId }: Room): void {
|
private updatePreviewUrlVisibility({ roomId }: Room): void {
|
||||||
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
||||||
const key = this.context.client.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
const key = this.context.client?.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
||||||
this.setState({
|
this.setState({
|
||||||
showUrlPreview: SettingsStore.getValue(key, roomId),
|
showUrlPreview: SettingsStore.getValue(key, roomId),
|
||||||
});
|
});
|
||||||
|
@ -1661,7 +1662,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: true,
|
rejecting: true,
|
||||||
});
|
});
|
||||||
this.context.client.leave(this.state.roomId).then(
|
this.context.client?.leave(this.state.roomId).then(
|
||||||
() => {
|
() => {
|
||||||
dis.dispatch({ action: Action.ViewHomePage });
|
dis.dispatch({ action: Action.ViewHomePage });
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -1691,13 +1692,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const myMember = this.state.room.getMember(this.context.client.getUserId());
|
const myMember = this.state.room!.getMember(this.context.client!.getSafeUserId());
|
||||||
const inviteEvent = myMember.events.member;
|
const inviteEvent = myMember!.events.member;
|
||||||
const ignoredUsers = this.context.client.getIgnoredUsers();
|
const ignoredUsers = this.context.client!.getIgnoredUsers();
|
||||||
ignoredUsers.push(inviteEvent.getSender()); // de-duped internally in the js-sdk
|
ignoredUsers.push(inviteEvent!.getSender()!); // de-duped internally in the js-sdk
|
||||||
await this.context.client.setIgnoredUsers(ignoredUsers);
|
await this.context.client!.setIgnoredUsers(ignoredUsers);
|
||||||
|
|
||||||
await this.context.client.leave(this.state.roomId);
|
await this.context.client!.leave(this.state.roomId!);
|
||||||
dis.dispatch({ action: Action.ViewHomePage });
|
dis.dispatch({ action: Action.ViewHomePage });
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: false,
|
rejecting: false,
|
||||||
|
|
|
@ -382,13 +382,12 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemlist = this.itemlist.current;
|
const itemlist = this.itemlist.current;
|
||||||
const firstTile = itemlist && (itemlist.firstElementChild as HTMLElement);
|
const firstTile = itemlist?.firstElementChild as HTMLElement | undefined;
|
||||||
const contentTop = firstTile && firstTile.offsetTop;
|
|
||||||
const fillPromises: Promise<void>[] = [];
|
const fillPromises: Promise<void>[] = [];
|
||||||
|
|
||||||
// if scrollTop gets to 1 screen from the top of the first tile,
|
// if scrollTop gets to 1 screen from the top of the first tile,
|
||||||
// try backward filling
|
// try backward filling
|
||||||
if (!firstTile || sn.scrollTop - contentTop < sn.clientHeight) {
|
if (!firstTile || sn.scrollTop - firstTile.offsetTop < sn.clientHeight) {
|
||||||
// need to back-fill
|
// need to back-fill
|
||||||
fillPromises.push(this.maybeFill(depth, true));
|
fillPromises.push(this.maybeFill(depth, true));
|
||||||
}
|
}
|
||||||
|
@ -424,7 +423,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
// check if unfilling is possible and send an unfill request if necessary
|
// check if unfilling is possible and send an unfill request if necessary
|
||||||
private checkUnfillState(backwards: boolean): void {
|
private checkUnfillState(backwards: boolean): void {
|
||||||
let excessHeight = this.getExcessHeight(backwards);
|
let excessHeight = this.getExcessHeight(backwards);
|
||||||
if (excessHeight <= 0) {
|
if (excessHeight <= 0 || !this.itemlist.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,10 +616,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
||||||
* defaults to 0.
|
* defaults to 0.
|
||||||
*/
|
*/
|
||||||
public scrollToToken = (scrollToken: string, pixelOffset: number, offsetBase: number): void => {
|
public scrollToToken = (scrollToken: string, pixelOffset = 0, offsetBase = 0): void => {
|
||||||
pixelOffset = pixelOffset || 0;
|
|
||||||
offsetBase = offsetBase || 0;
|
|
||||||
|
|
||||||
// set the trackedScrollToken, so we can get the node through getTrackedNode
|
// set the trackedScrollToken, so we can get the node through getTrackedNode
|
||||||
this.scrollState = {
|
this.scrollState = {
|
||||||
stuckAtBottom: false,
|
stuckAtBottom: false,
|
||||||
|
@ -652,6 +648,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
const viewportBottom = scrollNode.scrollHeight - (scrollNode.scrollTop + scrollNode.clientHeight);
|
const viewportBottom = scrollNode.scrollHeight - (scrollNode.scrollTop + scrollNode.clientHeight);
|
||||||
|
|
||||||
const itemlist = this.itemlist.current;
|
const itemlist = this.itemlist.current;
|
||||||
|
if (!itemlist) return;
|
||||||
const messages = itemlist.children;
|
const messages = itemlist.children;
|
||||||
let node: HTMLElement | null = null;
|
let node: HTMLElement | null = null;
|
||||||
|
|
||||||
|
@ -705,7 +702,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
this.bottomGrowth += bottomDiff;
|
this.bottomGrowth += bottomDiff;
|
||||||
scrollState.bottomOffset = newBottomOffset;
|
scrollState.bottomOffset = newBottomOffset;
|
||||||
const newHeight = `${this.getListHeight()}px`;
|
const newHeight = `${this.getListHeight()}px`;
|
||||||
if (itemlist.style.height !== newHeight) {
|
if (itemlist && itemlist.style.height !== newHeight) {
|
||||||
itemlist.style.height = newHeight;
|
itemlist.style.height = newHeight;
|
||||||
}
|
}
|
||||||
debuglog("balancing height because messages below viewport grew by", bottomDiff);
|
debuglog("balancing height because messages below viewport grew by", bottomDiff);
|
||||||
|
@ -755,7 +752,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
|
|
||||||
const scrollState = this.scrollState;
|
const scrollState = this.scrollState;
|
||||||
if (scrollState.stuckAtBottom) {
|
if (scrollState.stuckAtBottom) {
|
||||||
if (itemlist.style.height !== newHeight) {
|
if (itemlist && itemlist.style.height !== newHeight) {
|
||||||
itemlist.style.height = newHeight;
|
itemlist.style.height = newHeight;
|
||||||
}
|
}
|
||||||
if (sn.scrollTop !== sn.scrollHeight) {
|
if (sn.scrollTop !== sn.scrollHeight) {
|
||||||
|
@ -770,7 +767,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
// the currently filled piece of the timeline
|
// the currently filled piece of the timeline
|
||||||
if (trackedNode) {
|
if (trackedNode) {
|
||||||
const oldTop = trackedNode.offsetTop;
|
const oldTop = trackedNode.offsetTop;
|
||||||
if (itemlist.style.height !== newHeight) {
|
if (itemlist && itemlist.style.height !== newHeight) {
|
||||||
itemlist.style.height = newHeight;
|
itemlist.style.height = newHeight;
|
||||||
}
|
}
|
||||||
const newTop = trackedNode.offsetTop;
|
const newTop = trackedNode.offsetTop;
|
||||||
|
@ -823,9 +820,9 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
|
|
||||||
private getMessagesHeight(): number {
|
private getMessagesHeight(): number {
|
||||||
const itemlist = this.itemlist.current;
|
const itemlist = this.itemlist.current;
|
||||||
const lastNode = itemlist.lastElementChild as HTMLElement;
|
const lastNode = itemlist?.lastElementChild as HTMLElement;
|
||||||
const lastNodeBottom = lastNode ? lastNode.offsetTop + lastNode.clientHeight : 0;
|
const lastNodeBottom = lastNode ? lastNode.offsetTop + lastNode.clientHeight : 0;
|
||||||
const firstNodeTop = itemlist.firstElementChild ? (itemlist.firstElementChild as HTMLElement).offsetTop : 0;
|
const firstNodeTop = (itemlist?.firstElementChild as HTMLElement)?.offsetTop ?? 0;
|
||||||
// 18 is itemlist padding
|
// 18 is itemlist padding
|
||||||
return lastNodeBottom - firstNodeTop + 18 * 2;
|
return lastNodeBottom - firstNodeTop + 18 * 2;
|
||||||
}
|
}
|
||||||
|
@ -865,8 +862,8 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
*/
|
*/
|
||||||
public preventShrinking = (): void => {
|
public preventShrinking = (): void => {
|
||||||
const messageList = this.itemlist.current;
|
const messageList = this.itemlist.current;
|
||||||
const tiles = messageList && messageList.children;
|
const tiles = messageList?.children;
|
||||||
if (!messageList) {
|
if (!tiles) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let lastTileNode;
|
let lastTileNode;
|
||||||
|
|
|
@ -777,7 +777,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public canResetTimeline = (): boolean => this.messagePanel?.current?.isAtBottom();
|
public canResetTimeline = (): boolean => this.messagePanel?.current?.isAtBottom() === true;
|
||||||
|
|
||||||
private onRoomRedaction = (ev: MatrixEvent, room: Room): void => {
|
private onRoomRedaction = (ev: MatrixEvent, room: Room): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
@ -1044,7 +1044,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
||||||
const sendRRs = SettingsStore.getValue("sendReadReceipts", roomId);
|
const sendRRs = SettingsStore.getValue("sendReadReceipts", roomId);
|
||||||
|
|
||||||
debuglog(
|
debuglog(
|
||||||
`Sending Read Markers for ${this.props.timelineSet.room.roomId}: `,
|
`Sending Read Markers for ${roomId}: `,
|
||||||
`rm=${this.state.readMarkerEventId} `,
|
`rm=${this.state.readMarkerEventId} `,
|
||||||
`rr=${sendRRs ? lastReadEvent?.getId() : null} `,
|
`rr=${sendRRs ? lastReadEvent?.getId() : null} `,
|
||||||
`prr=${lastReadEvent?.getId()}`,
|
`prr=${lastReadEvent?.getId()}`,
|
||||||
|
@ -1092,7 +1092,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
||||||
// we only do this if we're right at the end, because we're just assuming
|
// we only do this if we're right at the end, because we're just assuming
|
||||||
// that sending an RR for the latest message will set our notif counter
|
// that sending an RR for the latest message will set our notif counter
|
||||||
// to zero: it may not do this if we send an RR for somewhere before the end.
|
// to zero: it may not do this if we send an RR for somewhere before the end.
|
||||||
if (this.isAtEndOfLiveTimeline()) {
|
if (this.isAtEndOfLiveTimeline() && this.props.timelineSet.room) {
|
||||||
this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Total, 0);
|
this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Total, 0);
|
||||||
this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Highlight, 0);
|
this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Highlight, 0);
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
|
|
@ -99,7 +99,7 @@ export default class MessageComposerFormatBar extends React.PureComponent<IProps
|
||||||
}
|
}
|
||||||
|
|
||||||
public showAt(selectionRect: DOMRect): void {
|
public showAt(selectionRect: DOMRect): void {
|
||||||
if (!this.formatBarRef.current) return;
|
if (!this.formatBarRef.current?.parentElement) return;
|
||||||
|
|
||||||
this.setState({ visible: true });
|
this.setState({ visible: true });
|
||||||
const parentRect = this.formatBarRef.current.parentElement.getBoundingClientRect();
|
const parentRect = this.formatBarRef.current.parentElement.getBoundingClientRect();
|
||||||
|
|
|
@ -216,7 +216,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const kickerMember = this.props.room?.currentState.getMember(myMember.events.member.getSender());
|
const kickerMember = this.props.room?.currentState.getMember(myMember.events.member.getSender());
|
||||||
const memberName = kickerMember ? kickerMember.name : myMember.events.member.getSender();
|
const memberName = kickerMember?.name ?? myMember.events.member?.getSender();
|
||||||
const reason = myMember.events.member?.getContent().reason;
|
const reason = myMember.events.member?.getContent().reason;
|
||||||
return { memberName, reason };
|
return { memberName, reason };
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,9 +433,11 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onHeaderClick = (): void => {
|
private onHeaderClick = (): void => {
|
||||||
const possibleSticky = this.headerButton.current.parentElement;
|
const possibleSticky = this.headerButton.current?.parentElement;
|
||||||
const sublist = possibleSticky.parentElement.parentElement;
|
const sublist = possibleSticky?.parentElement?.parentElement;
|
||||||
const list = sublist.parentElement.parentElement;
|
const list = sublist?.parentElement?.parentElement;
|
||||||
|
if (!possibleSticky || !list) return;
|
||||||
|
|
||||||
// the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky
|
// the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky
|
||||||
const listScrollTop = Math.round(list.scrollTop);
|
const listScrollTop = Math.round(list.scrollTop);
|
||||||
const isAtTop = listScrollTop <= Math.round(HEADER_HEIGHT);
|
const isAtTop = listScrollTop <= Math.round(HEADER_HEIGHT);
|
||||||
|
|
|
@ -91,7 +91,7 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onRoomTimeline = (event: MatrixEvent, room?: Room): void => {
|
private onRoomTimeline = (event: MatrixEvent, room?: Room): void => {
|
||||||
if (room?.roomId === this.props.room.roomId) {
|
if (room?.roomId === this.props.room.roomId) {
|
||||||
const userId = event.getSender();
|
const userId = event.getSender()!;
|
||||||
// remove user from usersTyping
|
// remove user from usersTyping
|
||||||
const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId);
|
const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId);
|
||||||
if (usersTyping.length !== this.state.usersTyping.length) {
|
if (usersTyping.length !== this.state.usersTyping.length) {
|
||||||
|
@ -200,14 +200,15 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let usersTyping = this.state.usersTyping;
|
const usersTyping = this.state.usersTyping;
|
||||||
const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers).map((userId) =>
|
|
||||||
this.props.room.getMember(userId),
|
|
||||||
);
|
|
||||||
// append the users that have been reported not typing anymore
|
// append the users that have been reported not typing anymore
|
||||||
// but have a timeout timer running so they can disappear
|
// but have a timeout timer running so they can disappear
|
||||||
// when a message comes in
|
// when a message comes in
|
||||||
usersTyping = usersTyping.concat(stoppedUsersOnTimer);
|
for (const userId in this.state.delayedStopTypingTimers) {
|
||||||
|
const member = this.props.room.getMember(userId);
|
||||||
|
if (member) usersTyping.push(member);
|
||||||
|
}
|
||||||
|
|
||||||
// sort them so the typing members don't change order when
|
// sort them so the typing members don't change order when
|
||||||
// moved to delayedStopTypingTimers
|
// moved to delayedStopTypingTimers
|
||||||
usersTyping.sort((a, b) => compare(a.name, b.name));
|
usersTyping.sort((a, b) => compare(a.name, b.name));
|
||||||
|
|
Loading…
Reference in a new issue