diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 15fc2e075e..c94718ba46 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -460,7 +460,7 @@ function formatEmojis(message: string, isHtmlMessage: boolean): (JSX.Element | s export function bodyToHtml(content: IContent, highlights: Optional, opts: IOptsReturnString): string; export function bodyToHtml(content: IContent, highlights: Optional, opts: IOptsReturnNode): ReactNode; export function bodyToHtml(content: IContent, highlights: Optional, opts: IOpts = {}) { - const isFormattedBody = content.format === "org.matrix.custom.html" && content.formatted_body; + const isFormattedBody = content.format === "org.matrix.custom.html" && !!content.formatted_body; let bodyHasEmoji = false; let isHtmlMessage = false; @@ -511,7 +511,7 @@ export function bodyToHtml(content: IContent, highlights: Optional, op decodeEntities: false, }); const isPlainText = phtml.html() === phtml.root().text(); - isHtmlMessage = isFormattedBody && !isPlainText; + isHtmlMessage = !isPlainText; if (isHtmlMessage && SettingsStore.getValue("feature_latex_maths")) { // @ts-ignore - The types for `replaceWith` wrongly expect diff --git a/src/Login.ts b/src/Login.ts index 4dc96bc17d..f14b97ee29 100644 --- a/src/Login.ts +++ b/src/Login.ts @@ -75,11 +75,13 @@ export default class Login { * @returns {MatrixClient} */ public createTemporaryClient(): MatrixClient { - if (this.tempClient) return this.tempClient; // use memoization - return this.tempClient = createClient({ - baseUrl: this.hsUrl, - idBaseUrl: this.isUrl, - }); + if (!this.tempClient) { + this.tempClient = createClient({ + baseUrl: this.hsUrl, + idBaseUrl: this.isUrl, + }); + } + return this.tempClient; } public async getFlows(): Promise> { diff --git a/src/NodeAnimator.tsx b/src/NodeAnimator.tsx index 2bb7954240..77ab976347 100644 --- a/src/NodeAnimator.tsx +++ b/src/NodeAnimator.tsx @@ -79,7 +79,6 @@ export default class NodeAnimator extends React.Component { if (oldNode && (oldNode as HTMLElement).style.left !== c.props.style.left) { this.applyStyles(oldNode as HTMLElement, { left: c.props.style.left }); - // console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left); } // clone the old element with the props (and children) of the new element // so prop updates are still received by the children. @@ -94,7 +93,6 @@ export default class NodeAnimator extends React.Component { if (startStyles.length > 0) { const startStyle = startStyles[0]; newProps.style = startStyle; - // console.log("mounted@startstyle0: "+JSON.stringify(startStyle)); } newProps.ref = ((n) => this.collectNode( @@ -118,18 +116,12 @@ export default class NodeAnimator extends React.Component { // to start with, so now we animate 1 etc. for (let i = 1; i < startStyles.length; ++i) { this.applyStyles(domNode as HTMLElement, startStyles[i]); - // console.log("start:" - // JSON.stringify(startStyles[i]), - // ); } // and then we animate to the resting state setTimeout(() => { this.applyStyles(domNode as HTMLElement, restingStyle); }, 0); - - // console.log("enter:", - // JSON.stringify(restingStyle)); } this.nodes[k] = node; } diff --git a/src/Notifier.ts b/src/Notifier.ts index 2f92569928..f675775b9e 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -47,11 +47,10 @@ import ErrorDialog from "./components/views/dialogs/ErrorDialog"; import LegacyCallHandler from "./LegacyCallHandler"; import VoipUserMapper from "./VoipUserMapper"; import { SdkContextClass } from "./contexts/SDKContext"; -import { localNotificationsAreSilenced } from "./utils/notifications"; +import { localNotificationsAreSilenced, createLocalNotificationSettingsIfNeeded } from "./utils/notifications"; import { getIncomingCallToastKey, IncomingCallToast } from "./toasts/IncomingCallToast"; import ToastStore from "./stores/ToastStore"; import { ElementCall } from "./models/Call"; -import { createLocalNotificationSettingsIfNeeded } from './utils/notifications'; /* * Dispatches: diff --git a/src/Searching.ts b/src/Searching.ts index 007aca4a51..fcbc7d0007 100644 --- a/src/Searching.ts +++ b/src/Searching.ts @@ -497,11 +497,10 @@ interface IEncryptedSeshatEvent { } function restoreEncryptionInfo(searchResultSlice: SearchResult[] = []): void { - for (let i = 0; i < searchResultSlice.length; i++) { - const timeline = searchResultSlice[i].context.getTimeline(); + for (const result of searchResultSlice) { + const timeline = result.context.getTimeline(); - for (let j = 0; j < timeline.length; j++) { - const mxEv = timeline[j]; + for (const mxEv of timeline) { const ev = mxEv.event as IEncryptedSeshatEvent; if (ev.curve25519Key) { diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index cce5c4ade4..767a42e38a 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -716,7 +716,7 @@ export const Commands = [ runFn: function(roomId, args) { const cli = MatrixClientPeg.get(); - let targetRoomId: string; + let targetRoomId: string | undefined; if (args) { const matches = args.match(/^(\S+)$/); if (matches) { @@ -729,15 +729,9 @@ export const Commands = [ // Try to find a room with this alias const rooms = cli.getRooms(); - for (let i = 0; i < rooms.length; i++) { - if (rooms[i].getCanonicalAlias() === roomAlias || - rooms[i].getAltAliases().includes(roomAlias) - ) { - targetRoomId = rooms[i].roomId; - break; - } - if (targetRoomId) break; - } + targetRoomId = rooms.find(room => { + return room.getCanonicalAlias() === roomAlias || room.getAltAliases().includes(roomAlias); + })?.roomId; if (!targetRoomId) { return reject( newTranslatableError( diff --git a/src/WhoIsTyping.ts b/src/WhoIsTyping.ts index 938218d270..0ea2490db3 100644 --- a/src/WhoIsTyping.ts +++ b/src/WhoIsTyping.ts @@ -39,9 +39,7 @@ export function usersTyping(room: Room, exclude: string[] = []): RoomMember[] { const whoIsTyping = []; const memberKeys = Object.keys(room.currentState.members); - for (let i = 0; i < memberKeys.length; ++i) { - const userId = memberKeys[i]; - + for (const userId of memberKeys) { if (room.currentState.members[userId].typing) { if (exclude.indexOf(userId) === -1) { whoIsTyping.push(room.currentState.members[userId]); diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index b62c4b6d7b..136f036f70 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -524,16 +524,11 @@ export const alwaysAboveLeftOf = ( const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace }; const buttonRight = elementRect.right + window.scrollX; - const buttonBottom = elementRect.bottom + window.scrollY; const buttonTop = elementRect.top + window.scrollY; // Align the right edge of the menu to the right edge of the button menuOptions.right = UIStore.instance.windowWidth - buttonRight; - // Align the menu vertically on whichever side of the button has more space available. - if (buttonBottom < UIStore.instance.windowHeight / 2) { - menuOptions.top = buttonBottom + vPadding; - } else { - menuOptions.bottom = (UIStore.instance.windowHeight - buttonTop) + vPadding; - } + // Align the menu vertically above the menu + menuOptions.bottom = (UIStore.instance.windowHeight - buttonTop) + vPadding; return menuOptions; }; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index cce4b9d0ab..56d1d8d7ab 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -546,8 +546,6 @@ export default class MatrixChat extends React.PureComponent { } private onAction = (payload: ActionPayload): void => { - // console.log(`MatrixClientPeg.onAction: ${payload.action}`); - // Start the onboarding process for certain actions if (MatrixClientPeg.get()?.isGuest() && ONBOARDING_FLOW_STARTERS.includes(payload.action)) { // This will cause `payload` to be dispatched later, once a diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index f0c0eb8786..d4523394f5 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -101,11 +101,12 @@ export default class RightPanel extends React.Component { if (!this.props.room || member.roomId !== this.props.room.roomId) { return; } + // redraw the badge on the membership list - if (this.state.phase === RightPanelPhases.RoomMemberList && member.roomId === this.props.room.roomId) { + if (this.state.phase === RightPanelPhases.RoomMemberList) { this.delayedUpdate(); } else if ( - this.state.phase === RightPanelPhases.RoomMemberInfo && member.roomId === this.props.room.roomId && + this.state.phase === RightPanelPhases.RoomMemberInfo && member.userId === this.state.cardState.member.userId ) { // refresh the member info (e.g. new power level) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 2dfe61aefa..426912c566 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2293,7 +2293,6 @@ export class RoomView extends React.Component { highlightedEventId = this.state.initialEventId; } - // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); const messagePanel = ( ; }; diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index b8c73ee0df..f0d9b816fa 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -477,8 +477,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { ev.preventDefault(); if (busy) return; setError(""); - for (let i = 0; i < fieldRefs.length; i++) { - const fieldRef = fieldRefs[i]; + for (const fieldRef of fieldRefs) { const valid = await fieldRef.current.validate({ allowEmpty: true }); if (valid === false) { // true/null are allowed diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 2592040f2a..25d40dfaab 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -645,8 +645,6 @@ class TimelinePanel extends React.Component { if (data.timeline.getTimelineSet() !== this.props.timelineSet) return; if (!Thread.hasServerSideSupport && this.context.timelineRenderingType === TimelineRenderingType.Thread) { - // const direction = toStartOfTimeline ? Direction.Backward : Direction.Forward; - // this.timelineWindow.extend(direction, 1); if (toStartOfTimeline && !this.state.canBackPaginate) { this.setState({ canBackPaginate: true, diff --git a/src/components/views/auth/PasswordLogin.tsx b/src/components/views/auth/PasswordLogin.tsx index f6b4e199dc..6678326d6e 100644 --- a/src/components/views/auth/PasswordLogin.tsx +++ b/src/components/views/auth/PasswordLogin.tsx @@ -191,14 +191,8 @@ export default class PasswordLogin extends React.PureComponent { return false; } - private allFieldsValid() { - const keys = Object.keys(this.state.fieldValid); - for (let i = 0; i < keys.length; ++i) { - if (!this.state.fieldValid[keys[i]]) { - return false; - } - } - return true; + private allFieldsValid(): boolean { + return Object.values(this.state.fieldValid).every(Boolean); } private findFirstInvalidField(fieldIDs: LoginField[]) { diff --git a/src/components/views/auth/RegistrationForm.tsx b/src/components/views/auth/RegistrationForm.tsx index d1f7c9acc4..de55d68347 100644 --- a/src/components/views/auth/RegistrationForm.tsx +++ b/src/components/views/auth/RegistrationForm.tsx @@ -224,14 +224,8 @@ export default class RegistrationForm extends React.PureComponent = ({ setError(null); setProgress(0); - let error; + let error: Error | undefined; for (const room of selectedToAdd) { const via = calculateRoomVia(room); @@ -197,13 +197,15 @@ export const AddExistingToSpace: React.FC = ({ setProgress(i => i + 1); } catch (e) { logger.error("Failed to add rooms to space", e); - setError(error = e); + error = e; break; } } if (!error) { onFinished(true); + } else { + setError(error); } }; diff --git a/src/components/views/dialogs/CreateRoomDialog.tsx b/src/components/views/dialogs/CreateRoomDialog.tsx index 2217af9387..bbe693dc37 100644 --- a/src/components/views/dialogs/CreateRoomDialog.tsx +++ b/src/components/views/dialogs/CreateRoomDialog.tsx @@ -128,9 +128,6 @@ export default class CreateRoomDialog extends React.Component { this.nameField.current.focus(); } - componentWillUnmount() { - } - private onKeyDown = (event: KeyboardEvent) => { const action = getKeyBindingsManager().getAccessibilityAction(event); switch (action) { diff --git a/src/components/views/dialogs/KeySignatureUploadFailedDialog.tsx b/src/components/views/dialogs/KeySignatureUploadFailedDialog.tsx index 72ce6bd3ba..ff7b5bd268 100644 --- a/src/components/views/dialogs/KeySignatureUploadFailedDialog.tsx +++ b/src/components/views/dialogs/KeySignatureUploadFailedDialog.tsx @@ -92,12 +92,13 @@ const KeySignatureUploadFailedDialog: React.FC = ({ /> ); } else { + let text = _t("Upload completed"); + if (!success) { + text = cancelled ? _t("Cancelled signature upload") : _t("Unable to upload"); + } + body = (
- { success ? - { _t("Upload completed") } : - cancelled ? - { _t("Cancelled signature upload") } : - { _t("Unable to upload") } } + { text } = type IProps = DynamicHtmlElementProps & { inputRef?: React.Ref; element?: T; - children?: ReactNode | undefined; + children?: ReactNode; // The kind of button, similar to how Bootstrap works. // See available classes for AccessibleButton for options. kind?: AccessibleButtonKind | string; diff --git a/src/components/views/elements/EditableText.tsx b/src/components/views/elements/EditableText.tsx index 344db02c3d..30f28624f4 100644 --- a/src/components/views/elements/EditableText.tsx +++ b/src/components/views/elements/EditableText.tsx @@ -117,8 +117,6 @@ export default class EditableText extends React.Component { }; private onKeyDown = (ev: React.KeyboardEvent): void => { - // console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); - if (this.placeholder) { this.showPlaceholder(false); } @@ -130,13 +128,9 @@ export default class EditableText extends React.Component { ev.preventDefault(); break; } - - // console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); }; private onKeyUp = (ev: React.KeyboardEvent): void => { - // console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); - if (!(ev.target as HTMLDivElement).textContent) { this.showPlaceholder(true); } else if (!this.placeholder) { @@ -152,8 +146,6 @@ export default class EditableText extends React.Component { this.onFinish(ev); break; } - - // console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); }; private onClickDiv = (): void => { @@ -165,8 +157,6 @@ export default class EditableText extends React.Component { }; private onFocus = (ev: React.FocusEvent): void => { - //ev.target.setSelectionRange(0, ev.target.textContent.length); - const node = ev.target.childNodes[0]; if (node) { const range = document.createRange(); diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index d52ea2f923..5b986dd405 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -215,12 +215,12 @@ export default class EventListSummary extends React.Component { repeats: number; }[] = []; - for (let i = 0; i < transitions.length; i++) { - if (res.length > 0 && res[res.length - 1].transitionType === transitions[i]) { + for (const transition of transitions) { + if (res.length > 0 && res[res.length - 1].transitionType === transition) { res[res.length - 1].repeats += 1; } else { res.push({ - transitionType: transitions[i], + transitionType: transition, repeats: 1, }); } @@ -399,7 +399,6 @@ export default class EventListSummary extends React.Component { } else if (e.mxEvent.getContent().avatar_url !== e.mxEvent.getPrevContent().avatar_url) { return TransitionType.ChangedAvatar; } - // console.log("MELS ignoring duplicate membership join event"); return TransitionType.NoChange; } else { return TransitionType.Joined; diff --git a/src/components/views/messages/MBeaconBody.tsx b/src/components/views/messages/MBeaconBody.tsx index 8c75de01ba..7f8c9b8c8a 100644 --- a/src/components/views/messages/MBeaconBody.tsx +++ b/src/components/views/messages/MBeaconBody.tsx @@ -171,48 +171,52 @@ const MBeaconBody: React.FC = React.forwardRef(({ mxEvent, getRelati ); }; + let map: JSX.Element; + if (displayStatus === BeaconDisplayStatus.Active && !isMapDisplayError) { + map = + { + ({ map }) => + + } + ; + } else if (isMapDisplayError) { + map = ; + } else { + map = ; + } + return (
- { (displayStatus === BeaconDisplayStatus.Active && !isMapDisplayError) ? - - { - ({ map }) => - - } - - : isMapDisplayError ? - : - - } + { map } { isOwnBeacon ? { cli.on(UserEvent.LastPresenceTs, this.onUserPresenceChange); cli.on(UserEvent.Presence, this.onUserPresenceChange); cli.on(UserEvent.CurrentlyActive, this.onUserPresenceChange); - // cli.on("Room.timeline", this.onRoomTimeline); } componentWillUnmount() { @@ -199,7 +198,6 @@ export default class MemberList extends React.Component { // member tile and re-render it. This is more efficient than every tile // ever attaching their own listener. const tile = this.refs[user.userId]; - // console.log(`Got presence update for ${user.userId}. hasTile=${!!tile}`); if (tile) { this.updateList(); // reorder the membership list } @@ -370,14 +368,9 @@ export default class MemberList extends React.Component { // ...and then alphabetically. // We could tiebreak instead by "last recently spoken in this room" if we wanted to. - // console.log(`Comparing userA=${this.memberString(memberA)} userB=${this.memberString(memberB)}`); - const userA = memberA.user; const userB = memberB.user; - // if (!userA) console.log("!! MISSING USER FOR A-SIDE: " + memberA.name + " !!"); - // if (!userB) console.log("!! MISSING USER FOR B-SIDE: " + memberB.name + " !!"); - if (!userA && !userB) return 0; if (userA && !userB) return -1; if (!userA && userB) return 1; @@ -393,22 +386,18 @@ export default class MemberList extends React.Component { const idxA = presenceIndex(userA.currentlyActive ? 'active' : userA.presence); const idxB = presenceIndex(userB.currentlyActive ? 'active' : userB.presence); - // console.log(`userA_presenceGroup=${idxA} userB_presenceGroup=${idxB}`); if (idxA !== idxB) { - // console.log("Comparing on presence group - returning"); return idxA - idxB; } } // Second by power level if (memberA.powerLevel !== memberB.powerLevel) { - // console.log("Comparing on power level - returning"); return memberB.powerLevel - memberA.powerLevel; } // Third by last active if (this.showPresence && userA.getLastActiveTs() !== userB.getLastActiveTs()) { - // console.log("Comparing on last active timestamp - returning"); return userB.getLastActiveTs() - userA.getLastActiveTs(); } diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index 9f8133d55c..a9d73e218e 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -274,7 +274,7 @@ export default class RoomSublist extends React.Component { }; private onListsUpdated = () => { - const stateUpdates: IState & any = {}; // &any is to avoid a cast on the initializer + const stateUpdates = {} as IState; const currentRooms = this.state.rooms; const newRooms = arrayFastClone(RoomListStore.instance.orderedLists[this.props.tagId] || []); diff --git a/src/components/views/settings/ChangePassword.tsx b/src/components/views/settings/ChangePassword.tsx index f7c174da3b..edd6a4caac 100644 --- a/src/components/views/settings/ChangePassword.tsx +++ b/src/components/views/settings/ChangePassword.tsx @@ -353,13 +353,7 @@ export default class ChangePassword extends React.Component { } private allFieldsValid(): boolean { - const keys = Object.keys(this.state.fieldValid); - for (let i = 0; i < keys.length; ++i) { - if (!this.state.fieldValid[keys[i]]) { - return false; - } - } - return true; + return Object.values(this.state.fieldValid).every(Boolean); } private findFirstInvalidField(fieldIDs: string[]): Field { diff --git a/src/components/views/terms/InlineTermsAgreement.tsx b/src/components/views/terms/InlineTermsAgreement.tsx index 2cca51e670..3507822786 100644 --- a/src/components/views/terms/InlineTermsAgreement.tsx +++ b/src/components/views/terms/InlineTermsAgreement.tsx @@ -50,7 +50,7 @@ export default class InlineTermsAgreement extends React.Component { }); }); }); + + describe("/part", () => { + it("should part room matching alias if found", async () => { + const room1 = new Room("room-id", client, client.getUserId()); + room1.getCanonicalAlias = jest.fn().mockReturnValue("#foo:bar"); + const room2 = new Room("other-room", client, client.getUserId()); + room2.getCanonicalAlias = jest.fn().mockReturnValue("#baz:bar"); + mocked(client.getRooms).mockReturnValue([room1, room2]); + + const command = getCommand("/part #foo:bar"); + expect(command.cmd).toBeDefined(); + expect(command.args).toBeDefined(); + await command.cmd.run("room-id", null, command.args); + expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything()); + }); + + it("should part room matching alt alias if found", async () => { + const room1 = new Room("room-id", client, client.getUserId()); + room1.getAltAliases = jest.fn().mockReturnValue(["#foo:bar"]); + const room2 = new Room("other-room", client, client.getUserId()); + room2.getAltAliases = jest.fn().mockReturnValue(["#baz:bar"]); + mocked(client.getRooms).mockReturnValue([room1, room2]); + + const command = getCommand("/part #foo:bar"); + expect(command.cmd).toBeDefined(); + expect(command.args).toBeDefined(); + await command.cmd.run("room-id", null, command.args); + expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything()); + }); + }); }); diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 85045a6da8..ae7f53865b 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -187,6 +187,7 @@ export function createTestClient(): MatrixClient { } as unknown as MediaHandler), uploadContent: jest.fn(), getEventMapper: () => (opts) => new MatrixEvent(opts), + leaveRoomChain: jest.fn(roomId => ({ [roomId]: null })), } as unknown as MatrixClient; client.reEmitter = new ReEmitter(client);