From fef36d3e1de101a14ef1a2a85b95d70f28f04258 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 27 Mar 2019 17:35:36 +0000 Subject: [PATCH 01/13] react-sdk rc.1 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8997d3c2af..5b753da182 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "linkifyjs": "^2.1.6", "lodash": "^4.13.1", "lolex": "2.3.2", - "matrix-js-sdk": "1.0.2", + "matrix-js-sdk": "^1.0.3-rc.1", "optimist": "^0.6.1", "pako": "^1.0.5", "prop-types": "^15.5.8", diff --git a/yarn.lock b/yarn.lock index 893ceac0a2..5dff5cbae0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4371,10 +4371,10 @@ math-random@^1.0.1: resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== -matrix-js-sdk@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-1.0.2.tgz#482d8d2076c7565cf7354722e96c9971e372182a" - integrity sha512-4WCBJFSoOLelHi7IUAcVxPQF+gTc/i9NUKZ77qwUfcZVED8VKTIyWZnwpeLgocK5gAOJV9fkAyO5mny9SkZaGg== +matrix-js-sdk@^1.0.3-rc.1: + version "1.0.3-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-1.0.3-rc.1.tgz#61dea7fc2efd6f0a23e709443b4ac6a276890e9a" + integrity sha512-31aFwoAR9AIWhqHgJCYplXVFbaykXydm7GBvM/ffCjzN5OgAQEzUqwX18PfWKHVZHqijtj2VZOqnBVsXLX0w6Q== dependencies: another-json "^0.2.0" babel-runtime "^6.26.0" From 8cd9132b379ef360abea9288f868c33f1cb54663 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 27 Mar 2019 17:40:04 +0000 Subject: [PATCH 02/13] Prepare changelog for v1.0.6-rc.1 --- CHANGELOG.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3313abacc..5a683d6e33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,94 @@ +Changes in [1.0.6-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.6-rc.1) (2019-03-27) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.5...v1.0.6-rc.1) + + * Catch errors when checking IndexedDB + [\#2836](https://github.com/matrix-org/matrix-react-sdk/pull/2836) + * Remove noreferrer on widget pop-out + [\#2835](https://github.com/matrix-org/matrix-react-sdk/pull/2835) + * Rework room directory so that new room is always available + [\#2834](https://github.com/matrix-org/matrix-react-sdk/pull/2834) + * Send telemetry about storage consistency + [\#2832](https://github.com/matrix-org/matrix-react-sdk/pull/2832) + * Widget OpenID reauth implementation + [\#2781](https://github.com/matrix-org/matrix-react-sdk/pull/2781) + * Log results of basic storage consistency check + [\#2826](https://github.com/matrix-org/matrix-react-sdk/pull/2826) + * Clarify devices affected by notification settings + [\#2828](https://github.com/matrix-org/matrix-react-sdk/pull/2828) + * Add a command for creating custom widgets without an integration manager + [\#2824](https://github.com/matrix-org/matrix-react-sdk/pull/2824) + * Minimize stickerpicker when the title is clicked + [\#2822](https://github.com/matrix-org/matrix-react-sdk/pull/2822) + * Add blocks around homeserver and identity server urls + [\#2825](https://github.com/matrix-org/matrix-react-sdk/pull/2825) + * Fixed drop shadow for tooltip. + [\#2815](https://github.com/matrix-org/matrix-react-sdk/pull/2815) + * Ask the user for debug logs when the timeline explodes + [\#2820](https://github.com/matrix-org/matrix-react-sdk/pull/2820) + * Fix typo preventing users from adding more widgets easily + [\#2823](https://github.com/matrix-org/matrix-react-sdk/pull/2823) + * Attach an onChange listener to the room's blacklist devices option + [\#2817](https://github.com/matrix-org/matrix-react-sdk/pull/2817) + * Use leaveRoomChain when leaving a room + [\#2818](https://github.com/matrix-org/matrix-react-sdk/pull/2818) + * Fix bug with NetworkList dropdown + [\#2821](https://github.com/matrix-org/matrix-react-sdk/pull/2821) + * Trim the logging for URL previews + [\#2816](https://github.com/matrix-org/matrix-react-sdk/pull/2816) + * Explicitly create `cryptoStore` in React SDK + [\#2814](https://github.com/matrix-org/matrix-react-sdk/pull/2814) + * Change to new consistent name for `MemoryStore` + [\#2812](https://github.com/matrix-org/matrix-react-sdk/pull/2812) + * Use medium agents for the more resource intensive builds + [\#2813](https://github.com/matrix-org/matrix-react-sdk/pull/2813) + * Add log grouping to buildkite + [\#2810](https://github.com/matrix-org/matrix-react-sdk/pull/2810) + * Switch to `git` protocol for CI dependencies + [\#2809](https://github.com/matrix-org/matrix-react-sdk/pull/2809) + * Go back to using mainine velocity + [\#2808](https://github.com/matrix-org/matrix-react-sdk/pull/2808) + * Warn that members won't be autojoined to upgraded rooms + [\#2796](https://github.com/matrix-org/matrix-react-sdk/pull/2796) + * Support CI for matching branches on forks + [\#2807](https://github.com/matrix-org/matrix-react-sdk/pull/2807) + * Discard old sticker picker when the URL changes + [\#2801](https://github.com/matrix-org/matrix-react-sdk/pull/2801) + * Reload widget messaging when widgets reload + [\#2799](https://github.com/matrix-org/matrix-react-sdk/pull/2799) + * Don't show calculated room name in room settings name input field + [\#2806](https://github.com/matrix-org/matrix-react-sdk/pull/2806) + * Disable big emoji for m.emote messages as it looks weird + [\#2805](https://github.com/matrix-org/matrix-react-sdk/pull/2805) + * Remove Edge from browser support statements + [\#2803](https://github.com/matrix-org/matrix-react-sdk/pull/2803) + * Update from Weblate + [\#2802](https://github.com/matrix-org/matrix-react-sdk/pull/2802) + * Really fix tag panel + [\#2800](https://github.com/matrix-org/matrix-react-sdk/pull/2800) + * Update CompatibilityPage to match officially supported browsers + [\#2793](https://github.com/matrix-org/matrix-react-sdk/pull/2793) + * Use Buildkite for CI + [\#2788](https://github.com/matrix-org/matrix-react-sdk/pull/2788) + * Fix CSS syntax errors preventing offline member opacity from working + [\#2794](https://github.com/matrix-org/matrix-react-sdk/pull/2794) + * Make the EntityTile chevron a masked SVG for theming + [\#2795](https://github.com/matrix-org/matrix-react-sdk/pull/2795) + * Remove refs from `RegistrationForm` + [\#2791](https://github.com/matrix-org/matrix-react-sdk/pull/2791) + * Fix initial letter avatar vertical offset in Firefox + [\#2792](https://github.com/matrix-org/matrix-react-sdk/pull/2792) + * Fix the custom tag panel + [\#2797](https://github.com/matrix-org/matrix-react-sdk/pull/2797) + * Ensure freshly invited members don't count towards the alone warning + [\#2786](https://github.com/matrix-org/matrix-react-sdk/pull/2786) + * Fix 'forgot password' warning to represent the reality of e2ee + [\#2787](https://github.com/matrix-org/matrix-react-sdk/pull/2787) + * Restore `Field` value getter for `RegistrationForm` + [\#2790](https://github.com/matrix-org/matrix-react-sdk/pull/2790) + * Initial portions of support for Field validation + [\#2780](https://github.com/matrix-org/matrix-react-sdk/pull/2780) + Changes in [1.0.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.5) (2019-03-21) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.4...v1.0.5) From 45e4948d9a17ff6126b70ecacbe8d3d25676ab85 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 27 Mar 2019 17:40:05 +0000 Subject: [PATCH 03/13] v1.0.6-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b753da182..a5175a9034 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "1.0.5", + "version": "1.0.6-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 5beec37c43c1765356d80ec3d55fb12f67c5f0c1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 28 Mar 2019 20:38:15 -0600 Subject: [PATCH 04/13] Add MemberInfo for 3pid invites and support revoking those invites Fixes https://github.com/vector-im/riot-web/issues/625 Fixes https://github.com/vector-im/riot-web/issues/6411 Fixes https://github.com/vector-im/riot-web/issues/5490 --- src/TextForEvent.js | 9 ++ src/components/structures/RightPanel.js | 5 + .../views/right_panel/RoomHeaderButtons.js | 7 + src/components/views/rooms/MemberList.js | 8 + .../views/rooms/ThirdPartyMemberInfo.js | 142 ++++++++++++++++++ src/i18n/strings/en_EN.json | 5 + 6 files changed, 176 insertions(+) create mode 100644 src/components/views/rooms/ThirdPartyMemberInfo.js diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 030c346ccc..05d83d740a 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -366,6 +366,15 @@ function textForCallInviteEvent(event) { function textForThreePidInviteEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); + + if (!event.getContent().display_name) { + const targetDisplayName = event.getPrevContent().display_name || _t("Someone"); + return _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', { + senderName, + targetDisplayName, + }); + } + return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', { senderName, targetDisplayName: event.getContent().display_name, diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 5c745b04cc..74820c804a 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -50,6 +50,7 @@ export default class RightPanel extends React.Component { FilePanel: 'FilePanel', NotificationPanel: 'NotificationPanel', RoomMemberInfo: 'RoomMemberInfo', + Room3pidMemberInfo: 'Room3pidMemberInfo', GroupMemberInfo: 'GroupMemberInfo', }); @@ -155,6 +156,7 @@ export default class RightPanel extends React.Component { groupRoomId: payload.groupRoomId, groupId: payload.groupId, member: payload.member, + event: payload.event, }); } } @@ -162,6 +164,7 @@ export default class RightPanel extends React.Component { render() { const MemberList = sdk.getComponent('rooms.MemberList'); const MemberInfo = sdk.getComponent('rooms.MemberInfo'); + const ThirdPartyMemberInfo = sdk.getComponent('rooms.ThirdPartyMemberInfo'); const NotificationPanel = sdk.getComponent('structures.NotificationPanel'); const FilePanel = sdk.getComponent('structures.FilePanel'); @@ -180,6 +183,8 @@ export default class RightPanel extends React.Component { panel = ; } else if (this.state.phase === RightPanel.Phase.RoomMemberInfo) { panel = ; + } else if (this.state.phase === RightPanel.Phase.Room3pidMemberInfo) { + panel = ; } else if (this.state.phase === RightPanel.Phase.GroupMemberInfo) { panel = { if (query) { @@ -408,6 +415,7 @@ module.exports = React.createClass({ return this._onPending3pidInviteClick(e)} />; })); } diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.js b/src/components/views/rooms/ThirdPartyMemberInfo.js new file mode 100644 index 0000000000..3fe8382251 --- /dev/null +++ b/src/components/views/rooms/ThirdPartyMemberInfo.js @@ -0,0 +1,142 @@ +/* +Copyright 2019 New Vector 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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import MatrixClientPeg from "../../../MatrixClientPeg"; +import {MatrixEvent} from "matrix-js-sdk"; +import {_t} from "../../../languageHandler"; +import dis from "../../../dispatcher"; +import sdk from "../../../index"; +import Modal from "../../../Modal"; + +export default class ThirdPartyMemberInfo extends React.Component { + static propTypes = { + event: PropTypes.instanceOf(MatrixEvent).isRequired, + }; + + constructor(props) { + super(props); + + const room = MatrixClientPeg.get().getRoom(this.props.event.getRoomId()); + const me = room.getMember(MatrixClientPeg.get().getUserId()); + const powerLevels = room.currentState.getStateEvents("m.room.power_levels", ""); + + let kickLevel = powerLevels ? powerLevels.getContent().kick : 50; + if (typeof(kickLevel) !== 'number') kickLevel = 50; + + const sender = room.getMember(this.props.event.getSender()); + + this.state = { + stateKey: this.props.event.getStateKey(), + roomId: this.props.event.getRoomId(), + displayName: this.props.event.getContent().display_name, + invited: true, + canKick: me ? me.powerLevel > kickLevel : false, + senderName: sender ? sender.name : this.props.event.getSender(), + }; + } + + componentWillMount(): void { + MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents); + } + + componentWillUnmount(): void { + const client = MatrixClientPeg.get(); + if (client) { + client.removeListener("RoomState.events", this.onRoomStateEvents); + } + } + + onRoomStateEvents = (ev) => { + if (ev.getType() === "m.room.third_party_invite" && ev.getStateKey() === this.state.stateKey) { + const newDisplayName = ev.getContent().display_name; + const isInvited = !!newDisplayName; // display_name indicates a valid invite + + const newState = {invited: isInvited}; + if (newDisplayName) newState['displayName'] = newDisplayName; + this.setState(newState); + } + }; + + onCancel = () => { + dis.dispatch({ + action: "view_3pid_invite", + event: null, + }); + }; + + onKickClick = () => { + MatrixClientPeg.get().sendStateEvent(this.state.roomId, "m.room.third_party_invite", {}, this.state.stateKey) + .catch((err) => { + console.error(err); + + // Revert echo because of error + this.setState({invited: true}); + + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Revoke 3pid invite failed', '', ErrorDialog, { + title: _t("Failed to revoke invite"), + description: _t( + "Could not revoke the invite. The server may be experiencing a temporary problem or " + + "you do not have sufficient permissions to revoke the invite.", + ), + }); + }); + + // Local echo + this.setState({invited: false}); + }; + + render() { + const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); + + let adminTools = null; + if (this.state.canKick && this.state.invited) { + adminTools = ( +
+

{_t("Admin Tools")}

+
+ + {_t("Revoke invite")} + +
+
+ ); + } + + // We shamelessly rip off the MemberInfo styles here. + return ( +
+
+ +

{this.state.displayName}

+
+
+
+
+ {_t("Invited by %(sender)s", {sender: this.state.senderName})} +
+
+
+ {adminTools} +
+ ); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 94d524d767..0f696e2893 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -223,6 +223,7 @@ "(unknown failure: %(reason)s)": "(unknown failure: %(reason)s)", "%(senderName)s ended the call.": "%(senderName)s ended the call.", "%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.", + "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.", "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.", "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s made future room history visible to all room members, from the point they are invited.", "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s made future room history visible to all room members, from the point they joined.", @@ -823,6 +824,10 @@ "Stickerpack": "Stickerpack", "Hide Stickers": "Hide Stickers", "Show Stickers": "Show Stickers", + "Failed to revoke invite": "Failed to revoke invite", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.", + "Revoke invite": "Revoke invite", + "Invited by %(sender)s": "Invited by %(sender)s", "Jump to first unread message.": "Jump to first unread message.", "Error updating main address": "Error updating main address", "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.", From 42cfe74f700b4db0fdbdc5e8f24e373df23a6246 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Mar 2019 11:45:07 -0600 Subject: [PATCH 05/13] Add common utility for checking 3pid invites We just need to make sure they are structurally sound - actual validation is done by other parties. --- src/RoomInvite.js | 18 ++++++++++++++++++ src/TextForEvent.js | 3 ++- src/components/views/rooms/MemberList.js | 7 ++----- .../views/rooms/ThirdPartyMemberInfo.js | 7 ++++--- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/RoomInvite.js b/src/RoomInvite.js index 3547b9195f..b808b935a6 100644 --- a/src/RoomInvite.js +++ b/src/RoomInvite.js @@ -65,6 +65,24 @@ export function showRoomInviteDialog(roomId) { }); } +/** + * Checks if the given MatrixEvent is a valid 3rd party user invite. + * @param {MatrixEvent} event The event to check + * @returns {boolean} True if valid, false otherwise + */ +export function isValid3pidInvite(event) { + if (!event || event.getType() !== "m.room.third_party_invite") return false; + + // any events without these keys are not valid 3pid invites, so we ignore them + const requiredKeys = ['key_validity_url', 'public_key', 'display_name']; + for (let i = 0; i < requiredKeys.length; ++i) { + if (!event.getContent()[requiredKeys[i]]) return false; + } + + // Valid enough by our standards + return true; +} + function _onStartChatFinished(shouldInvite, addrs) { if (!shouldInvite) return; diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 05d83d740a..a700fe2a3c 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -17,6 +17,7 @@ import MatrixClientPeg from './MatrixClientPeg'; import CallHandler from './CallHandler'; import { _t } from './languageHandler'; import * as Roles from './Roles'; +import {isValid3pidInvite} from "./RoomInvite"; function textForMemberEvent(ev) { // XXX: SYJS-16 "sender is sometimes null for join messages" @@ -367,7 +368,7 @@ function textForCallInviteEvent(event) { function textForThreePidInviteEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); - if (!event.getContent().display_name) { + if (!isValid3pidInvite(event)) { const targetDisplayName = event.getPrevContent().display_name || _t("Someone"); return _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', { senderName, diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index a8cc948f63..e79f2f21d4 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -20,6 +20,7 @@ import React from 'react'; import { _t } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; import dis from '../../../dispatcher'; +import {isValid3pidInvite} from "../../../RoomInvite"; const MatrixClientPeg = require("../../../MatrixClientPeg"); const sdk = require('../../../index'); const rate_limited_func = require('../../../ratelimitedfunc'); @@ -379,11 +380,7 @@ module.exports = React.createClass({ if (room) { return room.currentState.getStateEvents("m.room.third_party_invite").filter(function(e) { - // any events without these keys are not valid 3pid invites, so we ignore them - const requiredKeys = ['key_validity_url', 'public_key', 'display_name']; - for (let i = 0; i < requiredKeys.length; ++i) { - if (e.getContent()[requiredKeys[i]] === undefined) return false; - } + if (!isValid3pidInvite(e)) return false; // discard all invites which have a m.room.member event since we've // already added them. diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.js b/src/components/views/rooms/ThirdPartyMemberInfo.js index 3fe8382251..754e32871f 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.js +++ b/src/components/views/rooms/ThirdPartyMemberInfo.js @@ -22,6 +22,7 @@ import {_t} from "../../../languageHandler"; import dis from "../../../dispatcher"; import sdk from "../../../index"; import Modal from "../../../Modal"; +import {isValid3pidInvite} from "../../../RoomInvite"; export default class ThirdPartyMemberInfo extends React.Component { static propTypes = { @@ -64,7 +65,7 @@ export default class ThirdPartyMemberInfo extends React.Component { onRoomStateEvents = (ev) => { if (ev.getType() === "m.room.third_party_invite" && ev.getStateKey() === this.state.stateKey) { const newDisplayName = ev.getContent().display_name; - const isInvited = !!newDisplayName; // display_name indicates a valid invite + const isInvited = isValid3pidInvite(ev); const newState = {invited: isInvited}; if (newDisplayName) newState['displayName'] = newDisplayName; @@ -123,8 +124,8 @@ export default class ThirdPartyMemberInfo extends React.Component {

{this.state.displayName}

From 662a0e4785516bf46c297a633b0c0a028c552171 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Mar 2019 14:12:48 -0600 Subject: [PATCH 06/13] Download PDFs as blobs to avoid empty grey screens Fixes https://github.com/vector-im/riot-web/issues/8605 The grey screen of sadness comes up when Chrome tries to open the PDF but doesn't have the right CSP headers. To avoid this, we'll just force a download of the PDF through `fetch` and `Blob`. There are a few cases where the user might still get a grey screen though: namely if they open the URL in a new tab or when the event content is lying about the file type, or the file is too large to blobify. `fetch` works in Chrome, Firefox, and our packaged Electron version. --- src/components/views/messages/MFileBody.js | 55 +++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index 7960db0384..372b501558 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -294,6 +294,8 @@ module.exports = React.createClass({ const fileName = content.body && content.body.length > 0 ? content.body : _t("Attachment"); const contentUrl = this._getContentUrl(); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const fileSize = content.info ? content.info.size : null; + const fileType = content.info ? content.info.mimetype : "application/octet-stream"; if (isEncrypted) { if (this.state.decryptedBlob === null) { @@ -372,6 +374,55 @@ module.exports = React.createClass({ ); } else if (contentUrl) { + const downloadProps = { + target: "_blank", + rel: "noopener", + + // We set the href regardless of whether or not we intercept the download + // because we don't really want to convert the file to a blob eagerly, and + // still want "open in new tab" and "save link as" to work. + href: contentUrl, + }; + + // Blobs can only have up to 500mb, so if the file reports as being too large then + // we won't try and convert it. Likewise, if the file size is unknown then we'll assume + // it is too big. There is the risk of the reported file size and the actual file size + // being different, however the user shouldn't normally run into this problem. + const fileTooBig = typeof(fileSize) === 'number' ? fileSize > 524288000 : true; + + if (["application/pdf"].includes(fileType) && !fileTooBig) { + // We want to force a download on this type, so use an onClick handler. + downloadProps["onClick"] = (e) => { + console.log(`Downloading ${fileType} as blob (unencrypted)`); + + // Avoid letting the do its thing + e.preventDefault(); + e.stopPropagation(); + + // Start a fetch for the download + // Based upon https://stackoverflow.com/a/49500465 + fetch(contentUrl, { + headers: new Headers({ + 'Origin': window.location.origin, + }), + mode: 'cors', + }).then((response) => response.blob()).then((blob) => { + const blobUrl = URL.createObjectURL(blob); + + // We have to create an anchor to download the file + const tempAnchor = document.createElement('a'); + tempAnchor.download = fileName; + tempAnchor.href = blobUrl; + document.body.appendChild(tempAnchor); // for firefox: https://stackoverflow.com/a/32226068 + tempAnchor.click(); + tempAnchor.remove(); + }); + }; + } else { + // Else we are hoping the browser will do the right thing + downloadProps["download"] = fileName; + } + // If the attachment is not encrypted then we check whether we // are being displayed in the room timeline or in a list of // files in the right hand side of the screen. @@ -379,7 +430,7 @@ module.exports = React.createClass({ return (
- + { fileName }
@@ -392,7 +443,7 @@ module.exports = React.createClass({ return (
- + { _t("Download %(text)s", { text: text }) } From 82bc92c326b9ef436b3ae9ea66183e53a2f43d50 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Mar 2019 15:48:33 -0600 Subject: [PATCH 07/13] Don't send options we don't need to `fetch` --- src/components/views/messages/MFileBody.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index 372b501558..7e107c1ad8 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -401,12 +401,7 @@ module.exports = React.createClass({ // Start a fetch for the download // Based upon https://stackoverflow.com/a/49500465 - fetch(contentUrl, { - headers: new Headers({ - 'Origin': window.location.origin, - }), - mode: 'cors', - }).then((response) => response.blob()).then((blob) => { + fetch(contentUrl).then((response) => response.blob()).then((blob) => { const blobUrl = URL.createObjectURL(blob); // We have to create an anchor to download the file From 9c502fda9502887eed29495fcab79df9461ef3d2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Mar 2019 14:55:56 -0600 Subject: [PATCH 08/13] Set title attribute on images in lightbox Part of https://github.com/vector-im/riot-web/issues/9324 --- src/components/views/elements/ImageView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 2c0f4a0d86..48f587b4f2 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -173,7 +173,7 @@ module.exports = React.createClass({
- +
{ From 4c65587469185af8ac7d0a549c7a29813e6b00ed Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 28 Mar 2019 12:27:33 +0000 Subject: [PATCH 09/13] Check the local storage fallback for crypto store This adds additional consistency checks to examine the local storage fallback for the crypto store as well as the primary IndexedDB variant. Part of https://github.com/vector-im/riot-web/issues/9309 --- src/utils/StorageManager.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/utils/StorageManager.js b/src/utils/StorageManager.js index 472e1c93d4..a669ed9176 100644 --- a/src/utils/StorageManager.js +++ b/src/utils/StorageManager.js @@ -15,6 +15,7 @@ limitations under the License. */ import Matrix from 'matrix-js-sdk'; +import LocalStorageCryptoStore from 'matrix-js-sdk/lib/crypto/store/localStorage-crypto-store'; import Analytics from '../Analytics'; const localStorage = window.localStorage; @@ -78,15 +79,10 @@ export async function checkConsistency() { } if (indexedDB) { - try { - dataInCryptoStore = await Matrix.IndexedDBCryptoStore.exists( - indexedDB, CRYPTO_STORE_NAME, - ); - log(`Crypto store contains data? ${dataInCryptoStore}`); - } catch (e) { + const results = await checkCryptoStore(); + dataInCryptoStore = results.exists; + if (!results.healthy) { healthy = false; - error("Crypto store inaccessible", e); - track("Crypto store inaccessible"); } } else { healthy = false; @@ -111,3 +107,26 @@ export async function checkConsistency() { track("Consistency checks failed"); } } + +async function checkCryptoStore() { + let exists = false; + try { + exists = await Matrix.IndexedDBCryptoStore.exists( + indexedDB, CRYPTO_STORE_NAME, + ); + log(`Crypto store using IndexedDB contains data? ${exists}`); + return { exists, healthy: true }; + } catch (e) { + error("Crypto store using IndexedDB inaccessible", e); + track("Crypto store using IndexedDB inaccessible"); + } + try { + exists = await LocalStorageCryptoStore.exists(localStorage); + log(`Crypto store using local storage contains data? ${exists}`); + return { exists, healthy: true }; + } catch (e) { + error("Crypto store using local storage inaccessible", e); + track("Crypto store using local storage inaccessible"); + } + return { exists, healthy: false }; +} From 83931a4a4238239ce6e9ec40fd0c1027f65cdcf1 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 28 Mar 2019 12:40:38 +0000 Subject: [PATCH 10/13] Clarify when memory stores are being used This adds logging for the cases where memory only stores are being used. It also reorganises the sync store path to match the crypto store. Part of https://github.com/vector-im/riot-web/issues/9309 --- src/utils/StorageManager.js | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/utils/StorageManager.js b/src/utils/StorageManager.js index a669ed9176..89fa0d290b 100644 --- a/src/utils/StorageManager.js +++ b/src/utils/StorageManager.js @@ -62,15 +62,9 @@ export async function checkConsistency() { } if (indexedDB && localStorage) { - try { - const dataInSyncStore = await Matrix.IndexedDBStore.exists( - indexedDB, SYNC_STORE_NAME, - ); - log(`Sync store contains data? ${dataInSyncStore}`); - } catch (e) { + const results = await checkSyncStore(); + if (!results.healthy) { healthy = false; - error("Sync store inaccessible", e); - track("Sync store inaccessible"); } } else { healthy = false; @@ -108,6 +102,22 @@ export async function checkConsistency() { } } +async function checkSyncStore() { + let exists = false; + try { + exists = await Matrix.IndexedDBStore.exists( + indexedDB, SYNC_STORE_NAME, + ); + log(`Sync store using IndexedDB contains data? ${exists}`); + return { exists, healthy: true }; + } catch (e) { + error("Sync store using IndexedDB inaccessible", e); + track("Sync store using IndexedDB inaccessible"); + } + log("Sync store using memory only"); + return { exists, healthy: false }; +} + async function checkCryptoStore() { let exists = false; try { @@ -128,5 +138,6 @@ async function checkCryptoStore() { error("Crypto store using local storage inaccessible", e); track("Crypto store using local storage inaccessible"); } + log("Crypto store using memory only"); return { exists, healthy: false }; } From 6cde0eb1b25eebd089b6941a3ad40933541614c7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Apr 2019 13:38:06 +0100 Subject: [PATCH 11/13] released js-sdk --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a5175a9034..03136b18bb 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "linkifyjs": "^2.1.6", "lodash": "^4.13.1", "lolex": "2.3.2", - "matrix-js-sdk": "^1.0.3-rc.1", + "matrix-js-sdk": "1.0.3", "optimist": "^0.6.1", "pako": "^1.0.5", "prop-types": "^15.5.8", diff --git a/yarn.lock b/yarn.lock index 5dff5cbae0..d1bed804e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4371,10 +4371,10 @@ math-random@^1.0.1: resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== -matrix-js-sdk@^1.0.3-rc.1: - version "1.0.3-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-1.0.3-rc.1.tgz#61dea7fc2efd6f0a23e709443b4ac6a276890e9a" - integrity sha512-31aFwoAR9AIWhqHgJCYplXVFbaykXydm7GBvM/ffCjzN5OgAQEzUqwX18PfWKHVZHqijtj2VZOqnBVsXLX0w6Q== +matrix-js-sdk@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-1.0.3.tgz#d4cc46c4dc80278b78f8e0664741b08fcc395c79" + integrity sha512-YpF4NvnG2cttRmTPJ9yqs/KwlBXW15O7+nNMs1FKj1CqdW1Phwb0fcqvahjPgmfXyn5DFzU3Deiv9aNgDIlIog== dependencies: another-json "^0.2.0" babel-runtime "^6.26.0" From 9cba85e3f4bc57cb5b333161a3ea26a6b46d728f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Apr 2019 13:43:48 +0100 Subject: [PATCH 12/13] Prepare changelog for v1.0.6 --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a683d6e33..ef08c15a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +Changes in [1.0.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.6) (2019-04-01) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.6-rc.1...v1.0.6) + + * Handle storage fallback cases in consistency check + [\#2853](https://github.com/matrix-org/matrix-react-sdk/pull/2853) + * Set title attribute on images in lightbox + [\#2852](https://github.com/matrix-org/matrix-react-sdk/pull/2852) + * Download PDFs as blobs to avoid empty grey screens + [\#2851](https://github.com/matrix-org/matrix-react-sdk/pull/2851) + * Add MemberInfo for 3pid invites and support revoking those invites + [\#2850](https://github.com/matrix-org/matrix-react-sdk/pull/2850) + Changes in [1.0.6-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.6-rc.1) (2019-03-27) ============================================================================================================= [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.5...v1.0.6-rc.1) From 33f2401e63038e4f65d8c1e37d39c6ccd5d8fc1c Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Apr 2019 13:43:49 +0100 Subject: [PATCH 13/13] v1.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03136b18bb..cb26a18eeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "1.0.6-rc.1", + "version": "1.0.6", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": {