diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js
index d8f9a4daa..62bffa762 100644
--- a/src/components/views/context_menus/MessageContextMenu.js
+++ b/src/components/views/context_menus/MessageContextMenu.js
@@ -16,15 +16,16 @@ limitations under the License.
'use strict';
-const React = require('react');
+import React from 'react';
-const MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-const dis = require('matrix-react-sdk/lib/dispatcher');
-const sdk = require('matrix-react-sdk');
+import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
+import dis from 'matrix-react-sdk/lib/dispatcher';
+import sdk from 'matrix-react-sdk';
import { _t } from 'matrix-react-sdk/lib/languageHandler';
-const Modal = require('matrix-react-sdk/lib/Modal');
-const Resend = require("matrix-react-sdk/lib/Resend");
+import Modal from 'matrix-react-sdk/lib/Modal';
+import Resend from "matrix-react-sdk/lib/Resend";
import SettingsStore from "matrix-react-sdk/lib/settings/SettingsStore";
+import {makeEventPermalink} from 'matrix-react-sdk/lib/matrix-to';
module.exports = React.createClass({
displayName: 'MessageContextMenu',
@@ -107,15 +108,14 @@ module.exports = React.createClass({
onFinished: (proceed) => {
if (!proceed) return;
- MatrixClientPeg.get().redactEvent(
- this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
- ).catch(function(e) {
+ const cli = MatrixClientPeg.get();
+ cli.redactEvent(this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()).catch(function(e) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
// display error message stating you couldn't delete this.
const code = e.errcode || e.statusCode;
Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, {
title: _t('Error'),
- description: _t('You cannot delete this message. (%(code)s)', {code: code})
+ description: _t('You cannot delete this message. (%(code)s)', {code}),
});
}).done();
},
@@ -138,12 +138,12 @@ module.exports = React.createClass({
onPinClick: function() {
MatrixClientPeg.get().getStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', '')
- .catch(e => {
+ .catch((e) => {
// Intercept the Event Not Found error and fall through the promise chain with no event.
if (e.errcode === "M_NOT_FOUND") return null;
throw e;
})
- .then(event => {
+ .then((event) => {
const eventIds = (event ? event.pinned : []) || [];
if (!eventIds.includes(this.props.mxEvent.getId())) {
// Not pinned - add
@@ -153,7 +153,8 @@ module.exports = React.createClass({
eventIds.splice(eventIds.indexOf(this.props.mxEvent.getId()), 1);
}
- MatrixClientPeg.get().sendStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', {pinned: eventIds}, '');
+ const cli = MatrixClientPeg.get();
+ cli.sendStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', {pinned: eventIds}, '');
});
this.closeMenu();
},
@@ -177,6 +178,14 @@ module.exports = React.createClass({
this.closeMenu();
},
+ onReplyClick: function() {
+ dis.dispatch({
+ action: 'quote_event',
+ event: this.props.mxEvent,
+ });
+ this.closeMenu();
+ },
+
render: function() {
const eventStatus = this.props.mxEvent.status;
let resendButton;
@@ -184,12 +193,11 @@ module.exports = React.createClass({
let cancelButton;
let forwardButton;
let pinButton;
- let viewSourceButton;
let viewClearSourceButton;
let unhidePreviewButton;
- let permalinkButton;
let externalURLButton;
let quoteButton;
+ let replyButton;
if (eventStatus === 'not_sent') {
resendButton = (
@@ -227,14 +235,14 @@ module.exports = React.createClass({
if (this.state.canPin) {
pinButton = (
- {this._isPinned() ? _t('Unpin Message') : _t('Pin Message')}
+ { this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
);
}
}
}
- viewSourceButton = (
+ const viewSourceButton = (
{ _t('View Source') }
@@ -259,10 +267,10 @@ module.exports = React.createClass({
}
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
- permalinkButton = (
+ const permalinkButton = (
);
@@ -272,32 +280,41 @@ module.exports = React.createClass({
{ _t('Quote') }
);
+
+ if (SettingsStore.isFeatureEnabled("feature_rich_quoting")) {
+ replyButton = (
+
+ { _t('Reply') }
+
+ );
+ }
}
// Bridges can provide a 'external_url' to link back to the source.
- if( typeof(this.props.mxEvent.event.content.external_url) === "string") {
- externalURLButton = (
-
- );
+ if (typeof(this.props.mxEvent.event.content.external_url) === "string") {
+ externalURLButton = (
+
+ );
}
return (
- {resendButton}
- {redactButton}
- {cancelButton}
- {forwardButton}
- {pinButton}
- {viewSourceButton}
- {viewClearSourceButton}
- {unhidePreviewButton}
- {permalinkButton}
- {quoteButton}
- {externalURLButton}
+ { resendButton }
+ { redactButton }
+ { cancelButton }
+ { forwardButton }
+ { pinButton }
+ { viewSourceButton }
+ { viewClearSourceButton }
+ { unhidePreviewButton }
+ { permalinkButton }
+ { quoteButton }
+ { replyButton }
+ { externalURLButton }
);
},
diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js
index bdb9d17f2..d1a34d02d 100644
--- a/src/components/views/elements/ImageView.js
+++ b/src/components/views/elements/ImageView.js
@@ -20,7 +20,7 @@ var React = require('react');
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var DateUtils = require('matrix-react-sdk/lib/DateUtils');
+import {formatDate} from 'matrix-react-sdk/lib/DateUtils';
var filesize = require('filesize');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
const Modal = require('matrix-react-sdk/lib/Modal');
@@ -159,7 +159,7 @@ module.exports = React.createClass({
}
eventMeta = (
- { _t('Uploaded on %(date)s by %(user)s', {date: DateUtils.formatDate(new Date(this.props.mxEvent.getTs())), user: sender}) }
+ { _t('Uploaded on %(date)s by %(user)s', {date: formatDate(new Date(this.props.mxEvent.getTs())), user: sender}) }
);
}
diff --git a/src/components/views/messages/DateSeparator.js b/src/components/views/messages/DateSeparator.js
index 84d63dd5f..5d579e5d8 100644
--- a/src/components/views/messages/DateSeparator.js
+++ b/src/components/views/messages/DateSeparator.js
@@ -16,7 +16,7 @@ limitations under the License.
import React from 'react';
import { _t } from 'matrix-react-sdk/lib/languageHandler';
-import DateUtils from 'matrix-react-sdk/lib/DateUtils';
+import {formatFullDate} from 'matrix-react-sdk/lib/DateUtils';
function getdaysArray() {
return [
@@ -49,7 +49,7 @@ module.exports = React.createClass({
label = days[date.getDay()];
}
else {
- label = DateUtils.formatFullDate(date, this.props.showTwelveHour);
+ label = formatFullDate(date, this.props.showTwelveHour);
}
return (
diff --git a/src/components/views/messages/MessageTimestamp.js b/src/components/views/messages/MessageTimestamp.js
index 3102b5591..3d972e4c1 100644
--- a/src/components/views/messages/MessageTimestamp.js
+++ b/src/components/views/messages/MessageTimestamp.js
@@ -17,7 +17,7 @@ limitations under the License.
'use strict';
import React from 'react';
-import DateUtils from 'matrix-react-sdk/lib/DateUtils';
+import {formatFullDate, formatTime} from 'matrix-react-sdk/lib/DateUtils';
module.exports = React.createClass({
displayName: 'MessageTimestamp',
@@ -29,8 +29,8 @@ module.exports = React.createClass({
render: function() {
const date = new Date(this.props.ts);
return (
-
- { DateUtils.formatTime(date, this.props.showTwelveHour) }
+
+ { formatTime(date, this.props.showTwelveHour) }
);
},
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index c1857e314..cdca2570c 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -217,5 +217,6 @@
"Contributing code to Matrix and Riot": "Contributing code to Matrix and Riot",
"Dev chat for the Riot/Web dev team": "Dev chat for the Riot/Web dev team",
"Dev chat for the Dendrite dev team": "Dev chat for the Dendrite dev team",
- "Co-ordination for Riot/Web translators": "Co-ordination for Riot/Web translators"
+ "Co-ordination for Riot/Web translators": "Co-ordination for Riot/Web translators",
+ "Reply": "Reply"
}
diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss
index 0dd6a1b1b..c2725aeb9 100644
--- a/src/skins/vector/css/_components.scss
+++ b/src/skins/vector/css/_components.scss
@@ -39,6 +39,7 @@
@import "./matrix-react-sdk/views/elements/_RichText.scss";
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
@import "./matrix-react-sdk/views/elements/_ToolTipButton.scss";
+@import "./matrix-react-sdk/views/elements/_Quote.scss";
@import "./matrix-react-sdk/views/groups/_GroupPublicityToggle.scss";
@import "./matrix-react-sdk/views/groups/_GroupRoomList.scss";
@import "./matrix-react-sdk/views/groups/_GroupUserSettings.scss";
@@ -70,6 +71,7 @@
@import "./matrix-react-sdk/views/rooms/_RoomTile.scss";
@import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss";
@import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss";
+@import "./matrix-react-sdk/views/rooms/_QuotePreview.scss";
@import "./matrix-react-sdk/views/settings/_DevicesPanel.scss";
@import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss";
@import "./matrix-react-sdk/views/voip/_CallView.scss";
diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_Quote.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_Quote.scss
new file mode 100644
index 000000000..0af555b5a
--- /dev/null
+++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_Quote.scss
@@ -0,0 +1,26 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+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_Quote .mx_DateSeparator {
+ font-size: 1em !important;
+ margin-bottom: 0;
+ padding-bottom: 1px;
+ bottom: -5px;
+}
+
+.mx_Quote_show {
+ cursor: pointer;
+}
diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
index 6bf3f3b60..732ada088 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
@@ -1,7 +1,7 @@
.mx_Autocomplete {
position: absolute;
bottom: 0;
- z-index: 1000;
+ z-index: 1001;
width: 100%;
border: 1px solid $primary-hairline-color;
background: $primary-bg-color;
@@ -90,3 +90,4 @@
.mx_Autocomplete_Completion_description {
color: gray;
}
+
diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss
index 5bb8a5663..33edd045a 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss
@@ -109,7 +109,7 @@ limitations under the License.
/* this is used for the tile for the event which is selected via the URL.
* TODO: ultimately we probably want some transition on here.
*/
-.mx_EventTile_selected .mx_EventTile_line {
+.mx_EventTile_selected > .mx_EventTile_line {
border-left: $accent-color 5px solid;
padding-left: 60px;
background-color: $event-selected-color;
@@ -209,7 +209,7 @@ limitations under the License.
visibility: visible;
}
-.mx_EventTile_selected .mx_MessageTimestamp {
+.mx_EventTile_selected > div > a > .mx_MessageTimestamp {
left: 3px;
width: auto;
}
diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_QuotePreview.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_QuotePreview.scss
new file mode 100644
index 000000000..f0fb62b8e
--- /dev/null
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_QuotePreview.scss
@@ -0,0 +1,35 @@
+.mx_QuotePreview {
+ position: absolute;
+ bottom: 0;
+ z-index: 1000;
+ width: 100%;
+ border: 1px solid $primary-hairline-color;
+ background: $primary-bg-color;
+ border-bottom: none;
+ border-radius: 4px 4px 0 0;
+ max-height: 50vh;
+ overflow: auto
+}
+
+.mx_QuotePreview_section {
+ border-bottom: 1px solid $primary-hairline-color;
+}
+
+.mx_QuotePreview_header {
+ margin: 12px;
+ color: $primary-fg-color;
+ font-weight: 400;
+ opacity: 0.4;
+}
+
+.mx_QuotePreview_title {
+ float: left;
+}
+
+.mx_QuotePreview_cancel {
+ float: right;
+}
+
+.mx_QuotePreview_clear {
+ clear: both;
+}