Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/17686

 Conflicts:
	src/components/views/dialogs/AddExistingToSpaceDialog.tsx
This commit is contained in:
Michael Telatynski 2021-07-05 13:06:22 +01:00
commit 1b25ab930e
27 changed files with 178 additions and 123 deletions

View file

@ -57,7 +57,6 @@
@import "./views/avatars/_BaseAvatar.scss"; @import "./views/avatars/_BaseAvatar.scss";
@import "./views/avatars/_DecoratedRoomAvatar.scss"; @import "./views/avatars/_DecoratedRoomAvatar.scss";
@import "./views/avatars/_MemberStatusMessageAvatar.scss"; @import "./views/avatars/_MemberStatusMessageAvatar.scss";
@import "./views/avatars/_PulsedAvatar.scss";
@import "./views/avatars/_WidgetAvatar.scss"; @import "./views/avatars/_WidgetAvatar.scss";
@import "./views/beta/_BetaCard.scss"; @import "./views/beta/_BetaCard.scss";
@import "./views/context_menus/_CallContextMenu.scss"; @import "./views/context_menus/_CallContextMenu.scss";

View file

@ -121,23 +121,51 @@ $pulse-color: $pinned-unread-color;
box-shadow: 0 0 0 0 rgba($pulse-color, 1); box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_RightPanel_indicator_pulse 2s infinite; animation: mx_RightPanel_indicator_pulse 2s infinite;
animation-iteration-count: 1; 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 { @keyframes mx_RightPanel_indicator_pulse {
0% { 0% {
transform: scale(0.95); transform: scale(0.95);
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
} }
70% { 70% {
transform: scale(1); transform: scale(1);
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
} }
100% { 100% {
transform: scale(0.95); 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;
} }
} }

View file

@ -57,14 +57,15 @@ limitations under the License.
@keyframes mx_RoomView_fileDropTarget_image_animation { @keyframes mx_RoomView_fileDropTarget_image_animation {
from { from {
width: 0px; transform: scaleX(0);
} }
to { to {
width: 32px; transform: scaleX(1);
} }
} }
.mx_RoomView_fileDropTarget_image { .mx_RoomView_fileDropTarget_image {
width: 32px;
animation: mx_RoomView_fileDropTarget_image_animation; animation: mx_RoomView_fileDropTarget_image_animation;
animation-duration: 0.5s; animation-duration: 0.5s;
margin-bottom: 16px; margin-bottom: 16px;

View file

@ -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;
}
}

View file

@ -110,24 +110,52 @@ $dot-size: 12px;
width: $dot-size; width: $dot-size;
transform: scale(1); transform: scale(1);
background: rgba($pulse-color, 1); background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_Beta_bluePulse 2s infinite; animation: mx_Beta_bluePulse 2s infinite;
animation-iteration-count: 20; 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 { @keyframes mx_Beta_bluePulse {
0% { 0% {
transform: scale(0.95); transform: scale(0.95);
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
} }
70% { 70% {
transform: scale(1); transform: scale(1);
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
} }
100% { 100% {
transform: scale(0.95); 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;
} }
} }

View file

@ -28,6 +28,7 @@ limitations under the License.
left: 0; left: 0;
top: 2px; // alignment top: 2px; // alignment
background-image: url("$(res)/img/element-icons/warning-badge.svg"); background-image: url("$(res)/img/element-icons/warning-badge.svg");
background-size: contain;
} }
.mx_AccessSecretStorageDialog_reset_link { .mx_AccessSecretStorageDialog_reset_link {

View file

@ -16,12 +16,12 @@ limitations under the License.
import { randomString } from "matrix-js-sdk/src/randomstring"; import { randomString } from "matrix-js-sdk/src/randomstring";
import { IContent } from "matrix-js-sdk/src/models/event"; import { IContent } from "matrix-js-sdk/src/models/event";
import { sleep } from "matrix-js-sdk/src/utils";
import { getCurrentLanguage } from './languageHandler'; import { getCurrentLanguage } from './languageHandler';
import PlatformPeg from './PlatformPeg'; import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig'; import SdkConfig from './SdkConfig';
import { MatrixClientPeg } from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import { sleep } from "./utils/promise";
import RoomViewStore from "./stores/RoomViewStore"; import RoomViewStore from "./stores/RoomViewStore";
import { Action } from "./dispatcher/actions"; import { Action } from "./dispatcher/actions";

View file

@ -18,10 +18,10 @@ limitations under the License.
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import { defer } from "matrix-js-sdk/src/utils";
import Analytics from './Analytics'; import Analytics from './Analytics';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import { defer } from './utils/promise';
import AsyncWrapper from './AsyncWrapper'; import AsyncWrapper from './AsyncWrapper';
const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; const DIALOG_CONTAINER_ID = "mx_Dialog_Container";

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types';
import { MatrixClientPeg } from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import * as sdk from '.'; 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} baseUrl The Base URL of the service (ie. before '/_matrix')
* @param {string} accessToken The user's access token for the service * @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) {
} }
} }

View file

@ -19,6 +19,7 @@ import React from 'react';
import { Filter } from 'matrix-js-sdk/src/filter'; import { Filter } from 'matrix-js-sdk/src/filter';
import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set"; 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 { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from 'matrix-js-sdk/src/models/room'; import { Room } from 'matrix-js-sdk/src/models/room';
import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window'; import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window';
@ -129,7 +130,7 @@ class FilePanel extends React.Component<IProps, IState> {
} }
} }
public async fetchFileEventsServer(room: Room): Promise<void> { public async fetchFileEventsServer(room: Room): Promise<EventTimelineSet> {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const filter = new Filter(client.credentials.userId); const filter = new Filter(client.credentials.userId);
@ -153,7 +154,11 @@ class FilePanel extends React.Component<IProps, IState> {
return timelineSet; return timelineSet;
} }
private onPaginationRequest = (timelineWindow: TimelineWindow, direction: string, limit: number): void => { private onPaginationRequest = (
timelineWindow: TimelineWindow,
direction: Direction,
limit: number,
): Promise<boolean> => {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const eventIndex = EventIndexPeg.get(); const eventIndex = EventIndexPeg.get();
const roomId = this.props.roomId; const roomId = this.props.roomId;

View file

@ -36,7 +36,7 @@ import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import { makeGroupPermalink, makeUserPermalink } from "../../utils/permalinks/Permalinks"; import { makeGroupPermalink, makeUserPermalink } from "../../utils/permalinks/Permalinks";
import { Group } from "matrix-js-sdk/src/models/group"; 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 RightPanelStore from "../../stores/RightPanelStore";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import { mediaFromMxc } from "../../customisations/Media"; import { mediaFromMxc } from "../../customisations/Media";

View file

@ -19,6 +19,8 @@ import { createClient } from "matrix-js-sdk/src/matrix";
import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { InvalidStoreError } from "matrix-js-sdk/src/errors";
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; 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 // focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
import 'focus-visible'; import 'focus-visible';
// what-input helps improve keyboard accessibility // what-input helps improve keyboard accessibility
@ -55,7 +57,6 @@ import DMRoomMap from '../../utils/DMRoomMap';
import ThemeWatcher from "../../settings/watchers/ThemeWatcher"; import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
import { FontWatcher } from '../../settings/watchers/FontWatcher'; import { FontWatcher } from '../../settings/watchers/FontWatcher';
import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { storeRoomAliasInCache } from '../../RoomAliasCache';
import { defer, IDeferred, sleep } from "../../utils/promise";
import ToastStore from "../../stores/ToastStore"; import ToastStore from "../../stores/ToastStore";
import * as StorageManager from "../../utils/StorageManager"; import * as StorageManager from "../../utils/StorageManager";
import type LoggedInViewType from "./LoggedInView"; import type LoggedInViewType from "./LoggedInView";

View file

@ -16,11 +16,13 @@ limitations under the License.
import React, { createRef, ReactNode, SyntheticEvent } from 'react'; import React, { createRef, ReactNode, SyntheticEvent } from 'react';
import ReactDOM from "react-dom"; 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 { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { TimelineSet } from "matrix-js-sdk/src/models/event-timeline-set"; import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline"; import { Direction, EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { TimelineWindow } from "matrix-js-sdk/src/timeline-window"; 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 SettingsStore from "../../settings/SettingsStore";
import { Layout } from "../../settings/Layout"; import { Layout } from "../../settings/Layout";
@ -39,10 +41,8 @@ import { UIFeature } from "../../settings/UIFeature";
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import { arrayFastClone } from "../../utils/arrays"; import { arrayFastClone } from "../../utils/arrays";
import MessagePanel from "./MessagePanel"; import MessagePanel from "./MessagePanel";
import { SyncState } from 'matrix-js-sdk/src/sync.api';
import { IScrollState } from "./ScrollPanel"; import { IScrollState } from "./ScrollPanel";
import { ActionPayload } from "../../dispatcher/payloads"; import { ActionPayload } from "../../dispatcher/payloads";
import { EventType } from 'matrix-js-sdk/src/@types/event';
import ResizeNotifier from "../../utils/ResizeNotifier"; import ResizeNotifier from "../../utils/ResizeNotifier";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import Spinner from "../views/elements/Spinner"; 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 // 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 // a timeline representing. If it has a room, we maintain RRs etc for
// that room. // that room.
timelineSet: TimelineSet; timelineSet: EventTimelineSet;
showReadReceipts?: boolean; showReadReceipts?: boolean;
// Enable managing RRs and RMs. These require the timelineSet to have a room. // Enable managing RRs and RMs. These require the timelineSet to have a room.
manageReadReceipts?: boolean; manageReadReceipts?: boolean;
@ -388,7 +388,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
private onPaginationRequest = ( private onPaginationRequest = (
timelineWindow: TimelineWindow, timelineWindow: TimelineWindow,
direction: string, direction: Direction,
size: number, size: number,
): Promise<boolean> => { ): Promise<boolean> => {
if (this.props.onPaginationRequest) { if (this.props.onPaginationRequest) {
@ -579,7 +579,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
}); });
}; };
private onRoomTimelineReset = (room: Room, timelineSet: TimelineSet): void => { private onRoomTimelineReset = (room: Room, timelineSet: EventTimelineSet): void => {
if (timelineSet !== this.props.timelineSet) return; if (timelineSet !== this.props.timelineSet) return;
if (this.messagePanel.current && this.messagePanel.current.isAtBottom()) { if (this.messagePanel.current && this.messagePanel.current.isAtBottom()) {
@ -792,8 +792,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
// that sending an RR for the latest message will set our notif counter // 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. // to zero: it may not do this if we send an RR for somewhere before the end.
if (this.isAtEndOfLiveTimeline()) { if (this.isAtEndOfLiveTimeline()) {
this.props.timelineSet.room.setUnreadNotificationCount('total', 0); this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Total, 0);
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0); this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Highlight, 0);
dis.dispatch({ dis.dispatch({
action: 'on_room_read', action: 'on_room_read',
roomId: this.props.timelineSet.room.roomId, roomId: this.props.timelineSet.room.roomId,
@ -1416,7 +1416,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
}); });
} }
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() { render() {
// just show a spinner while the timeline loads. // just show a spinner while the timeline loads.

View file

@ -17,6 +17,7 @@ limitations under the License.
import React, { ReactNode, useContext, useMemo, useState } from "react"; import React, { ReactNode, useContext, useMemo, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { sleep } from "matrix-js-sdk/src/utils";
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { IDialogProps } from "./IDialogProps"; import { IDialogProps } from "./IDialogProps";
@ -28,7 +29,6 @@ import RoomAvatar from "../avatars/RoomAvatar";
import { getDisplayAliasForRoom } from "../../../Rooms"; import { getDisplayAliasForRoom } from "../../../Rooms";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import { sleep } from "../../../utils/promise";
import DMRoomMap from "../../../utils/DMRoomMap"; import DMRoomMap from "../../../utils/DMRoomMap";
import { calculateRoomVia } from "../../../utils/permalinks/Permalinks"; import { calculateRoomVia } from "../../../utils/permalinks/Permalinks";
import StyledCheckbox from "../elements/StyledCheckbox"; import StyledCheckbox from "../elements/StyledCheckbox";

View file

@ -19,6 +19,7 @@ limitations under the License.
import React, { createRef } from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { sleep } from "matrix-js-sdk/src/utils";
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
@ -30,7 +31,6 @@ import * as Email from '../../../email';
import IdentityAuthClient from '../../../IdentityAuthClient'; import IdentityAuthClient from '../../../IdentityAuthClient';
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils';
import { abbreviateUrl } from '../../../utils/UrlUtils'; import { abbreviateUrl } from '../../../utils/UrlUtils';
import { sleep } from "../../../utils/promise";
import { Key } from "../../../Keyboard"; import { Key } from "../../../Keyboard";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";

View file

@ -122,7 +122,7 @@ export default class ImageView extends React.Component<IProps, IState> {
const image = this.image.current; const image = this.image.current;
const imageWrapper = this.imageWrapper.current; const imageWrapper = this.imageWrapper.current;
const rotation = inputRotation || this.state.rotation; const rotation = inputRotation ?? this.state.rotation;
const imageIsNotFlipped = rotation % 180 === 0; const imageIsNotFlipped = rotation % 180 === 0;

View file

@ -17,17 +17,19 @@ limitations under the License.
import React, { ClipboardEvent, createRef, KeyboardEvent } from 'react'; import React, { ClipboardEvent, createRef, KeyboardEvent } from 'react';
import EMOJI_REGEX from 'emojibase-regex'; import EMOJI_REGEX from 'emojibase-regex';
import { IContent, MatrixEvent } from 'matrix-js-sdk/src/models/event'; 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 dis from '../../../dispatcher/dispatcher';
import EditorModel from '../../../editor/model'; import EditorModel from '../../../editor/model';
import { import {
htmlSerializeIfNeeded,
textSerialize,
containsEmote, containsEmote,
stripEmoteCommand, htmlSerializeIfNeeded,
unescapeMessage,
startsWith, startsWith,
stripEmoteCommand,
stripPrefix, stripPrefix,
textSerialize,
unescapeMessage,
} from '../../../editor/serialize'; } from '../../../editor/serialize';
import { CommandPartCreator, Part, PartCreator, SerializedPart } from '../../../editor/parts'; import { CommandPartCreator, Part, PartCreator, SerializedPart } from '../../../editor/parts';
import BasicMessageComposer from "./BasicMessageComposer"; import BasicMessageComposer from "./BasicMessageComposer";
@ -52,7 +54,6 @@ import { Room } from 'matrix-js-sdk/src/models/room';
import ErrorDialog from "../dialogs/ErrorDialog"; import ErrorDialog from "../dialogs/ErrorDialog";
import QuestionDialog from "../dialogs/QuestionDialog"; import QuestionDialog from "../dialogs/QuestionDialog";
import { ActionPayload } from "../../../dispatcher/payloads"; import { ActionPayload } from "../../../dispatcher/payloads";
import { DebouncedFunc, throttle } from 'lodash';
function addReplyToMessageContent( function addReplyToMessageContent(
content: IContent, content: IContent,
@ -258,12 +259,12 @@ export default class SendMessageComposer extends React.Component<IProps> {
const events = timeline.getEvents(); const events = timeline.getEvents();
const reaction = this.model.parts[1].text; const reaction = this.model.parts[1].text;
for (let i = events.length - 1; i >= 0; i--) { 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; let shouldReact = true;
const lastMessage = events[i]; const lastMessage = events[i];
const userId = MatrixClientPeg.get().getUserId(); const userId = MatrixClientPeg.get().getUserId();
const messageReactions = this.props.room.getUnfilteredTimelineSet() 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 we have already sent this reaction, don't redact but don't re-send
if (messageReactions) { if (messageReactions) {
@ -274,9 +275,9 @@ export default class SendMessageComposer extends React.Component<IProps> {
shouldReact = !myReactionKeys.includes(reaction); shouldReact = !myReactionKeys.includes(reaction);
} }
if (shouldReact) { if (shouldReact) {
MatrixClientPeg.get().sendEvent(lastMessage.getRoomId(), "m.reaction", { MatrixClientPeg.get().sendEvent(lastMessage.getRoomId(), EventType.Reaction, {
"m.relates_to": { "m.relates_to": {
"rel_type": "m.annotation", "rel_type": RelationType.Annotation,
"event_id": lastMessage.getId(), "event_id": lastMessage.getId(),
"key": reaction, "key": reaction,
}, },

View file

@ -24,6 +24,8 @@ import * as sdk from "../../../../..";
import PlatformPeg from "../../../../../PlatformPeg"; import PlatformPeg from "../../../../../PlatformPeg";
import { SettingLevel } from "../../../../../settings/SettingLevel"; import { SettingLevel } from "../../../../../settings/SettingLevel";
import { replaceableComponent } from "../../../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../../../utils/replaceableComponent";
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
import AccessibleButton from "../../../elements/AccessibleButton";
interface IState { interface IState {
autoLaunch: boolean; autoLaunch: boolean;
@ -45,6 +47,10 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
'breadcrumbs', 'breadcrumbs',
]; ];
static KEYBINDINGS_SETTINGS = [
'ctrlFForSearch',
];
static COMPOSER_SETTINGS = [ static COMPOSER_SETTINGS = [
'MessageComposerInput.autoReplaceEmoji', 'MessageComposerInput.autoReplaceEmoji',
'MessageComposerInput.suggestEmoji', 'MessageComposerInput.suggestEmoji',
@ -53,28 +59,32 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
'MessageComposerInput.showStickersButton', 'MessageComposerInput.showStickersButton',
]; ];
static TIMELINE_SETTINGS = [ static TIME_SETTINGS = [
'showTypingNotifications',
'autoplayGifsAndVideos',
'urlPreviewsEnabled',
'TextualBody.enableBigEmoji',
'showReadReceipts',
'showTwelveHourTimestamps', 'showTwelveHourTimestamps',
'alwaysShowTimestamps', 'alwaysShowTimestamps',
'showRedactions', ];
static CODE_BLOCKS_SETTINGS = [
'enableSyntaxHighlightLanguageDetection', 'enableSyntaxHighlightLanguageDetection',
'expandCodeByDefault', 'expandCodeByDefault',
'scrollToBottomOnMessageSent',
'showCodeLineNumbers', '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 = [ static GENERAL_SETTINGS = [
'TagPanel.enableTagPanel', 'TagPanel.enableTagPanel',
'promptBeforeInviteUnknownUsers', 'promptBeforeInviteUnknownUsers',
@ -222,11 +232,34 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
{this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)} {this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
</div> </div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Keyboard shortcuts")}</span>
<AccessibleButton className="mx_SettingsFlag" onClick={KeyboardShortcuts.toggleDialog}>
{ _t("To view all keyboard shortcuts, click here.") }
</AccessibleButton>
{this.renderGroup(PreferencesUserSettingsTab.KEYBINDINGS_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Displaying time")}</span>
{this.renderGroup(PreferencesUserSettingsTab.TIME_SETTINGS)}
</div>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span> <span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
{this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)} {this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
</div> </div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Code blocks")}</span>
{this.renderGroup(PreferencesUserSettingsTab.CODE_BLOCKS_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Images, GIFs and videos")}</span>
{this.renderGroup(PreferencesUserSettingsTab.IMAGES_AND_VIDEOS_SETTINGS)}
</div>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span> <span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)} {this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}

View file

@ -17,6 +17,8 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { sleep } from "matrix-js-sdk/src/utils";
import { _t } from "../../../../../languageHandler"; import { _t } from "../../../../../languageHandler";
import SdkConfig from "../../../../../SdkConfig"; import SdkConfig from "../../../../../SdkConfig";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
@ -25,7 +27,6 @@ import AccessibleButton from "../../../elements/AccessibleButton";
import Analytics from "../../../../../Analytics"; import Analytics from "../../../../../Analytics";
import Modal from "../../../../../Modal"; import Modal from "../../../../../Modal";
import * as sdk from "../../../../.."; import * as sdk from "../../../../..";
import { sleep } from "../../../../../utils/promise";
import dis from "../../../../../dispatcher/dispatcher"; import dis from "../../../../../dispatcher/dispatcher";
import { privateShouldBeEncrypted } from "../../../../../createRoom"; import { privateShouldBeEncrypted } from "../../../../../createRoom";
import { SettingLevel } from "../../../../../settings/SettingLevel"; import { SettingLevel } from "../../../../../settings/SettingLevel";

View file

@ -16,11 +16,11 @@ limitations under the License.
import React, { useState } from "react"; import React, { useState } from "react";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { sleep } from "matrix-js-sdk/src/utils";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import { copyPlaintext } from "../../../utils/strings"; import { copyPlaintext } from "../../../utils/strings";
import { sleep } from "../../../utils/promise";
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
import { showRoomInviteDialog } from "../../../RoomInvite"; import { showRoomInviteDialog } from "../../../RoomInvite";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";

View file

@ -850,8 +850,8 @@
"Enable big emoji in chat": "Enable big emoji in chat", "Enable big emoji in chat": "Enable big emoji in chat",
"Send typing notifications": "Send typing notifications", "Send typing notifications": "Send typing notifications",
"Show typing notifications": "Show typing notifications", "Show typing notifications": "Show typing notifications",
"Use Command + F to search": "Use Command + F to search", "Use Command + F to search timeline": "Use Command + F to search timeline",
"Use Ctrl + F to search": "Use Ctrl + F to search", "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 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", "Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji", "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", "Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close",
"Preferences": "Preferences", "Preferences": "Preferences",
"Room list": "Room list", "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", "Composer": "Composer",
"Code blocks": "Code blocks",
"Images, GIFs and videos": "Images, GIFs and videos",
"Timeline": "Timeline", "Timeline": "Timeline",
"Autocomplete delay (ms)": "Autocomplete delay (ms)", "Autocomplete delay (ms)": "Autocomplete delay (ms)",
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)", "Read Marker lifetime (ms)": "Read Marker lifetime (ms)",

View file

@ -16,16 +16,16 @@ limitations under the License.
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; 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 { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set'; import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set';
import { RoomState } from 'matrix-js-sdk/src/models/room-state'; import { RoomState } from 'matrix-js-sdk/src/models/room-state';
import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window'; import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window';
import { sleep } from "matrix-js-sdk/src/utils";
import PlatformPeg from "../PlatformPeg"; import PlatformPeg from "../PlatformPeg";
import { MatrixClientPeg } from "../MatrixClientPeg"; import { MatrixClientPeg } from "../MatrixClientPeg";
import { sleep } from "../utils/promise";
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import { SettingLevel } from "../settings/SettingLevel"; import { SettingLevel } from "../settings/SettingLevel";
import { ICrawlerCheckpoint, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager"; import { ICrawlerCheckpoint, ILoadArgs, ISearchArgs } from "./BaseEventIndexManager";
@ -109,7 +109,7 @@ export default class EventIndex extends EventEmitter {
// our message crawler. // our message crawler.
await Promise.all(encryptedRooms.map(async (room) => { await Promise.all(encryptedRooms.map(async (room) => {
const timeline = room.getLiveTimeline(); const timeline = room.getLiveTimeline();
const token = timeline.getPaginationToken("b"); const token = timeline.getPaginationToken(Direction.Backward);
const backCheckpoint: ICrawlerCheckpoint = { const backCheckpoint: ICrawlerCheckpoint = {
roomId: room.roomId, roomId: room.roomId,
@ -371,7 +371,7 @@ export default class EventIndex extends EventEmitter {
if (!room) return; if (!room) return;
const timeline = room.getLiveTimeline(); const timeline = room.getLiveTimeline();
const token = timeline.getPaginationToken("b"); const token = timeline.getPaginationToken(Direction.Backward);
if (!token) { if (!token) {
// The room doesn't contain any tokens, meaning the live timeline // The room doesn't contain any tokens, meaning the live timeline
@ -862,7 +862,7 @@ export default class EventIndex extends EventEmitter {
* @returns {Promise<boolean>} Resolves to a boolean which is true if more * @returns {Promise<boolean>} Resolves to a boolean which is true if more
* events were successfully retrieved. * 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); const tl = timelineWindow.getTimelineIndex(direction);
if (!tl) return Promise.resolve(false); if (!tl) return Promise.resolve(false);

View file

@ -455,7 +455,7 @@ export const SETTINGS: {[setting: string]: ISetting} = {
}, },
"ctrlFForSearch": { "ctrlFForSearch": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS, 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, default: false,
}, },
"MessageComposerInput.ctrlEnterToSend": { "MessageComposerInput.ctrlEnterToSend": {

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import { MatrixError } from "matrix-js-sdk/src/http-api"; import { MatrixError } from "matrix-js-sdk/src/http-api";
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
import { MatrixClientPeg } from '../MatrixClientPeg'; import { MatrixClientPeg } from '../MatrixClientPeg';
import { AddressType, getAddressType } from '../UserAddress'; import { AddressType, getAddressType } from '../UserAddress';
@ -22,7 +23,6 @@ import GroupStore from '../stores/GroupStore';
import { _t } from "../languageHandler"; import { _t } from "../languageHandler";
import Modal from "../Modal"; import Modal from "../Modal";
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import { defer, IDeferred } from "./promise";
import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog"; import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog";
export enum InviteState { export enum InviteState {

View file

@ -14,11 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Returns a promise which resolves with a given value after the given number of ms
export function sleep<T>(ms: number, value?: T): Promise<T> {
return new Promise((resolve => { setTimeout(resolve, ms, value); }));
}
// Returns a promise which resolves when the input promise resolves with its 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 // or when the timeout of ms is reached with the value of given timeoutValue
export async function timeout<T>(promise: Promise<T>, timeoutValue: T, ms: number): Promise<T> { export async function timeout<T>(promise: Promise<T>, timeoutValue: T, ms: number): Promise<T> {
@ -32,25 +27,6 @@ export async function timeout<T>(promise: Promise<T>, timeoutValue: T, ms: numbe
return Promise.race([promise, timeoutPromise]); return Promise.race([promise, timeoutPromise]);
} }
export interface IDeferred<T> {
resolve: (value: T) => void;
reject: (any) => void;
promise: Promise<T>;
}
// Returns a Deferred
export function defer<T>(): IDeferred<T> {
let resolve;
let reject;
const promise = new Promise<T>((_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 // Helper method to retry a Promise a given number of times or until a predicate fails
export async function retry<T, E extends Error>(fn: () => Promise<T>, num: number, predicate?: (e: E) => boolean) { export async function retry<T, E extends Error>(fn: () => Promise<T>, num: number, predicate?: (e: E) => boolean) {
let lastErr: E; let lastErr: E;

View file

@ -18,12 +18,12 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-dom/test-utils'; import ReactTestUtils from 'react-dom/test-utils';
import MatrixReactTestUtils from 'matrix-react-test-utils'; import MatrixReactTestUtils from 'matrix-react-test-utils';
import { sleep } from "matrix-js-sdk/src/utils";
import sdk from '../../../skinned-sdk'; import sdk from '../../../skinned-sdk';
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
import * as TestUtilsMatrix from '../../../test-utils'; import * as TestUtilsMatrix from '../../../test-utils';
import { sleep } from "../../../../src/utils/promise";
const InteractiveAuthDialog = sdk.getComponent( const InteractiveAuthDialog = sdk.getComponent(
'views.dialogs.InteractiveAuthDialog', 'views.dialogs.InteractiveAuthDialog',

View file

@ -19,6 +19,8 @@ import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { configure, mount } from "enzyme"; import { configure, mount } from "enzyme";
import React from "react"; import React from "react";
import { act } from "react-dom/test-utils"; import { act } from "react-dom/test-utils";
import { sleep } from "matrix-js-sdk/src/utils";
import SendMessageComposer, { import SendMessageComposer, {
createMessageContent, createMessageContent,
isQuickReaction, isQuickReaction,
@ -29,7 +31,6 @@ import { createPartCreator, createRenderer } from "../../../editor/mock";
import { createTestClient, mkEvent, mkStubRoom } from "../../../test-utils"; import { createTestClient, mkEvent, mkStubRoom } from "../../../test-utils";
import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer"; import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import { sleep } from "../../../../src/utils/promise";
import SpecPermalinkConstructor from "../../../../src/utils/permalinks/SpecPermalinkConstructor"; import SpecPermalinkConstructor from "../../../../src/utils/permalinks/SpecPermalinkConstructor";
import defaultDispatcher from "../../../../src/dispatcher/dispatcher"; import defaultDispatcher from "../../../../src/dispatcher/dispatcher";