{
{/* masked image in CSS */}
);
+ let dnd;
if (this.state.selectedSpace) {
name = (
@@ -560,6 +572,16 @@ export default class UserMenu extends React.Component {
);
isPrototype = true;
+ } else if (SettingsStore.getValue("feature_dnd")) {
+ const isDnd = SettingsStore.getValue("doNotDisturb");
+ dnd = ;
}
if (this.props.isMinimized) {
name = null;
@@ -595,6 +617,7 @@ export default class UserMenu extends React.Component {
/>
{name}
+ {dnd}
{buttons}
diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx
index 60f783e889..2ebc84ec7c 100644
--- a/src/components/views/dialogs/InviteDialog.tsx
+++ b/src/components/views/dialogs/InviteDialog.tsx
@@ -31,6 +31,7 @@ import Modal from "../../../Modal";
import {humanizeTime} from "../../../utils/humanize";
import createRoom, {
canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted,
+ IInvite3PID,
} from "../../../createRoom";
import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite";
import {Key} from "../../../Keyboard";
@@ -618,13 +619,14 @@ export default class InviteDialog extends React.PureComponent {
this.setState({busy: true});
+ const client = MatrixClientPeg.get();
const targets = this._convertFilter();
const targetIds = targets.map(t => t.userId);
// Check if there is already a DM with these people and reuse it if possible.
let existingRoom: Room;
if (targetIds.length === 1) {
- existingRoom = findDMForUser(MatrixClientPeg.get(), targetIds[0]);
+ existingRoom = findDMForUser(client, targetIds[0]);
} else {
existingRoom = DMRoomMap.shared().getDMRoomForIdentifiers(targetIds);
}
@@ -646,7 +648,6 @@ export default class InviteDialog extends React.PureComponent t instanceof ThreepidMember);
if (!has3PidMembers) {
- const client = MatrixClientPeg.get();
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
if (allHaveDeviceKeys) {
createRoomOptions.encryption = true;
@@ -656,35 +657,41 @@ export default class InviteDialog extends React.PureComponent;
- const isSelf = targetIds.length === 1 && targetIds[0] === MatrixClientPeg.get().getUserId();
- if (targetIds.length === 1 && !isSelf) {
- createRoomOptions.dmUserId = targetIds[0];
- createRoomPromise = createRoom(createRoomOptions);
- } else if (isSelf) {
- createRoomPromise = createRoom(createRoomOptions);
- } else {
- // Create a boring room and try to invite the targets manually.
- createRoomPromise = createRoom(createRoomOptions).then(roomId => {
- return inviteMultipleToRoom(roomId, targetIds);
- }).then(result => {
- if (this._shouldAbortAfterInviteError(result)) {
- return true; // abort
- }
- });
- }
+ try {
+ const isSelf = targetIds.length === 1 && targetIds[0] === client.getUserId();
+ if (targetIds.length === 1 && !isSelf) {
+ createRoomOptions.dmUserId = targetIds[0];
+ }
- // the createRoom call will show the room for us, so we don't need to worry about that.
- createRoomPromise.then(abort => {
- if (abort === true) return; // only abort on true booleans, not roomIds or something
+ if (targetIds.length > 1) {
+ createRoomOptions.createOpts = targetIds.reduce(
+ (roomOptions, address) => {
+ const type = getAddressType(address);
+ if (type === 'email') {
+ const invite: IInvite3PID = {
+ id_server: client.getIdentityServerUrl(true),
+ medium: 'email',
+ address,
+ };
+ roomOptions.invite_3pid.push(invite);
+ } else if (type === 'mx-user-id') {
+ roomOptions.invite.push(address);
+ }
+ return roomOptions;
+ },
+ { invite: [], invite_3pid: [] },
+ )
+ }
+
+ await createRoom(createRoomOptions);
this.props.onFinished();
- }).catch(err => {
+ } catch (err) {
console.error(err);
this.setState({
busy: false,
errorText: _t("We couldn't create your DM."),
});
- });
+ }
};
_inviteUsers = async () => {
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index a3474161d7..f6fb83c064 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -260,6 +260,9 @@ export default class EventTile extends React.Component {
// whether or not to show flair at all
enableFlair: PropTypes.bool,
+
+ // whether or not to show read receipts
+ showReadReceipts: PropTypes.bool,
};
static defaultProps = {
@@ -858,8 +861,6 @@ export default class EventTile extends React.Component {
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
}
- const readAvatars = this.getReadAvatars();
-
let avatar;
let sender;
let avatarSize;
@@ -988,6 +989,16 @@ export default class EventTile extends React.Component {
const groupPadlock = !useIRCLayout && !isBubbleMessage && this._renderE2EPadlock();
const ircPadlock = useIRCLayout && !isBubbleMessage && this._renderE2EPadlock();
+ let msgOption;
+ if (this.props.showReadReceipts) {
+ const readAvatars = this.getReadAvatars();
+ msgOption = (
+
+ { readAvatars }
+
+ );
+ }
+
switch (this.props.tileShape) {
case 'notif': {
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
@@ -1107,9 +1118,7 @@ export default class EventTile extends React.Component {
{ reactionsRow }
{ actionBar }
-
- { readAvatars }
-
+ {msgOption}
{
// The avatar goes after the event tile as it's absolutely positioned to be over the
// event tile line, so needs to be later in the DOM so it appears on top (this avoids
diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js
index 7473aac7cd..709e6a0db0 100644
--- a/src/components/views/rooms/ReadReceiptMarker.js
+++ b/src/components/views/rooms/ReadReceiptMarker.js
@@ -17,22 +17,13 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
-import '../../../VelocityBounce';
import { _t } from '../../../languageHandler';
import {formatDate} from '../../../DateUtils';
-import Velociraptor from "../../../Velociraptor";
+import NodeAnimator from "../../../NodeAnimator";
import * as sdk from "../../../index";
import {toPx} from "../../../utils/units";
import {replaceableComponent} from "../../../utils/replaceableComponent";
-let bounce = false;
-try {
- if (global.localStorage) {
- bounce = global.localStorage.getItem('avatar_bounce') == 'true';
- }
-} catch (e) {
-}
-
@replaceableComponent("views.rooms.ReadReceiptMarker")
export default class ReadReceiptMarker extends React.PureComponent {
static propTypes = {
@@ -115,7 +106,18 @@ export default class ReadReceiptMarker extends React.PureComponent {
// we've already done our display - nothing more to do.
return;
}
+ this._animateMarker();
+ }
+ componentDidUpdate(prevProps) {
+ const differentLeftOffset = prevProps.leftOffset !== this.props.leftOffset;
+ const visibilityChanged = prevProps.hidden !== this.props.hidden;
+ if (differentLeftOffset || visibilityChanged) {
+ this._animateMarker();
+ }
+ }
+
+ _animateMarker() {
// treat new RRs as though they were off the top of the screen
let oldTop = -15;
@@ -139,42 +141,18 @@ export default class ReadReceiptMarker extends React.PureComponent {
}
const startStyles = [];
- const enterTransitionOpts = [];
if (oldInfo && oldInfo.left) {
// start at the old height and in the old h pos
-
startStyles.push({ top: startTopOffset+"px",
left: toPx(oldInfo.left) });
-
- const reorderTransitionOpts = {
- duration: 100,
- easing: 'easeOut',
- };
-
- enterTransitionOpts.push(reorderTransitionOpts);
}
- // then shift to the rightmost column,
- // and then it will drop down to its resting position
- //
- // XXX: We use a small left value to trick velocity-animate into actually animating.
- // This is a very annoying bug where if it thinks there's no change to `left` then it'll
- // skip applying it, thus making our read receipt at +14px instead of +0px like it
- // should be. This does cause a tiny amount of drift for read receipts, however with a
- // value so small it's not perceived by a user.
- // Note: Any smaller values (or trying to interchange units) might cause read receipts to
- // fail to fall down or cause gaps.
- startStyles.push({ top: startTopOffset+'px', left: '1px' });
- enterTransitionOpts.push({
- duration: bounce ? Math.min(Math.log(Math.abs(startTopOffset)) * 200, 3000) : 300,
- easing: bounce ? 'easeOutBounce' : 'easeOutCubic',
- });
+ startStyles.push({ top: startTopOffset+'px', left: '0' });
this.setState({
suppressDisplay: false,
startStyles: startStyles,
- enterTransitionOpts: enterTransitionOpts,
});
}
@@ -187,7 +165,6 @@ export default class ReadReceiptMarker extends React.PureComponent {
const style = {
left: toPx(this.props.leftOffset),
top: '0px',
- visibility: this.props.hidden ? 'hidden' : 'visible',
};
let title;
@@ -210,9 +187,8 @@ export default class ReadReceiptMarker extends React.PureComponent {
}
return (
-
+
-
+
);
}
}
diff --git a/src/createRoom.ts b/src/createRoom.ts
index a5343076ac..310d894266 100644
--- a/src/createRoom.ts
+++ b/src/createRoom.ts
@@ -90,6 +90,12 @@ export interface IOpts {
parentSpace?: Room;
}
+export interface IInvite3PID {
+ id_server: string,
+ medium: 'email',
+ address: string,
+}
+
/**
* Create a new room, and switch to it.
*
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index e8839369b1..051372b1f2 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -786,6 +786,7 @@
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
"Change notification settings": "Change notification settings",
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.",
+ "Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
"Send and receive voice messages (in development)": "Send and receive voice messages (in development)",
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
"Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.",
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index b38dee6e1a..2a26eeac13 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -128,6 +128,12 @@ export const SETTINGS: {[setting: string]: ISetting} = {
default: false,
controller: new ReloadOnChangeController(),
},
+ "feature_dnd": {
+ isFeature: true,
+ displayName: _td("Show options to enable 'Do not disturb' mode"),
+ supportedLevels: LEVELS_FEATURE,
+ default: false,
+ },
"feature_voice_messages": {
isFeature: true,
displayName: _td("Send and receive voice messages (in development)"),
@@ -226,6 +232,10 @@ export const SETTINGS: {[setting: string]: ISetting} = {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
},
+ "doNotDisturb": {
+ supportedLevels: [SettingLevel.DEVICE],
+ default: false,
+ },
"mjolnirRooms": {
supportedLevels: [SettingLevel.ACCOUNT],
default: [],
diff --git a/src/stores/notifications/RoomNotificationStateStore.ts b/src/stores/notifications/RoomNotificationStateStore.ts
index 8b5da674f5..7253b46ddd 100644
--- a/src/stores/notifications/RoomNotificationStateStore.ts
+++ b/src/stores/notifications/RoomNotificationStateStore.ts
@@ -22,6 +22,7 @@ import { FetchRoomFn, ListNotificationState } from "./ListNotificationState";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomNotificationState } from "./RoomNotificationState";
import { SummarizedNotificationState } from "./SummarizedNotificationState";
+import { VisibilityProvider } from "../room-list/filters/VisibilityProvider";
interface IState {}
@@ -47,7 +48,9 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient {
// This will include highlights from the previous version of the room internally
const globalState = new SummarizedNotificationState();
for (const room of this.matrixClient.getVisibleRooms()) {
- globalState.add(this.getRoomState(room));
+ if (VisibilityProvider.instance.isRoomVisible(room)) {
+ globalState.add(this.getRoomState(room));
+ }
}
return globalState;
}
diff --git a/yarn.lock b/yarn.lock
index cfae85608a..66329cfa89 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8144,11 +8144,6 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
-velocity-animate@^2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/velocity-animate/-/velocity-animate-2.0.6.tgz#1811ca14df7fbbef05740256f6cec0fd1b76575f"
- integrity sha512-tU+/UtSo3GkIjEfk2KM4e24DvpgX0+FzfLr7XqNwm9BCvZUtbCHPq/AFutx/Mkp2bXlUS9EcX8yxu8XmzAv2Kw==
-
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"