diff --git a/res/css/_components.scss b/res/css/_components.scss index 7463f92037..7d387cea2e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -57,7 +57,6 @@ @import "./views/avatars/_BaseAvatar.scss"; @import "./views/avatars/_DecoratedRoomAvatar.scss"; @import "./views/avatars/_MemberStatusMessageAvatar.scss"; -@import "./views/avatars/_PulsedAvatar.scss"; @import "./views/avatars/_WidgetAvatar.scss"; @import "./views/beta/_BetaCard.scss"; @import "./views/context_menus/_CallContextMenu.scss"; diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index 52a2a68b6a..3222fe936c 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -121,23 +121,51 @@ $pulse-color: $pinned-unread-color; box-shadow: 0 0 0 0 rgba($pulse-color, 1); animation: mx_RightPanel_indicator_pulse 2s infinite; animation-iteration-count: 1; + + &::after { + content: ""; + position: absolute; + width: inherit; + height: inherit; + top: 0; + left: 0; + transform: scale(1); + transform-origin: center center; + animation-name: mx_RightPanel_indicator_pulse_shadow; + animation-duration: inherit; + animation-iteration-count: inherit; + border-radius: 50%; + background: rgba($pulse-color, 1); + } } } @keyframes mx_RightPanel_indicator_pulse { 0% { transform: scale(0.95); - box-shadow: 0 0 0 0 rgba($pulse-color, 0.7); } 70% { transform: scale(1); - box-shadow: 0 0 0 10px rgba($pulse-color, 0); } 100% { transform: scale(0.95); - box-shadow: 0 0 0 0 rgba($pulse-color, 0); + } +} + +@keyframes mx_RightPanel_indicator_pulse_shadow { + 0% { + opacity: 0.7; + } + + 70% { + transform: scale(2.2); + opacity: 0; + } + + 100% { + opacity: 0; } } diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 0efa2d01a1..831f186ed4 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -57,14 +57,15 @@ limitations under the License. @keyframes mx_RoomView_fileDropTarget_image_animation { from { - width: 0px; + transform: scaleX(0); } to { - width: 32px; + transform: scaleX(1); } } .mx_RoomView_fileDropTarget_image { + width: 32px; animation: mx_RoomView_fileDropTarget_image_animation; animation-duration: 0.5s; margin-bottom: 16px; diff --git a/res/css/views/avatars/_PulsedAvatar.scss b/res/css/views/avatars/_PulsedAvatar.scss deleted file mode 100644 index ce9e3382ab..0000000000 --- a/res/css/views/avatars/_PulsedAvatar.scss +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_PulsedAvatar { - @keyframes shadow-pulse { - 0% { - box-shadow: 0 0 0 0px rgba($accent-color, 0.2); - } - 100% { - box-shadow: 0 0 0 6px rgba($accent-color, 0); - } - } - - img { - animation: shadow-pulse 1s infinite; - } -} diff --git a/res/css/views/beta/_BetaCard.scss b/res/css/views/beta/_BetaCard.scss index 1a8241b65f..2af4e79ecd 100644 --- a/res/css/views/beta/_BetaCard.scss +++ b/res/css/views/beta/_BetaCard.scss @@ -110,24 +110,52 @@ $dot-size: 12px; width: $dot-size; transform: scale(1); background: rgba($pulse-color, 1); - box-shadow: 0 0 0 0 rgba($pulse-color, 1); animation: mx_Beta_bluePulse 2s infinite; animation-iteration-count: 20; + position: relative; + + &::after { + content: ""; + position: absolute; + width: inherit; + height: inherit; + top: 0; + left: 0; + transform: scale(1); + transform-origin: center center; + animation-name: mx_Beta_bluePulse_shadow; + animation-duration: inherit; + animation-iteration-count: inherit; + border-radius: 50%; + background: rgba($pulse-color, 1); + } } @keyframes mx_Beta_bluePulse { 0% { transform: scale(0.95); - box-shadow: 0 0 0 0 rgba($pulse-color, 0.7); } 70% { transform: scale(1); - box-shadow: 0 0 0 10px rgba($pulse-color, 0); } 100% { transform: scale(0.95); - box-shadow: 0 0 0 0 rgba($pulse-color, 0); + } +} + +@keyframes mx_Beta_bluePulse_shadow { + 0% { + opacity: 0.7; + } + + 70% { + transform: scale(2.2); + opacity: 0; + } + + 100% { + opacity: 0; } } diff --git a/res/css/views/dialogs/security/_AccessSecretStorageDialog.scss b/res/css/views/dialogs/security/_AccessSecretStorageDialog.scss index 30b79c1a9a..ec3bea0ef7 100644 --- a/res/css/views/dialogs/security/_AccessSecretStorageDialog.scss +++ b/res/css/views/dialogs/security/_AccessSecretStorageDialog.scss @@ -28,6 +28,7 @@ limitations under the License. left: 0; top: 2px; // alignment background-image: url("$(res)/img/element-icons/warning-badge.svg"); + background-size: contain; } .mx_AccessSecretStorageDialog_reset_link { diff --git a/src/CountlyAnalytics.ts b/src/CountlyAnalytics.ts index d4a340ddaf..a75c578536 100644 --- a/src/CountlyAnalytics.ts +++ b/src/CountlyAnalytics.ts @@ -16,12 +16,12 @@ limitations under the License. import { randomString } from "matrix-js-sdk/src/randomstring"; import { IContent } from "matrix-js-sdk/src/models/event"; +import { sleep } from "matrix-js-sdk/src/utils"; import { getCurrentLanguage } from './languageHandler'; import PlatformPeg from './PlatformPeg'; import SdkConfig from './SdkConfig'; import { MatrixClientPeg } from "./MatrixClientPeg"; -import { sleep } from "./utils/promise"; import RoomViewStore from "./stores/RoomViewStore"; import { Action } from "./dispatcher/actions"; diff --git a/src/Modal.tsx b/src/Modal.tsx index 0b0e621e89..55fc871d67 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -18,10 +18,10 @@ limitations under the License. import React from 'react'; import ReactDOM from 'react-dom'; import classNames from 'classnames'; +import { defer } from "matrix-js-sdk/src/utils"; import Analytics from './Analytics'; import dis from './dispatcher/dispatcher'; -import { defer } from './utils/promise'; import AsyncWrapper from './AsyncWrapper'; const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; diff --git a/src/Terms.ts b/src/Terms.ts index 0189810e7c..e9ba5ff8be 100644 --- a/src/Terms.ts +++ b/src/Terms.ts @@ -15,6 +15,7 @@ limitations under the License. */ import classNames from 'classnames'; +import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types'; import { MatrixClientPeg } from './MatrixClientPeg'; import * as sdk from '.'; @@ -32,7 +33,7 @@ export class Service { * @param {string} baseUrl The Base URL of the service (ie. before '/_matrix') * @param {string} accessToken The user's access token for the service */ - constructor(public serviceType: string, public baseUrl: string, public accessToken: string) { + constructor(public serviceType: SERVICE_TYPES, public baseUrl: string, public accessToken: string) { } } diff --git a/src/components/structures/FilePanel.tsx b/src/components/structures/FilePanel.tsx index 22a0e1283c..5865201069 100644 --- a/src/components/structures/FilePanel.tsx +++ b/src/components/structures/FilePanel.tsx @@ -19,6 +19,7 @@ import React from 'react'; import { Filter } from 'matrix-js-sdk/src/filter'; import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set"; +import { Direction } from "matrix-js-sdk/src/models/event-timeline"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from 'matrix-js-sdk/src/models/room'; import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window'; @@ -129,7 +130,7 @@ class FilePanel extends React.Component { } } - public async fetchFileEventsServer(room: Room): Promise { + public async fetchFileEventsServer(room: Room): Promise { const client = MatrixClientPeg.get(); const filter = new Filter(client.credentials.userId); @@ -153,7 +154,11 @@ class FilePanel extends React.Component { return timelineSet; } - private onPaginationRequest = (timelineWindow: TimelineWindow, direction: string, limit: number): void => { + private onPaginationRequest = ( + timelineWindow: TimelineWindow, + direction: Direction, + limit: number, + ): Promise => { const client = MatrixClientPeg.get(); const eventIndex = EventIndexPeg.get(); const roomId = this.props.roomId; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 93c44c4e50..f31f302b29 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -36,7 +36,7 @@ import FlairStore from '../../stores/FlairStore'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; import { makeGroupPermalink, makeUserPermalink } from "../../utils/permalinks/Permalinks"; import { Group } from "matrix-js-sdk/src/models/group"; -import { sleep } from "../../utils/promise"; +import { sleep } from "matrix-js-sdk/src/utils"; import RightPanelStore from "../../stores/RightPanelStore"; import AutoHideScrollbar from "./AutoHideScrollbar"; import { mediaFromMxc } from "../../customisations/Media"; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index a63767108f..06c7bfac8b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -19,6 +19,8 @@ import { createClient } from "matrix-js-sdk/src/matrix"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { sleep, defer, IDeferred } from "matrix-js-sdk/src/utils"; + // focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss import 'focus-visible'; // what-input helps improve keyboard accessibility @@ -55,7 +57,6 @@ import DMRoomMap from '../../utils/DMRoomMap'; import ThemeWatcher from "../../settings/watchers/ThemeWatcher"; import { FontWatcher } from '../../settings/watchers/FontWatcher'; import { storeRoomAliasInCache } from '../../RoomAliasCache'; -import { defer, IDeferred, sleep } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; import type LoggedInViewType from "./LoggedInView"; diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 8d5d733082..0b6d33b409 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -16,11 +16,13 @@ limitations under the License. import React, { createRef, ReactNode, SyntheticEvent } from 'react'; import ReactDOM from "react-dom"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { TimelineSet } from "matrix-js-sdk/src/models/event-timeline-set"; -import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline"; +import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set"; +import { Direction, EventTimeline } from "matrix-js-sdk/src/models/event-timeline"; import { TimelineWindow } from "matrix-js-sdk/src/timeline-window"; +import { EventType, RelationType } from 'matrix-js-sdk/src/@types/event'; +import { SyncState } from 'matrix-js-sdk/src/sync.api'; import SettingsStore from "../../settings/SettingsStore"; import { Layout } from "../../settings/Layout"; @@ -39,10 +41,8 @@ import { UIFeature } from "../../settings/UIFeature"; import { replaceableComponent } from "../../utils/replaceableComponent"; import { arrayFastClone } from "../../utils/arrays"; import MessagePanel from "./MessagePanel"; -import { SyncState } from 'matrix-js-sdk/src/sync.api'; import { IScrollState } from "./ScrollPanel"; import { ActionPayload } from "../../dispatcher/payloads"; -import { EventType } from 'matrix-js-sdk/src/@types/event'; import ResizeNotifier from "../../utils/ResizeNotifier"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; import Spinner from "../views/elements/Spinner"; @@ -65,7 +65,7 @@ interface IProps { // representing. This may or may not have a room, depending on what it's // a timeline representing. If it has a room, we maintain RRs etc for // that room. - timelineSet: TimelineSet; + timelineSet: EventTimelineSet; showReadReceipts?: boolean; // Enable managing RRs and RMs. These require the timelineSet to have a room. manageReadReceipts?: boolean; @@ -388,7 +388,7 @@ class TimelinePanel extends React.Component { private onPaginationRequest = ( timelineWindow: TimelineWindow, - direction: string, + direction: Direction, size: number, ): Promise => { if (this.props.onPaginationRequest) { @@ -579,7 +579,7 @@ class TimelinePanel extends React.Component { }); }; - private onRoomTimelineReset = (room: Room, timelineSet: TimelineSet): void => { + private onRoomTimelineReset = (room: Room, timelineSet: EventTimelineSet): void => { if (timelineSet !== this.props.timelineSet) return; if (this.messagePanel.current && this.messagePanel.current.isAtBottom()) { @@ -792,8 +792,8 @@ class TimelinePanel extends React.Component { // that sending an RR for the latest message will set our notif counter // to zero: it may not do this if we send an RR for somewhere before the end. if (this.isAtEndOfLiveTimeline()) { - this.props.timelineSet.room.setUnreadNotificationCount('total', 0); - this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0); + this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Total, 0); + this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Highlight, 0); dis.dispatch({ action: 'on_room_read', roomId: this.props.timelineSet.room.roomId, @@ -1416,7 +1416,11 @@ class TimelinePanel extends React.Component { }); } - private getRelationsForEvent = (...args) => this.props.timelineSet.getRelationsForEvent(...args); + private getRelationsForEvent = ( + eventId: string, + relationType: RelationType, + eventType: EventType | string, + ) => this.props.timelineSet.getRelationsForEvent(eventId, relationType, eventType); render() { // just show a spinner while the timeline loads. diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index adb36485a7..8dee21420e 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -17,6 +17,7 @@ limitations under the License. import React, { ReactNode, useContext, useMemo, useState } from "react"; import classNames from "classnames"; import { Room } from "matrix-js-sdk/src/models/room"; +import { sleep } from "matrix-js-sdk/src/utils"; import { _t } from '../../../languageHandler'; import { IDialogProps } from "./IDialogProps"; @@ -28,7 +29,6 @@ import RoomAvatar from "../avatars/RoomAvatar"; import { getDisplayAliasForRoom } from "../../../Rooms"; import AccessibleButton from "../elements/AccessibleButton"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; -import { sleep } from "../../../utils/promise"; import DMRoomMap from "../../../utils/DMRoomMap"; import { calculateRoomVia } from "../../../utils/permalinks/Permalinks"; import StyledCheckbox from "../elements/StyledCheckbox"; diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js index ad4fc8d672..09714e24e3 100644 --- a/src/components/views/dialogs/AddressPickerDialog.js +++ b/src/components/views/dialogs/AddressPickerDialog.js @@ -19,6 +19,7 @@ limitations under the License. import React, { createRef } from 'react'; import PropTypes from 'prop-types'; +import { sleep } from "matrix-js-sdk/src/utils"; import { _t, _td } from '../../../languageHandler'; import * as sdk from '../../../index'; @@ -30,7 +31,6 @@ import * as Email from '../../../email'; import IdentityAuthClient from '../../../IdentityAuthClient'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils'; import { abbreviateUrl } from '../../../utils/UrlUtils'; -import { sleep } from "../../../utils/promise"; import { Key } from "../../../Keyboard"; import { Action } from "../../../dispatcher/actions"; import { replaceableComponent } from "../../../utils/replaceableComponent"; diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index 2628170f9c..0de34251d8 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -122,7 +122,7 @@ export default class ImageView extends React.Component { const image = this.image.current; const imageWrapper = this.imageWrapper.current; - const rotation = inputRotation || this.state.rotation; + const rotation = inputRotation ?? this.state.rotation; const imageIsNotFlipped = rotation % 180 === 0; diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index ec190c829a..2c45c1bbf8 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -17,17 +17,19 @@ limitations under the License. import React, { ClipboardEvent, createRef, KeyboardEvent } from 'react'; import EMOJI_REGEX from 'emojibase-regex'; import { IContent, MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { DebouncedFunc, throttle } from 'lodash'; +import { EventType, RelationType } from "matrix-js-sdk/src/@types/event"; import dis from '../../../dispatcher/dispatcher'; import EditorModel from '../../../editor/model'; import { - htmlSerializeIfNeeded, - textSerialize, containsEmote, - stripEmoteCommand, - unescapeMessage, + htmlSerializeIfNeeded, startsWith, + stripEmoteCommand, stripPrefix, + textSerialize, + unescapeMessage, } from '../../../editor/serialize'; import { CommandPartCreator, Part, PartCreator, SerializedPart } from '../../../editor/parts'; import BasicMessageComposer from "./BasicMessageComposer"; @@ -52,7 +54,6 @@ import { Room } from 'matrix-js-sdk/src/models/room'; import ErrorDialog from "../dialogs/ErrorDialog"; import QuestionDialog from "../dialogs/QuestionDialog"; import { ActionPayload } from "../../../dispatcher/payloads"; -import { DebouncedFunc, throttle } from 'lodash'; function addReplyToMessageContent( content: IContent, @@ -258,12 +259,12 @@ export default class SendMessageComposer extends React.Component { const events = timeline.getEvents(); const reaction = this.model.parts[1].text; for (let i = events.length - 1; i >= 0; i--) { - if (events[i].getType() === "m.room.message") { + if (events[i].getType() === EventType.RoomMessage) { let shouldReact = true; const lastMessage = events[i]; const userId = MatrixClientPeg.get().getUserId(); const messageReactions = this.props.room.getUnfilteredTimelineSet() - .getRelationsForEvent(lastMessage.getId(), "m.annotation", "m.reaction"); + .getRelationsForEvent(lastMessage.getId(), RelationType.Annotation, EventType.Reaction); // if we have already sent this reaction, don't redact but don't re-send if (messageReactions) { @@ -274,9 +275,9 @@ export default class SendMessageComposer extends React.Component { shouldReact = !myReactionKeys.includes(reaction); } if (shouldReact) { - MatrixClientPeg.get().sendEvent(lastMessage.getRoomId(), "m.reaction", { + MatrixClientPeg.get().sendEvent(lastMessage.getRoomId(), EventType.Reaction, { "m.relates_to": { - "rel_type": "m.annotation", + "rel_type": RelationType.Annotation, "event_id": lastMessage.getId(), "key": reaction, }, diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index 1988ec50ac..405125bb59 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -24,6 +24,8 @@ import * as sdk from "../../../../.."; import PlatformPeg from "../../../../../PlatformPeg"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; +import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts"; +import AccessibleButton from "../../../elements/AccessibleButton"; interface IState { autoLaunch: boolean; @@ -45,6 +47,10 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta 'breadcrumbs', ]; + static KEYBINDINGS_SETTINGS = [ + 'ctrlFForSearch', + ]; + static COMPOSER_SETTINGS = [ 'MessageComposerInput.autoReplaceEmoji', 'MessageComposerInput.suggestEmoji', @@ -53,28 +59,32 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta 'MessageComposerInput.showStickersButton', ]; - static TIMELINE_SETTINGS = [ - 'showTypingNotifications', - 'autoplayGifsAndVideos', - 'urlPreviewsEnabled', - 'TextualBody.enableBigEmoji', - 'showReadReceipts', + static TIME_SETTINGS = [ 'showTwelveHourTimestamps', 'alwaysShowTimestamps', - 'showRedactions', + ]; + static CODE_BLOCKS_SETTINGS = [ 'enableSyntaxHighlightLanguageDetection', 'expandCodeByDefault', - 'scrollToBottomOnMessageSent', 'showCodeLineNumbers', - 'showJoinLeaves', - 'showAvatarChanges', - 'showDisplaynameChanges', - 'showImages', - 'showChatEffects', - 'Pill.shouldShowPillAvatar', - 'ctrlFForSearch', ]; - + static IMAGES_AND_VIDEOS_SETTINGS = [ + 'urlPreviewsEnabled', + 'autoplayGifsAndVideos', + 'showImages', + ]; + static TIMELINE_SETTINGS = [ + 'showTypingNotifications', + 'showRedactions', + 'showReadReceipts', + 'showJoinLeaves', + 'showDisplaynameChanges', + 'showChatEffects', + 'showAvatarChanges', + 'Pill.shouldShowPillAvatar', + 'TextualBody.enableBigEmoji', + 'scrollToBottomOnMessageSent', + ]; static GENERAL_SETTINGS = [ 'TagPanel.enableTagPanel', 'promptBeforeInviteUnknownUsers', @@ -222,11 +232,34 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta {this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)} +
+ {_t("Keyboard shortcuts")} + + { _t("To view all keyboard shortcuts, click here.") } + + {this.renderGroup(PreferencesUserSettingsTab.KEYBINDINGS_SETTINGS)} +
+ +
+ {_t("Displaying time")} + {this.renderGroup(PreferencesUserSettingsTab.TIME_SETTINGS)} +
+
{_t("Composer")} {this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
+
+ {_t("Code blocks")} + {this.renderGroup(PreferencesUserSettingsTab.CODE_BLOCKS_SETTINGS)} +
+ +
+ {_t("Images, GIFs and videos")} + {this.renderGroup(PreferencesUserSettingsTab.IMAGES_AND_VIDEOS_SETTINGS)} +
+
{_t("Timeline")} {this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)} diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 79d8c9224e..a03598b21f 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -17,6 +17,8 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; +import { sleep } from "matrix-js-sdk/src/utils"; + import { _t } from "../../../../../languageHandler"; import SdkConfig from "../../../../../SdkConfig"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; @@ -25,7 +27,6 @@ import AccessibleButton from "../../../elements/AccessibleButton"; import Analytics from "../../../../../Analytics"; import Modal from "../../../../../Modal"; import * as sdk from "../../../../.."; -import { sleep } from "../../../../../utils/promise"; import dis from "../../../../../dispatcher/dispatcher"; import { privateShouldBeEncrypted } from "../../../../../createRoom"; import { SettingLevel } from "../../../../../settings/SettingLevel"; diff --git a/src/components/views/spaces/SpacePublicShare.tsx b/src/components/views/spaces/SpacePublicShare.tsx index 4169e40269..39e5115e55 100644 --- a/src/components/views/spaces/SpacePublicShare.tsx +++ b/src/components/views/spaces/SpacePublicShare.tsx @@ -16,11 +16,11 @@ limitations under the License. import React, { useState } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; +import { sleep } from "matrix-js-sdk/src/utils"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import { copyPlaintext } from "../../../utils/strings"; -import { sleep } from "../../../utils/promise"; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import { showRoomInviteDialog } from "../../../RoomInvite"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a554345ec8..7d0f863b7f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -850,8 +850,8 @@ "Enable big emoji in chat": "Enable big emoji in chat", "Send typing notifications": "Send typing notifications", "Show typing notifications": "Show typing notifications", - "Use Command + F to search": "Use Command + F to search", - "Use Ctrl + F to search": "Use Ctrl + F to search", + "Use Command + F to search timeline": "Use Command + F to search timeline", + "Use Ctrl + F to search timeline": "Use Ctrl + F to search timeline", "Use Command + Enter to send a message": "Use Command + Enter to send a message", "Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message", "Automatically replace plain text Emoji": "Automatically replace plain text Emoji", @@ -1348,7 +1348,12 @@ "Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close", "Preferences": "Preferences", "Room list": "Room list", + "Keyboard shortcuts": "Keyboard shortcuts", + "To view all keyboard shortcuts, click here.": "To view all keyboard shortcuts, click here.", + "Displaying time": "Displaying time", "Composer": "Composer", + "Code blocks": "Code blocks", + "Images, GIFs and videos": "Images, GIFs and videos", "Timeline": "Timeline", "Autocomplete delay (ms)": "Autocomplete delay (ms)", "Read Marker lifetime (ms)": "Read Marker lifetime (ms)", diff --git a/src/indexing/EventIndex.ts b/src/indexing/EventIndex.ts index 43b3de42ed..76104455f7 100644 --- a/src/indexing/EventIndex.ts +++ b/src/indexing/EventIndex.ts @@ -16,16 +16,16 @@ limitations under the License. import { EventEmitter } from "events"; import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; -import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline'; +import { Direction, EventTimeline } from 'matrix-js-sdk/src/models/event-timeline'; import { Room } from 'matrix-js-sdk/src/models/room'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set'; import { RoomState } from 'matrix-js-sdk/src/models/room-state'; import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window'; +import { sleep } from "matrix-js-sdk/src/utils"; import PlatformPeg from "../PlatformPeg"; import { MatrixClientPeg } from "../MatrixClientPeg"; -import { sleep } from "../utils/promise"; import SettingsStore from "../settings/SettingsStore"; import { SettingLevel } from "../settings/SettingLevel"; import { ICrawlerCheckpoint, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager"; @@ -109,7 +109,7 @@ export default class EventIndex extends EventEmitter { // our message crawler. await Promise.all(encryptedRooms.map(async (room) => { const timeline = room.getLiveTimeline(); - const token = timeline.getPaginationToken("b"); + const token = timeline.getPaginationToken(Direction.Backward); const backCheckpoint: ICrawlerCheckpoint = { roomId: room.roomId, @@ -371,7 +371,7 @@ export default class EventIndex extends EventEmitter { if (!room) return; const timeline = room.getLiveTimeline(); - const token = timeline.getPaginationToken("b"); + const token = timeline.getPaginationToken(Direction.Backward); if (!token) { // The room doesn't contain any tokens, meaning the live timeline @@ -862,7 +862,7 @@ export default class EventIndex extends EventEmitter { * @returns {Promise} Resolves to a boolean which is true if more * events were successfully retrieved. */ - public paginateTimelineWindow(room: Room, timelineWindow: TimelineWindow, direction: string, limit: number) { + public paginateTimelineWindow(room: Room, timelineWindow: TimelineWindow, direction: Direction, limit: number) { const tl = timelineWindow.getTimelineIndex(direction); if (!tl) return Promise.resolve(false); diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 96820d0ff2..1751eddb2c 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -455,7 +455,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { }, "ctrlFForSearch": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: isMac ? _td("Use Command + F to search") : _td("Use Ctrl + F to search"), + displayName: isMac ? _td("Use Command + F to search timeline") : _td("Use Ctrl + F to search timeline"), default: false, }, "MessageComposerInput.ctrlEnterToSend": { diff --git a/src/utils/MultiInviter.ts b/src/utils/MultiInviter.ts index 4e66d4a99f..a7d1accde1 100644 --- a/src/utils/MultiInviter.ts +++ b/src/utils/MultiInviter.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { MatrixError } from "matrix-js-sdk/src/http-api"; +import { defer, IDeferred } from "matrix-js-sdk/src/utils"; import { MatrixClientPeg } from '../MatrixClientPeg'; import { AddressType, getAddressType } from '../UserAddress'; @@ -22,7 +23,6 @@ import GroupStore from '../stores/GroupStore'; import { _t } from "../languageHandler"; import Modal from "../Modal"; import SettingsStore from "../settings/SettingsStore"; -import { defer, IDeferred } from "./promise"; import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog"; export enum InviteState { diff --git a/src/utils/promise.ts b/src/utils/promise.ts index 902d3c7e31..853c172269 100644 --- a/src/utils/promise.ts +++ b/src/utils/promise.ts @@ -14,11 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Returns a promise which resolves with a given value after the given number of ms -export function sleep(ms: number, value?: T): Promise { - return new Promise((resolve => { setTimeout(resolve, ms, value); })); -} - // Returns a promise which resolves when the input promise resolves with its value // or when the timeout of ms is reached with the value of given timeoutValue export async function timeout(promise: Promise, timeoutValue: T, ms: number): Promise { @@ -32,25 +27,6 @@ export async function timeout(promise: Promise, timeoutValue: T, ms: numbe return Promise.race([promise, timeoutPromise]); } -export interface IDeferred { - resolve: (value: T) => void; - reject: (any) => void; - promise: Promise; -} - -// Returns a Deferred -export function defer(): IDeferred { - let resolve; - let reject; - - const promise = new Promise((_resolve, _reject) => { - resolve = _resolve; - reject = _reject; - }); - - return { resolve, reject, promise }; -} - // Helper method to retry a Promise a given number of times or until a predicate fails export async function retry(fn: () => Promise, num: number, predicate?: (e: E) => boolean) { let lastErr: E; diff --git a/test/components/views/dialogs/InteractiveAuthDialog-test.js b/test/components/views/dialogs/InteractiveAuthDialog-test.js index 86fa562b60..dcf89afb4d 100644 --- a/test/components/views/dialogs/InteractiveAuthDialog-test.js +++ b/test/components/views/dialogs/InteractiveAuthDialog-test.js @@ -18,12 +18,12 @@ import React from 'react'; import ReactDOM from 'react-dom'; import ReactTestUtils from 'react-dom/test-utils'; import MatrixReactTestUtils from 'matrix-react-test-utils'; +import { sleep } from "matrix-js-sdk/src/utils"; import sdk from '../../../skinned-sdk'; import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; import * as TestUtilsMatrix from '../../../test-utils'; -import { sleep } from "../../../../src/utils/promise"; const InteractiveAuthDialog = sdk.getComponent( 'views.dialogs.InteractiveAuthDialog', diff --git a/test/components/views/rooms/SendMessageComposer-test.js b/test/components/views/rooms/SendMessageComposer-test.js index 2947f0fe60..0c4bde76a8 100644 --- a/test/components/views/rooms/SendMessageComposer-test.js +++ b/test/components/views/rooms/SendMessageComposer-test.js @@ -19,6 +19,8 @@ import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; import { configure, mount } from "enzyme"; import React from "react"; import { act } from "react-dom/test-utils"; +import { sleep } from "matrix-js-sdk/src/utils"; + import SendMessageComposer, { createMessageContent, isQuickReaction, @@ -29,7 +31,6 @@ import { createPartCreator, createRenderer } from "../../../editor/mock"; import { createTestClient, mkEvent, mkStubRoom } from "../../../test-utils"; import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; -import { sleep } from "../../../../src/utils/promise"; import SpecPermalinkConstructor from "../../../../src/utils/permalinks/SpecPermalinkConstructor"; import defaultDispatcher from "../../../../src/dispatcher/dispatcher";