From 34530843f433331d69e70cc20c4cbefa19951385 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Sep 2019 10:54:52 +0100 Subject: [PATCH 1/9] Factor out generic EventListSummary from MELS Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 2 +- ...istSummary.scss => _EventListSummary.scss} | 20 ++-- .../views/elements/EventListSummary.js | 95 ++++++++++++++++++ .../views/elements/MemberEventListSummary.js | 97 +++---------------- src/hooks/useStateToggle.js | 27 ++++++ src/i18n/strings/en_EN.json | 4 +- .../elements/MemberEventListSummary-test.js | 26 ++--- 7 files changed, 164 insertions(+), 107 deletions(-) rename res/css/views/elements/{_MemberEventListSummary.scss => _EventListSummary.scss} (75%) create mode 100644 src/components/views/elements/EventListSummary.js create mode 100644 src/hooks/useStateToggle.js diff --git a/res/css/_components.scss b/res/css/_components.scss index f627fe3a29..561b1b4820 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -88,12 +88,12 @@ @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; @import "./views/elements/_ErrorBoundary.scss"; +@import "./views/elements/_EventListSummary"; @import "./views/elements/_Field.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; @import "./views/elements/_InteractiveTooltip.scss"; @import "./views/elements/_ManageIntegsButton.scss"; -@import "./views/elements/_MemberEventListSummary.scss"; @import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; @import "./views/elements/_ReplyThread.scss"; diff --git a/res/css/views/elements/_MemberEventListSummary.scss b/res/css/views/elements/_EventListSummary.scss similarity index 75% rename from res/css/views/elements/_MemberEventListSummary.scss rename to res/css/views/elements/_EventListSummary.scss index 02ecb5d84a..99a5c06a5f 100644 --- a/res/css/views/elements/_MemberEventListSummary.scss +++ b/res/css/views/elements/_EventListSummary.scss @@ -14,28 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MemberEventListSummary { +.mx_EventListSummary { position: relative; } -.mx_TextualEvent.mx_MemberEventListSummary_summary { +.mx_TextualEvent.mx_EventListSummary_summary { font-size: 14px; display: inline-flex; } -.mx_MemberEventListSummary_avatars { +.mx_EventListSummary_avatars { display: inline-block; margin-right: 8px; padding-top: 8px; line-height: 12px; } -.mx_MemberEventListSummary_avatars .mx_BaseAvatar { +.mx_EventListSummary_avatars .mx_BaseAvatar { margin-right: -4px; cursor: pointer; } -.mx_MemberEventListSummary_toggle { +.mx_EventListSummary_toggle { color: $accent-color; cursor: pointer; float: right; @@ -43,29 +43,29 @@ limitations under the License. margin-top: 8px; } -.mx_MemberEventListSummary_line { +.mx_EventListSummary_line { border-bottom: 1px solid $primary-hairline-color; margin-left: 63px; line-height: 30px; } .mx_MatrixChat_useCompactLayout { - .mx_MemberEventListSummary { + .mx_EventListSummary { font-size: 13px; .mx_EventTile_line { line-height: 20px; } } - .mx_MemberEventListSummary_line { + .mx_EventListSummary_line { line-height: 22px; } - .mx_MemberEventListSummary_toggle { + .mx_EventListSummary_toggle { margin-top: 3px; } - .mx_TextualEvent.mx_MemberEventListSummary_summary { + .mx_TextualEvent.mx_EventListSummary_summary { font-size: 13px; } } diff --git a/src/components/views/elements/EventListSummary.js b/src/components/views/elements/EventListSummary.js new file mode 100644 index 0000000000..d6971334d4 --- /dev/null +++ b/src/components/views/elements/EventListSummary.js @@ -0,0 +1,95 @@ +/* +Copyright 2019 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. +*/ + +import React, {useEffect} from 'react'; +import PropTypes from 'prop-types'; +import MemberAvatar from '../avatars/MemberAvatar'; +import { _t } from '../../../languageHandler'; +import {MatrixEvent, RoomMember} from "matrix-js-sdk"; +import {useStateToggle} from "../../../hooks/useStateToggle"; + +const EventListSummary = ({events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText}) => { + const [expanded, toggleExpanded] = useStateToggle(startExpanded); + + // Whenever expanded changes call onToggle + useEffect(() => { + if (onToggle) { + onToggle(); + } + }, [expanded]); + + const eventIds = events.map((e) => e.getId()).join(','); + + // If we are only given few events then just pass them through + if (events.length < threshold) { + return ( +
+ { children } +
+ ); + } + + if (expanded) { + return ( +
+
+ { _t('collapse') } +
+
 
+ { children } +
+ ); + } + + const avatars = summaryMembers.map((m) => ); + return ( +
+
+ { _t('expand') } +
+
+
+ + { avatars } + + + { summaryText } + +
+
+
+ ); +}; + +EventListSummary.propTypes = { + // An array of member events to summarise + events: PropTypes.arrayOf(PropTypes.instanceOf(MatrixEvent)).isRequired, + // An array of EventTiles to render when expanded + children: PropTypes.arrayOf(PropTypes.element).isRequired, + // The minimum number of events needed to trigger summarisation + threshold: PropTypes.number, + // Called when the event list expansion is toggled + onToggle: PropTypes.func, + // Whether or not to begin with state.expanded=true + startExpanded: PropTypes.bool, + + // The list of room members for which to show avatars next to the summary + summaryMembers: PropTypes.arrayOf(PropTypes.instanceOf(RoomMember)), + // The text to show as the summary of this event list + summaryText: PropTypes.string.isRequired, +}; + +export default EventListSummary; diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index ba31eb5a38..98adbb2e5c 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -19,9 +19,9 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import MemberAvatar from '../avatars/MemberAvatar'; import { _t } from '../../../languageHandler'; import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; +import sdk from "../../../index"; module.exports = createReactClass({ displayName: 'MemberEventListSummary', @@ -43,12 +43,6 @@ module.exports = createReactClass({ startExpanded: PropTypes.bool, }, - getInitialState: function() { - return { - expanded: Boolean(this.props.startExpanded), - }; - }, - getDefaultProps: function() { return { summaryLength: 1, @@ -57,37 +51,27 @@ module.exports = createReactClass({ }; }, - shouldComponentUpdate: function(nextProps, nextState) { + shouldComponentUpdate: function(nextProps) { // Update if // - The number of summarised events has changed - // - or if the summary is currently expanded // - or if the summary is about to toggle to become collapsed // - or if there are fewEvents, meaning the child eventTiles are shown as-is return ( nextProps.events.length !== this.props.events.length || - this.state.expanded || nextState.expanded || nextProps.events.length < this.props.threshold ); }, - _toggleSummary: function() { - this.setState({ - expanded: !this.state.expanded, - }); - this.props.onToggle(); - }, - /** - * Render the JSX for users aggregated by their transition sequences (`eventAggregates`) where + * Generate the text for users aggregated by their transition sequences (`eventAggregates`) where * the sequences are ordered by `orderedTransitionSequences`. * @param {object[]} eventAggregates a map of transition sequence to array of user display names * or user IDs. * @param {string[]} orderedTransitionSequences an array which is some ordering of * `Object.keys(eventAggregates)`. - * @returns {ReactElement} a single containing the textual summary of the aggregated - * events that occurred. + * @returns {string} the textual summary of the aggregated events that occurred. */ - _renderSummary: function(eventAggregates, orderedTransitionSequences) { + _generateSummary: function(eventAggregates, orderedTransitionSequences) { const summaries = orderedTransitionSequences.map((transitions) => { const userNames = eventAggregates[transitions]; const nameList = this._renderNameList(userNames); @@ -118,11 +102,7 @@ module.exports = createReactClass({ return null; } - return ( - - { summaries.join(", ") } - - ); + return summaries.join(", "); }, /** @@ -208,7 +188,7 @@ module.exports = createReactClass({ * For a certain transition, t, describe what happened to the users that * underwent the transition. * @param {string} t the transition type. - * @param {integer} userCount number of usernames + * @param {number} userCount number of usernames * @param {number} repeats the number of times the transition was repeated in a row. * @returns {string} the written Human Readable equivalent of the transition. */ @@ -288,19 +268,6 @@ module.exports = createReactClass({ return res; }, - _renderAvatars: function(roomMembers) { - const avatars = roomMembers.slice(0, this.props.avatarsMaxLength).map((m) => { - return ( - - ); - }); - return ( - - { avatars } - - ); - }, - _getTransitionSequence: function(events) { return events.map(this._getTransition); }, @@ -396,22 +363,6 @@ module.exports = createReactClass({ render: function() { const eventsToRender = this.props.events; - const eventIds = eventsToRender.map((e) => e.getId()).join(','); - const fewEvents = eventsToRender.length < this.props.threshold; - const expanded = this.state.expanded || fewEvents; - - let expandedEvents = null; - if (expanded) { - expandedEvents = this.props.children; - } - - if (fewEvents) { - return ( -
- { expandedEvents } -
- ); - } // Map user IDs to an array of objects: const userEvents = { @@ -455,30 +406,14 @@ module.exports = createReactClass({ (seq1, seq2) => aggregate.indices[seq1] > aggregate.indices[seq2], ); - let summaryContainer = null; - if (!expanded) { - summaryContainer = ( -
-
- { this._renderAvatars(avatarMembers) } - { this._renderSummary(aggregate.names, orderedTransitionSequences) } -
-
- ); - } - const toggleButton = ( -
- { expanded ? _t('collapse') : _t('expand') } -
- ); - - return ( -
- { toggleButton } - { summaryContainer } - { expanded ?
 
: null } - { expandedEvents } -
- ); + const EventListSummary = sdk.getComponent("views.elements.EventListSummary"); + return ; }, }); diff --git a/src/hooks/useStateToggle.js b/src/hooks/useStateToggle.js new file mode 100644 index 0000000000..58cf123bfb --- /dev/null +++ b/src/hooks/useStateToggle.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 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. +*/ + +import {useState} from 'react'; + +// Hook to simplify toggling of a boolean state value +// Returns value, method to toggle boolean value and method to set the boolean value +export const useStateToggle = (initialValue) => { + const [value, setValue] = useState(Boolean(initialValue)); + const toggleValue = () => { + setValue(!value); + }; + return [value, toggleValue, setValue]; +}; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 02d1d0e8d6..f7016d7fad 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1131,6 +1131,8 @@ "Yes": "Yes", "No": "No", "Please create a new issue on GitHub so that we can investigate this bug.": "Please create a new issue on GitHub so that we can investigate this bug.", + "collapse": "collapse", + "expand": "expand", "Communities": "Communities", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", @@ -1193,8 +1195,6 @@ "%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)smade no changes", "%(oneUser)smade no changes %(count)s times|other": "%(oneUser)smade no changes %(count)s times", "%(oneUser)smade no changes %(count)s times|one": "%(oneUser)smade no changes", - "collapse": "collapse", - "expand": "expand", "Power level": "Power level", "Custom level": "Custom level", "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.", diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js index 09a4739f06..db86b2ffab 100644 --- a/test/components/views/elements/MemberEventListSummary-test.js +++ b/test/components/views/elements/MemberEventListSummary-test.js @@ -162,7 +162,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -198,7 +198,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -246,7 +246,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -299,7 +299,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -358,7 +358,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -396,7 +396,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -447,7 +447,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -521,7 +521,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -568,7 +568,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -604,7 +604,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -632,7 +632,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -659,7 +659,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; @@ -684,7 +684,7 @@ describe('MemberEventListSummary', function() { , ); const summary = ReactTestUtils.findRenderedDOMComponentWithClass( - instance, "mx_MemberEventListSummary_summary", + instance, "mx_EventListSummary_summary", ); const summaryText = summary.innerText; From efde7f289f07c6bfa279cc290797913e411e68f8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Sep 2019 15:33:52 +0100 Subject: [PATCH 2/9] Use EventListSummary on m.room.create events and state events thereafter Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MessagePanel.js | 87 +++++++++++++++++++++++ src/i18n/strings/en_EN.json | 1 + 2 files changed, 88 insertions(+) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 33ae0c3f74..4a1af77739 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -28,6 +28,7 @@ import sdk from '../../index'; import MatrixClientPeg from '../../MatrixClientPeg'; import SettingsStore from '../../settings/SettingsStore'; +import {_t} from "../../languageHandler"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -323,6 +324,7 @@ module.exports = createReactClass({ _getEventTiles: function() { const DateSeparator = sdk.getComponent('messages.DateSeparator'); + const EventListSummary = sdk.getComponent('views.elements.EventListSummary'); const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary'); this.eventNodes = {}; @@ -382,6 +384,91 @@ module.exports = createReactClass({ const eventId = mxEv.getId(); const last = (mxEv === lastShownEvent); + // Wrap initial room creation events into an EventListSummary + // Grouping only events sent by the same user that sent the `m.room.create` and only until + // the first non-state event or membership event which is not regarding the sender of the `m.room.create` event + const shouldGroup = (ev) => { + if (ev.getType() === "m.room.member" + && (ev.getStateKey() !== mxEv.getSender() || ev.getContent()["membership"] !== "join")) { + return false; + } + if (ev.isState() && ev.getSender() === mxEv.getSender()) { + return true; + } + return false; + }; + if (mxEv.getType() === "m.room.create") { + let readMarkerInSummary = false; + const ts1 = mxEv.getTs(); + + if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) { + const dateSeparator =
  • ; + ret.push(dateSeparator); + } + + // If RM event is the first in the summary, append the RM after the summary + if (mxEv.getId() === this.props.readMarkerEventId) { + readMarkerInSummary = true; + } + + const summarisedEvents = []; // Don't add m.room.create here as we don't want it inside the summary + for (;i + 1 < this.props.events.length; i++) { + const collapsedMxEv = this.props.events[i + 1]; + + // Ignore redacted/hidden member events + if (!this._shouldShowEvent(collapsedMxEv)) { + // If this hidden event is the RM and in or at end of a summary put RM after the summary. + if (collapsedMxEv.getId() === this.props.readMarkerEventId) { + readMarkerInSummary = true; + } + continue; + } + + if (!shouldGroup(collapsedMxEv) || this._wantsDateSeparator(mxEv, collapsedMxEv.getDate())) { + break; + } + + // If RM event is in the summary mark it as such and the RM will be appended after the summary. + if (collapsedMxEv.getId() === this.props.readMarkerEventId) { + readMarkerInSummary = true; + } + + summarisedEvents.push(collapsedMxEv); + } + + // At this point, i = the index of the last event in the summary sequence + let eventTiles = summarisedEvents.map((e) => { + // In order to prevent DateSeparators from appearing in the expanded form + // of EventListSummary, render each member event as if the previous + // one was itself. This way, the timestamp of the previous event === the + // timestamp of the current event, and no DateSeparator is inserted. + return this._getTilesForEvent(e, e, e === lastShownEvent); + }).reduce((a, b) => a.concat(b)); + + if (eventTiles.length === 0) { + eventTiles = null; + } + + ret.push( + { eventTiles } + ); + + if (readMarkerInSummary) { + ret.push(this._getReadMarkerTile(visible)); + } + + prevEvent = mxEv; + continue; + } + const wantTile = this._shouldShowEvent(mxEv); // Wrap consecutive member events in a ListSummary, ignore if redacted diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f7016d7fad..f3b289cdc4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1609,6 +1609,7 @@ "Old cryptography data detected": "Old cryptography data detected", "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.", "Logout": "Logout", + "%(creator)s created and configured the room.": "%(creator)s created and configured the room.", "Your Communities": "Your Communities", "Did you know: you can use communities to filter your Riot.im experience!": "Did you know: you can use communities to filter your Riot.im experience!", "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.", From 37b8b52d0d81571dc1c0204001e54bca72aec6f5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Sep 2019 16:56:35 +0100 Subject: [PATCH 3/9] regenerate components Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/_components.scss b/res/css/_components.scss index 561b1b4820..4891fd90c0 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -88,7 +88,7 @@ @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; @import "./views/elements/_ErrorBoundary.scss"; -@import "./views/elements/_EventListSummary"; +@import "./views/elements/_EventListSummary.scss"; @import "./views/elements/_Field.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; From efeb6e87c427fda3cebca510ef4aaac86c0a7df0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 13 Sep 2019 10:32:13 +0100 Subject: [PATCH 4/9] Use new React Shallow renderer with hooks support Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/MemberEventListSummary.js | 3 ++- .../components/views/elements/MemberEventListSummary-test.js | 5 +++-- yarn.lock | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 98adbb2e5c..ef80efaa68 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -22,13 +22,14 @@ import createReactClass from 'create-react-class'; import { _t } from '../../../languageHandler'; import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; import sdk from "../../../index"; +import {MatrixEvent} from "matrix-js-sdk"; module.exports = createReactClass({ displayName: 'MemberEventListSummary', propTypes: { // An array of member events to summarise - events: PropTypes.array.isRequired, + events: PropTypes.arrayOf(PropTypes.instanceOf(MatrixEvent)).isRequired, // An array of EventTiles to render when expanded children: PropTypes.array.isRequired, // The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left" diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js index db86b2ffab..95f7e7999a 100644 --- a/test/components/views/elements/MemberEventListSummary-test.js +++ b/test/components/views/elements/MemberEventListSummary-test.js @@ -1,6 +1,7 @@ import expect from 'expect'; import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; +import ShallowRenderer from "react-test-renderer/shallow"; import sdk from 'matrix-react-sdk'; import * as languageHandler from '../../../../src/languageHandler'; import * as testUtils from '../../../test-utils'; @@ -112,7 +113,7 @@ describe('MemberEventListSummary', function() { threshold: 3, }; - const renderer = testUtils.getRenderer(); + const renderer = new ShallowRenderer(); renderer.render(); const result = renderer.getRenderOutput(); @@ -134,7 +135,7 @@ describe('MemberEventListSummary', function() { threshold: 3, }; - const renderer = testUtils.getRenderer(); + const renderer = new ShallowRenderer(); renderer.render(); const result = renderer.getRenderOutput(); diff --git a/yarn.lock b/yarn.lock index ba7fea21ba..b2815a496b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6582,6 +6582,11 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== +react-is@^16.9.0: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" + integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== + react-lifecycles-compat@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" From 5c960dccb733eba4a4cd3144aba4dfab89436de6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 18 Sep 2019 08:49:02 +0100 Subject: [PATCH 5/9] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/components/views/elements/MemberEventListSummary-test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js index 95f7e7999a..e9306ec184 100644 --- a/test/components/views/elements/MemberEventListSummary-test.js +++ b/test/components/views/elements/MemberEventListSummary-test.js @@ -1,7 +1,6 @@ import expect from 'expect'; import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; -import ShallowRenderer from "react-test-renderer/shallow"; import sdk from 'matrix-react-sdk'; import * as languageHandler from '../../../../src/languageHandler'; import * as testUtils from '../../../test-utils'; From 6be318b59d0b9bd72baec1074c1f289792f7655c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 3 Oct 2019 09:51:58 +0100 Subject: [PATCH 6/9] Only insert ELS if it'll be non-empty Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MessagePanel.js | 28 +++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 4a1af77739..713cdc867a 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -428,7 +428,7 @@ module.exports = createReactClass({ break; } - // If RM event is in the summary mark it as such and the RM will be appended after the summary. + // If RM event is in the summary, mark it as such and the RM will be appended after the summary. if (collapsedMxEv.getId() === this.props.readMarkerEventId) { readMarkerInSummary = true; } @@ -445,22 +445,20 @@ module.exports = createReactClass({ return this._getTilesForEvent(e, e, e === lastShownEvent); }).reduce((a, b) => a.concat(b)); - if (eventTiles.length === 0) { - eventTiles = null; + if (eventTiles.length > 0) { + ret.push( + { eventTiles } + ); } - ret.push( - { eventTiles } - ); - if (readMarkerInSummary) { ret.push(this._getReadMarkerTile(visible)); } From 9331ca9d07eeafde4397e10feb502f8a5eeeda45 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 3 Oct 2019 10:00:03 +0100 Subject: [PATCH 7/9] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MessagePanel.js | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 713cdc867a..d2aa407cfd 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -445,19 +445,17 @@ module.exports = createReactClass({ return this._getTilesForEvent(e, e, e === lastShownEvent); }).reduce((a, b) => a.concat(b)); - if (eventTiles.length > 0) { - ret.push( - { eventTiles } - ); - } + ret.push( + { eventTiles } + ); if (readMarkerInSummary) { ret.push(this._getReadMarkerTile(visible)); From f2d73793eea4a70e77fbcf97782dc7f23d726269 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 3 Oct 2019 10:04:27 +0100 Subject: [PATCH 8/9] delint again Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MessagePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index d2aa407cfd..7254059734 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -437,7 +437,7 @@ module.exports = createReactClass({ } // At this point, i = the index of the last event in the summary sequence - let eventTiles = summarisedEvents.map((e) => { + const eventTiles = summarisedEvents.map((e) => { // In order to prevent DateSeparators from appearing in the expanded form // of EventListSummary, render each member event as if the previous // one was itself. This way, the timestamp of the previous event === the From aae5bb9f39e485bf7282bca757394de24f21f2bd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 13 Sep 2019 10:32:13 +0100 Subject: [PATCH 9/9] Use new React Shallow renderer with hooks support Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../components/views/elements/MemberEventListSummary-test.js | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js index e9306ec184..95f7e7999a 100644 --- a/test/components/views/elements/MemberEventListSummary-test.js +++ b/test/components/views/elements/MemberEventListSummary-test.js @@ -1,6 +1,7 @@ import expect from 'expect'; import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; +import ShallowRenderer from "react-test-renderer/shallow"; import sdk from 'matrix-react-sdk'; import * as languageHandler from '../../../../src/languageHandler'; import * as testUtils from '../../../test-utils'; diff --git a/yarn.lock b/yarn.lock index b2815a496b..908c653e57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6587,6 +6587,11 @@ react-is@^16.9.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== +react-is@^16.9.0: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" + integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== + react-lifecycles-compat@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"