mirror of https://github.com/vector-im/riot-web
Enable strictFunctionTypes (#11201)
parent
40de66424d
commit
4207d182cd
|
@ -62,7 +62,7 @@
|
|||
"@babel/runtime": "^7.12.5",
|
||||
"@matrix-org/analytics-events": "^0.5.0",
|
||||
"@matrix-org/matrix-wysiwyg": "^2.3.0",
|
||||
"@matrix-org/react-sdk-module-api": "^0.0.5",
|
||||
"@matrix-org/react-sdk-module-api": "^0.0.6",
|
||||
"@sentry/browser": "^7.0.0",
|
||||
"@sentry/tracing": "^7.0.0",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { JSXElementConstructor } from "react";
|
||||
import { JSXElementConstructor } from "react";
|
||||
|
||||
// Based on https://stackoverflow.com/a/53229857/3532235
|
||||
export type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||
|
@ -22,7 +22,6 @@ export type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U,
|
|||
export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
|
||||
|
||||
export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
|
||||
export type ReactAnyComponent = React.Component | React.ExoticComponent;
|
||||
|
||||
// Utility type for string dot notation for accessing nested object properties
|
||||
// Based on https://stackoverflow.com/a/58436959
|
||||
|
|
|
@ -46,7 +46,7 @@ import RoomListStore from "../../stores/room-list/RoomListStore";
|
|||
import NonUrgentToastContainer from "./NonUrgentToastContainer";
|
||||
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
|
||||
import Modal from "../../Modal";
|
||||
import { ICollapseConfig } from "../../resizer/distributors/collapse";
|
||||
import { CollapseItem, ICollapseConfig } from "../../resizer/distributors/collapse";
|
||||
import { getKeyBindingsManager } from "../../KeyBindingsManager";
|
||||
import { IOpts } from "../../createRoom";
|
||||
import SpacePanel from "../views/spaces/SpacePanel";
|
||||
|
@ -134,7 +134,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
protected layoutWatcherRef?: string;
|
||||
protected compactLayoutWatcherRef?: string;
|
||||
protected backgroundImageWatcherRef?: string;
|
||||
protected resizer?: Resizer;
|
||||
protected resizer?: Resizer<ICollapseConfig, CollapseItem>;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -230,7 +230,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
return this._roomView.current.canResetTimeline();
|
||||
};
|
||||
|
||||
private createResizer(): Resizer {
|
||||
private createResizer(): Resizer<ICollapseConfig, CollapseItem> {
|
||||
let panelSize: number | null;
|
||||
let panelCollapsed: boolean;
|
||||
const collapseConfig: ICollapseConfig = {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { forwardRef, RefObject, useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import React, { forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import { ISearchResults } from "matrix-js-sdk/src/@types/search";
|
||||
import { IThreadBundledRelationship } from "matrix-js-sdk/src/models/event";
|
||||
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||
|
@ -57,10 +57,7 @@ interface Props {
|
|||
// XXX: todo: merge overlapping results somehow?
|
||||
// XXX: why doesn't searching on name work?
|
||||
export const RoomSearchView = forwardRef<ScrollPanel, Props>(
|
||||
(
|
||||
{ term, scope, promise, abortController, resizeNotifier, className, onUpdate }: Props,
|
||||
ref: RefObject<ScrollPanel>,
|
||||
) => {
|
||||
({ term, scope, promise, abortController, resizeNotifier, className, onUpdate }: Props, ref) => {
|
||||
const client = useContext(MatrixClientContext);
|
||||
const roomContext = useContext(RoomContext);
|
||||
const [inProgress, setInProgress] = useState(true);
|
||||
|
@ -69,6 +66,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
|
|||
const aborted = useRef(false);
|
||||
// A map from room ID to permalink creator
|
||||
const permalinkCreators = useRef(new Map<string, RoomPermalinkCreator>()).current;
|
||||
const innerRef = useRef<ScrollPanel | null>();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
@ -214,8 +212,16 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
|
|||
// once dynamic content in the search results load, make the scrollPanel check
|
||||
// the scroll offsets.
|
||||
const onHeightChanged = (): void => {
|
||||
const scrollPanel = ref.current;
|
||||
scrollPanel?.checkScroll();
|
||||
innerRef.current?.checkScroll();
|
||||
};
|
||||
|
||||
const onRef = (e: ScrollPanel | null): void => {
|
||||
if (typeof ref === "function") {
|
||||
ref(e);
|
||||
} else if (!!ref) {
|
||||
ref.current = e;
|
||||
}
|
||||
innerRef.current = e;
|
||||
};
|
||||
|
||||
let lastRoomId: string | undefined;
|
||||
|
@ -317,7 +323,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
|
|||
|
||||
return (
|
||||
<ScrollPanel
|
||||
ref={ref}
|
||||
ref={onRef}
|
||||
className={"mx_RoomView_searchResultsPanel " + className}
|
||||
onFillRequest={onSearchResultsFillRequest}
|
||||
resizeNotifier={resizeNotifier}
|
||||
|
|
|
@ -854,7 +854,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||
return this.divScroll;
|
||||
}
|
||||
|
||||
private collectScroll = (divScroll: HTMLDivElement): void => {
|
||||
private collectScroll = (divScroll: HTMLDivElement | null): void => {
|
||||
this.divScroll = divScroll;
|
||||
};
|
||||
|
||||
|
|
|
@ -91,6 +91,9 @@ interface IAuthEntryProps {
|
|||
onPhaseChange: (phase: number) => void;
|
||||
submitAuthDict: (auth: IAuthDict) => void;
|
||||
requestEmailToken?: () => Promise<void>;
|
||||
fail: (error: Error) => void;
|
||||
clientSecret: string;
|
||||
showContinue: boolean;
|
||||
}
|
||||
|
||||
interface IPasswordAuthEntryState {
|
||||
|
@ -248,7 +251,6 @@ interface ITermsAuthEntryProps extends IAuthEntryProps {
|
|||
stageParams?: {
|
||||
policies?: Policies;
|
||||
};
|
||||
showContinue: boolean;
|
||||
}
|
||||
|
||||
interface LocalisedPolicyWithId extends LocalisedPolicy {
|
||||
|
@ -416,7 +418,7 @@ interface IEmailIdentityAuthEntryProps extends IAuthEntryProps {
|
|||
emailAddress?: string;
|
||||
};
|
||||
stageState?: {
|
||||
emailSid: string;
|
||||
emailSid?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -540,12 +542,10 @@ export class EmailIdentityAuthEntry extends React.Component<
|
|||
}
|
||||
|
||||
interface IMsisdnAuthEntryProps extends IAuthEntryProps {
|
||||
inputs: {
|
||||
phoneCountry: string;
|
||||
phoneNumber: string;
|
||||
inputs?: {
|
||||
phoneCountry?: string;
|
||||
phoneNumber?: string;
|
||||
};
|
||||
clientSecret: string;
|
||||
fail: (error: Error) => void;
|
||||
}
|
||||
|
||||
interface IMsisdnAuthEntryState {
|
||||
|
@ -590,8 +590,8 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
|
|||
private requestMsisdnToken(): Promise<void> {
|
||||
return this.props.matrixClient
|
||||
.requestRegisterMsisdnToken(
|
||||
this.props.inputs.phoneCountry,
|
||||
this.props.inputs.phoneNumber,
|
||||
this.props.inputs?.phoneCountry ?? "",
|
||||
this.props.inputs?.phoneNumber ?? "",
|
||||
this.props.clientSecret,
|
||||
1, // TODO: Multiple send attempts?
|
||||
)
|
||||
|
@ -982,14 +982,11 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
|||
}
|
||||
|
||||
export interface IStageComponentProps extends IAuthEntryProps {
|
||||
clientSecret?: string;
|
||||
stageParams?: Record<string, any>;
|
||||
inputs?: IInputs;
|
||||
stageState?: IStageStatus;
|
||||
showContinue?: boolean;
|
||||
continueText?: string;
|
||||
continueKind?: string;
|
||||
fail?(e: Error): void;
|
||||
setEmailSid?(sid: string): void;
|
||||
onCancel?(): void;
|
||||
requestEmailToken?(): Promise<void>;
|
||||
|
|
|
@ -21,21 +21,24 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||
import ScrollableBaseModal, { IScrollableBaseState } from "./ScrollableBaseModal";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
interface IProps<C extends React.Component = React.Component> {
|
||||
contentFactory: (props: DialogProps, ref: React.Ref<C>) => React.ReactNode;
|
||||
contentProps: DialogProps;
|
||||
interface IProps<P extends DialogProps, C extends DialogContent<P>> {
|
||||
contentFactory: (props: P, ref: React.RefObject<C>) => React.ReactNode;
|
||||
contentProps: P;
|
||||
title: string;
|
||||
onFinished(ok?: boolean, model?: Awaited<ReturnType<DialogContent["trySubmit"]>>): void;
|
||||
onFinished(ok?: boolean, model?: Awaited<ReturnType<DialogContent<P>["trySubmit"]>>): void;
|
||||
}
|
||||
|
||||
interface IState extends IScrollableBaseState {
|
||||
// nothing special
|
||||
}
|
||||
|
||||
export class ModuleUiDialog extends ScrollableBaseModal<IProps, IState> {
|
||||
private contentRef = createRef<DialogContent>();
|
||||
export class ModuleUiDialog<P extends DialogProps, C extends DialogContent<P>> extends ScrollableBaseModal<
|
||||
IProps<P, C>,
|
||||
IState
|
||||
> {
|
||||
private contentRef = createRef<C>();
|
||||
|
||||
public constructor(props: IProps) {
|
||||
public constructor(props: IProps<P, C>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { forwardRef } from "react";
|
||||
import React, { forwardRef, ForwardRefExoticComponent } from "react";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
@ -27,12 +27,10 @@ function getErrorMessage(mxEvent?: MatrixEvent): string {
|
|||
}
|
||||
|
||||
// A placeholder element for messages that could not be decrypted
|
||||
export const DecryptionFailureBody = forwardRef<HTMLDivElement, Partial<IBodyProps>>(
|
||||
({ mxEvent }, ref): JSX.Element => {
|
||||
export const DecryptionFailureBody = forwardRef<HTMLDivElement, IBodyProps>(({ mxEvent }, ref): JSX.Element => {
|
||||
return (
|
||||
<div className="mx_DecryptionFailureBody mx_EventTile_content" ref={ref}>
|
||||
{getErrorMessage(mxEvent)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
}) as ForwardRefExoticComponent<IBodyProps>;
|
||||
|
|
|
@ -39,9 +39,9 @@ export interface IBodyProps {
|
|||
maxImageHeight?: number;
|
||||
replacingEventId?: string;
|
||||
editState?: EditorStateTransfer;
|
||||
onMessageAllowed: () => void; // TODO: Docs
|
||||
onMessageAllowed?: () => void; // TODO: Docs
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
mediaEventHelper: MediaEventHelper;
|
||||
mediaEventHelper?: MediaEventHelper;
|
||||
|
||||
/*
|
||||
If present and `true`, the message has been marked as hidden pending moderation
|
||||
|
|
|
@ -51,7 +51,7 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
|
|||
|
||||
try {
|
||||
try {
|
||||
const blob = await this.props.mediaEventHelper.sourceBlob.value;
|
||||
const blob = await this.props.mediaEventHelper!.sourceBlob.value;
|
||||
buffer = await blob.arrayBuffer();
|
||||
} catch (e) {
|
||||
this.setState({ error: e });
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import React, { ForwardRefExoticComponent, useCallback, useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
Beacon,
|
||||
BeaconEvent,
|
||||
|
@ -234,6 +234,6 @@ const MBeaconBody = React.forwardRef<HTMLDivElement, IBodyProps>(({ mxEvent, get
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}) as ForwardRefExoticComponent<IBodyProps>;
|
||||
|
||||
export default MBeaconBody;
|
||||
|
|
|
@ -168,7 +168,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
|
|||
try {
|
||||
this.userDidClick = true;
|
||||
this.setState({
|
||||
decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value,
|
||||
decryptedBlob: await this.props.mediaEventHelper!.sourceBlob.value,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn("Unable to decrypt attachment: ", err);
|
||||
|
@ -188,7 +188,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
|
|||
// As a button we're missing the `download` attribute for styling reasons, so
|
||||
// download with the file downloader.
|
||||
this.fileDownloader.download({
|
||||
blob: await mediaHelper.sourceBlob.value,
|
||||
blob: await mediaHelper!.sourceBlob.value,
|
||||
name: this.fileName,
|
||||
});
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
|
|||
|
||||
// Start a fetch for the download
|
||||
// Based upon https://stackoverflow.com/a/49500465
|
||||
this.props.mediaEventHelper.sourceBlob.value.then((blob) => {
|
||||
this.props.mediaEventHelper?.sourceBlob.value.then((blob) => {
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
// We have to create an anchor to download the file
|
||||
|
|
|
@ -261,7 +261,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
|
||||
let thumbUrl: string | null;
|
||||
let contentUrl: string | null;
|
||||
if (this.props.mediaEventHelper.media.isEncrypted) {
|
||||
if (this.props.mediaEventHelper?.media.isEncrypted) {
|
||||
try {
|
||||
[contentUrl, thumbUrl] = await Promise.all([
|
||||
this.props.mediaEventHelper.sourceUrl.value,
|
||||
|
@ -311,7 +311,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
}
|
||||
|
||||
try {
|
||||
const blob = await this.props.mediaEventHelper.sourceBlob.value;
|
||||
const blob = await this.props.mediaEventHelper!.sourceBlob.value;
|
||||
if (!(await blobIsAnimated(content.info?.mimetype, blob))) {
|
||||
isAnimated = false;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, useContext } from "react";
|
||||
import React, { useEffect, useState, useContext, ForwardRefExoticComponent } from "react";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { M_TEXT } from "matrix-js-sdk/src/@types/extensible_events";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
@ -109,9 +109,9 @@ export const MPollEndBody = React.forwardRef<any, IBodyProps>(({ mxEvent, ...pro
|
|||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div ref={ref}>
|
||||
<Caption>{_t("Ended a poll")}</Caption>
|
||||
<MPollBody mxEvent={pollStartEvent} {...props} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}) as ForwardRefExoticComponent<IBodyProps>;
|
||||
|
|
|
@ -143,7 +143,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
logger.error("Failed to load blurhash", e);
|
||||
}
|
||||
|
||||
if (this.props.mediaEventHelper.media.isEncrypted && this.state.decryptedUrl === null) {
|
||||
if (this.props.mediaEventHelper?.media.isEncrypted && this.state.decryptedUrl === null) {
|
||||
try {
|
||||
const autoplay = SettingsStore.getValue("autoplayVideo") as boolean;
|
||||
const thumbnailUrl = await this.props.mediaEventHelper.thumbnailUrl.value;
|
||||
|
@ -199,7 +199,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
// To stop subsequent download attempts
|
||||
fetchingData: true,
|
||||
});
|
||||
if (!this.props.mediaEventHelper.media.isEncrypted) {
|
||||
if (!this.props.mediaEventHelper!.media.isEncrypted) {
|
||||
this.setState({
|
||||
error: "No file given in content",
|
||||
});
|
||||
|
@ -207,8 +207,8 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
}
|
||||
this.setState(
|
||||
{
|
||||
decryptedUrl: await this.props.mediaEventHelper.sourceUrl.value,
|
||||
decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value,
|
||||
decryptedUrl: await this.props.mediaEventHelper!.sourceUrl.value,
|
||||
decryptedBlob: await this.props.mediaEventHelper!.sourceBlob.value,
|
||||
fetchingData: false,
|
||||
},
|
||||
() => {
|
||||
|
|
|
@ -27,7 +27,6 @@ import RedactedBody from "./RedactedBody";
|
|||
import UnknownBody from "./UnknownBody";
|
||||
import { IMediaBody } from "./IMediaBody";
|
||||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||
import { ReactAnyComponent } from "../../../@types/common";
|
||||
import { IBodyProps } from "./IBodyProps";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import TextualBody from "./TextualBody";
|
||||
|
@ -70,7 +69,7 @@ const baseBodyTypes = new Map<string, typeof React.Component>([
|
|||
[MsgType.Audio, MVoiceOrAudioBody],
|
||||
[MsgType.Video, MVideoBody],
|
||||
]);
|
||||
const baseEvTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>([
|
||||
const baseEvTypes = new Map<string, React.ComponentType<IBodyProps>>([
|
||||
[EventType.Sticker, MStickerBody],
|
||||
[M_POLL_START.name, MPollBody],
|
||||
[M_POLL_START.altName, MPollBody],
|
||||
|
@ -84,7 +83,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
|||
private body: React.RefObject<React.Component | IOperableEventTile> = createRef();
|
||||
private mediaHelper?: MediaEventHelper;
|
||||
private bodyTypes = new Map<string, typeof React.Component>(baseBodyTypes.entries());
|
||||
private evTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>(baseEvTypes.entries());
|
||||
private evTypes = new Map<string, React.ComponentType<IBodyProps>>(baseEvTypes.entries());
|
||||
|
||||
public static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
@ -123,7 +122,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
|||
this.bodyTypes.set(bodyType, bodyComponent);
|
||||
}
|
||||
|
||||
this.evTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>(baseEvTypes.entries());
|
||||
this.evTypes = new Map<string, React.ComponentType<IBodyProps>>(baseEvTypes.entries());
|
||||
for (const [evType, evComponent] of Object.entries(this.props.overrideEventTypes ?? {})) {
|
||||
this.evTypes.set(evType, evComponent);
|
||||
}
|
||||
|
@ -153,7 +152,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
|||
const content = this.props.mxEvent.getContent();
|
||||
const type = this.props.mxEvent.getType();
|
||||
const msgtype = content.msgtype;
|
||||
let BodyType: React.ComponentType<Partial<IBodyProps>> | ReactAnyComponent = RedactedBody;
|
||||
let BodyType: React.ComponentType<IBodyProps> = RedactedBody;
|
||||
if (!this.props.mxEvent.isRedacted()) {
|
||||
// only resolve BodyType if event is not redacted
|
||||
if (this.props.mxEvent.isDecryptionFailure()) {
|
||||
|
@ -195,7 +194,6 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
|||
}
|
||||
}
|
||||
|
||||
// @ts-ignore - this is a dynamic react component
|
||||
return BodyType ? (
|
||||
<BodyType
|
||||
ref={this.body}
|
||||
|
|
|
@ -15,24 +15,19 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { IBodyProps } from "./IBodyProps";
|
||||
|
||||
interface IProps {
|
||||
mxEvent: MatrixEvent;
|
||||
onMessageAllowed: () => void;
|
||||
}
|
||||
|
||||
export default class MjolnirBody extends React.Component<IProps> {
|
||||
export default class MjolnirBody extends React.Component<IBodyProps> {
|
||||
private onAllowClick = (e: ButtonEvent): void => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const key = `mx_mjolnir_render_${this.props.mxEvent.getRoomId()}__${this.props.mxEvent.getId()}`;
|
||||
localStorage.setItem(key, "true");
|
||||
this.props.onMessageAllowed();
|
||||
this.props.onMessageAllowed?.();
|
||||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
|
|
|
@ -14,20 +14,16 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useContext } from "react";
|
||||
import React, { ForwardRefExoticComponent, useContext } from "react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { formatFullDate } from "../../../DateUtils";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { IBodyProps } from "./IBodyProps";
|
||||
interface IProps {
|
||||
mxEvent: MatrixEvent;
|
||||
}
|
||||
|
||||
const RedactedBody = React.forwardRef<any, IProps | IBodyProps>(({ mxEvent }, ref) => {
|
||||
const RedactedBody = React.forwardRef<any, IBodyProps>(({ mxEvent }, ref) => {
|
||||
const cli: MatrixClient = useContext(MatrixClientContext);
|
||||
let text = _t("Message deleted");
|
||||
const unsigned = mxEvent.getUnsigned();
|
||||
|
@ -49,6 +45,6 @@ const RedactedBody = React.forwardRef<any, IProps | IBodyProps>(({ mxEvent }, re
|
|||
{text}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
}) as ForwardRefExoticComponent<IBodyProps>;
|
||||
|
||||
export default RedactedBody;
|
||||
|
|
|
@ -15,15 +15,11 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { forwardRef } from "react";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import React, { forwardRef, ForwardRefExoticComponent } from "react";
|
||||
|
||||
interface IProps {
|
||||
mxEvent: MatrixEvent;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
import { IBodyProps } from "./IBodyProps";
|
||||
|
||||
export default forwardRef<HTMLDivElement, IProps>(({ mxEvent, children }, ref) => {
|
||||
export default forwardRef<HTMLDivElement, IBodyProps>(({ mxEvent, children }, ref) => {
|
||||
const text = mxEvent.getContent().body;
|
||||
return (
|
||||
<div className="mx_UnknownBody" ref={ref}>
|
||||
|
@ -31,4 +27,4 @@ export default forwardRef<HTMLDivElement, IProps>(({ mxEvent, children }, ref) =
|
|||
{children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}) as ForwardRefExoticComponent<IBodyProps>;
|
||||
|
|
|
@ -28,7 +28,7 @@ import WidgetUtils from "../../../utils/WidgetUtils";
|
|||
import WidgetEchoStore from "../../../stores/WidgetEchoStore";
|
||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
|
||||
import ResizeHandle from "../elements/ResizeHandle";
|
||||
import Resizer from "../../../resizer/resizer";
|
||||
import Resizer, { IConfig } from "../../../resizer/resizer";
|
||||
import PercentageDistributor from "../../../resizer/distributors/percentage";
|
||||
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
||||
import { clamp, percentageOf, percentageWithin } from "../../../utils/numbers";
|
||||
|
@ -58,7 +58,7 @@ interface IState {
|
|||
export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||
private unmounted = false;
|
||||
private resizeContainer?: HTMLDivElement;
|
||||
private resizer: Resizer;
|
||||
private resizer: Resizer<IConfig>;
|
||||
private dispatcherRef?: string;
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
showApps: true,
|
||||
|
@ -104,7 +104,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
private createResizer(): Resizer {
|
||||
private createResizer(): Resizer<IConfig> {
|
||||
// This is the horizontal one, changing the distribution of the width between the app tiles
|
||||
// (ie. a vertical resize handle because, the handle itself is vertical...)
|
||||
const classNames = {
|
||||
|
|
|
@ -243,7 +243,7 @@ const CreateSpaceButton: React.FC<Pick<IInnerSpacePanelProps, "isPanelCollapsed"
|
|||
label={menuDisplayed ? _t("Cancel") : _t("Create a space")}
|
||||
onClick={onNewClick}
|
||||
isNarrow={isPanelCollapsed}
|
||||
ref={handle}
|
||||
innerRef={handle}
|
||||
/>
|
||||
|
||||
{contextMenu}
|
||||
|
|
|
@ -21,7 +21,6 @@ import React, {
|
|||
createRef,
|
||||
InputHTMLAttributes,
|
||||
LegacyRef,
|
||||
forwardRef,
|
||||
RefObject,
|
||||
} from "react";
|
||||
import classNames from "classnames";
|
||||
|
@ -59,13 +58,12 @@ interface IButtonProps extends Omit<ComponentProps<typeof AccessibleTooltipButto
|
|||
notificationState?: NotificationState;
|
||||
isNarrow?: boolean;
|
||||
avatarSize?: number;
|
||||
innerRef?: RefObject<HTMLElement>;
|
||||
ContextMenuComponent?: ComponentType<ComponentProps<typeof SpaceContextMenu>>;
|
||||
onClick?(ev?: ButtonEvent): void;
|
||||
}
|
||||
|
||||
export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
||||
(
|
||||
{
|
||||
export const SpaceButton: React.FC<IButtonProps> = ({
|
||||
space,
|
||||
spaceKey,
|
||||
className,
|
||||
|
@ -76,12 +74,11 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
|||
avatarSize,
|
||||
isNarrow,
|
||||
children,
|
||||
innerRef,
|
||||
ContextMenuComponent,
|
||||
...props
|
||||
},
|
||||
ref: RefObject<HTMLElement>,
|
||||
) => {
|
||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLElement>(ref);
|
||||
}) => {
|
||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLElement>(innerRef);
|
||||
const [onFocus, isActive] = useRovingTabIndex(handle);
|
||||
const tabIndex = isActive ? 0 : -1;
|
||||
|
||||
|
@ -175,8 +172,7 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
|||
</div>
|
||||
</AccessibleTooltipButton>
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
interface IItemProps extends InputHTMLAttributes<HTMLLIElement> {
|
||||
space: Room;
|
||||
|
|
|
@ -105,7 +105,7 @@ const EVENT_TILE_TYPES = new Map<string, Factory>([
|
|||
[M_POLL_END.altName, MessageEventFactory],
|
||||
[EventType.KeyVerificationCancel, KeyVerificationConclFactory],
|
||||
[EventType.KeyVerificationDone, KeyVerificationConclFactory],
|
||||
[EventType.CallInvite, LegacyCallEventFactory], // note that this requires a special factory type
|
||||
[EventType.CallInvite, LegacyCallEventFactory as Factory], // note that this requires a special factory type
|
||||
]);
|
||||
|
||||
const STATE_EVENT_TILE_TYPES = new Map<string, Factory>([
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
import { ModuleApi } from "@matrix-org/react-sdk-module-api/lib/ModuleApi";
|
||||
import { TranslationStringsObject } from "@matrix-org/react-sdk-module-api/lib/types/translations";
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
import { DialogProps } from "@matrix-org/react-sdk-module-api/lib/components/DialogContent";
|
||||
import { DialogContent, DialogProps } from "@matrix-org/react-sdk-module-api/lib/components/DialogContent";
|
||||
import React from "react";
|
||||
import { AccountAuthInfo } from "@matrix-org/react-sdk-module-api/lib/types/AccountAuthInfo";
|
||||
import { PlainSubstitution } from "@matrix-org/react-sdk-module-api/lib/types/translations";
|
||||
|
@ -81,23 +81,22 @@ export class ProxiedModuleApi implements ModuleApi {
|
|||
/**
|
||||
* @override
|
||||
*/
|
||||
public openDialog<
|
||||
M extends object,
|
||||
P extends DialogProps = DialogProps,
|
||||
C extends React.Component = React.Component,
|
||||
>(
|
||||
public openDialog<M extends object, P extends DialogProps, C extends DialogContent<P>>(
|
||||
title: string,
|
||||
body: (props: P, ref: React.RefObject<C>) => React.ReactNode,
|
||||
props?: Omit<P, keyof DialogProps>,
|
||||
): Promise<{ didOkOrSubmit: boolean; model: M }> {
|
||||
return new Promise<{ didOkOrSubmit: boolean; model: M }>((resolve) => {
|
||||
Modal.createDialog(
|
||||
ModuleUiDialog,
|
||||
ModuleUiDialog<P, C>,
|
||||
{
|
||||
title: title,
|
||||
contentFactory: body,
|
||||
contentProps: <DialogProps>{
|
||||
// Typescript isn't very happy understanding that `props` satisfies `Omit<P, keyof DialogProps>`
|
||||
contentProps: {
|
||||
...props,
|
||||
moduleApi: this,
|
||||
},
|
||||
} as unknown as P,
|
||||
},
|
||||
"mx_CompoundDialog",
|
||||
).finished.then(([didOkOrSubmit, model]) => {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import FixedDistributor from "./fixed";
|
||||
import { BaseDistributor } from "./fixed";
|
||||
import ResizeItem from "../item";
|
||||
import Resizer, { IConfig } from "../resizer";
|
||||
import Sizer from "../sizer";
|
||||
|
@ -25,7 +25,7 @@ export interface ICollapseConfig extends IConfig {
|
|||
isItemCollapsed(element: HTMLElement): boolean;
|
||||
}
|
||||
|
||||
class CollapseItem extends ResizeItem<ICollapseConfig> {
|
||||
export class CollapseItem extends ResizeItem<ICollapseConfig> {
|
||||
public notifyCollapsed(collapsed: boolean): void {
|
||||
this.resizer.config?.onCollapsed?.(collapsed, this.id, this.domNode);
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ class CollapseItem extends ResizeItem<ICollapseConfig> {
|
|||
}
|
||||
}
|
||||
|
||||
export default class CollapseDistributor extends FixedDistributor<ICollapseConfig, CollapseItem> {
|
||||
export default class CollapseDistributor extends BaseDistributor<ICollapseConfig, CollapseItem> {
|
||||
public static createItem(
|
||||
resizeHandle: HTMLDivElement,
|
||||
resizer: Resizer<ICollapseConfig>,
|
||||
resizer: Resizer<ICollapseConfig, CollapseItem>,
|
||||
sizer: Sizer,
|
||||
container?: HTMLElement,
|
||||
): CollapseItem {
|
||||
|
|
|
@ -18,21 +18,7 @@ import ResizeItem from "../item";
|
|||
import Sizer from "../sizer";
|
||||
import Resizer, { IConfig } from "../resizer";
|
||||
|
||||
/**
|
||||
distributors translate a moving cursor into
|
||||
CSS/DOM changes by calling the sizer
|
||||
|
||||
they have two methods:
|
||||
`resize` receives then new item size
|
||||
`resizeFromContainerOffset` receives resize handle location
|
||||
within the container bounding box. For internal use.
|
||||
This method usually ends up calling `resize` once the start offset is subtracted.
|
||||
*/
|
||||
export default class FixedDistributor<C extends IConfig, I extends ResizeItem<any> = ResizeItem<C>> {
|
||||
public static createItem(resizeHandle: HTMLDivElement, resizer: Resizer, sizer: Sizer): ResizeItem {
|
||||
return new ResizeItem(resizeHandle, resizer, sizer);
|
||||
}
|
||||
|
||||
export abstract class BaseDistributor<C extends IConfig, I extends ResizeItem<C> = ResizeItem<C>> {
|
||||
public static createSizer(containerElement: HTMLElement, vertical: boolean, reverse: boolean): Sizer {
|
||||
return new Sizer(containerElement, vertical, reverse);
|
||||
}
|
||||
|
@ -67,3 +53,22 @@ export default class FixedDistributor<C extends IConfig, I extends ResizeItem<an
|
|||
this.item.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
distributors translate a moving cursor into
|
||||
CSS/DOM changes by calling the sizer
|
||||
|
||||
they have two methods:
|
||||
`resize` receives then new item size
|
||||
`resizeFromContainerOffset` receives resize handle location
|
||||
within the container bounding box. For internal use.
|
||||
This method usually ends up calling `resize` once the start offset is subtracted.
|
||||
*/
|
||||
export default class FixedDistributor<
|
||||
C extends IConfig,
|
||||
I extends ResizeItem<C> = ResizeItem<C>,
|
||||
> extends BaseDistributor<C, I> {
|
||||
public static createItem(resizeHandle: HTMLDivElement, resizer: Resizer<any>, sizer: Sizer): ResizeItem<any> {
|
||||
return new ResizeItem(resizeHandle, resizer, sizer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,14 @@ limitations under the License.
|
|||
import Resizer, { IConfig } from "./resizer";
|
||||
import Sizer from "./sizer";
|
||||
|
||||
export default class ResizeItem<C extends IConfig = IConfig> {
|
||||
export default class ResizeItem<C extends IConfig> {
|
||||
public readonly domNode: HTMLElement;
|
||||
protected readonly id: string | null;
|
||||
protected reverse: boolean;
|
||||
|
||||
public constructor(
|
||||
handle: HTMLElement,
|
||||
public readonly resizer: Resizer<C>,
|
||||
public readonly resizer: Resizer<C, any>,
|
||||
public readonly sizer: Sizer,
|
||||
public readonly container?: HTMLElement,
|
||||
) {
|
||||
|
@ -37,12 +37,17 @@ export default class ResizeItem<C extends IConfig = IConfig> {
|
|||
this.id = handle.getAttribute("data-id");
|
||||
}
|
||||
|
||||
private copyWith(handle: HTMLElement, resizer: Resizer, sizer: Sizer, container?: HTMLElement): ResizeItem {
|
||||
private copyWith(
|
||||
handle: HTMLElement,
|
||||
resizer: Resizer<C, any>,
|
||||
sizer: Sizer,
|
||||
container?: HTMLElement,
|
||||
): ResizeItem<C> {
|
||||
const Ctor = this.constructor as typeof ResizeItem;
|
||||
return new Ctor(handle, resizer, sizer, container);
|
||||
}
|
||||
|
||||
private advance(forwards: boolean): ResizeItem | undefined {
|
||||
private advance(forwards: boolean): ResizeItem<C> | undefined {
|
||||
// opposite direction from fromResizeHandle to get back to handle
|
||||
let handle: Element | null | undefined = this.reverse
|
||||
? this.domNode.previousElementSibling
|
||||
|
@ -64,11 +69,11 @@ export default class ResizeItem<C extends IConfig = IConfig> {
|
|||
}
|
||||
}
|
||||
|
||||
public next(): ResizeItem | undefined {
|
||||
public next(): ResizeItem<C> | undefined {
|
||||
return this.advance(true);
|
||||
}
|
||||
|
||||
public previous(): ResizeItem | undefined {
|
||||
public previous(): ResizeItem<C> | undefined {
|
||||
return this.advance(false);
|
||||
}
|
||||
|
||||
|
@ -106,7 +111,7 @@ export default class ResizeItem<C extends IConfig = IConfig> {
|
|||
this.resizer.config?.onResized?.(null, this.id, this.domNode);
|
||||
}
|
||||
|
||||
public first(): ResizeItem | undefined {
|
||||
public first(): ResizeItem<C> | undefined {
|
||||
if (!this.domNode.parentElement?.children) {
|
||||
return;
|
||||
}
|
||||
|
@ -118,7 +123,7 @@ export default class ResizeItem<C extends IConfig = IConfig> {
|
|||
}
|
||||
}
|
||||
|
||||
public last(): ResizeItem | undefined {
|
||||
public last(): ResizeItem<C> | undefined {
|
||||
if (!this.domNode.parentElement?.children) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export interface IConfig {
|
|||
handler?: HTMLDivElement;
|
||||
}
|
||||
|
||||
export default class Resizer<C extends IConfig = IConfig> {
|
||||
export default class Resizer<C extends IConfig, I extends ResizeItem<C> = ResizeItem<C>> {
|
||||
private classNames: IClassNames;
|
||||
|
||||
// TODO move vertical/horizontal to config option/container class
|
||||
|
@ -46,13 +46,8 @@ export default class Resizer<C extends IConfig = IConfig> {
|
|||
public constructor(
|
||||
public container: HTMLElement | null,
|
||||
private readonly distributorCtor: {
|
||||
new (item: ResizeItem): FixedDistributor<C, any>;
|
||||
createItem(
|
||||
resizeHandle: HTMLDivElement,
|
||||
resizer: Resizer,
|
||||
sizer: Sizer,
|
||||
container?: HTMLElement,
|
||||
): ResizeItem;
|
||||
new (item: I): FixedDistributor<C, I>;
|
||||
createItem(resizeHandle: HTMLDivElement, resizer: Resizer<C, I>, sizer: Sizer, container?: HTMLElement): I;
|
||||
createSizer(containerElement: HTMLElement | null, vertical: boolean, reverse: boolean): Sizer;
|
||||
},
|
||||
public readonly config?: C,
|
||||
|
@ -87,7 +82,7 @@ export default class Resizer<C extends IConfig = IConfig> {
|
|||
@param {number} handleIndex the index of the resize handle in the container
|
||||
@return {FixedDistributor} a new distributor for the given handle
|
||||
*/
|
||||
public forHandleAt(handleIndex: number): FixedDistributor<C> | undefined {
|
||||
public forHandleAt(handleIndex: number): FixedDistributor<C, I> | undefined {
|
||||
const handles = this.getResizeHandles();
|
||||
const handle = handles[handleIndex];
|
||||
if (handle) {
|
||||
|
@ -96,7 +91,7 @@ export default class Resizer<C extends IConfig = IConfig> {
|
|||
}
|
||||
}
|
||||
|
||||
public forHandleWithId(id: string): FixedDistributor<C> | undefined {
|
||||
public forHandleWithId(id: string): FixedDistributor<C, I> | undefined {
|
||||
const handles = this.getResizeHandles();
|
||||
const handle = handles.find((h) => h.getAttribute("data-id") === id);
|
||||
if (handle) {
|
||||
|
@ -178,7 +173,7 @@ export default class Resizer<C extends IConfig = IConfig> {
|
|||
{ trailing: true, leading: true },
|
||||
);
|
||||
|
||||
public getDistributors = (): FixedDistributor<any, ResizeItem<any>>[] => {
|
||||
public getDistributors = (): FixedDistributor<C, I>[] => {
|
||||
return this.getResizeHandles().map((handle) => {
|
||||
const { distributor } = this.createSizerAndDistributor(<HTMLDivElement>handle);
|
||||
return distributor;
|
||||
|
@ -187,7 +182,7 @@ export default class Resizer<C extends IConfig = IConfig> {
|
|||
|
||||
private createSizerAndDistributor(resizeHandle: HTMLDivElement): {
|
||||
sizer: Sizer;
|
||||
distributor: FixedDistributor<any>;
|
||||
distributor: FixedDistributor<C, I>;
|
||||
} {
|
||||
const vertical = resizeHandle.classList.contains(this.classNames.vertical!);
|
||||
const reverse = this.isReverseResizeHandle(resizeHandle);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"jsx": "react",
|
||||
"lib": ["es2020", "dom", "dom.iterable"],
|
||||
"strict": true,
|
||||
"strictFunctionTypes": false,
|
||||
"useUnknownInCatchVariables": false
|
||||
},
|
||||
"include": [
|
||||
|
|
|
@ -1619,10 +1619,10 @@
|
|||
version "3.2.14"
|
||||
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984"
|
||||
|
||||
"@matrix-org/react-sdk-module-api@^0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-0.0.5.tgz#78bd80f42b918394978965ef3e08496e97948c7a"
|
||||
integrity sha512-QhH1T1E6Q6csCUitQzm32SRnX49Ox73TF5BZ4p5TOGFpPD3QuYc5/dDC1Yh3xUljgqOS2C6H24qaskw6olCtfQ==
|
||||
"@matrix-org/react-sdk-module-api@^0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-0.0.6.tgz#941872ed081acdca9d247ccd6e146265aa24010b"
|
||||
integrity sha512-FydbJYSMecpDIGk4fVQ9djjckQdbJPV9bH3px78TQ+MX/WHmzPmjEpMPTeP3uDSeg0EWmfoIFdNypJglMqAHpw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.9"
|
||||
|
||||
|
|
Loading…
Reference in New Issue