Enable @typescript-eslint/explicit-function-return-type in /src (#9788)

* Enable `@typescript-eslint/explicit-member-accessibility` on /src

* Prettier

* Enable `@typescript-eslint/explicit-function-return-type` in /src

* Fix types

* tsc strict fixes

* Delint

* Fix test

* Fix bad merge
This commit is contained in:
Michael Telatynski 2023-01-12 13:25:14 +00:00 committed by GitHub
parent 7a36ba0fde
commit 030b7e90bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
683 changed files with 3459 additions and 3013 deletions

View file

@ -26,7 +26,7 @@ import RoomContext from "../../../contexts/RoomContext";
const MAX_PROVIDER_MATCHES = 20;
export const generateCompletionDomId = (number) => `mx_Autocomplete_Completion_${number}`;
export const generateCompletionDomId = (n: number): string => `mx_Autocomplete_Completion_${n}`;
interface IProps {
// the query string for which to show autocomplete suggestions
@ -79,7 +79,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
};
}
public componentDidMount() {
public componentDidMount(): void {
this.autocompleter = new Autocompleter(this.props.room, this.context.timelineRenderingType);
this.applyNewProps();
}
@ -98,7 +98,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
this.complete(this.props.query, this.props.selection);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
this.autocompleter.destroy();
}
@ -265,7 +265,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
}
}
public componentDidUpdate(prevProps: IProps) {
public componentDidUpdate(prevProps: IProps): void {
this.applyNewProps(prevProps.query, prevProps.room);
// this is the selected completion, so scroll it into view if needed
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`] as HTMLElement;
@ -280,7 +280,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
}
}
public render() {
public render(): JSX.Element {
let position = 1;
const renderedCompletions = this.state.completions
.map((completionResult, i) => {
@ -290,7 +290,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
const componentPosition = position;
position++;
const onClick = () => {
const onClick = (): void => {
this.onCompletionClicked(componentPosition);
};

View file

@ -63,24 +63,24 @@ export default class AuxPanel extends React.Component<IProps, IState> {
};
}
public componentDidMount() {
public componentDidMount(): void {
const cli = MatrixClientPeg.get();
if (SettingsStore.getValue("feature_state_counters")) {
cli.on(RoomStateEvent.Events, this.onRoomStateEvents);
}
}
public componentWillUnmount() {
public componentWillUnmount(): void {
if (SettingsStore.getValue("feature_state_counters")) {
MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
}
}
public shouldComponentUpdate(nextProps, nextState) {
public shouldComponentUpdate(nextProps, nextState): boolean {
return objectHasDiff(this.props, nextProps) || objectHasDiff(this.state, nextState);
}
private onRoomStateEvents = (ev: MatrixEvent) => {
private onRoomStateEvents = (ev: MatrixEvent): void => {
if (ev.getType() === "re.jki.counter") {
this.updateCounters();
}
@ -94,8 +94,8 @@ export default class AuxPanel extends React.Component<IProps, IState> {
{ leading: true, trailing: true },
);
private computeCounters() {
const counters = [];
private computeCounters(): Counter[] {
const counters: Counter[] = [];
if (this.props.room && SettingsStore.getValue("feature_state_counters")) {
const stateEvs = this.props.room.currentState.getStateEvents("re.jki.counter");
@ -125,7 +125,7 @@ export default class AuxPanel extends React.Component<IProps, IState> {
return counters;
}
public render() {
public render(): JSX.Element {
const callView = (
<LegacyCallViewForRoom
roomId={this.props.room.roomId}

View file

@ -172,7 +172,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
);
}
public componentDidUpdate(prevProps: IProps) {
public componentDidUpdate(prevProps: IProps): void {
// We need to re-check the placeholder when the enabled state changes because it causes the
// placeholder element to remount, which gets rid of the `::before` class. Re-evaluating the
// placeholder means we get a proper `::before` with the placeholder.
@ -676,7 +676,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
this.setState({ showPillAvatar });
};
private surroundWithSettingChanged = () => {
private surroundWithSettingChanged = (): void => {
const surroundWith = SettingsStore.getValue("MessageComposerInput.surroundWith");
this.setState({ surroundWith });
};
@ -686,7 +686,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
if (shouldReplace) this.replaceEmoticon(documentPosition, REGEX_EMOTICON_WHITESPACE);
};
public componentWillUnmount() {
public componentWillUnmount(): void {
document.removeEventListener("selectionchange", this.onSelectionChange);
this.editorRef.current.removeEventListener("input", this.onInput, true);
this.editorRef.current.removeEventListener("compositionstart", this.onCompositionStart, true);
@ -697,7 +697,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
SettingsStore.unwatchSetting(this.surroundWithHandle);
}
public componentDidMount() {
public componentDidMount(): void {
const model = this.props.model;
model.setUpdateCallback(this.updateEditorState);
const partCreator = model.partCreator;
@ -746,7 +746,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
formatRange(range, action);
};
public render() {
public render(): JSX.Element {
let autoComplete;
if (this.state.autoComplete) {
const query = this.state.query;

View file

@ -27,7 +27,13 @@ interface ICollapsibleButtonProps extends ComponentProps<typeof MenuItem> {
iconClassName: string;
}
export const CollapsibleButton = ({ title, children, className, iconClassName, ...props }: ICollapsibleButtonProps) => {
export const CollapsibleButton: React.FC<ICollapsibleButtonProps> = ({
title,
children,
className,
iconClassName,
...props
}) => {
const inOverflowMenu = !!useContext(OverflowMenuContext);
if (inOverflowMenu) {
return <IconizedContextMenuOption {...props} iconClassName={iconClassName} label={title} />;

View file

@ -88,8 +88,8 @@ const E2EIcon: React.FC<IProps> = ({
style = { width: `${size}px`, height: `${size}px` };
}
const onMouseOver = () => setHover(true);
const onMouseLeave = () => setHover(false);
const onMouseOver = (): void => setHover(true);
const onMouseLeave = (): void => setHover(false);
let tip;
if (hover && !hideTooltip) {

View file

@ -369,7 +369,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
}
}
public componentWillUnmount() {
public componentWillUnmount(): void {
// store caret and serialized parts in the
// editorstate so it can be restored when the remote echo event tile gets rendered
// in case we're currently editing a pending event
@ -427,7 +427,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
});
};
private onAction = (payload: ActionPayload) => {
private onAction = (payload: ActionPayload): void => {
if (!this.editorRef.current) return;
if (payload.action === Action.ComposerInsert) {
@ -446,7 +446,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
}
};
public render() {
public render(): JSX.Element {
return (
<div className={classNames("mx_EditMessageComposer", this.props.className)} onKeyDown={this.onKeyDown}>
<BasicMessageComposer

View file

@ -18,18 +18,18 @@ import classNames from "classnames";
import React, { useContext } from "react";
import { _t } from "../../../languageHandler";
import ContextMenu, { aboveLeftOf, AboveLeftOf, useContextMenu } from "../../structures/ContextMenu";
import ContextMenu, { aboveLeftOf, MenuProps, useContextMenu } from "../../structures/ContextMenu";
import EmojiPicker from "../emojipicker/EmojiPicker";
import { CollapsibleButton } from "./CollapsibleButton";
import { OverflowMenuContext } from "./MessageComposerButtons";
interface IEmojiButtonProps {
addEmoji: (unicode: string) => boolean;
menuPosition: AboveLeftOf;
menuPosition: MenuProps;
className?: string;
}
export function EmojiButton({ addEmoji, menuPosition, className }: IEmojiButtonProps) {
export function EmojiButton({ addEmoji, menuPosition, className }: IEmojiButtonProps): JSX.Element {
const overflowMenuCloser = useContext(OverflowMenuContext);
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();

View file

@ -103,7 +103,7 @@ export default class EntityTile extends React.PureComponent<IProps, IState> {
};
}
public render() {
public render(): JSX.Element {
const mainClassNames = {
mx_EntityTile: true,
mx_EntityTile_noHover: this.props.suppressOnHover,

View file

@ -335,7 +335,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return true;
}
private get shouldShowSentReceipt() {
private get shouldShowSentReceipt(): boolean {
// If we're not even eligible, don't show the receipt.
if (!this.isEligibleForSpecialReceipt) return false;
@ -355,7 +355,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return true;
}
private get shouldShowSendingReceipt() {
private get shouldShowSendingReceipt(): boolean {
// If we're not even eligible, don't show the receipt.
if (!this.isEligibleForSpecialReceipt) return false;
@ -368,7 +368,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return true;
}
public componentDidMount() {
public componentDidMount(): void {
this.suppressReadReceiptAnimation = false;
const client = MatrixClientPeg.get();
if (!this.props.forExport) {
@ -435,7 +435,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
}
};
private updateThread = (thread: Thread) => {
private updateThread = (thread: Thread): void => {
if (thread !== this.state.thread && !this.supportsThreadNotifications) {
if (this.threadState) {
this.threadState.off(NotificationStateEvents.Update, this.onThreadStateUpdate);
@ -455,7 +455,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return !this.propsEqual(this.props, nextProps);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
const client = MatrixClientPeg.get();
if (client) {
client.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
@ -476,7 +476,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
this.threadState?.off(NotificationStateEvents.Update, this.onThreadStateUpdate);
}
public componentDidUpdate(prevProps: Readonly<EventTileProps>, prevState: Readonly<IState>) {
public componentDidUpdate(prevProps: Readonly<EventTileProps>, prevState: Readonly<IState>): void {
// If the verification state changed, the height might have changed
if (prevState.verified !== this.state.verified && this.props.onHeightChanged) {
this.props.onHeightChanged();
@ -492,7 +492,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
}
}
private onNewThread = (thread: Thread) => {
private onNewThread = (thread: Thread): void => {
if (thread.id === this.props.mxEvent.getId()) {
this.updateThread(thread);
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
@ -594,7 +594,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
/** called when the event is decrypted after we show it.
*/
private onDecrypted = () => {
private onDecrypted = (): void => {
// we need to re-verify the sending device.
this.verifyEvent();
// decryption might, of course, trigger a height change, so call onHeightChanged after the re-render
@ -614,7 +614,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
};
/** called when the event is edited after we show it. */
private onReplaced = () => {
private onReplaced = (): void => {
// re-verify the event if it is replaced (the edit may not be verified)
this.verifyEvent();
};
@ -735,7 +735,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return actions.tweaks.highlight;
}
private onSenderProfileClick = () => {
private onSenderProfileClick = (): void => {
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
userId: this.props.mxEvent.getSender(),
@ -743,7 +743,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
});
};
private onPermalinkClicked = (e: MouseEvent) => {
private onPermalinkClicked = (e: MouseEvent): void => {
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Element when clicked.
e.preventDefault();
@ -757,7 +757,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
});
};
private renderE2EPadlock() {
private renderE2EPadlock(): JSX.Element {
// if the event was edited, show the verification info for the edit, not
// the original
const ev = this.props.mxEvent.replacingEvent() ?? this.props.mxEvent;
@ -808,7 +808,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return null;
}
private onActionBarFocusChange = (actionBarFocused: boolean) => {
private onActionBarFocusChange = (actionBarFocused: boolean): void => {
this.setState({ actionBarFocused });
};
@ -816,7 +816,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
private getReplyChain = (): ReplyChain => this.replyChain.current;
private getReactions = () => {
private getReactions = (): Relations => {
if (!this.props.showReactions || !this.props.getRelationsForEvent) {
return null;
}
@ -882,7 +882,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
});
};
private setQuoteExpanded = (expanded: boolean) => {
private setQuoteExpanded = (expanded: boolean): void => {
this.setState({
isQuoteExpanded: expanded,
});
@ -924,7 +924,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
);
}
public render() {
public render(): JSX.Element {
const msgtype = this.props.mxEvent.getContent().msgtype;
const eventType = this.props.mxEvent.getType();
const {
@ -1476,19 +1476,19 @@ const SafeEventTile = forwardRef((props: EventTileProps, ref: RefObject<Unwrappe
});
export default SafeEventTile;
function E2ePadlockUnverified(props: Omit<IE2ePadlockProps, "title" | "icon">) {
function E2ePadlockUnverified(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
return <E2ePadlock title={_t("Encrypted by an unverified session")} icon={E2ePadlockIcon.Warning} {...props} />;
}
function E2ePadlockUnencrypted(props: Omit<IE2ePadlockProps, "title" | "icon">) {
function E2ePadlockUnencrypted(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
return <E2ePadlock title={_t("Unencrypted")} icon={E2ePadlockIcon.Warning} {...props} />;
}
function E2ePadlockUnknown(props: Omit<IE2ePadlockProps, "title" | "icon">) {
function E2ePadlockUnknown(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
return <E2ePadlock title={_t("Encrypted by a deleted session")} icon={E2ePadlockIcon.Normal} {...props} />;
}
function E2ePadlockUnauthenticated(props: Omit<IE2ePadlockProps, "title" | "icon">) {
function E2ePadlockUnauthenticated(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
return (
<E2ePadlock
title={_t("The authenticity of this encrypted message can't be guaranteed on this device.")}
@ -1498,7 +1498,7 @@ function E2ePadlockUnauthenticated(props: Omit<IE2ePadlockProps, "title" | "icon
);
}
function E2ePadlockDecryptionFailure(props: Omit<IE2ePadlockProps, "title" | "icon">) {
function E2ePadlockDecryptionFailure(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
return (
<E2ePadlock
title={_t("This message could not be decrypted")}
@ -1559,7 +1559,7 @@ interface ISentReceiptProps {
messageState: string; // TODO: Types for message sending state
}
function SentReceipt({ messageState }: ISentReceiptProps) {
function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element {
const isSent = !messageState || messageState === "sent";
const isFailed = messageState === "not_sent";
const receiptClasses = classNames({

View file

@ -29,7 +29,7 @@ export function EventTileThreadToolbar({
}: {
viewInRoom: (evt: ButtonEvent) => void;
copyLinkToThread: (evt: ButtonEvent) => void;
}) {
}): JSX.Element {
return (
<Toolbar className="mx_MessageActionBar" aria-label={_t("Message Actions")} aria-live="off">
<RovingAccessibleTooltipButton

View file

@ -44,11 +44,11 @@ export default class ExtraTile extends React.Component<IProps, IState> {
};
}
private onTileMouseEnter = () => {
private onTileMouseEnter = (): void => {
this.setState({ hover: true });
};
private onTileMouseLeave = () => {
private onTileMouseLeave = (): void => {
this.setState({ hover: false });
};

View file

@ -21,7 +21,7 @@ import EventTileBubble from "../messages/EventTileBubble";
import RoomContext from "../../../contexts/RoomContext";
import { _t } from "../../../languageHandler";
const HistoryTile = () => {
const HistoryTile: React.FC = () => {
const { room } = useContext(RoomContext);
const oldState = room.getLiveTimeline().getState(EventTimeline.BACKWARDS);

View file

@ -94,7 +94,7 @@ const LinkPreviewGroup: React.FC<IProps> = ({ links, mxEvent, onCancelClick, onH
const fetchPreviews = (cli: MatrixClient, links: string[], ts: number): Promise<[string, IPreviewUrlResponse][]> => {
return Promise.all<[string, IPreviewUrlResponse] | void>(
links.map(async (link) => {
links.map(async (link): Promise<[string, IPreviewUrlResponse]> => {
try {
const preview = await cli.getUrlPreview(link, ts);
if (preview && Object.keys(preview).length > 0) {

View file

@ -38,19 +38,19 @@ export default class LinkPreviewWidget extends React.Component<IProps> {
private readonly description = createRef<HTMLDivElement>();
private image = createRef<HTMLImageElement>();
public componentDidMount() {
public componentDidMount(): void {
if (this.description.current) {
linkifyElement(this.description.current);
}
}
public componentDidUpdate() {
public componentDidUpdate(): void {
if (this.description.current) {
linkifyElement(this.description.current);
}
}
private onImageClick = (ev) => {
private onImageClick = (ev): void => {
const p = this.props.preview;
if (ev.button != 0 || ev.metaKey) return;
ev.preventDefault();
@ -83,7 +83,7 @@ export default class LinkPreviewWidget extends React.Component<IProps> {
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
};
public render() {
public render(): JSX.Element {
const p = this.props.preview;
if (!p || Object.keys(p).length === 0) {
return <div />;

View file

@ -101,7 +101,7 @@ export default class MemberList extends React.Component<IProps, IState> {
this.updateListNow(true);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
this.mounted = false;
const cli = MatrixClientPeg.get();
if (cli) {
@ -321,7 +321,7 @@ export default class MemberList extends React.Component<IProps, IState> {
}
}
private makeMemberTiles(members: Array<RoomMember | MatrixEvent>) {
private makeMemberTiles(members: Array<RoomMember | MatrixEvent>): JSX.Element[] {
return members.map((m) => {
if (m instanceof RoomMember) {
// Is a Matrix invite
@ -359,7 +359,7 @@ export default class MemberList extends React.Component<IProps, IState> {
return this.state.filteredInvitedMembers.length + (this.getPending3PidInvites() || []).length;
};
public render() {
public render(): JSX.Element {
if (this.state.loading) {
return (
<BaseCard className="mx_MemberList" onClose={this.props.onClose}>

View file

@ -60,7 +60,7 @@ export default class MemberTile extends React.Component<IProps, IState> {
};
}
public componentDidMount() {
public componentDidMount(): void {
const cli = MatrixClientPeg.get();
const { roomId } = this.props.member;
@ -80,7 +80,7 @@ export default class MemberTile extends React.Component<IProps, IState> {
}
}
public componentWillUnmount() {
public componentWillUnmount(): void {
const cli = MatrixClientPeg.get();
if (cli) {
@ -183,7 +183,7 @@ export default class MemberTile extends React.Component<IProps, IState> {
}).trim();
}
public render() {
public render(): JSX.Element {
const member = this.props.member;
const name = this.getDisplayName();
const presenceState = member.user ? member.user.presence : null;

View file

@ -31,7 +31,7 @@ import Stickerpicker from "./Stickerpicker";
import { makeRoomPermalink, RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
import E2EIcon from "./E2EIcon";
import SettingsStore from "../../../settings/SettingsStore";
import { aboveLeftOf } from "../../structures/ContextMenu";
import { aboveLeftOf, MenuProps } from "../../structures/ContextMenu";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import ReplyPreview from "./ReplyPreview";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
@ -68,7 +68,7 @@ interface ISendButtonProps {
title?: string; // defaults to something generic
}
function SendButton(props: ISendButtonProps) {
function SendButton(props: ISendButtonProps): JSX.Element {
return (
<AccessibleTooltipButton
className="mx_MessageComposer_sendMessage"
@ -173,7 +173,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
}
}
public componentDidMount() {
public componentDidMount(): void {
this.dispatcherRef = dis.register(this.onAction);
this.waitForOwnMember();
UIStore.instance.trackElementDimensions(`MessageComposer${this.instanceId}`, this.ref.current!);
@ -181,7 +181,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
this.updateRecordingState(); // grab any cached recordings
}
private onResize = (type: UI_EVENTS, entry: ResizeObserverEntry) => {
private onResize = (type: UI_EVENTS, entry: ResizeObserverEntry): void => {
if (type === UI_EVENTS.Resize) {
const { narrow } = this.context;
this.setState({
@ -191,7 +191,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
}
};
private onAction = (payload: ActionPayload) => {
private onAction = (payload: ActionPayload): void => {
switch (payload.action) {
case "reply_to_event":
if (payload.context === this.context.timelineRenderingType) {
@ -239,7 +239,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
}
};
private waitForOwnMember() {
private waitForOwnMember(): void {
// If we have the member already, do that
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
if (me) {
@ -255,7 +255,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
});
}
public componentWillUnmount() {
public componentWillUnmount(): void {
VoiceRecordingStore.instance.off(UPDATE_EVENT, this.onVoiceStoreUpdate);
dis.unregister(this.dispatcherRef);
UIStore.instance.stopTrackingElementDimensions(`MessageComposer${this.instanceId}`);
@ -265,7 +265,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
this.voiceRecording = null;
}
private onTombstoneClick = (ev) => {
private onTombstoneClick = (ev): void => {
ev.preventDefault();
const replacementRoomId = this.context.tombstone.getContent()["replacement_room"];
@ -292,7 +292,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
});
};
private renderPlaceholderText = () => {
private renderPlaceholderText = (): string => {
if (this.props.replyToEvent) {
const replyingToThread = this.props.relation?.rel_type === THREAD_RELATION_TYPE.name;
if (replyingToThread && this.props.e2eStatus) {
@ -322,7 +322,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
return true;
};
private sendMessage = async () => {
private sendMessage = async (): Promise<void> => {
if (this.state.haveRecording && this.voiceRecordingButton.current) {
// There shouldn't be any text message to send when a voice recording is active, so
// just send out the voice recording.
@ -346,20 +346,20 @@ export class MessageComposer extends React.Component<IProps, IState> {
}
};
private onChange = (model: EditorModel) => {
private onChange = (model: EditorModel): void => {
this.setState({
isComposerEmpty: model.isEmpty,
});
};
private onWysiwygChange = (content: string) => {
private onWysiwygChange = (content: string): void => {
this.setState({
composerContent: content,
isComposerEmpty: content?.length === 0,
});
};
private onRichTextToggle = async () => {
private onRichTextToggle = async (): Promise<void> => {
const { richToPlain, plainToRich } = await getConversionFunctions();
const { isRichTextEnabled, composerContent } = this.state;
@ -374,11 +374,11 @@ export class MessageComposer extends React.Component<IProps, IState> {
});
};
private onVoiceStoreUpdate = () => {
private onVoiceStoreUpdate = (): void => {
this.updateRecordingState();
};
private updateRecordingState() {
private updateRecordingState(): void {
const voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation);
this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(voiceRecordingId);
if (this.voiceRecording) {
@ -393,7 +393,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
}
}
private onRecordingStarted = () => {
private onRecordingStarted = (): void => {
// update the recording instance, just in case
const voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation);
this.voiceRecording = VoiceRecordingStore.instance.getActiveRecording(voiceRecordingId);
@ -402,19 +402,19 @@ export class MessageComposer extends React.Component<IProps, IState> {
});
};
private onRecordingEndingSoon = ({ secondsLeft }) => {
private onRecordingEndingSoon = ({ secondsLeft }): void => {
this.setState({ recordingTimeLeftSeconds: secondsLeft });
window.setTimeout(() => this.setState({ recordingTimeLeftSeconds: null }), 3000);
};
private setStickerPickerOpen = (isStickerPickerOpen: boolean) => {
private setStickerPickerOpen = (isStickerPickerOpen: boolean): void => {
this.setState({
isStickerPickerOpen,
isMenuOpen: false,
});
};
private toggleStickerPickerOpen = () => {
private toggleStickerPickerOpen = (): void => {
this.setStickerPickerOpen(!this.state.isStickerPickerOpen);
};
@ -428,7 +428,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
return this.state.showStickersButton && !isLocalRoom(this.props.room);
}
private getMenuPosition() {
private getMenuPosition(): MenuProps | undefined {
if (this.ref.current) {
const hasFormattingButtons = this.state.isWysiwygLabEnabled && this.state.isRichTextEnabled;
const contentRect = this.ref.current.getBoundingClientRect();
@ -461,7 +461,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
}
};
public render() {
public render(): JSX.Element {
const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus);
const e2eIcon = hasE2EIcon && (
<E2EIcon key="e2eIcon" status={this.props.e2eStatus} className="mx_MessageComposer_e2eIcon" />

View file

@ -25,7 +25,7 @@ import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
import { _t } from "../../../languageHandler";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { CollapsibleButton } from "./CollapsibleButton";
import { AboveLeftOf } from "../../structures/ContextMenu";
import { MenuProps } from "../../structures/ContextMenu";
import dis from "../../../dispatcher/dispatcher";
import ErrorDialog from "../dialogs/ErrorDialog";
import LocationButton from "../location/LocationButton";
@ -46,7 +46,7 @@ interface IProps {
haveRecording: boolean;
isMenuOpen: boolean;
isStickerPickerOpen: boolean;
menuPosition?: AboveLeftOf;
menuPosition?: MenuProps;
onRecordStartEndClick: () => void;
relation?: IEventRelation;
setStickerPickerOpen: (isStickerPickerOpen: boolean) => void;
@ -181,7 +181,7 @@ const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, rel
const roomContext = useContext(RoomContext);
const uploadInput = useRef<HTMLInputElement>();
const onUploadClick = () => {
const onUploadClick = (): void => {
if (cli.isGuest()) {
dis.dispatch({ action: "require_registration" });
return;
@ -195,7 +195,7 @@ const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, rel
}
});
const onUploadFileInputChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
const onUploadFileInputChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
if (ev.target.files.length === 0) return;
// Take a copy, so we can safely reset the value of the form control
@ -232,11 +232,11 @@ const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, rel
};
// Must be rendered within an UploadButtonContextProvider
const UploadButton = () => {
const UploadButton: React.FC = () => {
const overflowMenuCloser = useContext(OverflowMenuContext);
const uploadButtonFn = useContext(UploadButtonContext);
const onClick = () => {
const onClick = (): void => {
uploadButtonFn?.();
overflowMenuCloser?.(); // close overflow menu
};
@ -302,7 +302,7 @@ class PollButton extends React.PureComponent<IPollButtonProps> {
public static contextType = OverflowMenuContext;
public context!: React.ContextType<typeof OverflowMenuContext>;
private onCreateClick = () => {
private onCreateClick = (): void => {
this.context?.(); // close overflow menu
const canSend = this.props.room.currentState.maySendEvent(
M_POLL_START.name,
@ -330,7 +330,7 @@ class PollButton extends React.PureComponent<IPollButtonProps> {
}
};
public render() {
public render(): JSX.Element {
// do not allow sending polls within threads at this time
if (this.props.relation?.rel_type === THREAD_RELATION_TYPE.name) return null;
@ -369,7 +369,7 @@ interface WysiwygToggleButtonProps {
onClick: MouseEventHandler<HTMLDivElement>;
}
function ComposerModeButton({ isRichTextEnabled, onClick }: WysiwygToggleButtonProps) {
function ComposerModeButton({ isRichTextEnabled, onClick }: WysiwygToggleButtonProps): JSX.Element {
const title = isRichTextEnabled ? _t("Hide formatting") : _t("Show formatting");
return (

View file

@ -46,7 +46,7 @@ export default class MessageComposerFormatBar extends React.PureComponent<IProps
this.state = { visible: false };
}
public render() {
public render(): JSX.Element {
const classes = classNames("mx_MessageComposerFormatBar", {
mx_MessageComposerFormatBar_shown: this.state.visible,
});
@ -124,7 +124,7 @@ interface IFormatButtonProps {
}
class FormatButton extends React.PureComponent<IFormatButtonProps> {
public render() {
public render(): JSX.Element {
const className = `mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIcon${this.props.icon}`;
let shortcut;
if (this.props.shortcut) {

View file

@ -24,7 +24,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import RoomContext from "../../../contexts/RoomContext";
import DMRoomMap from "../../../utils/DMRoomMap";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader";
import RoomAvatar from "../avatars/RoomAvatar";
import defaultDispatcher from "../../../dispatcher/dispatcher";
@ -46,7 +46,7 @@ function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room):
return isPublic || !privateShouldBeEncrypted() || isEncrypted;
}
const NewRoomIntro = () => {
const NewRoomIntro: React.FC = () => {
const cli = useContext(MatrixClientContext);
const { room, roomId } = useContext(RoomContext);
@ -100,7 +100,7 @@ const NewRoomIntro = () => {
const topic = room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
const canAddTopic = inRoom && room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getUserId());
const onTopicClick = () => {
const onTopicClick = (): void => {
defaultDispatcher.dispatch(
{
action: "open_room_settings",
@ -244,7 +244,7 @@ const NewRoomIntro = () => {
);
}
function openRoomSettings(event) {
function openRoomSettings(event: ButtonEvent): void {
event.preventDefault();
defaultDispatcher.dispatch({
action: "open_room_settings",

View file

@ -76,12 +76,12 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
return this.props.roomId || null;
}
public componentWillUnmount() {
public componentWillUnmount(): void {
SettingsStore.unwatchSetting(this.countWatcherRef);
this.props.notification.off(NotificationStateEvents.Update, this.onNotificationUpdate);
}
public componentDidUpdate(prevProps: Readonly<IProps>) {
public componentDidUpdate(prevProps: Readonly<IProps>): void {
if (prevProps.notification) {
prevProps.notification.off(NotificationStateEvents.Update, this.onNotificationUpdate);
}
@ -89,22 +89,22 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
this.props.notification.on(NotificationStateEvents.Update, this.onNotificationUpdate);
}
private countPreferenceChanged = () => {
private countPreferenceChanged = (): void => {
this.setState({ showCounts: SettingsStore.getValue("Notifications.alwaysShowBadgeCounts", this.roomId) });
};
private onNotificationUpdate = () => {
private onNotificationUpdate = (): void => {
this.forceUpdate(); // notification state changed - update
};
private onMouseOver = (e: MouseEvent) => {
private onMouseOver = (e: MouseEvent): void => {
e.stopPropagation();
this.setState({
showTooltip: true,
});
};
private onMouseLeave = () => {
private onMouseLeave = (): void => {
this.setState({
showTooltip: false,
});

View file

@ -33,7 +33,7 @@ interface Props {
label?: string;
}
export function StatelessNotificationBadge({ symbol, count, color, ...props }: Props) {
export function StatelessNotificationBadge({ symbol, count, color, ...props }: Props): JSX.Element {
const hideBold = useSettingValue("feature_hidebold");
// Don't show a badge if we don't need to

View file

@ -25,7 +25,7 @@ interface Props {
threadId?: string;
}
export function UnreadNotificationBadge({ room, threadId }: Props) {
export function UnreadNotificationBadge({ room, threadId }: Props): JSX.Element {
const { symbol, count, color } = useUnreadNotifications(room, threadId);
return <StatelessNotificationBadge symbol={symbol} count={count} color={color} />;

View file

@ -46,7 +46,7 @@ const AVATAR_SIZE = 24;
export default class PinnedEventTile extends React.Component<IProps> {
public static contextType = MatrixClientContext;
private onTileClicked = () => {
private onTileClicked = (): void => {
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
event_id: this.props.event.getId(),
@ -69,7 +69,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
}
};
public async componentDidMount() {
public async componentDidMount(): Promise<void> {
// Fetch poll responses
if (M_POLL_START.matches(this.props.event.getType())) {
const eventId = this.props.event.getId();
@ -79,7 +79,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
try {
await Promise.all(
[M_POLL_RESPONSE.name, M_POLL_RESPONSE.altName, M_POLL_END.name, M_POLL_END.altName].map(
async (eventType) => {
async (eventType): Promise<void> => {
const relations = new Relations(RelationType.Reference, eventType, room);
relations.setTargetEvent(this.props.event);
@ -110,7 +110,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
}
}
public render() {
public render(): JSX.Element {
const sender = this.props.event.getSender();
let unpinButton = null;

View file

@ -82,7 +82,7 @@ export default class PresenceLabel extends React.Component<IProps> {
}
}
public render() {
public render(): JSX.Element {
return (
<div className="mx_PresenceLabel">
{this.getPrettyPresence(this.props.presenceState, this.props.activeAgo, this.props.currentlyActive)}

View file

@ -89,7 +89,7 @@ export function ReadReceiptGroup({
checkUnmounting,
suppressAnimation,
isTwelveHour,
}: Props) {
}: Props): JSX.Element {
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
// If we are above MAX_READ_AVATARS, well have to remove a few to have space for the +n count.
@ -232,7 +232,13 @@ interface ReadReceiptPersonProps extends IReadReceiptProps {
onAfterClick?: () => void;
}
function ReadReceiptPerson({ userId, roomMember, ts, isTwelveHour, onAfterClick }: ReadReceiptPersonProps) {
function ReadReceiptPerson({
userId,
roomMember,
ts,
isTwelveHour,
onAfterClick,
}: ReadReceiptPersonProps): JSX.Element {
const [{ showTooltip, hideTooltip }, tooltip] = useTooltip({
alignment: Alignment.Top,
tooltipClassName: "mx_ReadReceiptGroup_person--tooltip",
@ -288,7 +294,7 @@ interface ISectionHeaderProps {
className?: string;
}
function SectionHeader({ className, children }: PropsWithChildren<ISectionHeaderProps>) {
function SectionHeader({ className, children }: PropsWithChildren<ISectionHeaderProps>): JSX.Element {
const ref = useRef<HTMLHeadingElement>();
const [onFocus] = useRovingTabIndex(ref);

View file

@ -29,7 +29,7 @@ import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import RoomAvatar from "../avatars/RoomAvatar";
const RecentlyViewedButton = () => {
const RecentlyViewedButton: React.FC = () => {
const tooltipRef = useRef<InteractiveTooltip>();
const crumbs = useEventEmitterState(BreadcrumbsStore.instance, UPDATE_EVENT, () => BreadcrumbsStore.instance.rooms);

View file

@ -24,7 +24,7 @@ import ReplyTile from "./ReplyTile";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import AccessibleButton from "../elements/AccessibleButton";
function cancelQuoting(context: TimelineRenderingType) {
function cancelQuoting(context: TimelineRenderingType): void {
dis.dispatch({
action: "reply_to_event",
event: null,

View file

@ -52,13 +52,13 @@ export default class ReplyTile extends React.PureComponent<IProps> {
onHeightChanged: () => {},
};
public componentDidMount() {
public componentDidMount(): void {
this.props.mxEvent.on(MatrixEventEvent.Decrypted, this.onDecrypted);
this.props.mxEvent.on(MatrixEventEvent.BeforeRedaction, this.onEventRequiresUpdate);
this.props.mxEvent.on(MatrixEventEvent.Replaced, this.onEventRequiresUpdate);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
this.props.mxEvent.removeListener(MatrixEventEvent.Decrypted, this.onDecrypted);
this.props.mxEvent.removeListener(MatrixEventEvent.BeforeRedaction, this.onEventRequiresUpdate);
this.props.mxEvent.removeListener(MatrixEventEvent.Replaced, this.onEventRequiresUpdate);
@ -104,7 +104,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
}
};
public render() {
public render(): JSX.Element {
const mxEvent = this.props.mxEvent;
const msgType = mxEvent.getContent().msgtype;
const evType = mxEvent.getType();

View file

@ -43,7 +43,7 @@ interface IState {
skipFirst: boolean;
}
const RoomBreadcrumbTile = ({ room, onClick }: { room: Room; onClick: (ev: ButtonEvent) => void }) => {
const RoomBreadcrumbTile: React.FC<{ room: Room; onClick: (ev: ButtonEvent) => void }> = ({ room, onClick }) => {
const [onFocus, isActive, ref] = useRovingTabIndex();
return (
@ -82,12 +82,12 @@ export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState>
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
this.isMounted = false;
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
}
private onBreadcrumbsUpdate = () => {
private onBreadcrumbsUpdate = (): void => {
if (!this.isMounted) return;
// We need to trick the CSSTransition component into updating, which means we need to
@ -101,7 +101,7 @@ export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState>
window.setTimeout(() => this.setState({ doAnimation: true, skipFirst: false }), 0);
};
private viewRoom = (room: Room, index: number, viaKeyboard = false) => {
private viewRoom = (room: Room, index: number, viaKeyboard = false): void => {
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,

View file

@ -24,7 +24,7 @@ type Props<T extends keyof ReactHTML> = HTMLAttributes<T> & {
room: Room;
};
export function RoomContextDetails<T extends keyof ReactHTML>({ room, component, ...other }: Props<T>) {
export function RoomContextDetails<T extends keyof ReactHTML>({ room, component, ...other }: Props<T>): JSX.Element {
const contextDetails = roomContextDetails(room);
if (contextDetails) {
return React.createElement(

View file

@ -96,7 +96,7 @@ const VoiceCallButton: FC<VoiceCallButtonProps> = ({ room, busy, setBusy, behavi
} else {
// behavior === "legacy_or_jitsi"
return {
onClick: async (ev: ButtonEvent) => {
onClick: async (ev: ButtonEvent): Promise<void> => {
ev.preventDefault();
setBusy(true);
await LegacyCallHandler.instance.placeCall(room.roomId, CallType.Voice);
@ -134,7 +134,7 @@ interface VideoCallButtonProps {
const VideoCallButton: FC<VideoCallButtonProps> = ({ room, busy, setBusy, behavior }) => {
const [menuOpen, buttonRef, openMenu, closeMenu] = useContextMenu();
const startLegacyCall = useCallback(async () => {
const startLegacyCall = useCallback(async (): Promise<void> => {
setBusy(true);
await LegacyCallHandler.instance.placeCall(room.roomId, CallType.Video);
setBusy(false);
@ -160,7 +160,7 @@ const VideoCallButton: FC<VideoCallButtonProps> = ({ room, busy, setBusy, behavi
};
} else if (behavior === "legacy_or_jitsi") {
return {
onClick: async (ev: ButtonEvent) => {
onClick: async (ev: ButtonEvent): Promise<void> => {
ev.preventDefault();
await startLegacyCall();
},
@ -168,7 +168,7 @@ const VideoCallButton: FC<VideoCallButtonProps> = ({ room, busy, setBusy, behavi
};
} else if (behavior === "element") {
return {
onClick: async (ev: ButtonEvent) => {
onClick: async (ev: ButtonEvent): Promise<void> => {
ev.preventDefault();
startElementCall();
},
@ -177,7 +177,7 @@ const VideoCallButton: FC<VideoCallButtonProps> = ({ room, busy, setBusy, behavi
} else {
// behavior === "jitsi_or_element"
return {
onClick: async (ev: ButtonEvent) => {
onClick: async (ev: ButtonEvent): Promise<void> => {
ev.preventDefault();
openMenu();
},
@ -187,7 +187,7 @@ const VideoCallButton: FC<VideoCallButtonProps> = ({ room, busy, setBusy, behavi
}, [behavior, startLegacyCall, startElementCall, openMenu]);
const onJitsiClick = useCallback(
async (ev: ButtonEvent) => {
async (ev: ButtonEvent): Promise<void> => {
ev.preventDefault();
closeMenu();
await startLegacyCall();
@ -503,23 +503,23 @@ export default class RoomHeader extends React.Component<IProps, IState> {
};
}
public componentDidMount() {
public componentDidMount(): void {
this.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
const notiStore = RoomNotificationStateStore.instance.getRoomState(this.props.room);
notiStore.removeListener(NotificationStateEvents.Update, this.onNotificationUpdate);
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
}
private onRightPanelStoreUpdate = () => {
private onRightPanelStoreUpdate = (): void => {
this.setState({ rightPanelOpen: RightPanelStore.instance.isOpen });
};
private onRoomStateEvents = (event: MatrixEvent) => {
private onRoomStateEvents = (event: MatrixEvent): void => {
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
return;
}
@ -528,7 +528,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
this.rateLimitedUpdate();
};
private onNotificationUpdate = () => {
private onNotificationUpdate = (): void => {
this.forceUpdate();
};
@ -540,18 +540,18 @@ export default class RoomHeader extends React.Component<IProps, IState> {
{ leading: true, trailing: true },
);
private onContextMenuOpenClick = (ev: ButtonEvent) => {
private onContextMenuOpenClick = (ev: ButtonEvent): void => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target as HTMLButtonElement;
this.setState({ contextMenuPosition: target.getBoundingClientRect() });
};
private onContextMenuCloseClick = () => {
private onContextMenuCloseClick = (): void => {
this.setState({ contextMenuPosition: undefined });
};
private onHideCallClick = (ev: ButtonEvent) => {
private onHideCallClick = (ev: ButtonEvent): void => {
ev.preventDefault();
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
@ -659,7 +659,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
);
}
private renderName(oobName: string) {
private renderName(oobName: string): JSX.Element {
let contextMenu: JSX.Element | null = null;
if (this.state.contextMenuPosition && this.props.room) {
contextMenu = (
@ -716,7 +716,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
return <div className="mx_RoomHeader_name mx_RoomHeader_name--textonly">{roomName}</div>;
}
public render() {
public render(): JSX.Element {
const isVideoRoom = SettingsStore.getValue("feature_video_rooms") && calcIsVideoRoom(this.props.room);
let roomAvatar: JSX.Element | null = null;
@ -788,7 +788,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
const topicElement = <RoomTopic room={this.props.room} className="mx_RoomHeader_topic" />;
const viewLabs = () =>
const viewLabs = (): void =>
defaultDispatcher.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,

View file

@ -17,6 +17,7 @@ limitations under the License.
import React, { FC } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { _t } from "../../../languageHandler";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
@ -33,7 +34,7 @@ interface IProps {
const RoomInfoLine: FC<IProps> = ({ room }) => {
// summary will begin as undefined whilst loading and go null if it fails to load or we are not invited.
const summary = useAsyncMemo(async () => {
const summary = useAsyncMemo(async (): Promise<Awaited<ReturnType<MatrixClient["getRoomSummary"]>> | null> => {
if (room.getMyMembership() !== "invite") return null;
try {
return room.client.getRoomSummary(room.roomId);
@ -71,7 +72,7 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
);
} else if (memberCount && summary !== undefined) {
// summary is not still loading
const viewMembers = () =>
const viewMembers = (): void =>
RightPanelStore.instance.setCard({
phase: room.isSpaceRoom() ? RightPanelPhases.SpaceMemberList : RightPanelPhases.RoomMemberList,
});

View file

@ -51,7 +51,7 @@ import { arrayFastClone, arrayHasDiff } from "../../../utils/arrays";
import { objectShallowClone, objectWithOnly } from "../../../utils/objects";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import { shouldShowSpaceInvite, showAddExistingRooms, showCreateNewRoom, showSpaceInvite } from "../../../utils/space";
import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
import { ChevronFace, ContextMenuTooltipButton, MenuProps, useContextMenu } from "../../structures/ContextMenu";
import RoomAvatar from "../avatars/RoomAvatar";
import { BetaPill } from "../beta/BetaCard";
import IconizedContextMenu, {
@ -107,7 +107,7 @@ interface ITagAestheticsMap {
[tagId: TagID]: ITagAesthetics;
}
const auxButtonContextMenuPosition = (handle: RefObject<HTMLDivElement>) => {
const auxButtonContextMenuPosition = (handle: RefObject<HTMLDivElement>): MenuProps => {
const rect = handle.current.getBoundingClientRect();
return {
chevronFace: ChevronFace.None,
@ -116,7 +116,7 @@ const auxButtonContextMenuPosition = (handle: RefObject<HTMLDivElement>) => {
};
};
const DmAuxButton = ({ tabIndex, dispatcher = defaultDispatcher }: IAuxButtonProps) => {
const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = defaultDispatcher }) => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
const activeSpace: Room = useEventEmitterState(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
return SpaceStore.instance.activeSpaceRoom;
@ -207,7 +207,7 @@ const DmAuxButton = ({ tabIndex, dispatcher = defaultDispatcher }: IAuxButtonPro
return null;
};
const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
const activeSpace = useEventEmitterState<Room>(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
return SpaceStore.instance.activeSpaceRoom;
@ -466,7 +466,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
this.updateLists(); // trigger the first update
}
public componentWillUnmount() {
public componentWillUnmount(): void {
SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
SettingsStore.unwatchSetting(this.favouriteMessageWatcher);
@ -474,13 +474,13 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
}
private onRoomViewStoreUpdate = () => {
private onRoomViewStoreUpdate = (): void => {
this.setState({
currentRoomId: SdkContextClass.instance.roomViewStore.getRoomId(),
});
};
private onAction = (payload: ActionPayload) => {
private onAction = (payload: ActionPayload): void => {
if (payload.action === Action.ViewRoomDelta) {
const viewRoomDeltaPayload = payload as ViewRoomDeltaPayload;
const currentRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
@ -499,7 +499,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
}
};
private getRoomDelta = (roomId: string, delta: number, unread = false) => {
private getRoomDelta = (roomId: string, delta: number, unread = false): Room => {
const lists = RoomListStore.instance.orderedLists;
const rooms: Room[] = [];
TAG_ORDER.forEach((t) => {
@ -522,11 +522,11 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
return room;
};
private updateSuggestedRooms = (suggestedRooms: ISuggestedRoom[]) => {
private updateSuggestedRooms = (suggestedRooms: ISuggestedRoom[]): void => {
this.setState({ suggestedRooms });
};
private updateLists = () => {
private updateLists = (): void => {
const newLists = RoomListStore.instance.orderedLists;
const previousListIds = Object.keys(this.state.sublists);
const newListIds = Object.keys(newLists);
@ -573,7 +573,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
resizeMethod="crop"
/>
);
const viewRoom = (ev) => {
const viewRoom = (ev): void => {
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_alias: room.canonical_alias || room.aliases?.[0],
@ -688,7 +688,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
[...treeItems].find((e) => e.offsetParent !== null)?.focus();
}
public render() {
public render(): JSX.Element {
const sublists = this.renderSublists();
return (
<RovingTabIndexProvider handleHomeEnd handleUpDown onKeyDown={this.props.onKeyDown}>

View file

@ -45,7 +45,7 @@ import {
showCreateNewSubspace,
showSpaceInvite,
} from "../../../utils/space";
import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
import { ChevronFace, ContextMenuTooltipButton, useContextMenu, MenuProps } from "../../structures/ContextMenu";
import { BetaPill } from "../beta/BetaCard";
import IconizedContextMenu, {
IconizedContextMenuOption,
@ -56,7 +56,7 @@ import InlineSpinner from "../elements/InlineSpinner";
import TooltipTarget from "../elements/TooltipTarget";
import { HomeButtonContextMenu } from "../spaces/SpacePanel";
const contextMenuBelow = (elementRect: DOMRect) => {
const contextMenuBelow = (elementRect: DOMRect): MenuProps => {
// align the context menu's icons with the icon which opened the context menu
const left = elementRect.left + window.scrollX;
const top = elementRect.bottom + window.scrollY + 12;
@ -74,12 +74,12 @@ const usePendingActions = (): Map<PendingActionType, Set<string>> => {
const cli = useContext(MatrixClientContext);
const [actions, setActions] = useState(new Map<PendingActionType, Set<string>>());
const addAction = (type: PendingActionType, key: string) => {
const addAction = (type: PendingActionType, key: string): void => {
const keys = new Set(actions.get(type));
keys.add(key);
setActions(new Map(actions).set(type, keys));
};
const removeAction = (type: PendingActionType, key: string) => {
const removeAction = (type: PendingActionType, key: string): void => {
const keys = new Set(actions.get(type));
if (keys.delete(key)) {
setActions(new Map(actions).set(type, keys));
@ -112,7 +112,7 @@ interface IProps {
onVisibilityChange?(): void;
}
const RoomListHeader = ({ onVisibilityChange }: IProps) => {
const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
const cli = useContext(MatrixClientContext);
const [mainMenuDisplayed, mainMenuHandle, openMainMenu, closeMainMenu] = useContextMenu<HTMLDivElement>();
const [plusMenuDisplayed, plusMenuHandle, openPlusMenu, closePlusMenu] = useContextMenu<HTMLDivElement>();

View file

@ -112,17 +112,17 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
};
}
public componentDidMount() {
public componentDidMount(): void {
this.checkInvitedEmail();
}
public componentDidUpdate(prevProps, prevState) {
public componentDidUpdate(prevProps, prevState): void {
if (this.props.invitedEmail !== prevProps.invitedEmail || this.props.inviterName !== prevProps.inviterName) {
this.checkInvitedEmail();
}
}
private async checkInvitedEmail() {
private async checkInvitedEmail(): Promise<void> {
// If this is an invite and we've been told what email address was
// invited, fetch the user's account emails and discovery bindings so we
// can check them against the email that was invited.
@ -262,15 +262,15 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
};
}
private onLoginClick = () => {
private onLoginClick = (): void => {
dis.dispatch({ action: "start_login", screenAfterLogin: this.makeScreenAfterLogin() });
};
private onRegisterClick = () => {
private onRegisterClick = (): void => {
dis.dispatch({ action: "start_registration", screenAfterLogin: this.makeScreenAfterLogin() });
};
public render() {
public render(): JSX.Element {
const brand = SdkConfig.get().brand;
const roomName = this.props.room?.name ?? this.props.roomAlias ?? "";
const isSpace = this.props.room?.isSpaceRoom() ?? this.props.oobData?.roomType === RoomType.Space;

View file

@ -66,7 +66,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
const cannotJoin =
getEffectiveMembership(myMembership) === EffectiveMembership.Leave && joinRule !== JoinRule.Public;
const viewLabs = () =>
const viewLabs = (): void =>
defaultDispatcher.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,

View file

@ -134,13 +134,13 @@ export default class RoomSublist extends React.Component<IProps, IState> {
this.state = Object.assign(this.state, { height: this.calculateInitialHeight() });
}
private calculateInitialHeight() {
private calculateInitialHeight(): number {
const requestedVisibleTiles = Math.max(Math.floor(this.layout.visibleTiles), this.layout.minVisibleTiles);
const tileCount = Math.min(this.numTiles, requestedVisibleTiles);
return this.layout.tilesToPixelsWithPadding(tileCount, this.padding);
}
private get padding() {
private get padding(): number {
let padding = RESIZE_HANDLE_HEIGHT;
// this is used for calculating the max height of the whole container,
// and takes into account whether there should be room reserved for the show more/less button
@ -170,7 +170,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
return RoomSublist.calcNumTiles(this.state.rooms, this.extraTiles);
}
private static calcNumTiles(rooms: Room[], extraTiles: any[]) {
private static calcNumTiles(rooms: Room[], extraTiles: any[]): number {
return (rooms || []).length + (extraTiles || []).length;
}
@ -182,7 +182,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
return Math.min(nVisible, this.numTiles);
}
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>) {
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>): void {
const prevExtraTiles = prevProps.extraTiles;
// as the rooms can come in one by one we need to reevaluate
// the amount of available rooms to cap the amount of requested visible rooms by the layout
@ -247,7 +247,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
return false;
}
public componentDidMount() {
public componentDidMount(): void {
this.dispatcherRef = defaultDispatcher.register(this.onAction);
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onListsUpdated);
RoomListStore.instance.on(LISTS_LOADING_EVENT, this.onListsLoading);
@ -257,14 +257,14 @@ export default class RoomSublist extends React.Component<IProps, IState> {
this.tilesRef.current?.addEventListener("scroll", this.onScrollPrevent, { passive: true });
}
public componentWillUnmount() {
public componentWillUnmount(): void {
defaultDispatcher.unregister(this.dispatcherRef);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onListsUpdated);
RoomListStore.instance.off(LISTS_LOADING_EVENT, this.onListsLoading);
this.tilesRef.current?.removeEventListener("scroll", this.onScrollPrevent);
}
private onListsLoading = (tagId: TagID, isLoading: boolean) => {
private onListsLoading = (tagId: TagID, isLoading: boolean): void => {
if (this.props.tagId !== tagId) {
return;
}
@ -273,7 +273,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
});
};
private onListsUpdated = () => {
private onListsUpdated = (): void => {
const stateUpdates = {} as IState;
const currentRooms = this.state.rooms;
@ -287,7 +287,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private onAction = (payload: ActionPayload) => {
private onAction = (payload: ActionPayload): void => {
if (payload.action === Action.ViewRoom && payload.show_room_tile && this.state.rooms) {
// XXX: we have to do this a tick later because we have incorrect intermediate props during a room change
// where we lose the room we are changing from temporarily and then it comes back in an update right after.
@ -306,7 +306,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private applyHeightChange(newHeight: number) {
private applyHeightChange(newHeight: number): void {
const heightInTiles = Math.ceil(this.layout.pixelsToTiles(newHeight - this.padding));
this.layout.visibleTiles = Math.min(this.numTiles, heightInTiles);
}
@ -316,13 +316,13 @@ export default class RoomSublist extends React.Component<IProps, IState> {
travelDirection: Direction,
refToElement: HTMLDivElement,
delta: ResizeDelta,
) => {
): void => {
const newHeight = this.heightAtStart + delta.height;
this.applyHeightChange(newHeight);
this.setState({ height: newHeight });
};
private onResizeStart = () => {
private onResizeStart = (): void => {
this.heightAtStart = this.state.height;
this.setState({ isResizing: true });
};
@ -332,13 +332,13 @@ export default class RoomSublist extends React.Component<IProps, IState> {
travelDirection: Direction,
refToElement: HTMLDivElement,
delta: ResizeDelta,
) => {
): void => {
const newHeight = this.heightAtStart + delta.height;
this.applyHeightChange(newHeight);
this.setState({ isResizing: false, height: newHeight });
};
private onShowAllClick = async () => {
private onShowAllClick = async (): Promise<void> => {
if (this.slidingSyncMode) {
const slidingSyncIndex = SlidingSyncManager.instance.getOrAllocateListIndex(this.props.tagId);
const count = RoomListStore.instance.getCount(this.props.tagId);
@ -356,13 +356,13 @@ export default class RoomSublist extends React.Component<IProps, IState> {
});
};
private onShowLessClick = () => {
private onShowLessClick = (): void => {
const newHeight = this.layout.tilesToPixelsWithPadding(this.layout.defaultVisibleTiles, this.padding);
this.applyHeightChange(newHeight);
this.setState({ height: newHeight });
};
private focusRoomTile = (index: number) => {
private focusRoomTile = (index: number): void => {
if (!this.sublistRef.current) return;
const elements = this.sublistRef.current.querySelectorAll<HTMLDivElement>(".mx_RoomTile");
const element = elements && elements[index];
@ -371,14 +371,14 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private onOpenMenuClick = (ev: React.MouseEvent) => {
private onOpenMenuClick = (ev: React.MouseEvent): void => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target as HTMLButtonElement;
this.setState({ contextMenuPosition: target.getBoundingClientRect() });
};
private onContextMenu = (ev: React.MouseEvent) => {
private onContextMenu = (ev: React.MouseEvent): void => {
ev.preventDefault();
ev.stopPropagation();
this.setState({
@ -390,28 +390,28 @@ export default class RoomSublist extends React.Component<IProps, IState> {
});
};
private onCloseMenu = () => {
private onCloseMenu = (): void => {
this.setState({ contextMenuPosition: null });
};
private onUnreadFirstChanged = () => {
private onUnreadFirstChanged = (): void => {
const isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance;
const newAlgorithm = isUnreadFirst ? ListAlgorithm.Natural : ListAlgorithm.Importance;
RoomListStore.instance.setListOrder(this.props.tagId, newAlgorithm);
this.forceUpdate(); // because if the sublist doesn't have any changes then we will miss the list order change
};
private onTagSortChanged = async (sort: SortAlgorithm) => {
private onTagSortChanged = async (sort: SortAlgorithm): Promise<void> => {
RoomListStore.instance.setTagSorting(this.props.tagId, sort);
this.forceUpdate();
};
private onMessagePreviewChanged = () => {
private onMessagePreviewChanged = (): void => {
this.layout.showPreviews = !this.layout.showPreviews;
this.forceUpdate(); // because the layout doesn't trigger a re-render
};
private onBadgeClick = (ev: React.MouseEvent) => {
private onBadgeClick = (ev: React.MouseEvent): void => {
ev.preventDefault();
ev.stopPropagation();
@ -438,7 +438,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private onHeaderClick = () => {
private onHeaderClick = (): void => {
const possibleSticky = this.headerButton.current.parentElement;
const sublist = possibleSticky.parentElement.parentElement;
const list = sublist.parentElement.parentElement;
@ -465,7 +465,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private toggleCollapsed = () => {
private toggleCollapsed = (): void => {
if (this.props.forceExpanded) return;
this.layout.isCollapsed = this.state.isExpanded;
this.setState({ isExpanded: !this.layout.isCollapsed });
@ -474,7 +474,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private onHeaderKeyDown = (ev: React.KeyboardEvent) => {
private onHeaderKeyDown = (ev: React.KeyboardEvent): void => {
const action = getKeyBindingsManager().getRoomListAction(ev);
switch (action) {
case KeyBindingAction.CollapseRoomListSection:
@ -501,7 +501,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
};
private onKeyDown = (ev: React.KeyboardEvent) => {
private onKeyDown = (ev: React.KeyboardEvent): void => {
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
// On ArrowLeft go to the sublist header
@ -733,7 +733,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
);
}
private onScrollPrevent(e: Event) {
private onScrollPrevent(e: Event): void {
// the RoomTile calls scrollIntoView and the browser may scroll a div we do not wish to be scrollable
// this fixes https://github.com/vector-im/element-web/issues/14413
(e.target as HTMLDivElement).scrollTop = 0;

View file

@ -25,7 +25,7 @@ import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleBu
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import { _t } from "../../../languageHandler";
import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextMenu";
import { ChevronFace, ContextMenuTooltipButton, MenuProps } from "../../structures/ContextMenu";
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
@ -71,9 +71,9 @@ interface State {
messagePreview?: string;
}
const messagePreviewId = (roomId: string) => `mx_RoomTile_messagePreview_${roomId}`;
const messagePreviewId = (roomId: string): string => `mx_RoomTile_messagePreview_${roomId}`;
export const contextMenuBelow = (elementRect: PartialDOMRect) => {
export const contextMenuBelow = (elementRect: PartialDOMRect): MenuProps => {
// align the context menu's icons with the icon which opened the context menu
const left = elementRect.left + window.scrollX - 9;
const top = elementRect.bottom + window.scrollY + 17;
@ -104,15 +104,15 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
this.roomProps = EchoChamber.forRoom(this.props.room);
}
private onRoomNameUpdate = (room: Room) => {
private onRoomNameUpdate = (room: Room): void => {
this.forceUpdate();
};
private onNotificationUpdate = () => {
private onNotificationUpdate = (): void => {
this.forceUpdate(); // notification state changed - update
};
private onRoomPropertyUpdate = (property: CachedRoomKey) => {
private onRoomPropertyUpdate = (property: CachedRoomKey): void => {
if (property === CachedRoomKey.NotificationVolume) this.onNotificationUpdate();
// else ignore - not important for this tile
};
@ -125,7 +125,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
return !this.props.isMinimized && this.props.showMessagePreview;
}
public componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>) {
public componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>): void {
const showMessageChanged = prevProps.showMessagePreview !== this.props.showMessagePreview;
const minimizedChanged = prevProps.isMinimized !== this.props.isMinimized;
if (showMessageChanged || minimizedChanged) {
@ -145,7 +145,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
}
}
public componentDidMount() {
public componentDidMount(): void {
// when we're first rendered (or our sublist is expanded) make sure we are visible if we're active
if (this.state.selected) {
this.scrollIntoView();
@ -167,7 +167,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
this.setState({ call: CallStore.instance.getCall(this.props.room.roomId) });
}
public componentWillUnmount() {
public componentWillUnmount(): void {
SdkContextClass.instance.roomViewStore.removeRoomListener(this.props.room.roomId, this.onActiveRoomUpdate);
MessagePreviewStore.instance.off(
MessagePreviewStore.getPreviewChangedEventName(this.props.room),
@ -180,7 +180,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
CallStore.instance.off(CallStoreEvent.Call, this.onCallChanged);
}
private onAction = (payload: ActionPayload) => {
private onAction = (payload: ActionPayload): void => {
if (
payload.action === Action.ViewRoom &&
payload.room_id === this.props.room.roomId &&
@ -192,17 +192,17 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
}
};
private onRoomPreviewChanged = (room: Room) => {
private onRoomPreviewChanged = (room: Room): void => {
if (this.props.room && room.roomId === this.props.room.roomId) {
this.generatePreview();
}
};
private onCallChanged = (call: Call, roomId: string) => {
private onCallChanged = (call: Call, roomId: string): void => {
if (roomId === this.props.room?.roomId) this.setState({ call });
};
private async generatePreview() {
private async generatePreview(): Promise<void> {
if (!this.showMessagePreview) {
return null;
}
@ -211,7 +211,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
this.setState({ messagePreview });
}
private scrollIntoView = () => {
private scrollIntoView = (): void => {
if (!this.roomTileRef.current) return;
this.roomTileRef.current.scrollIntoView({
block: "nearest",
@ -219,7 +219,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
});
};
private onTileClick = async (ev: React.KeyboardEvent) => {
private onTileClick = async (ev: React.KeyboardEvent): Promise<void> => {
ev.preventDefault();
ev.stopPropagation();
@ -238,11 +238,11 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
});
};
private onActiveRoomUpdate = (isActive: boolean) => {
private onActiveRoomUpdate = (isActive: boolean): void => {
this.setState({ selected: isActive });
};
private onNotificationsMenuOpenClick = (ev: ButtonEvent) => {
private onNotificationsMenuOpenClick = (ev: ButtonEvent): void => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target as HTMLButtonElement;
@ -251,18 +251,18 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
PosthogTrackers.trackInteraction("WebRoomListRoomTileNotificationsMenu", ev);
};
private onCloseNotificationsMenu = () => {
private onCloseNotificationsMenu = (): void => {
this.setState({ notificationsMenuPosition: null });
};
private onGeneralMenuOpenClick = (ev: ButtonEvent) => {
private onGeneralMenuOpenClick = (ev: ButtonEvent): void => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target as HTMLButtonElement;
this.setState({ generalMenuPosition: target.getBoundingClientRect() });
};
private onContextMenu = (ev: React.MouseEvent) => {
private onContextMenu = (ev: React.MouseEvent): void => {
// If we don't have a context menu to show, ignore the action.
if (!this.showContextMenu) return;
@ -276,7 +276,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
});
};
private onCloseGeneralMenu = () => {
private onCloseGeneralMenu = (): void => {
this.setState({ generalMenuPosition: null });
};

View file

@ -51,15 +51,15 @@ export default class SearchBar extends React.Component<IProps, IState> {
};
}
private onThisRoomClick = () => {
private onThisRoomClick = (): void => {
this.setState({ scope: SearchScope.Room }, () => this.searchIfQuery());
};
private onAllRoomsClick = () => {
private onAllRoomsClick = (): void => {
this.setState({ scope: SearchScope.All }, () => this.searchIfQuery());
};
private onSearchChange = (e: React.KeyboardEvent) => {
private onSearchChange = (e: React.KeyboardEvent): void => {
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.Enter:
@ -82,7 +82,7 @@ export default class SearchBar extends React.Component<IProps, IState> {
this.props.onSearch(this.searchTerm.current.value, this.state.scope);
};
public render() {
public render(): JSX.Element {
const searchButtonClasses = classNames("mx_SearchBar_searchButton", {
mx_SearchBar_searching: this.props.searchInProgress,
});

View file

@ -58,7 +58,7 @@ export default class SearchResultTile extends React.Component<IProps> {
this.callEventGroupers = buildLegacyCallEventGroupers(this.callEventGroupers, events);
}
public render() {
public render(): JSX.Element {
const timeline = this.props.timeline;
const resultEvent = timeline[this.props.ourEventsIndexes[0]];
const eventId = resultEvent.getId();

View file

@ -462,13 +462,13 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
}
}
public componentWillUnmount() {
public componentWillUnmount(): void {
dis.unregister(this.dispatcherRef);
window.removeEventListener("beforeunload", this.saveStoredEditorState);
this.saveStoredEditorState();
}
private get editorStateKey() {
private get editorStateKey(): string {
let key = `mx_cider_state_${this.props.room.roomId}`;
if (this.props.relation?.rel_type === THREAD_RELATION_TYPE.name) {
key += `_${this.props.relation.event_id}`;
@ -572,7 +572,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
this.editorRef.current?.focus();
};
public render() {
public render(): JSX.Element {
const threadId =
this.props.relation?.rel_type === THREAD_RELATION_TYPE.name ? this.props.relation.event_id : null;
return (

View file

@ -213,7 +213,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
}
};
private onRightPanelStoreUpdate = () => {
private onRightPanelStoreUpdate = (): void => {
this.props.setStickerPickerOpen(false);
};

View file

@ -80,7 +80,7 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
}
}
public onRoomStateEvents = (ev: MatrixEvent) => {
public onRoomStateEvents = (ev: MatrixEvent): void => {
if (ev.getType() === EventType.RoomThirdPartyInvite && ev.getStateKey() === this.state.stateKey) {
const newDisplayName = ev.getContent().display_name;
const isInvited = isValid3pidInvite(ev);
@ -91,14 +91,14 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
}
};
public onCancel = () => {
public onCancel = (): void => {
dis.dispatch({
action: "view_3pid_invite",
event: null,
});
};
public onKickClick = () => {
public onKickClick = (): void => {
MatrixClientPeg.get()
.sendStateEvent(this.state.roomId, "m.room.third_party_invite", {}, this.state.stateKey)
.catch((err) => {
@ -120,7 +120,7 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
this.setState({ invited: false });
};
public render() {
public render(): JSX.Element {
let adminTools = null;
if (this.state.canKick && this.state.invited) {
adminTools = (

View file

@ -37,7 +37,7 @@ interface IProps {
thread: Thread;
}
const ThreadSummary = ({ mxEvent, thread, ...props }: IProps) => {
const ThreadSummary: React.FC<IProps> = ({ mxEvent, thread, ...props }) => {
const roomContext = useContext(RoomContext);
const cardContext = useContext(CardContext);
const count = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.length);
@ -74,7 +74,7 @@ interface IPreviewProps {
showDisplayname?: boolean;
}
export const ThreadMessagePreview = ({ thread, showDisplayname = false }: IPreviewProps) => {
export const ThreadMessagePreview: React.FC<IPreviewProps> = ({ thread, showDisplayname = false }) => {
const cli = useContext(MatrixClientContext);
const lastReply = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.replyToEvent);
@ -88,7 +88,7 @@ export const ThreadMessagePreview = ({ thread, showDisplayname = false }: IPrevi
setContent(lastReply.getContent());
});
const preview = useAsyncMemo(async () => {
const preview = useAsyncMemo(async (): Promise<string> => {
if (!lastReply) return;
await cli.decryptEventIfNeeded(lastReply);
return MessagePreviewStore.instance.generatePreviewForEvent(lastReply);

View file

@ -77,7 +77,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
this.voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation);
}
public componentDidMount() {
public componentDidMount(): void {
const recorder = VoiceRecordingStore.instance.getActiveRecording(this.voiceRecordingId);
if (recorder) {
if (recorder.isRecording || !recorder.hasRecording) {
@ -88,7 +88,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
}
}
public async componentWillUnmount() {
public async componentWillUnmount(): Promise<void> {
// Stop recording, but keep the recording memory (don't dispose it). This is to let the user
// come back and finish working with it.
const recording = VoiceRecordingStore.instance.getActiveRecording(this.voiceRecordingId);
@ -99,7 +99,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
}
// called by composer
public async send() {
public async send(): Promise<void> {
if (!this.state.recorder) {
throw new Error("No recording started - cannot send anything");
}
@ -159,25 +159,25 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
await this.disposeRecording();
}
private async disposeRecording() {
private async disposeRecording(): Promise<void> {
await VoiceRecordingStore.instance.disposeRecording(this.voiceRecordingId);
// Reset back to no recording, which means no phase (ie: restart component entirely)
this.setState({ recorder: null, recordingPhase: null, didUploadFail: false });
}
private onCancel = async () => {
private onCancel = async (): Promise<void> => {
await this.disposeRecording();
};
public onRecordStartEndClick = async () => {
public onRecordStartEndClick = async (): Promise<void> => {
if (this.state.recorder) {
await this.state.recorder.stop();
return;
}
// The "microphone access error" dialogs are used a lot, so let's functionify them
const accessError = () => {
const accessError = (): void => {
Modal.createDialog(ErrorDialog, {
title: _t("Unable to access your microphone"),
description: (
@ -236,7 +236,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
}
};
private bindNewRecorder(recorder: Optional<VoiceMessageRecording>) {
private bindNewRecorder(recorder: Optional<VoiceMessageRecording>): void {
if (this.state.recorder) {
this.state.recorder.off(UPDATE_EVENT, this.onRecordingUpdate);
}
@ -245,7 +245,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
}
}
private onRecordingUpdate = (ev: RecordingState) => {
private onRecordingUpdate = (ev: RecordingState): void => {
if (ev === RecordingState.EndingSoon) return; // ignore this state: it has no UI purpose here
this.setState({ recordingPhase: ev });
};

View file

@ -56,12 +56,12 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
delayedStopTypingTimers: {},
};
public componentDidMount() {
public componentDidMount(): void {
MatrixClientPeg.get().on(RoomMemberEvent.Typing, this.onRoomMemberTyping);
MatrixClientPeg.get().on(RoomEvent.Timeline, this.onRoomTimeline);
}
public componentDidUpdate(_, prevState) {
public componentDidUpdate(_, prevState): void {
const wasVisible = WhoIsTypingTile.isVisible(prevState);
const isVisible = WhoIsTypingTile.isVisible(this.state);
if (this.props.onShown && !wasVisible && isVisible) {
@ -71,7 +71,7 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
}
}
public componentWillUnmount() {
public componentWillUnmount(): void {
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
const client = MatrixClientPeg.get();
if (client) {
@ -199,7 +199,7 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
return avatars;
}
public render() {
public render(): JSX.Element {
let usersTyping = this.state.usersTyping;
const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers).map((userId) =>
this.props.room.getMember(userId),

View file

@ -31,6 +31,6 @@ export interface ComposerContextState {
export const ComposerContext = createContext<ComposerContextState>(getDefaultContextValue());
ComposerContext.displayName = "ComposerContext";
export function useComposerContext() {
export function useComposerContext(): ComposerContextState {
return useContext(ComposerContext);
}

View file

@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { ComponentProps, lazy, Suspense } from "react";
import { ISendEventResponse } from "matrix-js-sdk/src/@types/requests";
// we need to import the types for TS, but do not import the sendMessage
// function to avoid importing from "@matrix-org/matrix-wysiwyg"
@ -23,19 +24,26 @@ import { SendMessageParams } from "./utils/message";
const SendComposer = lazy(() => import("./SendWysiwygComposer"));
const EditComposer = lazy(() => import("./EditWysiwygComposer"));
export const dynamicImportSendMessage = async (message: string, isHTML: boolean, params: SendMessageParams) => {
export const dynamicImportSendMessage = async (
message: string,
isHTML: boolean,
params: SendMessageParams,
): Promise<ISendEventResponse> => {
const { sendMessage } = await import("./utils/message");
return sendMessage(message, isHTML, params);
};
export const dynamicImportConversionFunctions = async () => {
export const dynamicImportConversionFunctions = async (): Promise<{
richToPlain(rich: string): Promise<string>;
plainToRich(plain: string): Promise<string>;
}> => {
const { richToPlain, plainToRich } = await import("@matrix-org/matrix-wysiwyg");
return { richToPlain, plainToRich };
};
export function DynamicImportSendWysiwygComposer(props: ComponentProps<typeof SendComposer>) {
export function DynamicImportSendWysiwygComposer(props: ComponentProps<typeof SendComposer>): JSX.Element {
return (
<Suspense fallback={<div />}>
<SendComposer {...props} />
@ -43,7 +51,7 @@ export function DynamicImportSendWysiwygComposer(props: ComponentProps<typeof Se
);
}
export function DynamicImportEditWysiwygComposer(props: ComponentProps<typeof EditComposer>) {
export function DynamicImportEditWysiwygComposer(props: ComponentProps<typeof EditComposer>): JSX.Element {
return (
<Suspense fallback={<div />}>
<EditComposer {...props} />

View file

@ -47,7 +47,11 @@ interface EditWysiwygComposerProps {
}
// Default needed for React.lazy
export default function EditWysiwygComposer({ editorStateTransfer, className, ...props }: EditWysiwygComposerProps) {
export default function EditWysiwygComposer({
editorStateTransfer,
className,
...props
}: EditWysiwygComposerProps): JSX.Element {
const defaultContextValue = useRef(getDefaultContextValue());
const initialContent = useInitialContent(editorStateTransfer);
const isReady = !editorStateTransfer || initialContent !== undefined;

View file

@ -22,7 +22,7 @@ import { PlainTextComposer } from "./components/PlainTextComposer";
import { ComposerFunctions } from "./types";
import { E2EStatus } from "../../../../utils/ShieldUtils";
import E2EIcon from "../E2EIcon";
import { AboveLeftOf } from "../../../structures/ContextMenu";
import { MenuProps } from "../../../structures/ContextMenu";
import { Emoji } from "./components/Emoji";
import { ComposerContext, getDefaultContextValue } from "./ComposerContext";
@ -47,7 +47,7 @@ interface SendWysiwygComposerProps {
e2eStatus?: E2EStatus;
onChange: (content: string) => void;
onSend: () => void;
menuPosition: AboveLeftOf;
menuPosition: MenuProps;
}
// Default needed for React.lazy
@ -56,7 +56,7 @@ export default function SendWysiwygComposer({
e2eStatus,
menuPosition,
...props
}: SendWysiwygComposerProps) {
}: SendWysiwygComposerProps): JSX.Element {
const Composer = isRichTextEnabled ? WysiwygComposer : PlainTextComposer;
const defaultContextValue = useRef(getDefaultContextValue());

View file

@ -25,7 +25,11 @@ interface EditionButtonsProps {
isSaveDisabled?: boolean;
}
export function EditionButtons({ onCancelClick, onSaveClick, isSaveDisabled = false }: EditionButtonsProps) {
export function EditionButtons({
onCancelClick,
onSaveClick,
isSaveDisabled = false,
}: EditionButtonsProps): JSX.Element {
return (
<div className="mx_EditWysiwygComposer_buttons">
<AccessibleButton kind="secondary" onClick={onCancelClick}>

View file

@ -16,7 +16,7 @@ limitations under the License.
import React from "react";
import { AboveLeftOf } from "../../../../structures/ContextMenu";
import { MenuProps } from "../../../../structures/ContextMenu";
import { EmojiButton } from "../../EmojiButton";
import dis from "../../../../../dispatcher/dispatcher";
import { ComposerInsertPayload } from "../../../../../dispatcher/payloads/ComposerInsertPayload";
@ -24,10 +24,10 @@ import { Action } from "../../../../../dispatcher/actions";
import { useRoomContext } from "../../../../../contexts/RoomContext";
interface EmojiProps {
menuPosition: AboveLeftOf;
menuPosition: MenuProps;
}
export function Emoji({ menuPosition }: EmojiProps) {
export function Emoji({ menuPosition }: EmojiProps): JSX.Element {
const roomContext = useRoomContext();
return (

View file

@ -38,7 +38,7 @@ interface TooltipProps {
keyCombo?: KeyCombo;
}
function Tooltip({ label, keyCombo }: TooltipProps) {
function Tooltip({ label, keyCombo }: TooltipProps): JSX.Element {
return (
<div className="mx_FormattingButtons_Tooltip">
{label}
@ -55,7 +55,7 @@ interface ButtonProps extends TooltipProps {
onClick: MouseEventHandler<HTMLButtonElement>;
}
function Button({ label, keyCombo, onClick, isActive, icon }: ButtonProps) {
function Button({ label, keyCombo, onClick, isActive, icon }: ButtonProps): JSX.Element {
return (
<AccessibleTooltipButton
element="button"
@ -78,9 +78,8 @@ interface FormattingButtonsProps {
actionStates: AllActionStates;
}
export function FormattingButtons({ composer, actionStates }: FormattingButtonsProps) {
export function FormattingButtons({ composer, actionStates }: FormattingButtonsProps): JSX.Element {
const composerContext = useComposerContext();
return (
<div className="mx_FormattingButtons">
<Button

View file

@ -29,7 +29,7 @@ export function openLinkModal(
composer: FormattingFunctions,
composerContext: ComposerContextState,
isEditing: boolean,
) {
): void {
const modal = Modal.createDialog(
LinkModal,
{
@ -45,7 +45,7 @@ export function openLinkModal(
);
}
function isEmpty(text: string) {
function isEmpty(text: string): boolean {
return text.length < 1;
}
@ -57,7 +57,13 @@ interface LinkModalProps {
isEditing: boolean;
}
export function LinkModal({ composer, isTextEnabled, onClose, composerContext, isEditing }: LinkModalProps) {
export function LinkModal({
composer,
isTextEnabled,
onClose,
composerContext,
isEditing,
}: LinkModalProps): JSX.Element {
const [hasLinkChanged, setHasLinkChanged] = useState(false);
const [fields, setFields] = useState({ text: "", link: isEditing ? composer.getLink() : "" });
const hasText = !isEditing && isTextEnabled;

View file

@ -47,7 +47,7 @@ export function PlainTextComposer({
initialContent,
leftComponent,
rightComponent,
}: PlainTextComposerProps) {
}: PlainTextComposerProps): JSX.Element {
const { ref, onInput, onPaste, onKeyDown, content, setContent } = usePlainTextListeners(
initialContent,
onChange,

View file

@ -18,7 +18,13 @@ import { RefObject, useMemo } from "react";
import { setSelection } from "../utils/selection";
export function useComposerFunctions(ref: RefObject<HTMLDivElement>, setContent: (content: string) => void) {
export function useComposerFunctions(
ref: RefObject<HTMLDivElement>,
setContent: (content: string) => void,
): {
clear(): void;
insertText(text: string): void;
} {
return useMemo(
() => ({
clear: () => {

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ISendEventResponse } from "matrix-js-sdk/src/@types/requests";
import { useCallback, useState } from "react";
import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext";
@ -22,7 +23,15 @@ import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
import { endEditing } from "../utils/editing";
import { editMessage } from "../utils/message";
export function useEditing(editorStateTransfer: EditorStateTransfer, initialContent?: string) {
export function useEditing(
editorStateTransfer: EditorStateTransfer,
initialContent?: string,
): {
isSaveDisabled: boolean;
onChange(content: string): void;
editMessage(): Promise<ISendEventResponse>;
endEditing(): void;
} {
const roomContext = useRoomContext();
const mxClient = useMatrixClientContext();

View file

@ -64,7 +64,7 @@ function parseEditorStateTransfer(
// this.saveStoredEditorState();
}
export function useInitialContent(editorStateTransfer: EditorStateTransfer) {
export function useInitialContent(editorStateTransfer: EditorStateTransfer): string {
const roomContext = useRoomContext();
const mxClient = useMatrixClientContext();

View file

@ -19,7 +19,7 @@ import { useCallback } from "react";
import { useSettingValue } from "../../../../../hooks/useSettings";
export function useInputEventProcessor(onSend: () => void) {
export function useInputEventProcessor(onSend: () => void): (event: WysiwygEvent) => WysiwygEvent | null {
const isCtrlEnter = useSettingValue<boolean>("MessageComposerInput.ctrlEnterToSend");
return useCallback(
(event: WysiwygEvent) => {

View file

@ -16,7 +16,7 @@ limitations under the License.
import { MutableRefObject, useEffect, useState } from "react";
export function useIsExpanded(ref: MutableRefObject<HTMLElement | null>, breakingPoint: number) {
export function useIsExpanded(ref: MutableRefObject<HTMLElement | null>, breakingPoint: number): boolean {
const [isExpanded, setIsExpanded] = useState(false);
useEffect(() => {
if (ref.current) {

View file

@ -16,7 +16,10 @@ limitations under the License.
import { FocusEvent, useCallback, useEffect, useRef, useState } from "react";
export function useIsFocused() {
export function useIsFocused(): {
isFocused: boolean;
onFocus(event: FocusEvent<HTMLElement>): void;
} {
const [isFocused, setIsFocused] = useState(false);
const timeoutIDRef = useRef<number>();

View file

@ -16,7 +16,7 @@ limitations under the License.
import { RefObject, useEffect } from "react";
export function usePlainTextInitialization(initialContent = "", ref: RefObject<HTMLElement>) {
export function usePlainTextInitialization(initialContent = "", ref: RefObject<HTMLElement>): void {
useEffect(() => {
if (ref.current) {
ref.current.innerText = initialContent;

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { KeyboardEvent, SyntheticEvent, useCallback, useRef, useState } from "react";
import { KeyboardEvent, RefObject, SyntheticEvent, useCallback, useRef, useState } from "react";
import { useSettingValue } from "../../../../../hooks/useSettings";
import { IS_MAC, Key } from "../../../../../Keyboard";
@ -26,7 +26,7 @@ function isDivElement(target: EventTarget): target is HTMLDivElement {
// Hitting enter inside the editor inserts an editable div, initially containing a <br />
// For correct display, first replace this pattern with a newline character and then remove divs
// noting that they are used to delimit paragraphs
function amendInnerHtml(text: string) {
function amendInnerHtml(text: string): string {
return text
.replace(/<div><br><\/div>/g, "\n") // this is pressing enter then not typing
.replace(/<div>/g, "\n") // this is from pressing enter, then typing inside the div
@ -37,7 +37,14 @@ export function usePlainTextListeners(
initialContent?: string,
onChange?: (content: string) => void,
onSend?: () => void,
) {
): {
ref: RefObject<HTMLDivElement | null>;
content?: string;
onInput(event: SyntheticEvent<HTMLDivElement, InputEvent | ClipboardEvent>): void;
onPaste(event: SyntheticEvent<HTMLDivElement, InputEvent | ClipboardEvent>): void;
onKeyDown(event: KeyboardEvent<HTMLDivElement>): void;
setContent(text: string): void;
} {
const ref = useRef<HTMLDivElement | null>(null);
const [content, setContent] = useState<string | undefined>(initialContent);
const send = useCallback(() => {

View file

@ -19,7 +19,7 @@ import { useCallback, useEffect } from "react";
import useFocus from "../../../../../hooks/useFocus";
import { useComposerContext, ComposerContextState } from "../ComposerContext";
function setSelectionContext(composerContext: ComposerContextState) {
function setSelectionContext(composerContext: ComposerContextState): void {
const selection = document.getSelection();
if (selection) {
@ -32,12 +32,14 @@ function setSelectionContext(composerContext: ComposerContextState) {
}
}
export function useSelection() {
export function useSelection(): ReturnType<typeof useFocus>[1] & {
onInput(): void;
} {
const composerContext = useComposerContext();
const [isFocused, focusProps] = useFocus();
useEffect(() => {
function onSelectionChange() {
function onSelectionChange(): void {
setSelectionContext(composerContext);
}

View file

@ -18,7 +18,7 @@ import { RefObject, useEffect } from "react";
import { setCursorPositionAtTheEnd } from "./utils";
export function useSetCursorPosition(disabled: boolean, ref: RefObject<HTMLElement>) {
export function useSetCursorPosition(disabled: boolean, ref: RefObject<HTMLElement>): void {
useEffect(() => {
if (ref.current && !disabled) {
setCursorPositionAtTheEnd(ref.current);

View file

@ -31,7 +31,7 @@ export function useWysiwygEditActionHandler(
disabled: boolean,
composerElement: RefObject<HTMLElement>,
composerFunctions: ComposerFunctions,
) {
): void {
const roomContext = useRoomContext();
const composerContext = useComposerContext();
const timeoutId = useRef<number | null>(null);

View file

@ -31,7 +31,7 @@ export function useWysiwygSendActionHandler(
disabled: boolean,
composerElement: MutableRefObject<HTMLElement>,
composerFunctions: ComposerFunctions,
) {
): void {
const roomContext = useRoomContext();
const composerContext = useComposerContext();
const timeoutId = useRef<number | null>(null);

View file

@ -24,7 +24,7 @@ export function focusComposer(
renderingType: TimelineRenderingType,
roomContext: IRoomState,
timeoutId: MutableRefObject<number | null>,
) {
): void {
if (renderingType === roomContext.timelineRenderingType) {
// Immediately set the focus, so if you start typing it
// will appear in the composer
@ -41,7 +41,7 @@ export function focusComposer(
}
}
export function setCursorPositionAtTheEnd(element: HTMLElement) {
export function setCursorPositionAtTheEnd(element: HTMLElement): void {
const range = document.createRange();
range.selectNodeContents(element);
range.collapse(false);

View file

@ -21,7 +21,7 @@ import dis from "../../../../../dispatcher/dispatcher";
import { Action } from "../../../../../dispatcher/actions";
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
export function endEditing(roomContext: IRoomState) {
export function endEditing(roomContext: IRoomState): void {
// todo local storage
// localStorage.removeItem(this.editorRoomKey);
// localStorage.removeItem(this.editorStateKey);
@ -38,7 +38,7 @@ export function endEditing(roomContext: IRoomState) {
});
}
export function cancelPreviousPendingEdit(mxClient: MatrixClient, editorStateTransfer: EditorStateTransfer) {
export function cancelPreviousPendingEdit(mxClient: MatrixClient, editorStateTransfer: EditorStateTransfer): void {
const originalEvent = editorStateTransfer.getEvent();
const previousEdit = originalEvent.replacingEvent();
if (previousEdit && (previousEdit.status === EventStatus.QUEUED || previousEdit.status === EventStatus.NOT_SENT)) {

View file

@ -47,7 +47,7 @@ export async function sendMessage(
message: string,
isHTML: boolean,
{ roomContext, mxClient, ...params }: SendMessageParams,
) {
): Promise<ISendEventResponse> {
const { relation, replyToEvent } = params;
const { room } = roomContext;
const roomId = room?.roomId;
@ -143,7 +143,10 @@ interface EditMessageParams {
editorStateTransfer: EditorStateTransfer;
}
export async function editMessage(html: string, { roomContext, mxClient, editorStateTransfer }: EditMessageParams) {
export async function editMessage(
html: string,
{ roomContext, mxClient, editorStateTransfer }: EditMessageParams,
): Promise<ISendEventResponse> {
const editedEvent = editorStateTransfer.getEvent();
PosthogAnalytics.instance.trackEvent<ComposerEvent>({

View file

@ -16,7 +16,7 @@ limitations under the License.
import { SubSelection } from "../types";
export function setSelection(selection: SubSelection) {
export function setSelection(selection: SubSelection): Promise<void> {
if (selection.anchorNode && selection.focusNode) {
const range = new Range();
range.setStart(selection.anchorNode, selection.anchorOffset);
@ -30,7 +30,7 @@ export function setSelection(selection: SubSelection) {
return new Promise((resolve) => setTimeout(resolve, 0));
}
export function isSelectionEmpty() {
export function isSelectionEmpty(): boolean {
const selection = document.getSelection();
return Boolean(selection?.isCollapsed);
}