Conform more code to strictNullChecks
(#10368
* Conform more code to `strictNullChecks` * Iterate
This commit is contained in:
parent
05e3fb09d6
commit
8cb8cd4eb1
24 changed files with 176 additions and 157 deletions
|
@ -32,8 +32,19 @@ export type ComponentType = React.ComponentType<{
|
||||||
onFinished?(...args: any): void;
|
onFinished?(...args: any): void;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
type Defaultize<P, D> = P extends any
|
||||||
|
? string extends keyof P
|
||||||
|
? P
|
||||||
|
: Pick<P, Exclude<keyof P, keyof D>> &
|
||||||
|
Partial<Pick<P, Extract<keyof P, keyof D>>> &
|
||||||
|
Partial<Pick<D, Exclude<keyof D, keyof P>>>
|
||||||
|
: never;
|
||||||
|
|
||||||
// Generic type which returns the props of the Modal component with the onFinished being optional.
|
// Generic type which returns the props of the Modal component with the onFinished being optional.
|
||||||
export type ComponentProps<C extends ComponentType> = Omit<React.ComponentProps<C>, "onFinished"> &
|
export type ComponentProps<C extends ComponentType> = Defaultize<
|
||||||
|
Omit<React.ComponentProps<C>, "onFinished">,
|
||||||
|
C["defaultProps"]
|
||||||
|
> &
|
||||||
Partial<Pick<React.ComponentProps<C>, "onFinished">>;
|
Partial<Pick<React.ComponentProps<C>, "onFinished">>;
|
||||||
|
|
||||||
export interface IModal<C extends ComponentType> {
|
export interface IModal<C extends ComponentType> {
|
||||||
|
|
|
@ -1020,7 +1020,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPageUnload = (event: BeforeUnloadEvent): string => {
|
private onPageUnload = (event: BeforeUnloadEvent): string | undefined => {
|
||||||
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
||||||
return (event.returnValue = _t("You seem to be uploading files, are you sure you want to quit?"));
|
return (event.returnValue = _t("You seem to be uploading files, are you sure you want to quit?"));
|
||||||
} else if (this.getCallForRoom() && this.state.callState !== "ended") {
|
} else if (this.getCallForRoom() && this.state.callState !== "ended") {
|
||||||
|
@ -1034,7 +1034,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
const action = getKeyBindingsManager().getRoomAction(ev);
|
const action = getKeyBindingsManager().getRoomAction(ev);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case KeyBindingAction.DismissReadMarker:
|
case KeyBindingAction.DismissReadMarker:
|
||||||
this.messagePanel.forgetReadMarker();
|
this.messagePanel?.forgetReadMarker();
|
||||||
this.jumpToLiveTimeline();
|
this.jumpToLiveTimeline();
|
||||||
handled = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1067,7 +1067,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
if (!roomId) return;
|
if (!roomId) return;
|
||||||
const call = this.getCallForRoom();
|
const call = this.getCallForRoom();
|
||||||
this.setState({ callState: call ? call.state : null });
|
this.setState({ callState: call?.state });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = async (payload: ActionPayload): Promise<void> => {
|
private onAction = async (payload: ActionPayload): Promise<void> => {
|
||||||
|
@ -1087,7 +1087,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||||
[payload.file],
|
[payload.file],
|
||||||
this.state.room.roomId,
|
this.state.room.roomId,
|
||||||
null,
|
undefined,
|
||||||
this.context.client,
|
this.context.client,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -1117,7 +1117,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
if (!this.state.matrixClientIsReady) {
|
if (!this.state.matrixClientIsReady) {
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
matrixClientIsReady: this.context.client?.isInitialSyncComplete(),
|
matrixClientIsReady: !!this.context.client?.isInitialSyncComplete(),
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
// send another "initial" RVS update to trigger peeking if needed
|
// send another "initial" RVS update to trigger peeking if needed
|
||||||
|
@ -1137,7 +1137,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
case Action.EditEvent: {
|
case Action.EditEvent: {
|
||||||
// Quit early if we're trying to edit events in wrong rendering context
|
// Quit early if we're trying to edit events in wrong rendering context
|
||||||
if (payload.timelineRenderingType !== this.state.timelineRenderingType) return;
|
if (payload.timelineRenderingType !== this.state.timelineRenderingType) return;
|
||||||
const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
|
const editState = payload.event ? new EditorStateTransfer(payload.event) : undefined;
|
||||||
this.setState({ editState }, () => {
|
this.setState({ editState }, () => {
|
||||||
if (payload.event) {
|
if (payload.event) {
|
||||||
this.messagePanel?.scrollToEventIfNeeded(payload.event.getId());
|
this.messagePanel?.scrollToEventIfNeeded(payload.event.getId());
|
||||||
|
@ -1194,7 +1194,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
private onRoomTimeline = (
|
private onRoomTimeline = (
|
||||||
ev: MatrixEvent,
|
ev: MatrixEvent,
|
||||||
room: Room | null,
|
room: Room | undefined,
|
||||||
toStartOfTimeline: boolean,
|
toStartOfTimeline: boolean,
|
||||||
removed: boolean,
|
removed: boolean,
|
||||||
data?: IRoomTimelineData,
|
data?: IRoomTimelineData,
|
||||||
|
@ -1228,7 +1228,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.handleEffects(ev);
|
this.handleEffects(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.getSender() !== this.context.client.credentials.userId) {
|
if (ev.getSender() !== this.context.client.getSafeUserId()) {
|
||||||
// update unread count when scrolled up
|
// update unread count when scrolled up
|
||||||
if (!this.state.search && this.state.atEndOfLiveTimeline) {
|
if (!this.state.search && this.state.atEndOfLiveTimeline) {
|
||||||
// no change
|
// no change
|
||||||
|
@ -1325,7 +1325,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private getRoomTombstone(room = this.state.room): MatrixEvent | undefined {
|
private getRoomTombstone(room = this.state.room): MatrixEvent | undefined {
|
||||||
return room?.currentState.getStateEvents(EventType.RoomTombstone, "");
|
return room?.currentState.getStateEvents(EventType.RoomTombstone, "") ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
||||||
|
@ -1336,7 +1336,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
private async loadMembersIfJoined(room: Room): Promise<void> {
|
private async loadMembersIfJoined(room: Room): Promise<void> {
|
||||||
// lazy load members if enabled
|
// lazy load members if enabled
|
||||||
if (this.context.client.hasLazyLoadMembersEnabled()) {
|
if (this.context.client?.hasLazyLoadMembersEnabled()) {
|
||||||
if (room && room.getMyMembership() === "join") {
|
if (room && room.getMyMembership() === "join") {
|
||||||
try {
|
try {
|
||||||
await room.loadMembersIfNeeded();
|
await room.loadMembersIfNeeded();
|
||||||
|
@ -1415,7 +1415,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private async updateE2EStatus(room: Room): Promise<void> {
|
private async updateE2EStatus(room: Room): Promise<void> {
|
||||||
if (!this.context.client.isRoomEncrypted(room.roomId)) return;
|
if (!this.context.client?.isRoomEncrypted(room.roomId)) return;
|
||||||
|
|
||||||
// If crypto is not currently enabled, we aren't tracking devices at all,
|
// If crypto is not currently enabled, we aren't tracking devices at all,
|
||||||
// so we don't know what the answer is. Let's error on the safe side and show
|
// so we don't know what the answer is. Let's error on the safe side and show
|
||||||
|
@ -2093,7 +2093,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
// We have successfully loaded this room, and are not previewing.
|
// We have successfully loaded this room, and are not previewing.
|
||||||
// Display the "normal" room view.
|
// Display the "normal" room view.
|
||||||
|
|
||||||
let activeCall = null;
|
let activeCall: MatrixCall | null = null;
|
||||||
{
|
{
|
||||||
// New block because this variable doesn't need to hang around for the rest of the function
|
// New block because this variable doesn't need to hang around for the rest of the function
|
||||||
const call = this.getCallForRoom();
|
const call = this.getCallForRoom();
|
||||||
|
@ -2102,7 +2102,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let statusBar;
|
let statusBar: JSX.Element | undefined;
|
||||||
let isStatusAreaExpanded = true;
|
let isStatusAreaExpanded = true;
|
||||||
|
|
||||||
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
||||||
|
@ -2301,7 +2301,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let topUnreadMessagesBar = null;
|
let topUnreadMessagesBar: JSX.Element | undefined;
|
||||||
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
||||||
if (this.state.showTopUnreadMessagesBar && !this.state.search) {
|
if (this.state.showTopUnreadMessagesBar && !this.state.search) {
|
||||||
topUnreadMessagesBar = (
|
topUnreadMessagesBar = (
|
||||||
|
@ -2342,7 +2342,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
const showChatEffects = SettingsStore.getValue("showChatEffects");
|
const showChatEffects = SettingsStore.getValue("showChatEffects");
|
||||||
|
|
||||||
let mainSplitBody: React.ReactFragment;
|
let mainSplitBody: JSX.Element | undefined;
|
||||||
let mainSplitContentClassName: string;
|
let mainSplitContentClassName: string;
|
||||||
// Decide what to show in the main split
|
// Decide what to show in the main split
|
||||||
switch (this.state.mainSplitContentType) {
|
switch (this.state.mainSplitContentType) {
|
||||||
|
@ -2396,10 +2396,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
const mainSplitContentClasses = classNames("mx_RoomView_body", mainSplitContentClassName);
|
const mainSplitContentClasses = classNames("mx_RoomView_body", mainSplitContentClassName);
|
||||||
|
|
||||||
let excludedRightPanelPhaseButtons = [RightPanelPhases.Timeline];
|
let excludedRightPanelPhaseButtons = [RightPanelPhases.Timeline];
|
||||||
let onAppsClick = this.onAppsClick;
|
let onAppsClick: (() => void) | null = this.onAppsClick;
|
||||||
let onForgetClick = this.onForgetClick;
|
let onForgetClick: (() => void) | null = this.onForgetClick;
|
||||||
let onSearchClick = this.onSearchClick;
|
let onSearchClick: (() => void) | null = this.onSearchClick;
|
||||||
let onInviteClick = null;
|
let onInviteClick: (() => void) | null = null;
|
||||||
let viewingCall = false;
|
let viewingCall = false;
|
||||||
|
|
||||||
// Simplify the header for other main split types
|
// Simplify the header for other main split types
|
||||||
|
|
|
@ -100,9 +100,9 @@ const Tile: React.FC<ITileProps> = ({
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const [joinedRoom, setJoinedRoom] = useState<Room>(() => {
|
const [joinedRoom, setJoinedRoom] = useState<Room | undefined>(() => {
|
||||||
const cliRoom = cli.getRoom(room.room_id);
|
const cliRoom = cli.getRoom(room.room_id);
|
||||||
return cliRoom?.getMyMembership() === "join" ? cliRoom : null;
|
return cliRoom?.getMyMembership() === "join" ? cliRoom : undefined;
|
||||||
});
|
});
|
||||||
const joinedRoomName = useTypedEventEmitterState(joinedRoom, RoomEvent.Name, (room) => room?.name);
|
const joinedRoomName = useTypedEventEmitterState(joinedRoom, RoomEvent.Name, (room) => room?.name);
|
||||||
const name =
|
const name =
|
||||||
|
@ -264,9 +264,9 @@ const Tile: React.FC<ITileProps> = ({
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
let childToggle: JSX.Element;
|
let childToggle: JSX.Element | undefined;
|
||||||
let childSection: JSX.Element;
|
let childSection: JSX.Element | undefined;
|
||||||
let onKeyDown: KeyboardEventHandler;
|
let onKeyDown: KeyboardEventHandler | undefined;
|
||||||
if (children) {
|
if (children) {
|
||||||
// the chevron is purposefully a div rather than a button as it should be ignored for a11y
|
// the chevron is purposefully a div rather than a button as it should be ignored for a11y
|
||||||
childToggle = (
|
childToggle = (
|
||||||
|
@ -386,7 +386,7 @@ export const showRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: st
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const joinRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): Promise<unknown> => {
|
export const joinRoom = async (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): Promise<unknown> => {
|
||||||
// Don't let the user view a room they won't be able to either peek or join:
|
// Don't let the user view a room they won't be able to either peek or join:
|
||||||
// fail earlier so they don't have to click back to the directory.
|
// fail earlier so they don't have to click back to the directory.
|
||||||
if (cli.isGuest()) {
|
if (cli.isGuest()) {
|
||||||
|
@ -394,24 +394,20 @@ export const joinRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: st
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prom = cli.joinRoom(roomId, {
|
try {
|
||||||
|
await cli.joinRoom(roomId, {
|
||||||
viaServers: Array.from(hierarchy.viaMap.get(roomId) || []),
|
viaServers: Array.from(hierarchy.viaMap.get(roomId) || []),
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
SdkContextClass.instance.roomViewStore.showJoinRoomError(err, roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
prom.then(
|
|
||||||
() => {
|
|
||||||
defaultDispatcher.dispatch<JoinRoomReadyPayload>({
|
defaultDispatcher.dispatch<JoinRoomReadyPayload>({
|
||||||
action: Action.JoinRoomReady,
|
action: Action.JoinRoomReady,
|
||||||
roomId,
|
roomId,
|
||||||
metricsTrigger: "SpaceHierarchy",
|
metricsTrigger: "SpaceHierarchy",
|
||||||
});
|
});
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
SdkContextClass.instance.roomViewStore.showJoinRoomError(err, roomId);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return prom;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IHierarchyLevelProps {
|
interface IHierarchyLevelProps {
|
||||||
|
@ -433,7 +429,7 @@ export const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom, hierarchy:
|
||||||
);
|
);
|
||||||
|
|
||||||
// Pick latest room that is actually part of the hierarchy
|
// Pick latest room that is actually part of the hierarchy
|
||||||
let cliRoom = null;
|
let cliRoom: Room | null = null;
|
||||||
for (let idx = history.length - 1; idx >= 0; --idx) {
|
for (let idx = history.length - 1; idx >= 0; --idx) {
|
||||||
if (hierarchy.roomMap.get(history[idx].roomId)) {
|
if (hierarchy.roomMap.get(history[idx].roomId)) {
|
||||||
cliRoom = history[idx];
|
cliRoom = history[idx];
|
||||||
|
@ -448,7 +444,7 @@ export const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom, hierarchy:
|
||||||
room_type: cliRoom.getType(),
|
room_type: cliRoom.getType(),
|
||||||
name: cliRoom.name,
|
name: cliRoom.name,
|
||||||
topic: cliRoom.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent().topic,
|
topic: cliRoom.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent().topic,
|
||||||
avatar_url: cliRoom.getMxcAvatarUrl(),
|
avatar_url: cliRoom.getMxcAvatarUrl() ?? undefined,
|
||||||
canonical_alias: cliRoom.getCanonicalAlias() ?? undefined,
|
canonical_alias: cliRoom.getCanonicalAlias() ?? undefined,
|
||||||
aliases: cliRoom.getAltAliases(),
|
aliases: cliRoom.getAltAliases(),
|
||||||
world_readable:
|
world_readable:
|
||||||
|
@ -476,7 +472,7 @@ export const HierarchyLevel: React.FC<IHierarchyLevelProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const space = cli.getRoom(root.room_id);
|
const space = cli.getRoom(root.room_id);
|
||||||
const hasPermissions = space?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
|
const hasPermissions = space?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getSafeUserId());
|
||||||
|
|
||||||
const sortedChildren = sortBy(root.children_state, (ev) => {
|
const sortedChildren = sortBy(root.children_state, (ev) => {
|
||||||
return getChildOrder(ev.content.order, ev.origin_server_ts, ev.state_key);
|
return getChildOrder(ev.content.order, ev.origin_server_ts, ev.state_key);
|
||||||
|
@ -579,7 +575,7 @@ export const useRoomHierarchy = (
|
||||||
|
|
||||||
const loadMore = useCallback(
|
const loadMore = useCallback(
|
||||||
async (pageSize?: number): Promise<void> => {
|
async (pageSize?: number): Promise<void> => {
|
||||||
if (hierarchy.loading || !hierarchy.canLoadMore || hierarchy.noSupport || error) return;
|
if (!hierarchy || hierarchy.loading || !hierarchy.canLoadMore || hierarchy.noSupport || error) return;
|
||||||
await hierarchy.load(pageSize).catch(setError);
|
await hierarchy.load(pageSize).catch(setError);
|
||||||
setRooms(hierarchy.rooms);
|
setRooms(hierarchy.rooms);
|
||||||
},
|
},
|
||||||
|
@ -673,7 +669,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
|
||||||
onClick={async (): Promise<void> => {
|
onClick={async (): Promise<void> => {
|
||||||
setRemoving(true);
|
setRemoving(true);
|
||||||
try {
|
try {
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getSafeUserId();
|
||||||
for (const [parentId, childId] of selectedRelations) {
|
for (const [parentId, childId] of selectedRelations) {
|
||||||
await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, childId);
|
await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, childId);
|
||||||
|
|
||||||
|
@ -759,7 +755,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
|
||||||
const visited = new Set<string>();
|
const visited = new Set<string>();
|
||||||
const queue = [...directMatches.map((r) => r.room_id)];
|
const queue = [...directMatches.map((r) => r.room_id)];
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const roomId = queue.pop();
|
const roomId = queue.pop()!;
|
||||||
visited.add(roomId);
|
visited.add(roomId);
|
||||||
hierarchy.backRefs.get(roomId)?.forEach((parentId) => {
|
hierarchy.backRefs.get(roomId)?.forEach((parentId) => {
|
||||||
if (!visited.has(parentId)) {
|
if (!visited.has(parentId)) {
|
||||||
|
@ -797,7 +793,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentSet = selected.get(parentId);
|
const parentSet = selected.get(parentId)!;
|
||||||
if (!parentSet.has(childId)) {
|
if (!parentSet.has(childId)) {
|
||||||
setSelected(new Map(selected.set(parentId, new Set([...parentSet, childId]))));
|
setSelected(new Map(selected.set(parentId, new Set([...parentSet, childId]))));
|
||||||
return;
|
return;
|
||||||
|
@ -816,9 +812,9 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
|
||||||
} else {
|
} else {
|
||||||
const hasPermissions =
|
const hasPermissions =
|
||||||
space?.getMyMembership() === "join" &&
|
space?.getMyMembership() === "join" &&
|
||||||
space.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
|
space.currentState.maySendStateEvent(EventType.SpaceChild, cli.getSafeUserId());
|
||||||
|
|
||||||
let results: JSX.Element;
|
let results: JSX.Element | undefined;
|
||||||
if (filteredRoomSet.size) {
|
if (filteredRoomSet.size) {
|
||||||
results = (
|
results = (
|
||||||
<>
|
<>
|
||||||
|
@ -843,7 +839,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let loader: JSX.Element;
|
let loader: JSX.Element | undefined;
|
||||||
if (hierarchy.canLoadMore) {
|
if (hierarchy.canLoadMore) {
|
||||||
loader = (
|
loader = (
|
||||||
<div ref={loaderRef}>
|
<div ref={loaderRef}>
|
||||||
|
|
|
@ -43,12 +43,12 @@ interface IProps {
|
||||||
interface IState {
|
interface IState {
|
||||||
sendLogs: boolean;
|
sendLogs: boolean;
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
err: string;
|
err: string | null;
|
||||||
issueUrl: string;
|
issueUrl: string;
|
||||||
text: string;
|
text: string;
|
||||||
progress: string;
|
progress: string | null;
|
||||||
downloadBusy: boolean;
|
downloadBusy: boolean;
|
||||||
downloadProgress: string;
|
downloadProgress: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class BugReportDialog extends React.Component<IProps, IState> {
|
export default class BugReportDialog extends React.Component<IProps, IState> {
|
||||||
|
@ -181,12 +181,12 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let error = null;
|
let error: JSX.Element | undefined;
|
||||||
if (this.state.err) {
|
if (this.state.err) {
|
||||||
error = <div className="error">{this.state.err}</div>;
|
error = <div className="error">{this.state.err}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = null;
|
let progress: JSX.Element | undefined;
|
||||||
if (this.state.busy) {
|
if (this.state.busy) {
|
||||||
progress = (
|
progress = (
|
||||||
<div className="progress">
|
<div className="progress">
|
||||||
|
@ -196,7 +196,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let warning;
|
let warning: JSX.Element | undefined;
|
||||||
if (window.Modernizr && Object.values(window.Modernizr).some((support) => support === false)) {
|
if (window.Modernizr && Object.values(window.Modernizr).some((support) => support === false)) {
|
||||||
warning = (
|
warning = (
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -126,7 +126,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
// move focus to first field when showing dialog
|
// move focus to first field when showing dialog
|
||||||
this.nameField.current.focus();
|
this.nameField.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onKeyDown = (event: KeyboardEvent): void => {
|
private onKeyDown = (event: KeyboardEvent): void => {
|
||||||
|
@ -141,10 +141,9 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onOk = async (): Promise<void> => {
|
private onOk = async (): Promise<void> => {
|
||||||
|
if (!this.nameField.current) return;
|
||||||
const activeElement = document.activeElement as HTMLElement;
|
const activeElement = document.activeElement as HTMLElement;
|
||||||
if (activeElement) {
|
activeElement?.blur();
|
||||||
activeElement.blur();
|
|
||||||
}
|
|
||||||
await this.nameField.current.validate({ allowEmpty: false });
|
await this.nameField.current.validate({ allowEmpty: false });
|
||||||
if (this.aliasField.current) {
|
if (this.aliasField.current) {
|
||||||
await this.aliasField.current.validate({ allowEmpty: false });
|
await this.aliasField.current.validate({ allowEmpty: false });
|
||||||
|
@ -155,7 +154,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
if (this.state.nameIsValid && (!this.aliasField.current || this.aliasField.current.isValid)) {
|
if (this.state.nameIsValid && (!this.aliasField.current || this.aliasField.current.isValid)) {
|
||||||
this.props.onFinished(true, this.roomCreateOptions());
|
this.props.onFinished(true, this.roomCreateOptions());
|
||||||
} else {
|
} else {
|
||||||
let field;
|
let field: RoomAliasField | Field | null = null;
|
||||||
if (!this.state.nameIsValid) {
|
if (!this.state.nameIsValid) {
|
||||||
field = this.nameField.current;
|
field = this.nameField.current;
|
||||||
} else if (this.aliasField.current && !this.aliasField.current.isValid) {
|
} else if (this.aliasField.current && !this.aliasField.current.isValid) {
|
||||||
|
@ -163,7 +162,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
if (field) {
|
if (field) {
|
||||||
field.focus();
|
field.focus();
|
||||||
field.validate({ allowEmpty: false, focused: true });
|
await field.validate({ allowEmpty: false, focused: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -202,7 +201,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onNameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
private onNameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
||||||
const result = await CreateRoomDialog.validateRoomName(fieldState);
|
const result = await CreateRoomDialog.validateRoomName(fieldState);
|
||||||
this.setState({ nameIsValid: result.valid });
|
this.setState({ nameIsValid: !!result.valid });
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,9 +218,9 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const isVideoRoom = this.props.type === RoomType.ElementVideo;
|
const isVideoRoom = this.props.type === RoomType.ElementVideo;
|
||||||
|
|
||||||
let aliasField: JSX.Element;
|
let aliasField: JSX.Element | undefined;
|
||||||
if (this.state.joinRule === JoinRule.Public) {
|
if (this.state.joinRule === JoinRule.Public) {
|
||||||
const domain = MatrixClientPeg.get().getDomain();
|
const domain = MatrixClientPeg.get().getDomain()!;
|
||||||
aliasField = (
|
aliasField = (
|
||||||
<div className="mx_CreateRoomDialog_aliasContainer">
|
<div className="mx_CreateRoomDialog_aliasContainer">
|
||||||
<RoomAliasField
|
<RoomAliasField
|
||||||
|
@ -234,7 +233,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let publicPrivateLabel: JSX.Element;
|
let publicPrivateLabel: JSX.Element | undefined;
|
||||||
if (this.state.joinRule === JoinRule.Restricted) {
|
if (this.state.joinRule === JoinRule.Restricted) {
|
||||||
publicPrivateLabel = (
|
publicPrivateLabel = (
|
||||||
<p>
|
<p>
|
||||||
|
@ -242,7 +241,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
"Everyone in <SpaceName/> will be able to find and join this room.",
|
"Everyone in <SpaceName/> will be able to find and join this room.",
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
SpaceName: () => <b>{this.props.parentSpace.name}</b>,
|
SpaceName: () => <b>{this.props.parentSpace?.name ?? _t("Unnamed Space")}</b>,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -256,7 +255,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
"Anyone will be able to find and join this room, not just members of <SpaceName/>.",
|
"Anyone will be able to find and join this room, not just members of <SpaceName/>.",
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
SpaceName: () => <b>{this.props.parentSpace.name}</b>,
|
SpaceName: () => <b>{this.props.parentSpace?.name ?? _t("Unnamed Space")}</b>,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -281,7 +280,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let e2eeSection: JSX.Element;
|
let e2eeSection: JSX.Element | undefined;
|
||||||
if (this.state.joinRule !== JoinRule.Public) {
|
if (this.state.joinRule !== JoinRule.Public) {
|
||||||
let microcopy: string;
|
let microcopy: string;
|
||||||
if (privateShouldBeEncrypted()) {
|
if (privateShouldBeEncrypted()) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
|
||||||
const spaceNameField = useRef<Field>();
|
const spaceNameField = useRef<Field>();
|
||||||
const [alias, setAlias] = useState("");
|
const [alias, setAlias] = useState("");
|
||||||
const spaceAliasField = useRef<RoomAliasField>();
|
const spaceAliasField = useRef<RoomAliasField>();
|
||||||
const [avatar, setAvatar] = useState<File>(null);
|
const [avatar, setAvatar] = useState<File | undefined>();
|
||||||
const [topic, setTopic] = useState<string>("");
|
const [topic, setTopic] = useState<string>("");
|
||||||
|
|
||||||
const spaceJoinRule = space.getJoinRule();
|
const spaceJoinRule = space.getJoinRule();
|
||||||
|
@ -56,7 +56,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
|
||||||
|
|
||||||
const onCreateSubspaceClick = async (e: ButtonEvent): Promise<void> => {
|
const onCreateSubspaceClick = async (e: ButtonEvent): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (busy) return;
|
if (busy || !spaceNameField.current || !spaceAliasField.current) return;
|
||||||
|
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
// require & validate the space name field
|
// require & validate the space name field
|
||||||
|
@ -83,7 +83,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let joinRuleMicrocopy: JSX.Element;
|
let joinRuleMicrocopy: JSX.Element | undefined;
|
||||||
if (joinRule === JoinRule.Restricted) {
|
if (joinRule === JoinRule.Restricted) {
|
||||||
joinRuleMicrocopy = (
|
joinRuleMicrocopy = (
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -73,7 +73,7 @@ const useExportFormState = (): ExportConfig => {
|
||||||
const [exportType, setExportType] = useState(config.range ?? ExportType.Timeline);
|
const [exportType, setExportType] = useState(config.range ?? ExportType.Timeline);
|
||||||
const [includeAttachments, setAttachments] = useState(config.includeAttachments ?? false);
|
const [includeAttachments, setAttachments] = useState(config.includeAttachments ?? false);
|
||||||
const [numberOfMessages, setNumberOfMessages] = useState<number>(config.numberOfMessages ?? 100);
|
const [numberOfMessages, setNumberOfMessages] = useState<number>(config.numberOfMessages ?? 100);
|
||||||
const [sizeLimit, setSizeLimit] = useState<number | null>(config.sizeMb ?? 8);
|
const [sizeLimit, setSizeLimit] = useState<number>(config.sizeMb ?? 8);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
exportFormat,
|
exportFormat,
|
||||||
|
@ -260,7 +260,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let messageCount = null;
|
let messageCount: JSX.Element | undefined;
|
||||||
if (exportType === ExportType.LastNMessages && setNumberOfMessages) {
|
if (exportType === ExportType.LastNMessages && setNumberOfMessages) {
|
||||||
messageCount = (
|
messageCount = (
|
||||||
<Field
|
<Field
|
||||||
|
|
|
@ -96,7 +96,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
dis.dispatch({ action: "logout" });
|
dis.dispatch({ action: "logout" });
|
||||||
}
|
}
|
||||||
// close dialog
|
// close dialog
|
||||||
this.props.onFinished(confirmed);
|
this.props.onFinished(!!confirmed);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSetRecoveryMethodClick = (): void => {
|
private onSetRecoveryMethodClick = (): void => {
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
||||||
|
|
||||||
this.widget = new ElementWidget({
|
this.widget = new ElementWidget({
|
||||||
...this.props.widgetDefinition,
|
...this.props.widgetDefinition,
|
||||||
creatorUserId: MatrixClientPeg.get().getUserId(),
|
creatorUserId: MatrixClientPeg.get().getSafeUserId(),
|
||||||
id: `modal_${this.props.sourceWidgetId}`,
|
id: `modal_${this.props.sourceWidgetId}`,
|
||||||
});
|
});
|
||||||
this.possibleButtons = (this.props.widgetDefinition.buttons || []).map((b) => b.id);
|
this.possibleButtons = (this.props.widgetDefinition.buttons || []).map((b) => b.id);
|
||||||
|
@ -78,21 +78,23 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
const driver = new StopGapWidgetDriver([], this.widget, WidgetKind.Modal, false);
|
const driver = new StopGapWidgetDriver([], this.widget, WidgetKind.Modal, false);
|
||||||
const messaging = new ClientWidgetApi(this.widget, this.appFrame.current, driver);
|
const messaging = new ClientWidgetApi(this.widget, this.appFrame.current!, driver);
|
||||||
this.setState({ messaging });
|
this.setState({ messaging });
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
|
if (!this.state.messaging) return;
|
||||||
this.state.messaging.off("ready", this.onReady);
|
this.state.messaging.off("ready", this.onReady);
|
||||||
this.state.messaging.off(`action:${WidgetApiFromWidgetAction.CloseModalWidget}`, this.onWidgetClose);
|
this.state.messaging.off(`action:${WidgetApiFromWidgetAction.CloseModalWidget}`, this.onWidgetClose);
|
||||||
this.state.messaging.stop();
|
this.state.messaging.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onReady = (): void => {
|
private onReady = (): void => {
|
||||||
this.state.messaging.sendWidgetConfig(this.props.widgetDefinition);
|
this.state.messaging?.sendWidgetConfig(this.props.widgetDefinition);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onLoad = (): void => {
|
private onLoad = (): void => {
|
||||||
|
if (!this.state.messaging) return;
|
||||||
this.state.messaging.once("ready", this.onReady);
|
this.state.messaging.once("ready", this.onReady);
|
||||||
this.state.messaging.on(`action:${WidgetApiFromWidgetAction.CloseModalWidget}`, this.onWidgetClose);
|
this.state.messaging.on(`action:${WidgetApiFromWidgetAction.CloseModalWidget}`, this.onWidgetClose);
|
||||||
this.state.messaging.on(`action:${WidgetApiFromWidgetAction.SetModalButtonEnabled}`, this.onButtonEnableToggle);
|
this.state.messaging.on(`action:${WidgetApiFromWidgetAction.SetModalButtonEnabled}`, this.onButtonEnableToggle);
|
||||||
|
@ -106,7 +108,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const isClose = ev.detail.data.button === BuiltInModalButtonID.Close;
|
const isClose = ev.detail.data.button === BuiltInModalButtonID.Close;
|
||||||
if (isClose || !this.possibleButtons.includes(ev.detail.data.button)) {
|
if (isClose || !this.possibleButtons.includes(ev.detail.data.button)) {
|
||||||
return this.state.messaging.transport.reply(ev.detail, {
|
return this.state.messaging?.transport.reply(ev.detail, {
|
||||||
error: { message: "Invalid button" },
|
error: { message: "Invalid button" },
|
||||||
} as IWidgetApiErrorResponseData);
|
} as IWidgetApiErrorResponseData);
|
||||||
}
|
}
|
||||||
|
@ -121,15 +123,15 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
||||||
buttonIds = Array.from(tempSet);
|
buttonIds = Array.from(tempSet);
|
||||||
}
|
}
|
||||||
this.setState({ disabledButtonIds: buttonIds });
|
this.setState({ disabledButtonIds: buttonIds });
|
||||||
this.state.messaging.transport.reply(ev.detail, {} as IWidgetApiAcknowledgeResponseData);
|
this.state.messaging?.transport.reply(ev.detail, {} as IWidgetApiAcknowledgeResponseData);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const templated = this.widget.getCompleteUrl({
|
const templated = this.widget.getCompleteUrl({
|
||||||
widgetRoomId: this.props.widgetRoomId,
|
widgetRoomId: this.props.widgetRoomId,
|
||||||
currentUserId: MatrixClientPeg.get().getUserId(),
|
currentUserId: MatrixClientPeg.get().getSafeUserId(),
|
||||||
userDisplayName: OwnProfileStore.instance.displayName,
|
userDisplayName: OwnProfileStore.instance.displayName ?? undefined,
|
||||||
userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(),
|
userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl() ?? undefined,
|
||||||
clientId: ELEMENT_CLIENT_ID,
|
clientId: ELEMENT_CLIENT_ID,
|
||||||
clientTheme: SettingsStore.getValue("theme"),
|
clientTheme: SettingsStore.getValue("theme"),
|
||||||
clientLanguage: getUserLanguage(),
|
clientLanguage: getUserLanguage(),
|
||||||
|
@ -168,7 +170,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClick = (): void => {
|
const onClick = (): void => {
|
||||||
this.state.messaging.notifyModalWidgetButtonClicked(def.id);
|
this.state.messaging?.notifyModalWidgetButtonClicked(def.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDisabled = this.state.disabledButtonIds.includes(def.id);
|
const isDisabled = this.state.disabledButtonIds.includes(def.id);
|
||||||
|
@ -201,7 +203,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<iframe
|
<iframe
|
||||||
title={this.widget.name}
|
title={this.widget.name ?? undefined}
|
||||||
ref={this.appFrame}
|
ref={this.appFrame}
|
||||||
sandbox="allow-forms allow-scripts allow-same-origin"
|
sandbox="allow-forms allow-scripts allow-same-origin"
|
||||||
src={widgetUrl}
|
src={widgetUrl}
|
||||||
|
|
|
@ -99,8 +99,8 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
let moderatedByRoomId = null;
|
let moderatedByRoomId: string | null = null;
|
||||||
let moderatedByUserId = null;
|
let moderatedByUserId: string | null = null;
|
||||||
|
|
||||||
if (SettingsStore.getValue("feature_report_to_moderators")) {
|
if (SettingsStore.getValue("feature_report_to_moderators")) {
|
||||||
// The client supports reporting to moderators.
|
// The client supports reporting to moderators.
|
||||||
|
@ -111,7 +111,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
const room = client.getRoom(props.mxEvent.getRoomId());
|
const room = client.getRoom(props.mxEvent.getRoomId());
|
||||||
|
|
||||||
for (const stateEventType of MODERATED_BY_STATE_EVENT_TYPE) {
|
for (const stateEventType of MODERATED_BY_STATE_EVENT_TYPE) {
|
||||||
const stateEvent = room.currentState.getStateEvents(stateEventType, stateEventType);
|
const stateEvent = room?.currentState.getStateEvents(stateEventType, stateEventType);
|
||||||
if (!stateEvent) {
|
if (!stateEvent) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -177,9 +177,9 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
// A free-form text describing the abuse.
|
// A free-form text describing the abuse.
|
||||||
reason: "",
|
reason: "",
|
||||||
busy: false,
|
busy: false,
|
||||||
err: null,
|
err: undefined,
|
||||||
// If specified, the nature of the abuse, as specified by MSC3215.
|
// If specified, the nature of the abuse, as specified by MSC3215.
|
||||||
nature: null,
|
nature: undefined,
|
||||||
ignoreUserToo: false, // default false, for now. Could easily be argued as default true
|
ignoreUserToo: false, // default false, for now. Could easily be argued as default true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -233,14 +233,14 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
err: null,
|
err: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const ev = this.props.mxEvent;
|
const ev = this.props.mxEvent;
|
||||||
if (this.moderation && this.state.nature !== NonStandardValue.Admin) {
|
if (this.moderation && this.state.nature !== NonStandardValue.Admin) {
|
||||||
const nature: Nature = this.state.nature;
|
const nature = this.state.nature;
|
||||||
|
|
||||||
// Report to moderators through to the dedicated bot,
|
// Report to moderators through to the dedicated bot,
|
||||||
// as configured in the room's state events.
|
// as configured in the room's state events.
|
||||||
|
@ -274,12 +274,12 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let error = null;
|
let error: JSX.Element | undefined;
|
||||||
if (this.state.err) {
|
if (this.state.err) {
|
||||||
error = <div className="error">{this.state.err}</div>;
|
error = <div className="error">{this.state.err}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = null;
|
let progress: JSX.Element | undefined;
|
||||||
if (this.state.busy) {
|
if (this.state.busy) {
|
||||||
progress = (
|
progress = (
|
||||||
<div className="progress">
|
<div className="progress">
|
||||||
|
@ -299,7 +299,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
|
|
||||||
const adminMessageMD = SdkConfig.getObject("report_event")?.get("admin_message_md", "adminMessageMD");
|
const adminMessageMD = SdkConfig.getObject("report_event")?.get("admin_message_md", "adminMessageMD");
|
||||||
let adminMessage;
|
let adminMessage: JSX.Element | undefined;
|
||||||
if (adminMessageMD) {
|
if (adminMessageMD) {
|
||||||
const html = new Markdown(adminMessageMD).toHTML({ externalLinks: true });
|
const html = new Markdown(adminMessageMD).toHTML({ externalLinks: true });
|
||||||
adminMessage = <p dangerouslySetInnerHTML={{ __html: html }} />;
|
adminMessage = <p dangerouslySetInnerHTML={{ __html: html }} />;
|
||||||
|
|
|
@ -42,16 +42,20 @@ interface IProps {
|
||||||
onFinished(opts?: IFinishedOpts): void;
|
onFinished(opts?: IFinishedOpts): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Progress {
|
||||||
|
text: string;
|
||||||
|
progress: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
inviteUsersToNewRoom: boolean;
|
inviteUsersToNewRoom: boolean;
|
||||||
progressText?: string;
|
progress?: Progress;
|
||||||
progress?: number;
|
|
||||||
total?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class RoomUpgradeWarningDialog extends React.Component<IProps, IState> {
|
export default class RoomUpgradeWarningDialog extends React.Component<IProps, IState> {
|
||||||
private readonly isPrivate: boolean;
|
private readonly isPrivate: boolean;
|
||||||
private readonly currentVersion: string;
|
private readonly currentVersion?: string;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -66,8 +70,14 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private onProgressCallback = (progressText: string, progress: number, total: number): void => {
|
private onProgressCallback = (text: string, progress: number, total: number): void => {
|
||||||
this.setState({ progressText, progress, total });
|
this.setState({
|
||||||
|
progress: {
|
||||||
|
text,
|
||||||
|
progress,
|
||||||
|
total,
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onContinue = async (): Promise<void> => {
|
private onContinue = async (): Promise<void> => {
|
||||||
|
@ -98,7 +108,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
let inviteToggle = null;
|
let inviteToggle: JSX.Element | undefined;
|
||||||
if (this.isPrivate) {
|
if (this.isPrivate) {
|
||||||
inviteToggle = (
|
inviteToggle = (
|
||||||
<LabelledToggleSwitch
|
<LabelledToggleSwitch
|
||||||
|
@ -144,11 +154,11 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
|
||||||
}
|
}
|
||||||
|
|
||||||
let footer: JSX.Element;
|
let footer: JSX.Element;
|
||||||
if (this.state.progressText) {
|
if (this.state.progress) {
|
||||||
footer = (
|
footer = (
|
||||||
<span className="mx_RoomUpgradeWarningDialog_progress">
|
<span className="mx_RoomUpgradeWarningDialog_progress">
|
||||||
<ProgressBar value={this.state.progress} max={this.state.total} />
|
<ProgressBar value={this.state.progress.progress} max={this.state.progress.total} />
|
||||||
<div className="mx_RoomUpgradeWarningDialog_progressText">{this.state.progressText}</div>
|
<div className="mx_RoomUpgradeWarningDialog_progressText">{this.state.progress.text}</div>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -96,7 +96,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
test: async ({ value }) => !!value || hasNativeSupport,
|
test: async ({ value }) => !!value || !!hasNativeSupport,
|
||||||
invalid: () => _t("Your server lacks native support, you must specify a proxy"),
|
invalid: () => _t("Your server lacks native support, you must specify a proxy"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -104,7 +104,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
|
||||||
final: true,
|
final: true,
|
||||||
test: async (_, { error }) => !error,
|
test: async (_, { error }) => !error,
|
||||||
valid: () => _t("Looks good"),
|
valid: () => _t("Looks good"),
|
||||||
invalid: ({ error }) => error?.message,
|
invalid: ({ error }) => error?.message ?? null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,14 +23,14 @@ import BaseDialog from "./BaseDialog";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title?: string;
|
title: string;
|
||||||
description?: React.ReactNode;
|
description: React.ReactNode;
|
||||||
value?: string;
|
value: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
button?: string;
|
button?: string;
|
||||||
busyMessage?: string; // pass _td string
|
busyMessage: string; // pass _td string
|
||||||
focus?: boolean;
|
focus: boolean;
|
||||||
hasCancel?: boolean;
|
hasCancel: boolean;
|
||||||
validator?: (fieldState: IFieldState) => Promise<IValidationResult>; // result of withValidation
|
validator?: (fieldState: IFieldState) => Promise<IValidationResult>; // result of withValidation
|
||||||
fixedWidth?: boolean;
|
fixedWidth?: boolean;
|
||||||
onFinished(ok?: boolean, text?: string): void;
|
onFinished(ok?: boolean, text?: string): void;
|
||||||
|
@ -68,12 +68,13 @@ export default class TextInputDialog extends React.Component<IProps, IState> {
|
||||||
if (this.props.focus) {
|
if (this.props.focus) {
|
||||||
// Set the cursor at the end of the text input
|
// Set the cursor at the end of the text input
|
||||||
// this._field.current.value = this.props.value;
|
// this._field.current.value = this.props.value;
|
||||||
this.field.current.focus();
|
this.field.current?.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onOk = async (ev: React.FormEvent): Promise<void> => {
|
private onOk = async (ev: React.FormEvent): Promise<void> => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
if (!this.field.current) return;
|
||||||
if (this.props.validator) {
|
if (this.props.validator) {
|
||||||
this.setState({ busy: true });
|
this.setState({ busy: true });
|
||||||
await this.field.current.validate({ allowEmpty: false });
|
await this.field.current.validate({ allowEmpty: false });
|
||||||
|
@ -101,7 +102,7 @@ export default class TextInputDialog extends React.Component<IProps, IState> {
|
||||||
private onValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
private onValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
||||||
const result = await this.props.validator(fieldState);
|
const result = await this.props.validator(fieldState);
|
||||||
this.setState({
|
this.setState({
|
||||||
valid: result.valid,
|
valid: !!result.valid,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,8 +26,8 @@ import DialogButtons from "../elements/DialogButtons";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
file: File;
|
file: File;
|
||||||
currentIndex?: number;
|
currentIndex: number;
|
||||||
totalFiles?: number;
|
totalFiles: number;
|
||||||
onFinished: (uploadConfirmed: boolean, uploadAll?: boolean) => void;
|
onFinished: (uploadConfirmed: boolean, uploadAll?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
|
||||||
|
|
||||||
public static defaultProps: Partial<IProps> = {
|
public static defaultProps: Partial<IProps> = {
|
||||||
totalFiles: 1,
|
totalFiles: 1,
|
||||||
|
currentIndex: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
|
@ -77,8 +78,8 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileId = `mx-uploadconfirmdialog-${this.props.file.name}`;
|
const fileId = `mx-uploadconfirmdialog-${this.props.file.name}`;
|
||||||
let preview: JSX.Element;
|
let preview: JSX.Element | undefined;
|
||||||
let placeholder: JSX.Element;
|
let placeholder: JSX.Element | undefined;
|
||||||
if (this.mimeType.startsWith("image/")) {
|
if (this.mimeType.startsWith("image/")) {
|
||||||
preview = (
|
preview = (
|
||||||
<img className="mx_UploadConfirmDialog_imagePreview" src={this.objectUrl} aria-labelledby={fileId} />
|
<img className="mx_UploadConfirmDialog_imagePreview" src={this.objectUrl} aria-labelledby={fileId} />
|
||||||
|
@ -96,7 +97,7 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
|
||||||
placeholder = <FileIcon className="mx_UploadConfirmDialog_fileIcon" height={18} width={18} />;
|
placeholder = <FileIcon className="mx_UploadConfirmDialog_fileIcon" height={18} width={18} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let uploadAllButton;
|
let uploadAllButton: JSX.Element | undefined;
|
||||||
if (this.props.currentIndex + 1 < this.props.totalFiles) {
|
if (this.props.currentIndex + 1 < this.props.totalFiles) {
|
||||||
uploadAllButton = <button onClick={this.onUploadAllClick}>{_t("Upload all")}</button>;
|
uploadAllButton = <button onClick={this.onUploadAllClick}>{_t("Upload all")}</button>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,19 +52,19 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
backupInfo: IKeyBackupInfo;
|
backupInfo: IKeyBackupInfo | null;
|
||||||
backupKeyStored: Record<string, ISecretStorageKeyInfo>;
|
backupKeyStored: Record<string, ISecretStorageKeyInfo> | null;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
loadError: string;
|
loadError: string | null;
|
||||||
restoreError: {
|
restoreError: {
|
||||||
errcode: string;
|
errcode: string;
|
||||||
};
|
} | null;
|
||||||
recoveryKey: string;
|
recoveryKey: string;
|
||||||
recoverInfo: IKeyBackupRestoreResult;
|
recoverInfo: IKeyBackupRestoreResult | null;
|
||||||
recoveryKeyValid: boolean;
|
recoveryKeyValid: boolean;
|
||||||
forceRecoveryKey: boolean;
|
forceRecoveryKey: boolean;
|
||||||
passPhrase: string;
|
passPhrase: string;
|
||||||
restoreType: RestoreType;
|
restoreType: RestoreType | null;
|
||||||
progress: {
|
progress: {
|
||||||
stage: ProgressState;
|
stage: ProgressState;
|
||||||
total?: number;
|
total?: number;
|
||||||
|
@ -247,7 +247,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restoreWithCachedKey(backupInfo?: IKeyBackupInfo): Promise<boolean> {
|
private async restoreWithCachedKey(backupInfo: IKeyBackupInfo | null): Promise<boolean> {
|
||||||
if (!backupInfo) return false;
|
if (!backupInfo) return false;
|
||||||
try {
|
try {
|
||||||
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache(
|
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache(
|
||||||
|
@ -275,7 +275,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const backupInfo = await cli.getKeyBackupVersion();
|
const backupInfo = await cli.getKeyBackupVersion();
|
||||||
const has4S = await cli.hasSecretStorageKey();
|
const has4S = await cli.hasSecretStorageKey();
|
||||||
const backupKeyStored = has4S && (await cli.isKeyBackupKeyStored());
|
const backupKeyStored = has4S ? await cli.isKeyBackupKeyStored() : null;
|
||||||
this.setState({
|
this.setState({
|
||||||
backupInfo,
|
backupInfo,
|
||||||
backupKeyStored,
|
backupKeyStored,
|
||||||
|
|
|
@ -685,7 +685,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
<Option
|
<Option
|
||||||
id={`mx_SpotlightDialog_button_result_${result.name}`}
|
id={`mx_SpotlightDialog_button_result_${result.name}`}
|
||||||
key={`${Section[result.section]}-${result.name}`}
|
key={`${Section[result.section]}-${result.name}`}
|
||||||
onClick={result.onClick}
|
onClick={result.onClick ?? null}
|
||||||
>
|
>
|
||||||
{result.avatar}
|
{result.avatar}
|
||||||
{result.name}
|
{result.name}
|
||||||
|
|
|
@ -141,7 +141,7 @@ function useHasCrossSigningKeys(
|
||||||
member: User,
|
member: User,
|
||||||
canVerify: boolean,
|
canVerify: boolean,
|
||||||
setUpdating: SetUpdating,
|
setUpdating: SetUpdating,
|
||||||
): boolean {
|
): boolean | undefined {
|
||||||
return useAsyncMemo(async () => {
|
return useAsyncMemo(async () => {
|
||||||
if (!canVerify) {
|
if (!canVerify) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -196,7 +196,7 @@ export function DeviceItem({ userId, device }: { userId: string; device: IDevice
|
||||||
: device.getDisplayName();
|
: device.getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
let trustedLabel = null;
|
let trustedLabel: string | undefined;
|
||||||
if (userTrust.isVerified()) trustedLabel = isVerified ? _t("Trusted") : _t("Not trusted");
|
if (userTrust.isVerified()) trustedLabel = isVerified ? _t("Trusted") : _t("Not trusted");
|
||||||
|
|
||||||
if (isVerified) {
|
if (isVerified) {
|
||||||
|
@ -343,13 +343,12 @@ export const UserOptionsSection: React.FC<{
|
||||||
}> = ({ member, isIgnored, canInvite, isSpace }) => {
|
}> = ({ member, isIgnored, canInvite, isSpace }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
let ignoreButton = null;
|
let ignoreButton: JSX.Element | undefined;
|
||||||
let insertPillButton = null;
|
let insertPillButton: JSX.Element | undefined;
|
||||||
let inviteUserButton = null;
|
let inviteUserButton: JSX.Element | undefined;
|
||||||
let readReceiptButton = null;
|
let readReceiptButton: JSX.Element | undefined;
|
||||||
|
|
||||||
const isMe = member.userId === cli.getUserId();
|
const isMe = member.userId === cli.getUserId();
|
||||||
|
|
||||||
const onShareUserClick = (): void => {
|
const onShareUserClick = (): void => {
|
||||||
Modal.createDialog(ShareDialog, {
|
Modal.createDialog(ShareDialog, {
|
||||||
target: member,
|
target: member,
|
||||||
|
@ -517,7 +516,7 @@ const warnSelfDemote = async (isSpace: boolean): Promise<boolean> => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const [confirmed] = await finished;
|
const [confirmed] = await finished;
|
||||||
return confirmed;
|
return !!confirmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GenericAdminToolsContainer: React.FC<{
|
const GenericAdminToolsContainer: React.FC<{
|
||||||
|
@ -600,7 +599,7 @@ export const RoomKickButton = ({
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
// check if user can be kicked/disinvited
|
// check if user can be kicked/disinvited
|
||||||
if (member.membership !== "invite" && member.membership !== "join") return null;
|
if (member.membership !== "invite" && member.membership !== "join") return <></>;
|
||||||
|
|
||||||
const onKick = async (): Promise<void> => {
|
const onKick = async (): Promise<void> => {
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
|
@ -633,8 +632,8 @@ export const RoomKickButton = ({
|
||||||
const myMember = child.getMember(cli.credentials.userId || "");
|
const myMember = child.getMember(cli.credentials.userId || "");
|
||||||
const theirMember = child.getMember(member.userId);
|
const theirMember = child.getMember(member.userId);
|
||||||
return (
|
return (
|
||||||
myMember &&
|
!!myMember &&
|
||||||
theirMember &&
|
!!theirMember &&
|
||||||
theirMember.membership === member.membership &&
|
theirMember.membership === member.membership &&
|
||||||
myMember.powerLevel > theirMember.powerLevel &&
|
myMember.powerLevel > theirMember.powerLevel &&
|
||||||
child.currentState.hasSufficientPowerLevelFor("kick", myMember.powerLevel)
|
child.currentState.hasSufficientPowerLevelFor("kick", myMember.powerLevel)
|
||||||
|
@ -755,8 +754,8 @@ export const BanToggleButton = ({
|
||||||
const myMember = child.getMember(cli.credentials.userId || "");
|
const myMember = child.getMember(cli.credentials.userId || "");
|
||||||
const theirMember = child.getMember(member.userId);
|
const theirMember = child.getMember(member.userId);
|
||||||
return (
|
return (
|
||||||
myMember &&
|
!!myMember &&
|
||||||
theirMember &&
|
!!theirMember &&
|
||||||
theirMember.membership === "ban" &&
|
theirMember.membership === "ban" &&
|
||||||
myMember.powerLevel > theirMember.powerLevel &&
|
myMember.powerLevel > theirMember.powerLevel &&
|
||||||
child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel)
|
child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel)
|
||||||
|
@ -1555,7 +1554,7 @@ export const UserInfoHeader: React.FC<{
|
||||||
showPresence = enablePresenceByHsUrl[cli.baseUrl];
|
showPresence = enablePresenceByHsUrl[cli.baseUrl];
|
||||||
}
|
}
|
||||||
|
|
||||||
let presenceLabel = null;
|
let presenceLabel: JSX.Element | undefined;
|
||||||
if (showPresence) {
|
if (showPresence) {
|
||||||
presenceLabel = (
|
presenceLabel = (
|
||||||
<PresenceLabel
|
<PresenceLabel
|
||||||
|
@ -1614,7 +1613,7 @@ const UserInfo: React.FC<IProps> = ({ user, room, onClose, phase = RightPanelPha
|
||||||
const isRoomEncrypted = useIsEncrypted(cli, room);
|
const isRoomEncrypted = useIsEncrypted(cli, room);
|
||||||
const devices = useDevices(user.userId);
|
const devices = useDevices(user.userId);
|
||||||
|
|
||||||
let e2eStatus;
|
let e2eStatus: E2EStatus | undefined;
|
||||||
if (isRoomEncrypted && devices) {
|
if (isRoomEncrypted && devices) {
|
||||||
e2eStatus = getE2EStatus(cli, user.userId, devices);
|
e2eStatus = getE2EStatus(cli, user.userId, devices);
|
||||||
}
|
}
|
||||||
|
@ -1659,7 +1658,7 @@ const UserInfo: React.FC<IProps> = ({ user, room, onClose, phase = RightPanelPha
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let closeLabel = undefined;
|
let closeLabel: string | undefined;
|
||||||
if (phase === RightPanelPhases.EncryptionPanel) {
|
if (phase === RightPanelPhases.EncryptionPanel) {
|
||||||
const verificationRequest = (props as React.ComponentProps<typeof EncryptionPanel>).verificationRequest;
|
const verificationRequest = (props as React.ComponentProps<typeof EncryptionPanel>).verificationRequest;
|
||||||
if (verificationRequest && verificationRequest.pending) {
|
if (verificationRequest && verificationRequest.pending) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
|
||||||
/>
|
/>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
avatarUploadRef.current.value = "";
|
if (avatarUploadRef.current) avatarUploadRef.current.value = "";
|
||||||
setAvatarDataUrl(undefined);
|
setAvatarDataUrl(undefined);
|
||||||
setAvatar(undefined);
|
setAvatar(undefined);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -42,7 +42,7 @@ const getForceChatExportParameters = (): ForceChatExportParameters => {
|
||||||
// them all as optional. This allows customisers to only define and export the
|
// them all as optional. This allows customisers to only define and export the
|
||||||
// customisations they need while still maintaining type safety.
|
// customisations they need while still maintaining type safety.
|
||||||
export interface IChatExportCustomisations {
|
export interface IChatExportCustomisations {
|
||||||
getForceChatExportParameters?: typeof getForceChatExportParameters;
|
getForceChatExportParameters: typeof getForceChatExportParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A real customisation module will define and export one or more of the
|
// A real customisation module will define and export one or more of the
|
||||||
|
|
|
@ -2736,6 +2736,7 @@
|
||||||
"Clear all data": "Clear all data",
|
"Clear all data": "Clear all data",
|
||||||
"Please enter a name for the room": "Please enter a name for the room",
|
"Please enter a name for the room": "Please enter a name for the room",
|
||||||
"Everyone in <SpaceName/> will be able to find and join this room.": "Everyone in <SpaceName/> will be able to find and join this room.",
|
"Everyone in <SpaceName/> will be able to find and join this room.": "Everyone in <SpaceName/> will be able to find and join this room.",
|
||||||
|
"Unnamed Space": "Unnamed Space",
|
||||||
"You can change this at any time from room settings.": "You can change this at any time from room settings.",
|
"You can change this at any time from room settings.": "You can change this at any time from room settings.",
|
||||||
"Anyone will be able to find and join this room, not just members of <SpaceName/>.": "Anyone will be able to find and join this room, not just members of <SpaceName/>.",
|
"Anyone will be able to find and join this room, not just members of <SpaceName/>.": "Anyone will be able to find and join this room, not just members of <SpaceName/>.",
|
||||||
"Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.",
|
"Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.",
|
||||||
|
@ -2871,7 +2872,6 @@
|
||||||
"Some suggestions may be hidden for privacy.": "Some suggestions may be hidden for privacy.",
|
"Some suggestions may be hidden for privacy.": "Some suggestions may be hidden for privacy.",
|
||||||
"If you can't see who you're looking for, send them your invite link below.": "If you can't see who you're looking for, send them your invite link below.",
|
"If you can't see who you're looking for, send them your invite link below.": "If you can't see who you're looking for, send them your invite link below.",
|
||||||
"Or send invite link": "Or send invite link",
|
"Or send invite link": "Or send invite link",
|
||||||
"Unnamed Space": "Unnamed Space",
|
|
||||||
"Invite to %(roomName)s": "Invite to %(roomName)s",
|
"Invite to %(roomName)s": "Invite to %(roomName)s",
|
||||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
|
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
|
||||||
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this space</a>.",
|
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this space</a>.",
|
||||||
|
|
|
@ -214,7 +214,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
|
||||||
*
|
*
|
||||||
* @return {Promise<string>} URL returned by the rageshake server
|
* @return {Promise<string>} URL returned by the rageshake server
|
||||||
*/
|
*/
|
||||||
export default async function sendBugReport(bugReportEndpoint: string, opts: IOpts = {}): Promise<string> {
|
export default async function sendBugReport(bugReportEndpoint?: string, opts: IOpts = {}): Promise<string> {
|
||||||
if (!bugReportEndpoint) {
|
if (!bugReportEndpoint) {
|
||||||
throw new Error("No bug report endpoint has been set.");
|
throw new Error("No bug report endpoint has been set.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,7 @@ async function getContexts(): Promise<Contexts> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendSentryReport(userText: string, issueUrl: string, error: Error): Promise<void> {
|
export async function sendSentryReport(userText: string, issueUrl: string, error?: Error): Promise<void> {
|
||||||
const sentryConfig = SdkConfig.getObject("sentry");
|
const sentryConfig = SdkConfig.getObject("sentry");
|
||||||
if (!sentryConfig) return;
|
if (!sentryConfig) return;
|
||||||
|
|
||||||
|
|
|
@ -76,14 +76,14 @@ export function pillifyLinks(nodes: ArrayLike<Element>, mxEvent: MatrixEvent, pi
|
||||||
// to clear the pills from the last run of pillifyLinks
|
// to clear the pills from the last run of pillifyLinks
|
||||||
!node.parentElement.classList.contains("mx_AtRoomPill")
|
!node.parentElement.classList.contains("mx_AtRoomPill")
|
||||||
) {
|
) {
|
||||||
let currentTextNode = node as Node as Text;
|
let currentTextNode = node as Node as Text | null;
|
||||||
const roomNotifTextNodes = [];
|
const roomNotifTextNodes = [];
|
||||||
|
|
||||||
// Take a textNode and break it up to make all the instances of @room their
|
// Take a textNode and break it up to make all the instances of @room their
|
||||||
// own textNode, adding those nodes to roomNotifTextNodes
|
// own textNode, adding those nodes to roomNotifTextNodes
|
||||||
while (currentTextNode !== null) {
|
while (currentTextNode !== null) {
|
||||||
const roomNotifPos = pillRoomNotifPos(currentTextNode.textContent);
|
const roomNotifPos = pillRoomNotifPos(currentTextNode.textContent);
|
||||||
let nextTextNode = null;
|
let nextTextNode: Text | null = null;
|
||||||
if (roomNotifPos > -1) {
|
if (roomNotifPos > -1) {
|
||||||
let roomTextNode = currentTextNode;
|
let roomTextNode = currentTextNode;
|
||||||
|
|
||||||
|
|
|
@ -793,7 +793,7 @@ describe("<RoomKickButton />", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(callback(mockRoom)).toBe(null);
|
expect(callback(mockRoom)).toBe(false);
|
||||||
expect(callback(mockRoom)).toBe(true);
|
expect(callback(mockRoom)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -915,7 +915,7 @@ describe("<BanToggleButton />", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(callback(mockRoom)).toBe(null);
|
expect(callback(mockRoom)).toBe(false);
|
||||||
expect(callback(mockRoom)).toBe(true);
|
expect(callback(mockRoom)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue