Conform more code to strictNullChecks
(#10444
* Conform more code to `strictNullChecks` * Fix tests * Fix tests
This commit is contained in:
parent
ba2608ec74
commit
c225b8ec29
29 changed files with 85 additions and 75 deletions
|
@ -24,8 +24,6 @@ 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 { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
|
|
||||||
import { ListenerMap } from "matrix-js-sdk/src/models/typed-event-emitter";
|
|
||||||
|
|
||||||
import shouldHideEvent from "../../shouldHideEvent";
|
import shouldHideEvent from "../../shouldHideEvent";
|
||||||
import { wantsDateSeparator } from "../../DateUtils";
|
import { wantsDateSeparator } from "../../DateUtils";
|
||||||
|
@ -543,7 +541,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private collectGhostReadMarker = (node: HTMLElement): void => {
|
private collectGhostReadMarker = (node: HTMLElement | null): void => {
|
||||||
if (node) {
|
if (node) {
|
||||||
// now the element has appeared, change the style which will trigger the CSS transition
|
// now the element has appeared, change the style which will trigger the CSS transition
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
|
@ -788,13 +786,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
continuation={continuation}
|
continuation={continuation}
|
||||||
isRedacted={mxEv.isRedacted()}
|
isRedacted={mxEv.isRedacted()}
|
||||||
replacingEventId={mxEv.replacingEventId()}
|
replacingEventId={mxEv.replacingEventId()}
|
||||||
editState={isEditing && this.props.editState}
|
editState={isEditing ? this.props.editState : undefined}
|
||||||
onHeightChanged={this.onHeightChanged}
|
onHeightChanged={this.onHeightChanged}
|
||||||
readReceipts={readReceipts}
|
readReceipts={readReceipts}
|
||||||
readReceiptMap={this.readReceiptMap}
|
readReceiptMap={this.readReceiptMap}
|
||||||
showUrlPreview={this.props.showUrlPreview}
|
showUrlPreview={this.props.showUrlPreview}
|
||||||
checkUnmounting={this.isUnmounting}
|
checkUnmounting={this.isUnmounting}
|
||||||
eventSendStatus={mxEv.getAssociatedStatus()}
|
eventSendStatus={mxEv.getAssociatedStatus() ?? undefined}
|
||||||
isTwelveHour={this.props.isTwelveHour}
|
isTwelveHour={this.props.isTwelveHour}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
last={last}
|
last={last}
|
||||||
|
@ -836,9 +834,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const receiptDestination: ReadReceipt<string, ListenerMap<string>> = this.context.threadId
|
const receiptDestination = this.context.threadId ? room.getThread(this.context.threadId) : room;
|
||||||
? room.getThread(this.context.threadId)
|
|
||||||
: room;
|
|
||||||
|
|
||||||
const receipts: IReadReceiptProps[] = [];
|
const receipts: IReadReceiptProps[] = [];
|
||||||
|
|
||||||
|
@ -1215,7 +1211,7 @@ class CreationGrouper extends BaseGrouper {
|
||||||
|
|
||||||
let summaryText: string;
|
let summaryText: string;
|
||||||
const roomId = ev.getRoomId();
|
const roomId = ev.getRoomId();
|
||||||
const creator = ev.sender ? ev.sender.name : ev.getSender();
|
const creator = ev.sender?.name ?? ev.getSender();
|
||||||
if (DMRoomMap.shared().getUserIdForRoomId(roomId)) {
|
if (DMRoomMap.shared().getUserIdForRoomId(roomId)) {
|
||||||
summaryText = _t("%(creator)s created this DM.", { creator });
|
summaryText = _t("%(creator)s created this DM.", { creator });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -377,7 +377,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
private roomView = createRef<HTMLElement>();
|
private roomView = createRef<HTMLElement>();
|
||||||
private searchResultsPanel = createRef<ScrollPanel>();
|
private searchResultsPanel = createRef<ScrollPanel>();
|
||||||
private messagePanel?: TimelinePanel;
|
private messagePanel: TimelinePanel | null = null;
|
||||||
private roomViewBody = createRef<HTMLDivElement>();
|
private roomViewBody = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
public static contextType = SDKContext;
|
public static contextType = SDKContext;
|
||||||
|
@ -611,11 +611,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
const newState: Partial<IRoomState> = {
|
const newState: Partial<IRoomState> = {
|
||||||
roomId: roomId ?? undefined,
|
roomId: roomId ?? undefined,
|
||||||
roomAlias: this.context.roomViewStore.getRoomAlias(),
|
roomAlias: this.context.roomViewStore.getRoomAlias() ?? undefined,
|
||||||
roomLoading: this.context.roomViewStore.isRoomLoading(),
|
roomLoading: this.context.roomViewStore.isRoomLoading(),
|
||||||
roomLoadError: this.context.roomViewStore.getRoomLoadError(),
|
roomLoadError: this.context.roomViewStore.getRoomLoadError() ?? undefined,
|
||||||
joining: this.context.roomViewStore.isJoining(),
|
joining: this.context.roomViewStore.isJoining(),
|
||||||
replyToEvent: this.context.roomViewStore.getQuotingEvent(),
|
replyToEvent: this.context.roomViewStore.getQuotingEvent() ?? undefined,
|
||||||
// we should only peek once we have a ready client
|
// we should only peek once we have a ready client
|
||||||
shouldPeek: this.state.matrixClientIsReady && this.context.roomViewStore.shouldPeek(),
|
shouldPeek: this.state.matrixClientIsReady && this.context.roomViewStore.shouldPeek(),
|
||||||
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
||||||
|
@ -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) {
|
if (!initialEvent && this.context.client) {
|
||||||
initialEvent = (await fetchInitialEvent(this.context.client, roomId, initialEventId)) ?? undefined;
|
initialEvent = (await fetchInitialEvent(this.context.client, roomId, initialEventId)) ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +848,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
isPeeking: true, // this will change to false if peeking fails
|
isPeeking: true, // this will change to false if peeking fails
|
||||||
});
|
});
|
||||||
this.context.client
|
this.context.client
|
||||||
.peekInRoom(roomId)
|
?.peekInRoom(roomId)
|
||||||
.then((room) => {
|
.then((room) => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
|
@ -883,7 +883,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
} else if (room) {
|
} else if (room) {
|
||||||
// Stop peeking because we have joined this room previously
|
// Stop peeking because we have joined this room previously
|
||||||
this.context.client.stopPeeking();
|
this.context.client?.stopPeeking();
|
||||||
this.setState({ isPeeking: false });
|
this.setState({ isPeeking: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -909,9 +909,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.onRoomViewStoreUpdate(true);
|
this.onRoomViewStoreUpdate(true);
|
||||||
|
|
||||||
const call = this.getCallForRoom();
|
const call = this.getCallForRoom();
|
||||||
const callState = call ? call.state : null;
|
const callState = call?.state;
|
||||||
this.setState({
|
this.setState({
|
||||||
callState: callState,
|
callState,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.context.legacyCallHandler.on(LegacyCallHandlerEvent.CallState, this.onCallState);
|
this.context.legacyCallHandler.on(LegacyCallHandlerEvent.CallState, this.onCallState);
|
||||||
|
@ -959,7 +959,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.shouldPeek) {
|
if (this.state.shouldPeek) {
|
||||||
this.context.client.stopPeeking();
|
this.context.client?.stopPeeking();
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop tracking room changes to format permalinks
|
// stop tracking room changes to format permalinks
|
||||||
|
@ -1010,7 +1010,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
if (this.viewsLocalRoom) {
|
if (this.viewsLocalRoom) {
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1469,7 +1469,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
private updatePermissions(room: Room): void {
|
private updatePermissions(room: Room): void {
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = this.context.client.getUserId();
|
const me = this.context.client.getSafeUserId();
|
||||||
const canReact =
|
const canReact =
|
||||||
room.getMyMembership() === "join" && room.currentState.maySendEvent(EventType.Reaction, me);
|
room.getMyMembership() === "join" && room.currentState.maySendEvent(EventType.Reaction, me);
|
||||||
const canSendMessages = room.maySendMessage();
|
const canSendMessages = room.maySendMessage();
|
||||||
|
@ -1866,7 +1866,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
// this has to be a proper method rather than an unnamed function,
|
// this has to be a proper method rather than an unnamed function,
|
||||||
// otherwise react calls it with null on each update.
|
// otherwise react calls it with null on each update.
|
||||||
private gatherTimelinePanelRef = (r?: TimelinePanel): void => {
|
private gatherTimelinePanelRef = (r: TimelinePanel | null): void => {
|
||||||
this.messagePanel = r;
|
this.messagePanel = r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
if (payload.event && !payload.event.getThread()) return;
|
if (payload.event && !payload.event.getThread()) return;
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
editState: payload.event ? new EditorStateTransfer(payload.event) : null,
|
editState: payload.event ? new EditorStateTransfer(payload.event) : undefined,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if (payload.event) {
|
if (payload.event) {
|
||||||
|
@ -213,9 +213,11 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private get threadLastReply(): MatrixEvent | undefined {
|
private get threadLastReply(): MatrixEvent | undefined {
|
||||||
return this.state.thread?.lastReply((ev: MatrixEvent) => {
|
return (
|
||||||
|
this.state.thread?.lastReply((ev: MatrixEvent) => {
|
||||||
return ev.isRelation(THREAD_RELATION_TYPE.name) && !ev.status;
|
return ev.isRelation(THREAD_RELATION_TYPE.name) && !ev.status;
|
||||||
});
|
}) ?? undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateThread = (thread?: Thread): void => {
|
private updateThread = (thread?: Thread): void => {
|
||||||
|
@ -245,7 +247,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private setupThreadListeners(thread?: Thread | undefined, oldThread?: Thread | undefined): void {
|
private setupThreadListeners(thread?: Thread | undefined, oldThread?: Thread | undefined): void {
|
||||||
if (oldThread) {
|
if (oldThread) {
|
||||||
this.state.thread.off(ThreadEvent.NewReply, this.updateThreadRelation);
|
this.state.thread?.off(ThreadEvent.NewReply, this.updateThreadRelation);
|
||||||
this.props.room.off(RoomEvent.LocalEchoUpdated, this.updateThreadRelation);
|
this.props.room.off(RoomEvent.LocalEchoUpdated, this.updateThreadRelation);
|
||||||
}
|
}
|
||||||
if (thread) {
|
if (thread) {
|
||||||
|
@ -344,7 +346,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const highlightedEventId = this.props.isInitialEventHighlighted ? this.props.initialEvent?.getId() : null;
|
const highlightedEventId = this.props.isInitialEventHighlighted ? this.props.initialEvent?.getId() : undefined;
|
||||||
|
|
||||||
const threadRelation = this.threadRelation;
|
const threadRelation = this.threadRelation;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ interface IProps {
|
||||||
// representing. This may or may not have a room, depending on what it's
|
// representing. This may or may not have a room, depending on what it's
|
||||||
// a timeline representing. If it has a room, we maintain RRs etc for
|
// a timeline representing. If it has a room, we maintain RRs etc for
|
||||||
// that room.
|
// that room.
|
||||||
timelineSet?: EventTimelineSet;
|
timelineSet: EventTimelineSet;
|
||||||
// overlay events from a second timelineset on the main timeline
|
// overlay events from a second timelineset on the main timeline
|
||||||
// added to support virtual rooms
|
// added to support virtual rooms
|
||||||
// events from the overlay timeline set will be added by localTimestamp
|
// events from the overlay timeline set will be added by localTimestamp
|
||||||
|
|
|
@ -33,6 +33,7 @@ interface IProps {
|
||||||
|
|
||||||
const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
|
const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
|
||||||
const info = SettingsStore.getBetaInfo(featureId);
|
const info = SettingsStore.getBetaInfo(featureId);
|
||||||
|
if (!info) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericFeatureFeedbackDialog
|
<GenericFeatureFeedbackDialog
|
||||||
|
|
|
@ -42,7 +42,7 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
|
||||||
const { matrixClient: cli, room, member, onFinished } = props;
|
const { matrixClient: cli, room, member, onFinished } = props;
|
||||||
const [keepStateEvents, setKeepStateEvents] = useState(true);
|
const [keepStateEvents, setKeepStateEvents] = useState(true);
|
||||||
|
|
||||||
let timeline = room.getLiveTimeline();
|
let timeline: EventTimeline | null = room.getLiveTimeline();
|
||||||
let eventsToRedact: MatrixEvent[] = [];
|
let eventsToRedact: MatrixEvent[] = [];
|
||||||
while (timeline) {
|
while (timeline) {
|
||||||
eventsToRedact = [
|
eventsToRedact = [
|
||||||
|
@ -93,7 +93,7 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
eventsToRedact.reverse().map(async (event): Promise<void> => {
|
eventsToRedact.reverse().map(async (event): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await cli.redactEvent(room.roomId, event.getId());
|
await cli.redactEvent(room.roomId, event.getId()!);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// log and swallow errors
|
// log and swallow errors
|
||||||
logger.error("Could not redact", event.getId());
|
logger.error("Could not redact", event.getId());
|
||||||
|
|
|
@ -71,7 +71,7 @@ interface IProps {
|
||||||
type ToolInfo = [label: string, tool: Tool];
|
type ToolInfo = [label: string, tool: Tool];
|
||||||
|
|
||||||
const DevtoolsDialog: React.FC<IProps> = ({ roomId, onFinished }) => {
|
const DevtoolsDialog: React.FC<IProps> = ({ roomId, onFinished }) => {
|
||||||
const [tool, setTool] = useState<ToolInfo>(null);
|
const [tool, setTool] = useState<ToolInfo | null>(null);
|
||||||
|
|
||||||
let body: JSX.Element;
|
let body: JSX.Element;
|
||||||
let onBack: () => void;
|
let onBack: () => void;
|
||||||
|
|
|
@ -51,6 +51,7 @@ import { ButtonEvent } from "../elements/AccessibleButton";
|
||||||
import { isLocationEvent } from "../../../utils/EventUtils";
|
import { isLocationEvent } from "../../../utils/EventUtils";
|
||||||
import { isSelfLocation, locationEventGeoUri } from "../../../utils/location";
|
import { isSelfLocation, locationEventGeoUri } from "../../../utils/location";
|
||||||
import { RoomContextDetails } from "../rooms/RoomContextDetails";
|
import { RoomContextDetails } from "../rooms/RoomContextDetails";
|
||||||
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
|
|
||||||
const AVATAR_SIZE = 30;
|
const AVATAR_SIZE = 30;
|
||||||
|
|
||||||
|
@ -194,7 +195,7 @@ const transformEvent = (event: MatrixEvent): { type: string; content: IContent }
|
||||||
};
|
};
|
||||||
|
|
||||||
const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCreator, onFinished }) => {
|
const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCreator, onFinished }) => {
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getSafeUserId();
|
||||||
const [profileInfo, setProfileInfo] = useState<any>({});
|
const [profileInfo, setProfileInfo] = useState<any>({});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
cli.getProfileInfo(userId).then((info) => setProfileInfo(info));
|
cli.getProfileInfo(userId).then((info) => setProfileInfo(info));
|
||||||
|
@ -242,7 +243,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
|
||||||
if (lcQuery) {
|
if (lcQuery) {
|
||||||
rooms = new QueryMatcher<Room>(rooms, {
|
rooms = new QueryMatcher<Room>(rooms, {
|
||||||
keys: ["name"],
|
keys: ["name"],
|
||||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)],
|
funcs: [(r) => filterBoolean([r.getCanonicalAlias(), ...r.getAltAliases()])],
|
||||||
shouldMatchWordsOnly: false,
|
shouldMatchWordsOnly: false,
|
||||||
}).match(lcQuery);
|
}).match(lcQuery);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
|
||||||
|
|
||||||
const onSubmit = async (e: SyntheticEvent): Promise<void> => {
|
const onSubmit = async (e: SyntheticEvent): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (!fieldRef.current) return;
|
||||||
if (email) {
|
if (email) {
|
||||||
const valid = await fieldRef.current.validate({});
|
const valid = await fieldRef.current.validate({});
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
|
||||||
return !error;
|
return !error;
|
||||||
},
|
},
|
||||||
invalid: function ({ error }) {
|
invalid: function ({ error }) {
|
||||||
return error;
|
return error ?? null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -49,7 +49,7 @@ interface ITermsDialogProps {
|
||||||
/**
|
/**
|
||||||
* urls that the user has already agreed to
|
* urls that the user has already agreed to
|
||||||
*/
|
*/
|
||||||
agreedUrls?: string[];
|
agreedUrls: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called with:
|
* Called with:
|
||||||
|
@ -127,7 +127,7 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const rows = [];
|
const rows: JSX.Element[] = [];
|
||||||
for (const policiesAndService of this.props.policiesAndServicePairs) {
|
for (const policiesAndService of this.props.policiesAndServicePairs) {
|
||||||
const parsedBaseUrl = url.parse(policiesAndService.service.baseUrl);
|
const parsedBaseUrl = url.parse(policiesAndService.service.baseUrl);
|
||||||
|
|
||||||
|
@ -135,8 +135,8 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
|
||||||
for (let i = 0; i < policyValues.length; ++i) {
|
for (let i = 0; i < policyValues.length; ++i) {
|
||||||
const termDoc = policyValues[i];
|
const termDoc = policyValues[i];
|
||||||
const termsLang = pickBestLanguage(Object.keys(termDoc).filter((k) => k !== "version"));
|
const termsLang = pickBestLanguage(Object.keys(termDoc).filter((k) => k !== "version"));
|
||||||
let serviceName;
|
let serviceName: JSX.Element | undefined;
|
||||||
let summary;
|
let summary: JSX.Element | undefined;
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
serviceName = this.nameForServiceType(policiesAndService.service.serviceType, parsedBaseUrl.host);
|
serviceName = this.nameForServiceType(policiesAndService.service.serviceType, parsedBaseUrl.host);
|
||||||
summary = this.summaryForServiceType(policiesAndService.service.serviceType);
|
summary = this.summaryForServiceType(policiesAndService.service.serviceType);
|
||||||
|
|
|
@ -100,7 +100,7 @@ export default class TextInputDialog extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
private onValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
||||||
const result = await this.props.validator(fieldState);
|
const result = await this.props.validator!(fieldState);
|
||||||
this.setState({
|
this.setState({
|
||||||
valid: !!result.valid,
|
valid: !!result.valid,
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,7 +38,7 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
|
||||||
const [generalMenuPosition, setGeneralMenuPosition] = useState<DOMRect | null>(null);
|
const [generalMenuPosition, setGeneralMenuPosition] = useState<DOMRect | null>(null);
|
||||||
const [notificationMenuPosition, setNotificationMenuPosition] = useState<DOMRect | null>(null);
|
const [notificationMenuPosition, setNotificationMenuPosition] = useState<DOMRect | null>(null);
|
||||||
|
|
||||||
let generalMenu: JSX.Element;
|
let generalMenu: JSX.Element | undefined;
|
||||||
if (generalMenuPosition !== null) {
|
if (generalMenuPosition !== null) {
|
||||||
if (room.isSpaceRoom()) {
|
if (room.isSpaceRoom()) {
|
||||||
generalMenu = (
|
generalMenu = (
|
||||||
|
@ -59,7 +59,7 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let notificationMenu: JSX.Element;
|
let notificationMenu: JSX.Element | undefined;
|
||||||
if (notificationMenuPosition !== null) {
|
if (notificationMenuPosition !== null) {
|
||||||
notificationMenu = (
|
notificationMenu = (
|
||||||
<RoomNotificationContextMenu
|
<RoomNotificationContextMenu
|
||||||
|
|
|
@ -440,7 +440,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
|
|
||||||
// Sort results by most recent activity
|
// Sort results by most recent activity
|
||||||
|
|
||||||
const myUserId = cli.getUserId();
|
const myUserId = cli.getSafeUserId();
|
||||||
for (const resultArray of Object.values(results)) {
|
for (const resultArray of Object.values(results)) {
|
||||||
resultArray.sort((a: Result, b: Result) => {
|
resultArray.sort((a: Result, b: Result) => {
|
||||||
if (isRoomResult(a) || isRoomResult(b)) {
|
if (isRoomResult(a) || isRoomResult(b)) {
|
||||||
|
|
|
@ -27,17 +27,17 @@ enum Phases {
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onValueChanged?: (value: string, shouldSubmit: boolean) => void;
|
onValueChanged?: (value: string, shouldSubmit: boolean) => void;
|
||||||
initialValue?: string;
|
initialValue: string;
|
||||||
label?: string;
|
label: string;
|
||||||
placeholder?: string;
|
placeholder: string;
|
||||||
className?: string;
|
className: string;
|
||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
placeholderClassName?: string;
|
placeholderClassName: string;
|
||||||
// Overrides blurToSubmit if true
|
// Overrides blurToSubmit if true
|
||||||
blurToCancel?: boolean;
|
blurToCancel?: boolean;
|
||||||
// Will cause onValueChanged(value, true) to fire on blur
|
// Will cause onValueChanged(value, true) to fire on blur
|
||||||
blurToSubmit?: boolean;
|
blurToSubmit: boolean;
|
||||||
editable?: boolean;
|
editable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -109,11 +109,11 @@ export default class EditableText extends React.Component<IProps, IState> {
|
||||||
this.value = this.props.initialValue;
|
this.value = this.props.initialValue;
|
||||||
this.showPlaceholder(!this.value);
|
this.showPlaceholder(!this.value);
|
||||||
this.onValueChanged(false);
|
this.onValueChanged(false);
|
||||||
this.editableDiv.current.blur();
|
this.editableDiv.current?.blur();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onValueChanged = (shouldSubmit: boolean): void => {
|
private onValueChanged = (shouldSubmit: boolean): void => {
|
||||||
this.props.onValueChanged(this.value, shouldSubmit);
|
this.props.onValueChanged?.(this.value, shouldSubmit);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onKeyDown = (ev: React.KeyboardEvent<HTMLDivElement>): void => {
|
private onKeyDown = (ev: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||||
|
@ -171,7 +171,7 @@ export default class EditableText extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onFinish = (
|
private onFinish = (
|
||||||
ev: React.KeyboardEvent<HTMLDivElement> | React.FocusEvent<HTMLDivElement>,
|
ev: React.KeyboardEvent<HTMLDivElement> | React.FocusEvent<HTMLDivElement>,
|
||||||
shouldSubmit?: boolean,
|
shouldSubmit = false,
|
||||||
): void => {
|
): void => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
|
@ -187,8 +187,8 @@ export default class EventListSummary extends React.Component<IProps> {
|
||||||
|
|
||||||
let transition = t;
|
let transition = t;
|
||||||
|
|
||||||
if (i < transitions.length - 1 && modMap[t] && modMap[t].after === t2) {
|
if (i < transitions.length - 1 && modMap[t] && modMap[t]!.after === t2) {
|
||||||
transition = modMap[t].newTransition;
|
transition = modMap[t]!.newTransition;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ export default class EventListSummary extends React.Component<IProps> {
|
||||||
return res ?? null;
|
return res ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getTransitionSequence(events: IUserEvents[]): TransitionType[] {
|
private static getTransitionSequence(events: IUserEvents[]): Array<TransitionType | null> {
|
||||||
return events.map(EventListSummary.getTransition);
|
return events.map(EventListSummary.getTransition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
||||||
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent | null> {
|
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent | null> {
|
||||||
try {
|
try {
|
||||||
const inReplyToEventId = getParentEventId(ev);
|
const inReplyToEventId = getParentEventId(ev);
|
||||||
|
if (!inReplyToEventId) return null;
|
||||||
return await this.getEvent(inReplyToEventId);
|
return await this.getEvent(inReplyToEventId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -196,7 +197,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private getReplyChainColorClass(ev: MatrixEvent): string {
|
private getReplyChainColorClass(ev: MatrixEvent): string {
|
||||||
return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyChain");
|
return getUserNameColorClass(ev.getSender()!).replace("Username", "ReplyChain");
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
@ -231,8 +232,8 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
||||||
pill: (
|
pill: (
|
||||||
<Pill
|
<Pill
|
||||||
type={PillType.UserMention}
|
type={PillType.UserMention}
|
||||||
room={room}
|
room={room ?? undefined}
|
||||||
url={makeUserPermalink(ev.getSender())}
|
url={makeUserPermalink(ev.getSender()!)}
|
||||||
shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")}
|
shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|
|
@ -63,7 +63,7 @@ export default class EditHistoryMessage extends React.PureComponent<IProps, ISta
|
||||||
const event = this.props.mxEvent;
|
const event = this.props.mxEvent;
|
||||||
const room = cli.getRoom(event.getRoomId());
|
const room = cli.getRoom(event.getRoomId());
|
||||||
event.localRedactionEvent()?.on(MatrixEventEvent.Status, this.onAssociatedStatusChanged);
|
event.localRedactionEvent()?.on(MatrixEventEvent.Status, this.onAssociatedStatusChanged);
|
||||||
const canRedact = room.currentState.maySendRedactionForEvent(event, userId);
|
const canRedact = room?.currentState.maySendRedactionForEvent(event, userId) ?? false;
|
||||||
this.state = { canRedact, sendStatus: event.getAssociatedStatus() };
|
this.state = { canRedact, sendStatus: event.getAssociatedStatus() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const usePinnedEvents = (room?: Room): string[] => {
|
||||||
};
|
};
|
||||||
|
|
||||||
function getReadPinnedEventIds(room?: Room): Set<string> {
|
function getReadPinnedEventIds(room?: Room): Set<string> {
|
||||||
return new Set(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids ?? []);
|
return new Set(room?.getAccountData(ReadPinsEventId)?.getContent()?.event_ids ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useReadPinnedEvents = (room?: Room): Set<string> => {
|
export const useReadPinnedEvents = (room?: Room): Set<string> => {
|
||||||
|
|
|
@ -448,7 +448,7 @@ export const UserOptionsSection: React.FC<{
|
||||||
const inviter = new MultiInviter(roomId || "");
|
const inviter = new MultiInviter(roomId || "");
|
||||||
await inviter.invite([member.userId]).then(() => {
|
await inviter.invite([member.userId]).then(() => {
|
||||||
if (inviter.getCompletionState(member.userId) !== "invited") {
|
if (inviter.getCompletionState(member.userId) !== "invited") {
|
||||||
throw new Error(inviter.getErrorText(member.userId));
|
throw new Error(inviter.getErrorText(member.userId) ?? undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -766,8 +766,8 @@ export const BanToggleButton = ({
|
||||||
const myMember = child.getMember(cli.credentials.userId || "");
|
const myMember = child.getMember(cli.credentials.userId || "");
|
||||||
const theirMember = child.getMember(member.userId);
|
const theirMember = child.getMember(member.userId);
|
||||||
return (
|
return (
|
||||||
myMember &&
|
!!myMember &&
|
||||||
theirMember &&
|
!!theirMember &&
|
||||||
theirMember.membership !== "ban" &&
|
theirMember.membership !== "ban" &&
|
||||||
myMember.powerLevel > theirMember.powerLevel &&
|
myMember.powerLevel > theirMember.powerLevel &&
|
||||||
child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel)
|
child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel)
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { OverflowMenuContext } from "./MessageComposerButtons";
|
||||||
|
|
||||||
interface IEmojiButtonProps {
|
interface IEmojiButtonProps {
|
||||||
addEmoji: (unicode: string) => boolean;
|
addEmoji: (unicode: string) => boolean;
|
||||||
menuPosition: MenuProps;
|
menuPosition?: MenuProps;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,8 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mainButtons: ReactElement[];
|
let mainButtons: ReactNode[];
|
||||||
let moreButtons: ReactElement[];
|
let moreButtons: ReactNode[];
|
||||||
if (narrow) {
|
if (narrow) {
|
||||||
mainButtons = [
|
mainButtons = [
|
||||||
isWysiwygLabEnabled ? (
|
isWysiwygLabEnabled ? (
|
||||||
|
|
|
@ -75,8 +75,8 @@ export const usePermalinkEvent = (
|
||||||
const fetchRoomEvent = async (): Promise<void> => {
|
const fetchRoomEvent = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const eventData = await MatrixClientPeg.get().fetchRoomEvent(
|
const eventData = await MatrixClientPeg.get().fetchRoomEvent(
|
||||||
parseResult.roomIdOrAlias,
|
parseResult.roomIdOrAlias!,
|
||||||
parseResult.eventId,
|
parseResult.eventId!,
|
||||||
);
|
);
|
||||||
setEvent(new MatrixEvent(eventData));
|
setEvent(new MatrixEvent(eventData));
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
|
@ -83,7 +83,7 @@ const findRoom = (roomIdOrAlias: string): Room | null => {
|
||||||
export const usePermalinkTargetRoom = (
|
export const usePermalinkTargetRoom = (
|
||||||
type: PillType | null,
|
type: PillType | null,
|
||||||
parseResult: PermalinkParts | null,
|
parseResult: PermalinkParts | null,
|
||||||
permalinkRoom: Room | null,
|
permalinkRoom: Room | undefined,
|
||||||
): Room | null => {
|
): Room | null => {
|
||||||
// The listed permalink types require a room.
|
// The listed permalink types require a room.
|
||||||
// If it cannot be initially determined, it will be looked up later by a memo hook.
|
// If it cannot be initially determined, it will be looked up later by a memo hook.
|
||||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Optional } from "matrix-events-sdk";
|
|
||||||
|
|
||||||
import { _t } from "../languageHandler";
|
import { _t } from "../languageHandler";
|
||||||
import SdkConfig from "../SdkConfig";
|
import SdkConfig from "../SdkConfig";
|
||||||
|
@ -75,7 +74,7 @@ const onLearnMorePreviouslyOptedIn = (): void => {
|
||||||
|
|
||||||
const TOAST_KEY = "analytics";
|
const TOAST_KEY = "analytics";
|
||||||
|
|
||||||
export function getPolicyUrl(): Optional<string> {
|
export function getPolicyUrl(): string | undefined {
|
||||||
return SdkConfig.get("privacy_policy_url");
|
return SdkConfig.get("privacy_policy_url");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,10 @@ import { Member } from "./direct-messages";
|
||||||
import DMRoomMap from "./DMRoomMap";
|
import DMRoomMap from "./DMRoomMap";
|
||||||
|
|
||||||
export const compareMembers =
|
export const compareMembers =
|
||||||
(activityScores: Record<string, IActivityScore>, memberScores: Record<string, IMemberScore>) =>
|
(
|
||||||
|
activityScores: Record<string, IActivityScore | undefined>,
|
||||||
|
memberScores: Record<string, IMemberScore | undefined>,
|
||||||
|
) =>
|
||||||
(a: Member | RoomMember, b: Member | RoomMember): number => {
|
(a: Member | RoomMember, b: Member | RoomMember): number => {
|
||||||
const aActivityScore = activityScores[a.userId]?.score ?? 0;
|
const aActivityScore = activityScores[a.userId]?.score ?? 0;
|
||||||
const aMemberScore = memberScores[a.userId]?.score ?? 0;
|
const aMemberScore = memberScores[a.userId]?.score ?? 0;
|
||||||
|
|
|
@ -716,7 +716,7 @@ describe("MessagePanel", function () {
|
||||||
// Increase the length of the loop here to test performance issues with
|
// Increase the length of the loop here to test performance issues with
|
||||||
// rendering
|
// rendering
|
||||||
|
|
||||||
const events = [];
|
const events: MatrixEvent[] = [];
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
events.push(
|
events.push(
|
||||||
TestUtilsMatrix.mkMembership({
|
TestUtilsMatrix.mkMembership({
|
||||||
|
|
|
@ -54,6 +54,7 @@ describe("ForwardDialog", () => {
|
||||||
});
|
});
|
||||||
const mockClient = getMockClientWithEventEmitter({
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||||
|
getSafeUserId: jest.fn().mockReturnValue(aliceId),
|
||||||
isGuest: jest.fn().mockReturnValue(false),
|
isGuest: jest.fn().mockReturnValue(false),
|
||||||
getVisibleRooms: jest.fn().mockReturnValue([]),
|
getVisibleRooms: jest.fn().mockReturnValue([]),
|
||||||
getRoom: jest.fn(),
|
getRoom: jest.fn(),
|
||||||
|
@ -92,6 +93,7 @@ describe("ForwardDialog", () => {
|
||||||
DMRoomMap.makeShared();
|
DMRoomMap.makeShared();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
mockClient.getUserId.mockReturnValue("@bob:example.org");
|
mockClient.getUserId.mockReturnValue("@bob:example.org");
|
||||||
|
mockClient.getSafeUserId.mockReturnValue("@bob:example.org");
|
||||||
mockClient.sendEvent.mockReset();
|
mockClient.sendEvent.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ const mockClient = mocked({
|
||||||
setIgnoredUsers: jest.fn(),
|
setIgnoredUsers: jest.fn(),
|
||||||
isCryptoEnabled: jest.fn(),
|
isCryptoEnabled: jest.fn(),
|
||||||
getUserId: jest.fn(),
|
getUserId: jest.fn(),
|
||||||
|
getSafeUserId: jest.fn(),
|
||||||
on: jest.fn(),
|
on: jest.fn(),
|
||||||
off: jest.fn(),
|
off: jest.fn(),
|
||||||
isSynapseAdministrator: jest.fn().mockResolvedValue(false),
|
isSynapseAdministrator: jest.fn().mockResolvedValue(false),
|
||||||
|
@ -348,6 +349,7 @@ describe("<DeviceItem />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", () => {
|
it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", () => {
|
||||||
|
mockClient.getSafeUserId.mockReturnValueOnce(defaultUserId);
|
||||||
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
|
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
|
||||||
renderComponent();
|
renderComponent();
|
||||||
|
|
||||||
|
@ -431,6 +433,7 @@ describe("<UserOptionsSection />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not show ignore or direct message buttons when member userId matches client userId", () => {
|
it("does not show ignore or direct message buttons when member userId matches client userId", () => {
|
||||||
|
mockClient.getSafeUserId.mockReturnValueOnce(member.userId);
|
||||||
mockClient.getUserId.mockReturnValueOnce(member.userId);
|
mockClient.getUserId.mockReturnValueOnce(member.userId);
|
||||||
renderComponent();
|
renderComponent();
|
||||||
|
|
||||||
|
@ -676,6 +679,7 @@ describe("<PowerLevelEditor />", () => {
|
||||||
content: { users: { [defaultUserId]: startPowerLevel }, users_default: 1 },
|
content: { users: { [defaultUserId]: startPowerLevel }, users_default: 1 },
|
||||||
});
|
});
|
||||||
mockRoom.currentState.getStateEvents.mockReturnValue(powerLevelEvent);
|
mockRoom.currentState.getStateEvents.mockReturnValue(powerLevelEvent);
|
||||||
|
mockClient.getSafeUserId.mockReturnValueOnce(defaultUserId);
|
||||||
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
|
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
|
||||||
mockClient.setPowerLevel.mockResolvedValueOnce({ event_id: "123" });
|
mockClient.setPowerLevel.mockResolvedValueOnce({ event_id: "123" });
|
||||||
renderComponent();
|
renderComponent();
|
||||||
|
@ -879,7 +883,7 @@ describe("<BanToggleButton />", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(callback(mockRoom)).toBe(null);
|
expect(callback(mockRoom)).toBe(false);
|
||||||
expect(callback(mockRoom)).toBe(true);
|
expect(callback(mockRoom)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue