Conform more of the codebase to strictNullChecks
(#10573)
* Conform more of the codebase to `strictNullChecks` * Iterate
This commit is contained in:
parent
b4d7f6b592
commit
605ef084ec
34 changed files with 119 additions and 104 deletions
|
@ -28,9 +28,16 @@ limitations under the License.
|
||||||
* consume in the timeline, when performing scroll offset calculations
|
* consume in the timeline, when performing scroll offset calculations
|
||||||
* (e.g. scroll locking)
|
* (e.g. scroll locking)
|
||||||
*/
|
*/
|
||||||
|
export function thumbHeight(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number): number;
|
||||||
export function thumbHeight(
|
export function thumbHeight(
|
||||||
fullWidth: number,
|
fullWidth: number | undefined,
|
||||||
fullHeight: number,
|
fullHeight: number | undefined,
|
||||||
|
thumbWidth: number,
|
||||||
|
thumbHeight: number,
|
||||||
|
): null;
|
||||||
|
export function thumbHeight(
|
||||||
|
fullWidth: number | undefined,
|
||||||
|
fullHeight: number | undefined,
|
||||||
thumbWidth: number,
|
thumbWidth: number,
|
||||||
thumbHeight: number,
|
thumbHeight: number,
|
||||||
): number | null {
|
): number | null {
|
||||||
|
|
|
@ -23,7 +23,7 @@ type DynamicHtmlElementProps<T extends keyof JSX.IntrinsicElements> =
|
||||||
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<Omit<JSX.IntrinsicElements[T], "ref">>;
|
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<Omit<JSX.IntrinsicElements[T], "ref">>;
|
||||||
|
|
||||||
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<DynamicHtmlElementProps<T>, "onScroll"> & {
|
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<DynamicHtmlElementProps<T>, "onScroll"> & {
|
||||||
element?: T;
|
element: T;
|
||||||
className?: string;
|
className?: string;
|
||||||
onScroll?: (event: Event) => void;
|
onScroll?: (event: Event) => void;
|
||||||
onWheel?: (event: WheelEvent) => void;
|
onWheel?: (event: WheelEvent) => void;
|
||||||
|
|
|
@ -19,7 +19,8 @@ import React, { createRef } from "react";
|
||||||
import AutoHideScrollbar, { IProps as AutoHideScrollbarProps } from "./AutoHideScrollbar";
|
import AutoHideScrollbar, { IProps as AutoHideScrollbarProps } from "./AutoHideScrollbar";
|
||||||
import UIStore, { UI_EVENTS } from "../../stores/UIStore";
|
import UIStore, { UI_EVENTS } from "../../stores/UIStore";
|
||||||
|
|
||||||
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<AutoHideScrollbarProps<T>, "onWheel"> & {
|
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<AutoHideScrollbarProps<T>, "onWheel" | "element"> & {
|
||||||
|
element?: T;
|
||||||
// If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator
|
// If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator
|
||||||
// and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning
|
// and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning
|
||||||
// by the parent element.
|
// by the parent element.
|
||||||
|
|
|
@ -90,11 +90,13 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
if (this.listContainerRef.current) {
|
||||||
|
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
||||||
|
// Using the passive option to not block the main thread
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
|
||||||
|
this.listContainerRef.current.addEventListener("scroll", this.onScroll, { passive: true });
|
||||||
|
}
|
||||||
UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
|
UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
|
||||||
// Using the passive option to not block the main thread
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
|
|
||||||
this.listContainerRef.current?.addEventListener("scroll", this.onScroll, { passive: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
|
|
|
@ -177,9 +177,9 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private setState = (): void => {
|
private setState = (): void => {
|
||||||
if (CONNECTING_STATES.includes(this.call?.state)) {
|
if (this.call && CONNECTING_STATES.includes(this.call.state)) {
|
||||||
this.state = CallState.Connecting;
|
this.state = CallState.Connecting;
|
||||||
} else if (SUPPORTED_STATES.includes(this.call?.state)) {
|
} else if (this.call && SUPPORTED_STATES.includes(this.call.state)) {
|
||||||
this.state = this.call.state;
|
this.state = this.call.state;
|
||||||
} else {
|
} else {
|
||||||
if (this.callWasMissed) this.state = CustomCallState.Missed;
|
if (this.callWasMissed) this.state = CustomCallState.Missed;
|
||||||
|
|
|
@ -471,11 +471,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
);
|
);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
private getFallbackHsUrl(): string | null {
|
private getFallbackHsUrl(): string | undefined {
|
||||||
if (this.props.serverConfig?.isDefault) {
|
if (this.props.serverConfig?.isDefault) {
|
||||||
return this.props.config.fallback_hs_url ?? null;
|
return this.props.config.fallback_hs_url;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +575,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
if (payload.event_type === "m.identity_server") {
|
if (payload.event_type === "m.identity_server") {
|
||||||
const fullUrl = payload.event_content ? payload.event_content["base_url"] : null;
|
const fullUrl = payload.event_content ? payload.event_content["base_url"] : null;
|
||||||
if (!fullUrl) {
|
if (!fullUrl) {
|
||||||
MatrixClientPeg.get().setIdentityServerUrl(null);
|
MatrixClientPeg.get().setIdentityServerUrl(undefined);
|
||||||
localStorage.removeItem("mx_is_access_token");
|
localStorage.removeItem("mx_is_access_token");
|
||||||
localStorage.removeItem("mx_is_url");
|
localStorage.removeItem("mx_is_url");
|
||||||
} else {
|
} else {
|
||||||
|
@ -1229,6 +1227,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
* @returns {string} The room ID of the new room, or null if no room was created
|
* @returns {string} The room ID of the new room, or null if no room was created
|
||||||
*/
|
*/
|
||||||
private async startWelcomeUserChat(): Promise<string | null> {
|
private async startWelcomeUserChat(): Promise<string | null> {
|
||||||
|
const snakedConfig = new SnakedObject<IConfigOptions>(this.props.config);
|
||||||
|
const welcomeUserId = snakedConfig.get("welcome_user_id");
|
||||||
|
if (!welcomeUserId) return null;
|
||||||
|
|
||||||
// We can end up with multiple tabs post-registration where the user
|
// We can end up with multiple tabs post-registration where the user
|
||||||
// might then end up with a session and we don't want them all making
|
// might then end up with a session and we don't want them all making
|
||||||
// a chat with the welcome user: try to de-dupe.
|
// a chat with the welcome user: try to de-dupe.
|
||||||
|
@ -1242,8 +1244,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
await waitFor;
|
await waitFor;
|
||||||
|
|
||||||
const snakedConfig = new SnakedObject<IConfigOptions>(this.props.config);
|
const welcomeUserRooms = DMRoomMap.shared().getDMRoomsForUserId(welcomeUserId);
|
||||||
const welcomeUserRooms = DMRoomMap.shared().getDMRoomsForUserId(snakedConfig.get("welcome_user_id"));
|
|
||||||
if (welcomeUserRooms.length === 0) {
|
if (welcomeUserRooms.length === 0) {
|
||||||
const roomId = await createRoom({
|
const roomId = await createRoom({
|
||||||
dmUserId: snakedConfig.get("welcome_user_id"),
|
dmUserId: snakedConfig.get("welcome_user_id"),
|
||||||
|
@ -1260,7 +1261,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
// user room (it doesn't wait for new data from the server, just
|
// user room (it doesn't wait for new data from the server, just
|
||||||
// the saved sync to be loaded).
|
// the saved sync to be loaded).
|
||||||
const saveWelcomeUser = (ev: MatrixEvent): void => {
|
const saveWelcomeUser = (ev: MatrixEvent): void => {
|
||||||
if (ev.getType() === EventType.Direct && ev.getContent()[snakedConfig.get("welcome_user_id")]) {
|
if (ev.getType() === EventType.Direct && ev.getContent()[welcomeUserId]) {
|
||||||
MatrixClientPeg.get().store.save(true);
|
MatrixClientPeg.get().store.save(true);
|
||||||
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, saveWelcomeUser);
|
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, saveWelcomeUser);
|
||||||
}
|
}
|
||||||
|
|
|
@ -869,7 +869,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
const receiptsByEvent: Map<string, IReadReceiptProps[]> = new Map();
|
const receiptsByEvent: Map<string, IReadReceiptProps[]> = new Map();
|
||||||
const receiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
|
const receiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
|
||||||
|
|
||||||
let lastShownEventId: string;
|
let lastShownEventId: string | undefined;
|
||||||
for (const event of this.props.events) {
|
for (const event of this.props.events) {
|
||||||
if (this.shouldShowEvent(event)) {
|
if (this.shouldShowEvent(event)) {
|
||||||
lastShownEventId = event.getId();
|
lastShownEventId = event.getId();
|
||||||
|
@ -1018,11 +1018,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
let ircResizer: JSX.Element | undefined;
|
let ircResizer: JSX.Element | undefined;
|
||||||
if (this.props.layout == Layout.IRC) {
|
if (this.props.layout == Layout.IRC) {
|
||||||
ircResizer = (
|
ircResizer = (
|
||||||
<IRCTimelineProfileResizer
|
<IRCTimelineProfileResizer minWidth={20} maxWidth={600} roomId={this.props.room?.roomId ?? null} />
|
||||||
minWidth={20}
|
|
||||||
maxWidth={600}
|
|
||||||
roomId={this.props.room ? this.props.room.roomId : null}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,7 +1202,7 @@ class CreationGrouper extends BaseGrouper {
|
||||||
let summaryText: string;
|
let summaryText: string;
|
||||||
const roomId = ev.getRoomId();
|
const roomId = ev.getRoomId();
|
||||||
const creator = ev.sender?.name ?? ev.getSender();
|
const creator = ev.sender?.name ?? ev.getSender();
|
||||||
if (DMRoomMap.shared().getUserIdForRoomId(roomId)) {
|
if (roomId && DMRoomMap.shared().getUserIdForRoomId(roomId)) {
|
||||||
summaryText = _t("%(creator)s created this DM.", { creator });
|
summaryText = _t("%(creator)s created this DM.", { creator });
|
||||||
} else {
|
} else {
|
||||||
summaryText = _t("%(creator)s created and configured the room.", { creator });
|
summaryText = _t("%(creator)s created and configured the room.", { creator });
|
||||||
|
|
|
@ -141,7 +141,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
// When the user clicks close on the encryption panel cancel the pending request first if any
|
// When the user clicks close on the encryption panel cancel the pending request first if any
|
||||||
this.state.cardState.verificationRequest.cancel();
|
this.state.cardState.verificationRequest.cancel();
|
||||||
} else {
|
} else {
|
||||||
RightPanelStore.instance.togglePanel(this.props.room?.roomId);
|
RightPanelStore.instance.togglePanel(this.props.room?.roomId ?? null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1597,6 +1597,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private injectSticker(url: string, info: object, text: string, threadId: string | null): void {
|
private injectSticker(url: string, info: object, text: string, threadId: string | null): void {
|
||||||
|
if (!this.context.client) return;
|
||||||
if (this.context.client.isGuest()) {
|
if (this.context.client.isGuest()) {
|
||||||
dis.dispatch({ action: "require_registration" });
|
dis.dispatch({ action: "require_registration" });
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -189,7 +189,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
// Is that next fill request scheduled because of a props update?
|
// Is that next fill request scheduled because of a props update?
|
||||||
private pendingFillDueToPropsUpdate: boolean;
|
private pendingFillDueToPropsUpdate: boolean;
|
||||||
private scrollState: IScrollState;
|
private scrollState: IScrollState;
|
||||||
private preventShrinkingState: IPreventShrinkingState;
|
private preventShrinkingState: IPreventShrinkingState | null;
|
||||||
private unfillDebouncer: number | null;
|
private unfillDebouncer: number | null;
|
||||||
private bottomGrowth: number;
|
private bottomGrowth: number;
|
||||||
private minListHeight: number;
|
private minListHeight: number;
|
||||||
|
@ -676,7 +676,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||||
debuglog("unable to save scroll state: found no children in the viewport");
|
debuglog("unable to save scroll state: found no children in the viewport");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const scrollToken = node!.dataset.scrollTokens.split(",")[0];
|
const scrollToken = node!.dataset.scrollTokens?.split(",")[0];
|
||||||
debuglog("saving anchored scroll state to message", scrollToken);
|
debuglog("saving anchored scroll state to message", scrollToken);
|
||||||
const bottomOffset = this.topFromBottom(node);
|
const bottomOffset = this.topFromBottom(node);
|
||||||
this.scrollState = {
|
this.scrollState = {
|
||||||
|
|
|
@ -50,7 +50,7 @@ const debuglog = (...args: any[]): void => {
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
serverConfig: ValidatedServerConfig;
|
serverConfig: ValidatedServerConfig;
|
||||||
defaultDeviceDisplayName: string;
|
defaultDeviceDisplayName?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
brand?: string;
|
brand?: string;
|
||||||
clientSecret?: string;
|
clientSecret?: string;
|
||||||
|
|
|
@ -53,7 +53,7 @@ const ConfirmSpaceUserActionDialog: React.FC<IProps> = ({
|
||||||
const [roomsToLeave, setRoomsToLeave] = useState<Room[]>([]);
|
const [roomsToLeave, setRoomsToLeave] = useState<Room[]>([]);
|
||||||
const selectedRooms = useMemo(() => new Set(roomsToLeave), [roomsToLeave]);
|
const selectedRooms = useMemo(() => new Set(roomsToLeave), [roomsToLeave]);
|
||||||
|
|
||||||
let warning: JSX.Element;
|
let warning: JSX.Element | undefined;
|
||||||
if (warningMessage) {
|
if (warningMessage) {
|
||||||
warning = <div className="mx_ConfirmSpaceUserActionDialog_warning">{warningMessage}</div>;
|
warning = <div className="mx_ConfirmSpaceUserActionDialog_warning">{warningMessage}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useRef, useState } from "react";
|
import React, { RefObject, useRef, useState } from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
@ -41,9 +41,9 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
|
||||||
|
|
||||||
const [busy, setBusy] = useState<boolean>(false);
|
const [busy, setBusy] = useState<boolean>(false);
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const spaceNameField = useRef<Field>();
|
const spaceNameField = useRef() as RefObject<Field>;
|
||||||
const [alias, setAlias] = useState("");
|
const [alias, setAlias] = useState("");
|
||||||
const spaceAliasField = useRef<RoomAliasField>();
|
const spaceAliasField = useRef() as RefObject<RoomAliasField>;
|
||||||
const [avatar, setAvatar] = useState<File | undefined>();
|
const [avatar, setAvatar] = useState<File | undefined>();
|
||||||
const [topic, setTopic] = useState<string>("");
|
const [topic, setTopic] = useState<string>("");
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -65,7 +66,7 @@ const Entry: React.FC<{
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<StyledCheckbox
|
<StyledCheckbox
|
||||||
onChange={onChange ? (e) => onChange(e.target.checked) : null}
|
onChange={onChange ? (e) => onChange(e.target.checked) : undefined}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
disabled={!onChange}
|
disabled={!onChange}
|
||||||
/>
|
/>
|
||||||
|
@ -80,7 +81,7 @@ const addAllParents = (set: Set<Room>, room: Room): void => {
|
||||||
);
|
);
|
||||||
|
|
||||||
parents.forEach((parent) => {
|
parents.forEach((parent) => {
|
||||||
if (set.has(parent)) return;
|
if (!parent || set.has(parent)) return;
|
||||||
set.add(parent);
|
set.add(parent);
|
||||||
addAllParents(set, parent);
|
addAllParents(set, parent);
|
||||||
});
|
});
|
||||||
|
@ -97,8 +98,8 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
|
||||||
addAllParents(parents, room);
|
addAllParents(parents, room);
|
||||||
return [
|
return [
|
||||||
Array.from(parents),
|
Array.from(parents),
|
||||||
selected
|
filterBoolean(
|
||||||
.map((roomId) => {
|
selected.map((roomId) => {
|
||||||
const room = cli.getRoom(roomId);
|
const room = cli.getRoom(roomId);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return { roomId, name: roomId } as Room;
|
return { roomId, name: roomId } as Room;
|
||||||
|
@ -106,8 +107,8 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
|
||||||
if (room.getMyMembership() !== "join" || !room.isSpaceRoom()) {
|
if (room.getMyMembership() !== "join" || !room.isSpaceRoom()) {
|
||||||
return room;
|
return room;
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
.filter(Boolean),
|
),
|
||||||
];
|
];
|
||||||
}, [cli, selected, room]);
|
}, [cli, selected, room]);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { SyntheticEvent, useRef, useState } from "react";
|
import { RefObject, SyntheticEvent, useRef, useState } from "react";
|
||||||
|
|
||||||
import { _t, _td } from "../../../languageHandler";
|
import { _t, _td } from "../../../languageHandler";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
|
@ -30,7 +30,7 @@ interface IProps {
|
||||||
|
|
||||||
const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
|
const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const fieldRef = useRef<Field>();
|
const fieldRef = useRef() as RefObject<Field>;
|
||||||
|
|
||||||
const onSubmit = async (e: SyntheticEvent): Promise<void> => {
|
const onSubmit = async (e: SyntheticEvent): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -288,8 +288,8 @@ interface IDirectoryOpts {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = null, onFinished }) => {
|
const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = null, onFinished }) => {
|
||||||
const inputRef = useRef<HTMLInputElement>();
|
const inputRef = useRef() as RefObject<HTMLInputElement>;
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>();
|
const scrollContainerRef = useRef() as RefObject<HTMLDivElement>;
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const rovingContext = useContext(RovingTabIndexContext);
|
const rovingContext = useContext(RovingTabIndexContext);
|
||||||
const [query, _setQuery] = useState(initialText);
|
const [query, _setQuery] = useState(initialText);
|
||||||
|
|
|
@ -38,7 +38,7 @@ interface IProps {
|
||||||
// The text to show as the summary of this event list
|
// The text to show as the summary of this event list
|
||||||
"summaryText"?: ReactNode;
|
"summaryText"?: ReactNode;
|
||||||
// An array of EventTiles to render when expanded
|
// An array of EventTiles to render when expanded
|
||||||
"children": ReactNode[];
|
"children": ReactNode[] | null;
|
||||||
// Called when the event list expansion is toggled
|
// Called when the event list expansion is toggled
|
||||||
onToggle?(): void;
|
onToggle?(): void;
|
||||||
// The layout currently used
|
// The layout currently used
|
||||||
|
|
|
@ -45,7 +45,11 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
apps: Partial<{ [id in Container]: IApp[] }>;
|
apps: {
|
||||||
|
[Container.Top]: IApp[];
|
||||||
|
[Container.Center]: IApp[];
|
||||||
|
[Container.Right]?: IApp[];
|
||||||
|
};
|
||||||
resizingVertical: boolean; // true when changing the height of the apps drawer
|
resizingVertical: boolean; // true when changing the height of the apps drawer
|
||||||
resizingHorizontal: boolean; // true when changing the distribution of the width between widgets
|
resizingHorizontal: boolean; // true when changing the distribution of the width between widgets
|
||||||
resizing: boolean;
|
resizing: boolean;
|
||||||
|
@ -203,12 +207,10 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private getApps = (): Partial<{ [id in Container]: IApp[] }> => {
|
private getApps = (): IState["apps"] => ({
|
||||||
const appsDict: Partial<{ [id in Container]: IApp[] }> = {};
|
[Container.Top]: WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Top),
|
||||||
appsDict[Container.Top] = WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Top);
|
[Container.Center]: WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Center),
|
||||||
appsDict[Container.Center] = WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Center);
|
});
|
||||||
return appsDict;
|
|
||||||
};
|
|
||||||
private topApps = (): IApp[] => this.state.apps[Container.Top];
|
private topApps = (): IApp[] => this.state.apps[Container.Top];
|
||||||
private centerApps = (): IApp[] => this.state.apps[Container.Center];
|
private centerApps = (): IApp[] => this.state.apps[Container.Center];
|
||||||
|
|
||||||
|
@ -348,7 +350,7 @@ const PersistentVResizer: React.FC<IPersistentResizerProps> = ({
|
||||||
resizeNotifier.notifyTimelineHeightChanged();
|
resizeNotifier.notifyTimelineHeightChanged();
|
||||||
}}
|
}}
|
||||||
onResizeStop={(e, dir, ref, d) => {
|
onResizeStop={(e, dir, ref, d) => {
|
||||||
let newHeight = defaultHeight + d.height;
|
let newHeight = defaultHeight! + d.height;
|
||||||
newHeight = percentageOf(newHeight, minHeight, maxHeight) * 100;
|
newHeight = percentageOf(newHeight, minHeight, maxHeight) * 100;
|
||||||
|
|
||||||
WidgetLayoutStore.instance.setContainerHeight(room, Container.Top, newHeight);
|
WidgetLayoutStore.instance.setContainerHeight(room, Container.Top, newHeight);
|
||||||
|
|
|
@ -231,6 +231,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateEditorState = (selection: Caret, inputType?: string, diff?: IDiff): void => {
|
private updateEditorState = (selection: Caret, inputType?: string, diff?: IDiff): void => {
|
||||||
|
if (!this.editorRef.current) return;
|
||||||
renderModel(this.editorRef.current, this.props.model);
|
renderModel(this.editorRef.current, this.props.model);
|
||||||
if (selection) {
|
if (selection) {
|
||||||
// set the caret/selection
|
// set the caret/selection
|
||||||
|
@ -358,6 +359,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
|
|
||||||
private onPaste = (event: ClipboardEvent<HTMLDivElement>): boolean | undefined => {
|
private onPaste = (event: ClipboardEvent<HTMLDivElement>): boolean | undefined => {
|
||||||
event.preventDefault(); // we always handle the paste ourselves
|
event.preventDefault(); // we always handle the paste ourselves
|
||||||
|
if (!this.editorRef.current) return;
|
||||||
if (this.props.onPaste?.(event, this.props.model)) {
|
if (this.props.onPaste?.(event, this.props.model)) {
|
||||||
// to prevent double handling, allow props.onPaste to skip internal onPaste
|
// to prevent double handling, allow props.onPaste to skip internal onPaste
|
||||||
return true;
|
return true;
|
||||||
|
@ -377,7 +379,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modifiedFlag = true;
|
this.modifiedFlag = true;
|
||||||
const range = getRangeForSelection(this.editorRef.current, model, document.getSelection());
|
const range = getRangeForSelection(this.editorRef.current, model, document.getSelection()!);
|
||||||
|
|
||||||
// If the user is pasting a link, and has a range selected which is not a link, wrap the range with the link
|
// If the user is pasting a link, and has a range selected which is not a link, wrap the range with the link
|
||||||
if (plainText && range.length > 0 && linkify.test(plainText) && !linkify.test(range.text)) {
|
if (plainText && range.length > 0 && linkify.test(plainText) && !linkify.test(range.text)) {
|
||||||
|
@ -388,18 +390,20 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
|
|
||||||
private onInput = (event: Partial<InputEvent>): void => {
|
private onInput = (event: Partial<InputEvent>): void => {
|
||||||
|
if (!this.editorRef.current) return;
|
||||||
// ignore any input while doing IME compositions
|
// ignore any input while doing IME compositions
|
||||||
if (this.isIMEComposing) {
|
if (this.isIMEComposing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.modifiedFlag = true;
|
this.modifiedFlag = true;
|
||||||
const sel = document.getSelection();
|
const sel = document.getSelection()!;
|
||||||
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, sel);
|
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, sel);
|
||||||
this.props.model.update(text, event.inputType, caret);
|
this.props.model.update(text, event.inputType, caret);
|
||||||
};
|
};
|
||||||
|
|
||||||
private insertText(textToInsert: string, inputType = "insertText"): void {
|
private insertText(textToInsert: string, inputType = "insertText"): void {
|
||||||
const sel = document.getSelection();
|
if (!this.editorRef.current) return;
|
||||||
|
const sel = document.getSelection()!;
|
||||||
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, sel);
|
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, sel);
|
||||||
const newText = text.slice(0, caret.offset) + textToInsert + text.slice(caret.offset);
|
const newText = text.slice(0, caret.offset) + textToInsert + text.slice(caret.offset);
|
||||||
caret.offset += textToInsert.length;
|
caret.offset += textToInsert.length;
|
||||||
|
@ -468,6 +472,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSelectionChange = (): void => {
|
private onSelectionChange = (): void => {
|
||||||
|
if (!this.editorRef.current) return;
|
||||||
const { isEmpty } = this.props.model;
|
const { isEmpty } = this.props.model;
|
||||||
|
|
||||||
this.refreshLastCaretIfNeeded();
|
this.refreshLastCaretIfNeeded();
|
||||||
|
@ -486,6 +491,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
};
|
};
|
||||||
|
|
||||||
private onKeyDown = (event: React.KeyboardEvent): void => {
|
private onKeyDown = (event: React.KeyboardEvent): void => {
|
||||||
|
if (!this.editorRef.current) return;
|
||||||
const model = this.props.model;
|
const model = this.props.model;
|
||||||
let handled = false;
|
let handled = false;
|
||||||
|
|
||||||
|
@ -497,7 +503,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
const selectionRange = getRangeForSelection(
|
const selectionRange = getRangeForSelection(
|
||||||
this.editorRef.current,
|
this.editorRef.current,
|
||||||
this.props.model,
|
this.props.model,
|
||||||
document.getSelection(),
|
document.getSelection()!,
|
||||||
);
|
);
|
||||||
// trim the range as we want it to exclude leading/trailing spaces
|
// trim the range as we want it to exclude leading/trailing spaces
|
||||||
selectionRange.trim();
|
selectionRange.trim();
|
||||||
|
@ -745,11 +751,11 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
public onFormatAction = (action: Formatting): void => {
|
public onFormatAction = (action: Formatting): void => {
|
||||||
if (!this.state.useMarkdown) {
|
if (!this.state.useMarkdown || !this.editorRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const range: Range = getRangeForSelection(this.editorRef.current, this.props.model, document.getSelection());
|
const range: Range = getRangeForSelection(this.editorRef.current, this.props.model, document.getSelection()!);
|
||||||
|
|
||||||
this.historyManager.ensureLastChangesPushed(this.props.model);
|
this.historyManager.ensureLastChangesPushed(this.props.model);
|
||||||
this.modifiedFlag = true;
|
this.modifiedFlag = true;
|
||||||
|
|
|
@ -238,9 +238,11 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
|
|
||||||
private get events(): MatrixEvent[] {
|
private get events(): MatrixEvent[] {
|
||||||
const liveTimelineEvents = this.context.liveTimeline?.getEvents();
|
const liveTimelineEvents = this.context.liveTimeline?.getEvents();
|
||||||
const pendingEvents = this.getRoom()?.getPendingEvents();
|
const room = this.getRoom();
|
||||||
|
if (!liveTimelineEvents || !room) return [];
|
||||||
|
const pendingEvents = room.getPendingEvents();
|
||||||
const isInThread = Boolean(this.props.editState.getEvent().getThread());
|
const isInThread = Boolean(this.props.editState.getEvent().getThread());
|
||||||
return liveTimelineEvents?.concat(isInThread ? [] : pendingEvents) ?? [];
|
return liveTimelineEvents.concat(isInThread ? [] : pendingEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
private cancelEdit = (): void => {
|
private cancelEdit = (): void => {
|
||||||
|
|
|
@ -89,15 +89,9 @@ export default class LinkPreviewWidget extends React.Component<IProps> {
|
||||||
image = mediaFromMxc(image).getThumbnailOfSourceHttp(imageMaxWidth, imageMaxHeight, "scale");
|
image = mediaFromMxc(image).getThumbnailOfSourceHttp(imageMaxWidth, imageMaxHeight, "scale");
|
||||||
}
|
}
|
||||||
|
|
||||||
let thumbHeight = imageMaxHeight;
|
const thumbHeight =
|
||||||
if (p["og:image:width"] && p["og:image:height"]) {
|
ImageUtils.thumbHeight(p["og:image:width"], p["og:image:height"], imageMaxWidth, imageMaxHeight) ??
|
||||||
thumbHeight = ImageUtils.thumbHeight(
|
imageMaxHeight;
|
||||||
p["og:image:width"],
|
|
||||||
p["og:image:height"],
|
|
||||||
imageMaxWidth,
|
|
||||||
imageMaxHeight,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let img: JSX.Element | undefined;
|
let img: JSX.Element | undefined;
|
||||||
if (image) {
|
if (image) {
|
||||||
|
|
|
@ -276,7 +276,8 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
if (createEvent?.getId()) createEventId = createEvent.getId();
|
if (createEvent?.getId()) createEventId = createEvent.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
const viaServers = [this.context.tombstone.getSender().split(":").slice(1).join(":")];
|
const sender = this.context.tombstone?.getSender();
|
||||||
|
const viaServers = sender ? [sender.split(":").slice(1).join(":")] : undefined;
|
||||||
|
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { IEventRelation } from "matrix-js-sdk/src/models/event";
|
import { IEventRelation } from "matrix-js-sdk/src/models/event";
|
||||||
import { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
|
import { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
|
||||||
import React, { createContext, MouseEventHandler, ReactElement, ReactNode, useContext, useRef } from "react";
|
import React, { createContext, MouseEventHandler, ReactElement, ReactNode, RefObject, useContext, useRef } from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||||
|
@ -180,7 +180,7 @@ interface IUploadButtonProps {
|
||||||
const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, relation, children }) => {
|
const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, relation, children }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const roomContext = useContext(RoomContext);
|
const roomContext = useContext(RoomContext);
|
||||||
const uploadInput = useRef<HTMLInputElement>();
|
const uploadInput = useRef() as RefObject<HTMLInputElement>;
|
||||||
|
|
||||||
const onUploadClick = (): void => {
|
const onUploadClick = (): void => {
|
||||||
if (cli?.isGuest()) {
|
if (cli?.isGuest()) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useRef } from "react";
|
import React, { RefObject, useRef } from "react";
|
||||||
|
|
||||||
import { BreadcrumbsStore } from "../../../stores/BreadcrumbsStore";
|
import { BreadcrumbsStore } from "../../../stores/BreadcrumbsStore";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
|
@ -30,7 +30,7 @@ import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
import RoomAvatar from "../avatars/RoomAvatar";
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
|
|
||||||
const RecentlyViewedButton: React.FC = () => {
|
const RecentlyViewedButton: React.FC = () => {
|
||||||
const tooltipRef = useRef<InteractiveTooltip>();
|
const tooltipRef = useRef() as RefObject<InteractiveTooltip>;
|
||||||
const crumbs = useEventEmitterState(BreadcrumbsStore.instance, UPDATE_EVENT, () => BreadcrumbsStore.instance.rooms);
|
const crumbs = useEventEmitterState(BreadcrumbsStore.instance, UPDATE_EVENT, () => BreadcrumbsStore.instance.rooms);
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
|
|
|
@ -33,7 +33,7 @@ function cancelQuoting(context: TimelineRenderingType): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
permalinkCreator: RoomPermalinkCreator;
|
permalinkCreator?: RoomPermalinkCreator;
|
||||||
replyToEvent?: MatrixEvent;
|
replyToEvent?: MatrixEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ interface IProps {
|
||||||
interface IState {
|
interface IState {
|
||||||
defaultIdServer?: string;
|
defaultIdServer?: string;
|
||||||
currentClientIdServer?: string;
|
currentClientIdServer?: string;
|
||||||
idServer?: string;
|
idServer: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
disconnectBusy: boolean;
|
disconnectBusy: boolean;
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import React, { useCallback, useMemo, useState } from "react";
|
import React, { useCallback, useMemo, useState } from "react";
|
||||||
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { RoomState } from "matrix-js-sdk/src/models/room-state";
|
||||||
|
|
||||||
import { _t } from "../../../../../languageHandler";
|
import { _t } from "../../../../../languageHandler";
|
||||||
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
|
||||||
|
@ -33,15 +34,15 @@ interface ElementCallSwitchProps {
|
||||||
|
|
||||||
const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ roomId }) => {
|
const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ roomId }) => {
|
||||||
const room = useMemo(() => MatrixClientPeg.get().getRoom(roomId), [roomId]);
|
const room = useMemo(() => MatrixClientPeg.get().getRoom(roomId), [roomId]);
|
||||||
const isPublic = useMemo(() => room.getJoinRule() === JoinRule.Public, [room]);
|
const isPublic = useMemo(() => room?.getJoinRule() === JoinRule.Public, [room]);
|
||||||
const [content, events, maySend] = useRoomState(
|
const [content, events, maySend] = useRoomState(
|
||||||
room,
|
room ?? undefined,
|
||||||
useCallback((state) => {
|
useCallback((state: RoomState) => {
|
||||||
const content = state?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent();
|
const content = state?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent();
|
||||||
return [
|
return [
|
||||||
content ?? {},
|
content ?? {},
|
||||||
content?.["events"] ?? {},
|
content?.["events"] ?? {},
|
||||||
state?.maySendStateEvent(EventType.RoomPowerLevels, MatrixClientPeg.get().getUserId()),
|
state?.maySendStateEvent(EventType.RoomPowerLevels, MatrixClientPeg.get().getSafeUserId()),
|
||||||
];
|
];
|
||||||
}, []),
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent, useRef, useState } from "react";
|
import React, { ChangeEvent, RefObject, useRef, useState } from "react";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
@ -38,7 +38,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
|
||||||
avatarDisabled = false,
|
avatarDisabled = false,
|
||||||
setAvatar,
|
setAvatar,
|
||||||
}) => {
|
}) => {
|
||||||
const avatarUploadRef = useRef<HTMLInputElement>();
|
const avatarUploadRef = useRef() as RefObject<HTMLInputElement>;
|
||||||
const [avatar, setAvatarDataUrl] = useState(avatarUrl); // avatar data url cache
|
const [avatar, setAvatarDataUrl] = useState(avatarUrl); // avatar data url cache
|
||||||
|
|
||||||
let avatarSection;
|
let avatarSection;
|
||||||
|
|
|
@ -28,7 +28,7 @@ export interface ICallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateCallback = (data: ICallback) => void;
|
export type UpdateCallback = (data: ICallback) => void;
|
||||||
export type GetAutocompleterComponent = () => Autocomplete;
|
export type GetAutocompleterComponent = () => Autocomplete | null;
|
||||||
export type UpdateQuery = (test: string) => Promise<void>;
|
export type UpdateQuery = (test: string) => Promise<void>;
|
||||||
|
|
||||||
export default class AutocompleteWrapperModel {
|
export default class AutocompleteWrapperModel {
|
||||||
|
@ -42,7 +42,7 @@ export default class AutocompleteWrapperModel {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public onEscape(e: KeyboardEvent): void {
|
public onEscape(e: KeyboardEvent): void {
|
||||||
this.getAutocompleterComponent().onEscape(e);
|
this.getAutocompleterComponent()?.onEscape(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public close(): void {
|
public close(): void {
|
||||||
|
@ -50,16 +50,16 @@ export default class AutocompleteWrapperModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasSelection(): boolean {
|
public hasSelection(): boolean {
|
||||||
return this.getAutocompleterComponent().hasSelection();
|
return !!this.getAutocompleterComponent()?.hasSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasCompletions(): boolean {
|
public hasCompletions(): boolean {
|
||||||
const ac = this.getAutocompleterComponent();
|
const ac = this.getAutocompleterComponent();
|
||||||
return ac && ac.countCompletions() > 0;
|
return !!ac && ac.countCompletions() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public confirmCompletion(): void {
|
public confirmCompletion(): void {
|
||||||
this.getAutocompleterComponent().onConfirmCompletion();
|
this.getAutocompleterComponent()?.onConfirmCompletion();
|
||||||
this.updateCallback({ close: true });
|
this.updateCallback({ close: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,18 +68,18 @@ export default class AutocompleteWrapperModel {
|
||||||
*/
|
*/
|
||||||
public async startSelection(): Promise<void> {
|
public async startSelection(): Promise<void> {
|
||||||
const acComponent = this.getAutocompleterComponent();
|
const acComponent = this.getAutocompleterComponent();
|
||||||
if (acComponent.countCompletions() === 0) {
|
if (acComponent && acComponent.countCompletions() === 0) {
|
||||||
// Force completions to show for the text currently entered
|
// Force completions to show for the text currently entered
|
||||||
await acComponent.forceComplete();
|
await acComponent.forceComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectPreviousSelection(): void {
|
public selectPreviousSelection(): void {
|
||||||
this.getAutocompleterComponent().moveSelection(-1);
|
this.getAutocompleterComponent()?.moveSelection(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectNextSelection(): void {
|
public selectNextSelection(): void {
|
||||||
this.getAutocompleterComponent().moveSelection(+1);
|
this.getAutocompleterComponent()?.moveSelection(+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onPartUpdate(part: Part, pos: DocumentPosition): Promise<void> {
|
public onPartUpdate(part: Part, pos: DocumentPosition): Promise<void> {
|
||||||
|
|
|
@ -44,7 +44,7 @@ import { Caret } from "./caret";
|
||||||
* @return the caret position
|
* @return the caret position
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type TransformCallback = (caretPosition: DocumentPosition, inputType: string, diff: IDiff) => number | void;
|
type TransformCallback = (caretPosition: DocumentPosition, inputType: string | undefined, diff: IDiff) => number | void;
|
||||||
type UpdateCallback = (caret?: Caret, inputType?: string, diff?: IDiff) => void;
|
type UpdateCallback = (caret?: Caret, inputType?: string, diff?: IDiff) => void;
|
||||||
type ManualTransformCallback = () => Caret;
|
type ManualTransformCallback = () => Caret;
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ export default class EditorModel {
|
||||||
return this._parts.map((p) => p.serialize());
|
return this._parts.map((p) => p.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
private diff(newValue: string, inputType: string, caret: DocumentOffset): IDiff {
|
private diff(newValue: string, inputType: string | undefined, caret: DocumentOffset): IDiff {
|
||||||
const previousValue = this.parts.reduce((text, p) => text + p.text, "");
|
const previousValue = this.parts.reduce((text, p) => text + p.text, "");
|
||||||
// can't use caret position with drag and drop
|
// can't use caret position with drag and drop
|
||||||
if (inputType === "deleteByDrag") {
|
if (inputType === "deleteByDrag") {
|
||||||
|
@ -196,7 +196,7 @@ export default class EditorModel {
|
||||||
return newTextLength;
|
return newTextLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(newValue: string, inputType: string, caret: DocumentOffset): Promise<void> {
|
public update(newValue: string, inputType: string | undefined, caret: DocumentOffset): Promise<void> {
|
||||||
const diff = this.diff(newValue, inputType, caret);
|
const diff = this.diff(newValue, inputType, caret);
|
||||||
const position = this.positionForOffset(diff.at || 0, caret.atNodeEnd);
|
const position = this.positionForOffset(diff.at || 0, caret.atNodeEnd);
|
||||||
let removedOffsetDecrease = 0;
|
let removedOffsetDecrease = 0;
|
||||||
|
@ -220,7 +220,7 @@ export default class EditorModel {
|
||||||
return acPromise;
|
return acPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTransformAddedLen(newPosition: DocumentPosition, inputType: string, diff: IDiff): number {
|
private getTransformAddedLen(newPosition: DocumentPosition, inputType: string | undefined, diff: IDiff): number {
|
||||||
const result = this.transformCallback?.(newPosition, inputType, diff);
|
const result = this.transformCallback?.(newPosition, inputType, diff);
|
||||||
return Number.isFinite(result) ? (result as number) : 0;
|
return Number.isFinite(result) ? (result as number) : 0;
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ export default class EditorModel {
|
||||||
* @return {Number} how far from position (in characters) the insertion ended.
|
* @return {Number} how far from position (in characters) the insertion ended.
|
||||||
* This can be more than the length of `str` when crossing non-editable parts, which are skipped.
|
* This can be more than the length of `str` when crossing non-editable parts, which are skipped.
|
||||||
*/
|
*/
|
||||||
private addText(pos: IPosition, str: string, inputType: string): number {
|
private addText(pos: IPosition, str: string, inputType: string | undefined): number {
|
||||||
let { index } = pos;
|
let { index } = pos;
|
||||||
const { offset } = pos;
|
const { offset } = pos;
|
||||||
let addLen = str.length;
|
let addLen = str.length;
|
||||||
|
|
|
@ -65,8 +65,8 @@ interface IBasePart {
|
||||||
serialize(): SerializedPart;
|
serialize(): SerializedPart;
|
||||||
remove(offset: number, len: number): string | undefined;
|
remove(offset: number, len: number): string | undefined;
|
||||||
split(offset: number): IBasePart;
|
split(offset: number): IBasePart;
|
||||||
validateAndInsert(offset: number, str: string, inputType: string): boolean;
|
validateAndInsert(offset: number, str: string, inputType: string | undefined): boolean;
|
||||||
appendUntilRejected(str: string, inputType: string): string | undefined;
|
appendUntilRejected(str: string, inputType: string | undefined): string | undefined;
|
||||||
updateDOMNode(node: Node): void;
|
updateDOMNode(node: Node): void;
|
||||||
canUpdateDOMNode(node: Node): boolean;
|
canUpdateDOMNode(node: Node): boolean;
|
||||||
toDOMNode(): Node;
|
toDOMNode(): Node;
|
||||||
|
|
|
@ -28,8 +28,8 @@ const defaultMapper: Mapper<RoomState> = (roomState: RoomState) => roomState;
|
||||||
export const useRoomState = <T extends any = RoomState>(
|
export const useRoomState = <T extends any = RoomState>(
|
||||||
room?: Room,
|
room?: Room,
|
||||||
mapper: Mapper<T> = defaultMapper as Mapper<T>,
|
mapper: Mapper<T> = defaultMapper as Mapper<T>,
|
||||||
): T | undefined => {
|
): T => {
|
||||||
const [value, setValue] = useState<T | undefined>(room ? mapper(room.currentState) : undefined);
|
const [value, setValue] = useState<T>(room ? mapper(room.currentState) : (undefined as T));
|
||||||
|
|
||||||
const update = useCallback(() => {
|
const update = useCallback(() => {
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
@ -40,8 +40,8 @@ export const useRoomState = <T extends any = RoomState>(
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
update();
|
update();
|
||||||
return () => {
|
return () => {
|
||||||
setValue(undefined);
|
setValue(room ? mapper(room.currentState) : (undefined as T));
|
||||||
};
|
};
|
||||||
}, [update]);
|
}, [room, mapper, update]);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,8 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface ScrollState {
|
export interface ScrollState {
|
||||||
focussedEvent: string;
|
focussedEvent?: string;
|
||||||
pixelOffset: number;
|
pixelOffset?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,7 +22,7 @@ import url from "url";
|
||||||
* @param {string} u The url to be abbreviated
|
* @param {string} u The url to be abbreviated
|
||||||
* @returns {string} The abbreviated url
|
* @returns {string} The abbreviated url
|
||||||
*/
|
*/
|
||||||
export function abbreviateUrl(u: string): string {
|
export function abbreviateUrl(u?: string): string {
|
||||||
if (!u) return "";
|
if (!u) return "";
|
||||||
|
|
||||||
const parsedUrl = url.parse(u);
|
const parsedUrl = url.parse(u);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue