From 4ec8cf11ea572d7e5ac3e1f27bc95e5ac3f9975d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 15 Jun 2021 18:52:40 -0400 Subject: [PATCH 01/11] Add more types to TextForEvent Signed-off-by: Robin Townsend --- src/TextForEvent.ts | 50 +++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/TextForEvent.ts b/src/TextForEvent.ts index 649c53664e..6956da098e 100644 --- a/src/TextForEvent.ts +++ b/src/TextForEvent.ts @@ -13,6 +13,8 @@ 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 {MatrixEvent} from "matrix-js-sdk/src/models/event"; + import {MatrixClientPeg} from './MatrixClientPeg'; import { _t } from './languageHandler'; import * as Roles from './Roles'; @@ -25,7 +27,7 @@ import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore"; // any text to display at all. For this reason they return deferred values // to avoid the expense of looking up translations when they're not needed. -function textForMemberEvent(ev): () => string | null { +function textForMemberEvent(ev: MatrixEvent): () => string | null { // XXX: SYJS-16 "sender is sometimes null for join messages" const senderName = ev.sender ? ev.sender.name : ev.getSender(); const targetName = ev.target ? ev.target.name : ev.getStateKey(); @@ -107,7 +109,7 @@ function textForMemberEvent(ev): () => string | null { } } -function textForTopicEvent(ev): () => string | null { +function textForTopicEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); return () => _t('%(senderDisplayName)s changed the topic to "%(topic)s".', { senderDisplayName, @@ -115,7 +117,7 @@ function textForTopicEvent(ev): () => string | null { }); } -function textForRoomNameEvent(ev): () => string | null { +function textForRoomNameEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); if (!ev.getContent().name || ev.getContent().name.trim().length === 0) { @@ -134,12 +136,12 @@ function textForRoomNameEvent(ev): () => string | null { }); } -function textForTombstoneEvent(ev): () => string | null { +function textForTombstoneEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); return () => _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName}); } -function textForJoinRulesEvent(ev): () => string | null { +function textForJoinRulesEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().join_rule) { case "public": @@ -159,7 +161,7 @@ function textForJoinRulesEvent(ev): () => string | null { } } -function textForGuestAccessEvent(ev): () => string | null { +function textForGuestAccessEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().guest_access) { case "can_join": @@ -175,7 +177,7 @@ function textForGuestAccessEvent(ev): () => string | null { } } -function textForRelatedGroupsEvent(ev): () => string | null { +function textForRelatedGroupsEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const groups = ev.getContent().groups || []; const prevGroups = ev.getPrevContent().groups || []; @@ -205,7 +207,7 @@ function textForRelatedGroupsEvent(ev): () => string | null { } } -function textForServerACLEvent(ev): () => string | null { +function textForServerACLEvent(ev: MatrixEvent): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const prevContent = ev.getPrevContent(); const current = ev.getContent(); @@ -235,7 +237,7 @@ function textForServerACLEvent(ev): () => string | null { return getText; } -function textForMessageEvent(ev): () => string | null { +function textForMessageEvent(ev: MatrixEvent): () => string | null { return () => { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); let message = senderDisplayName + ': ' + ev.getContent().body; @@ -248,7 +250,7 @@ function textForMessageEvent(ev): () => string | null { }; } -function textForCanonicalAliasEvent(ev): () => string | null { +function textForCanonicalAliasEvent(ev: MatrixEvent): () => string | null { const senderName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const oldAlias = ev.getPrevContent().alias; const oldAltAliases = ev.getPrevContent().alt_aliases || []; @@ -299,7 +301,7 @@ function textForCanonicalAliasEvent(ev): () => string | null { }); } -function textForCallAnswerEvent(event): () => string | null { +function textForCallAnswerEvent(event: MatrixEvent): () => string | null { return () => { const senderName = event.sender ? event.sender.name : _t('Someone'); const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)'); @@ -307,7 +309,7 @@ function textForCallAnswerEvent(event): () => string | null { }; } -function textForCallHangupEvent(event): () => string | null { +function textForCallHangupEvent(event: MatrixEvent): () => string | null { const getSenderName = () => event.sender ? event.sender.name : _t('Someone'); const eventContent = event.getContent(); let getReason = () => ""; @@ -344,14 +346,14 @@ function textForCallHangupEvent(event): () => string | null { return () => _t('%(senderName)s ended the call.', {senderName: getSenderName()}) + ' ' + getReason(); } -function textForCallRejectEvent(event): () => string | null { +function textForCallRejectEvent(event: MatrixEvent): () => string | null { return () => { const senderName = event.sender ? event.sender.name : _t('Someone'); return _t('%(senderName)s declined the call.', {senderName}); }; } -function textForCallInviteEvent(event): () => string | null { +function textForCallInviteEvent(event: MatrixEvent): () => string | null { const getSenderName = () => event.sender ? event.sender.name : _t('Someone'); // FIXME: Find a better way to determine this from the event? let isVoice = true; @@ -383,7 +385,7 @@ function textForCallInviteEvent(event): () => string | null { } } -function textForThreePidInviteEvent(event): () => string | null { +function textForThreePidInviteEvent(event: MatrixEvent): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); if (!isValid3pidInvite(event)) { @@ -399,7 +401,7 @@ function textForThreePidInviteEvent(event): () => string | null { }); } -function textForHistoryVisibilityEvent(event): () => string | null { +function textForHistoryVisibilityEvent(event: MatrixEvent): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); switch (event.getContent().history_visibility) { case 'invited': @@ -421,7 +423,7 @@ function textForHistoryVisibilityEvent(event): () => string | null { } // Currently will only display a change if a user's power level is changed -function textForPowerEvent(event): () => string | null { +function textForPowerEvent(event: MatrixEvent): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); if (!event.getPrevContent() || !event.getPrevContent().users || !event.getContent() || !event.getContent().users) { @@ -466,12 +468,12 @@ function textForPowerEvent(event): () => string | null { }); } -function textForPinnedEvent(event): () => string | null { +function textForPinnedEvent(event: MatrixEvent): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); return () => _t("%(senderName)s changed the pinned messages for the room.", {senderName}); } -function textForWidgetEvent(event): () => string | null { +function textForWidgetEvent(event: MatrixEvent): () => string | null { const senderName = event.getSender(); const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent(); const {name, type, url} = event.getContent() || {}; @@ -501,12 +503,12 @@ function textForWidgetEvent(event): () => string | null { } } -function textForWidgetLayoutEvent(event): () => string | null { +function textForWidgetLayoutEvent(event: MatrixEvent): () => string | null { const senderName = event.sender?.name || event.getSender(); return () => _t("%(senderName)s has updated the widget layout", {senderName}); } -function textForMjolnirEvent(event): () => string | null { +function textForMjolnirEvent(event: MatrixEvent): () => string | null { const senderName = event.getSender(); const {entity: prevEntity} = event.getPrevContent(); const {entity, recommendation, reason} = event.getContent(); @@ -594,7 +596,7 @@ function textForMjolnirEvent(event): () => string | null { } interface IHandlers { - [type: string]: (ev: any) => (() => string | null); + [type: string]: (ev: MatrixEvent) => (() => string | null); } const handlers: IHandlers = { @@ -630,12 +632,12 @@ for (const evType of ALL_RULE_TYPES) { stateHandlers[evType] = textForMjolnirEvent; } -export function hasText(ev): boolean { +export function hasText(ev: MatrixEvent): boolean { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return Boolean(handler?.(ev)); } -export function textForEvent(ev): string { +export function textForEvent(ev: MatrixEvent): string { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return handler?.(ev)?.() || ''; } From 819fe419b749f641a941a21cb21c08fbc637aca3 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 15 Jun 2021 18:59:42 -0400 Subject: [PATCH 02/11] Allow using cached setting values in TextForEvent Signed-off-by: Robin Townsend --- src/TextForEvent.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/TextForEvent.ts b/src/TextForEvent.ts index 6956da098e..652a1d6e54 100644 --- a/src/TextForEvent.ts +++ b/src/TextForEvent.ts @@ -27,7 +27,7 @@ import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore"; // any text to display at all. For this reason they return deferred values // to avoid the expense of looking up translations when they're not needed. -function textForMemberEvent(ev: MatrixEvent): () => string | null { +function textForMemberEvent(ev: MatrixEvent, showHiddenEvents?: boolean): () => string | null { // XXX: SYJS-16 "sender is sometimes null for join messages" const senderName = ev.sender ? ev.sender.name : ev.getSender(); const targetName = ev.target ? ev.target.name : ev.getStateKey(); @@ -77,7 +77,7 @@ function textForMemberEvent(ev: MatrixEvent): () => string | null { return () => _t('%(senderName)s changed their profile picture.', {senderName}); } else if (!prevContent.avatar_url && content.avatar_url) { return () => _t('%(senderName)s set a profile picture.', {senderName}); - } else if (SettingsStore.getValue("showHiddenEventsInTimeline")) { + } else if (showHiddenEvents ?? SettingsStore.getValue("showHiddenEventsInTimeline")) { // This is a null rejoin, it will only be visible if the Labs option is enabled return () => _t("%(senderName)s made no change.", {senderName}); } else { @@ -596,7 +596,7 @@ function textForMjolnirEvent(event: MatrixEvent): () => string | null { } interface IHandlers { - [type: string]: (ev: MatrixEvent) => (() => string | null); + [type: string]: (ev: MatrixEvent, showHiddenEvents?: boolean) => (() => string | null); } const handlers: IHandlers = { @@ -632,12 +632,24 @@ for (const evType of ALL_RULE_TYPES) { stateHandlers[evType] = textForMjolnirEvent; } -export function hasText(ev: MatrixEvent): boolean { +/** + * Determines whether the given event has text to display. + * @param ev The event + * @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline + * to avoid hitting the settings store + */ +export function hasText(ev: MatrixEvent, showHiddenEvents?: boolean): boolean { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; - return Boolean(handler?.(ev)); + return Boolean(handler?.(ev, showHiddenEvents)); } -export function textForEvent(ev: MatrixEvent): string { +/** + * Gets the textual content of the given event. + * @param ev The event + * @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline + * to avoid hitting the settings store + */ +export function textForEvent(ev: MatrixEvent, showHiddenEvents?: boolean): string { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; - return handler?.(ev)?.() || ''; + return handler?.(ev, showHiddenEvents)?.() || ''; } From af11878e0c22212093c5a85aa4ce6b9a3dbc77b2 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Wed, 16 Jun 2021 20:40:47 -0400 Subject: [PATCH 03/11] Use cached setting values when calling TextForEvent Signed-off-by: Robin Townsend --- src/components/structures/MessagePanel.js | 16 +++++++++------- src/components/structures/RoomView.tsx | 7 ++++++- src/components/structures/TimelinePanel.js | 3 ++- src/components/views/messages/TextualEvent.js | 5 ++++- src/components/views/rooms/EventTile.tsx | 4 ++-- src/components/views/rooms/SearchResultTile.js | 5 ++++- src/contexts/RoomContext.ts | 1 + 7 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index eb9611a6fc..b8d3f4f830 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -41,7 +41,7 @@ const continuedTypes = ['m.sticker', 'm.room.message']; // check if there is a previous event and it has the same sender as this event // and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL -function shouldFormContinuation(prevEvent, mxEvent) { +function shouldFormContinuation(prevEvent, mxEvent, showHiddenEvents) { // sanity check inputs if (!prevEvent || !prevEvent.sender || !mxEvent.sender) return false; // check if within the max continuation period @@ -61,7 +61,7 @@ function shouldFormContinuation(prevEvent, mxEvent) { mxEvent.sender.getMxcAvatarUrl() !== prevEvent.sender.getMxcAvatarUrl()) return false; // if we don't have tile for previous event then it was shown by showHiddenEvents and has no SenderProfile - if (!haveTileForEvent(prevEvent)) return false; + if (!haveTileForEvent(prevEvent, showHiddenEvents)) return false; return true; } @@ -202,7 +202,8 @@ export default class MessagePanel extends React.Component { this._readReceiptsByUserId = {}; // Cache hidden events setting on mount since Settings is expensive to - // query, and we check this in a hot code path. + // query, and we check this in a hot code path. This is also cached in + // our RoomContext, however we still need a fallback for roomless MessagePanels. this._showHiddenEventsInTimeline = SettingsStore.getValue("showHiddenEventsInTimeline"); @@ -372,11 +373,11 @@ export default class MessagePanel extends React.Component { return false; // ignored = no show (only happens if the ignore happens after an event was received) } - if (this._showHiddenEventsInTimeline) { + if (this.context?.showHiddenEventsInTimeline ?? this._showHiddenEventsInTimeline) { return true; } - if (!haveTileForEvent(mxEv)) { + if (!haveTileForEvent(mxEv, this.context?.showHiddenEventsInTimeline)) { return false; // no tile = no show } @@ -613,7 +614,8 @@ export default class MessagePanel extends React.Component { } // is this a continuation of the previous message? - const continuation = !wantsDateSeparator && shouldFormContinuation(prevEvent, mxEv); + const continuation = !wantsDateSeparator && + shouldFormContinuation(prevEvent, mxEv, this.context?.showHiddenEventsInTimeline); const eventId = mxEv.getId(); const highlight = (eventId === this.props.highlightedEventId); @@ -1168,7 +1170,7 @@ class MemberGrouper { add(ev) { if (ev.getType() === 'm.room.member') { // We can ignore any events that don't actually have a message to display - if (!hasText(ev)) return; + if (!hasText(ev, this.context?.showHiddenEventsInTimeline)) return; } this.readMarker = this.readMarker || this.panel._readMarkerForEvent( ev.getId(), diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index fe90d2f873..d1c68f0cc7 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -181,6 +181,7 @@ export interface IState { canReply: boolean; layout: Layout; lowBandwidth: boolean; + showHiddenEventsInTimeline: boolean; showReadReceipts: boolean; showRedactions: boolean; showJoinLeaves: boolean; @@ -244,6 +245,7 @@ export default class RoomView extends React.Component { canReply: false, layout: SettingsStore.getValue("layout"), lowBandwidth: SettingsStore.getValue("lowBandwidth"), + showHiddenEventsInTimeline: SettingsStore.getValue("showHiddenEventsInTimeline"), showReadReceipts: true, showRedactions: true, showJoinLeaves: true, @@ -282,6 +284,9 @@ export default class RoomView extends React.Component { SettingsStore.watchSetting("lowBandwidth", null, () => this.setState({ lowBandwidth: SettingsStore.getValue("lowBandwidth") }), ), + SettingsStore.watchSetting("showHiddenEventsInTimeline", null, () => + this.setState({ showHiddenEventsInTimeline: SettingsStore.getValue("showHiddenEventsInTimeline") }), + ), ]; } @@ -1411,7 +1416,7 @@ export default class RoomView extends React.Component { continue; } - if (!haveTileForEvent(mxEv)) { + if (!haveTileForEvent(mxEv, this.state.showHiddenEventsInTimeline)) { // XXX: can this ever happen? It will make the result count // not match the displayed count. continue; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index bb62745d98..20f70df4dc 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -1291,7 +1291,8 @@ class TimelinePanel extends React.Component { const shouldIgnore = !!ev.status || // local echo (ignoreOwn && ev.sender && ev.sender.userId == myUserId); // own message - const isWithoutTile = !haveTileForEvent(ev) || shouldHideEvent(ev, this.context); + const isWithoutTile = !haveTileForEvent(ev, this.context?.showHiddenEventsInTimeline) || + shouldHideEvent(ev, this.context); if (isWithoutTile || !node) { // don't start counting if the event should be ignored, diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.js index a020cc6c52..0cdd573076 100644 --- a/src/components/views/messages/TextualEvent.js +++ b/src/components/views/messages/TextualEvent.js @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; +import RoomContext from "../../../contexts/RoomContext"; import * as TextForEvent from "../../../TextForEvent"; import {replaceableComponent} from "../../../utils/replaceableComponent"; @@ -27,8 +28,10 @@ export default class TextualEvent extends React.Component { mxEvent: PropTypes.object.isRequired, }; + static contextType = RoomContext; + render() { - const text = TextForEvent.textForEvent(this.props.mxEvent); + const text = TextForEvent.textForEvent(this.props.mxEvent, this.context?.showHiddenEventsInTimeline); if (text == null || text.length === 0) return null; return (
{ text }
diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 85b9cac2c4..8de371ea15 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -1217,7 +1217,7 @@ function isMessageEvent(ev) { return (messageTypes.includes(ev.getType())); } -export function haveTileForEvent(e) { +export function haveTileForEvent(e: MatrixEvent, showHiddenEvents?: boolean) { // Only messages have a tile (black-rectangle) if redacted if (e.isRedacted() && !isMessageEvent(e)) return false; @@ -1227,7 +1227,7 @@ export function haveTileForEvent(e) { const handler = getHandlerTile(e); if (handler === undefined) return false; if (handler === 'messages.TextualEvent') { - return hasText(e); + return hasText(e, showHiddenEvents); } else if (handler === 'messages.RoomCreate') { return Boolean(e.getContent()['predecessor']); } else { diff --git a/src/components/views/rooms/SearchResultTile.js b/src/components/views/rooms/SearchResultTile.js index 3b79aa6246..2963265317 100644 --- a/src/components/views/rooms/SearchResultTile.js +++ b/src/components/views/rooms/SearchResultTile.js @@ -18,6 +18,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../index'; +import RoomContext from "../../../contexts/RoomContext"; import {haveTileForEvent} from "./EventTile"; import SettingsStore from "../../../settings/SettingsStore"; import {UIFeature} from "../../../settings/UIFeature"; @@ -38,6 +39,8 @@ export default class SearchResultTile extends React.Component { onHeightChanged: PropTypes.func, }; + static contextType = RoomContext; + render() { const DateSeparator = sdk.getComponent('messages.DateSeparator'); const EventTile = sdk.getComponent('rooms.EventTile'); @@ -57,7 +60,7 @@ export default class SearchResultTile extends React.Component { if (!contextual) { highlights = this.props.searchHighlights; } - if (haveTileForEvent(ev)) { + if (haveTileForEvent(ev, this.context?.showHiddenEventsInTimeline)) { ret.push(( ({ canReply: false, layout: Layout.Group, lowBandwidth: false, + showHiddenEventsInTimeline: false, showReadReceipts: true, showRedactions: true, showJoinLeaves: true, From 9e2ab0d432d5ef7facae1ecccdf25dd71b0baeca Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 17 Jun 2021 07:35:40 -0400 Subject: [PATCH 04/11] Fix import whitespace in TextForEvent Signed-off-by: Robin Townsend --- src/TextForEvent.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TextForEvent.ts b/src/TextForEvent.ts index 652a1d6e54..5275ff0a63 100644 --- a/src/TextForEvent.ts +++ b/src/TextForEvent.ts @@ -13,15 +13,15 @@ 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 {MatrixEvent} from "matrix-js-sdk/src/models/event"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import {MatrixClientPeg} from './MatrixClientPeg'; +import { MatrixClientPeg } from './MatrixClientPeg'; import { _t } from './languageHandler'; import * as Roles from './Roles'; -import {isValid3pidInvite} from "./RoomInvite"; +import { isValid3pidInvite } from "./RoomInvite"; import SettingsStore from "./settings/SettingsStore"; -import {ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES} from "./mjolnir/BanList"; -import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore"; +import { ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES } from "./mjolnir/BanList"; +import { WIDGET_LAYOUT_EVENT_TYPE } from "./stores/widgets/WidgetLayoutStore"; // These functions are frequently used just to check whether an event has // any text to display at all. For this reason they return deferred values From e4250e254c7253dc1679b0e9ae84065a35fa6b61 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 17 Jun 2021 09:52:15 -0400 Subject: [PATCH 05/11] Propertly thread showHiddenEventsInTimeline through groupers --- src/components/structures/MessagePanel.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index b8d3f4f830..16563bd4e9 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -537,7 +537,7 @@ export default class MessagePanel extends React.Component { if (grouper) { if (grouper.shouldGroup(mxEv)) { - grouper.add(mxEv); + grouper.add(mxEv, this.context?.showHiddenEventsInTimeline); continue; } else { // not part of group, so get the group tiles, close the @@ -1167,10 +1167,10 @@ class MemberGrouper { return isMembershipChange(ev); } - add(ev) { + add(ev, showHiddenEvents) { if (ev.getType() === 'm.room.member') { // We can ignore any events that don't actually have a message to display - if (!hasText(ev, this.context?.showHiddenEventsInTimeline)) return; + if (!hasText(ev, showHiddenEvents)) return; } this.readMarker = this.readMarker || this.panel._readMarkerForEvent( ev.getId(), From e35e836052d4f918c36f4c017aabf6a44534d8ae Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 24 Jun 2021 18:45:23 -0400 Subject: [PATCH 06/11] Convert TextualEvent and SearchResultTile to TypeScript Signed-off-by: Robin Townsend --- .../{TextualEvent.js => TextualEvent.tsx} | 24 ++++---- ...archResultTile.js => SearchResultTile.tsx} | 61 +++++++++---------- 2 files changed, 41 insertions(+), 44 deletions(-) rename src/components/views/messages/{TextualEvent.js => TextualEvent.tsx} (70%) rename src/components/views/rooms/{SearchResultTile.js => SearchResultTile.tsx} (64%) diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.tsx similarity index 70% rename from src/components/views/messages/TextualEvent.js rename to src/components/views/messages/TextualEvent.tsx index 0cdd573076..e96390d7bc 100644 --- a/src/components/views/messages/TextualEvent.js +++ b/src/components/views/messages/TextualEvent.tsx @@ -15,26 +15,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import React from "react"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import RoomContext from "../../../contexts/RoomContext"; import * as TextForEvent from "../../../TextForEvent"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; + +interface IProps { + // The event to show + mxEvent: MatrixEvent; +} @replaceableComponent("views.messages.TextualEvent") -export default class TextualEvent extends React.Component { - static propTypes = { - /* the MatrixEvent to show */ - mxEvent: PropTypes.object.isRequired, - }; - +export default class TextualEvent extends React.Component { static contextType = RoomContext; - render() { + public render() { const text = TextForEvent.textForEvent(this.props.mxEvent, this.context?.showHiddenEventsInTimeline); if (text == null || text.length === 0) return null; - return ( -
{ text }
- ); + return
{ text }
; } } diff --git a/src/components/views/rooms/SearchResultTile.js b/src/components/views/rooms/SearchResultTile.tsx similarity index 64% rename from src/components/views/rooms/SearchResultTile.js rename to src/components/views/rooms/SearchResultTile.tsx index 2963265317..8af0fa5abd 100644 --- a/src/components/views/rooms/SearchResultTile.js +++ b/src/components/views/rooms/SearchResultTile.tsx @@ -15,41 +15,41 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; -import * as sdk from '../../../index'; +import React from "react"; +import { SearchResult } from "matrix-js-sdk/src/models/search-result"; import RoomContext from "../../../contexts/RoomContext"; -import {haveTileForEvent} from "./EventTile"; +import { haveTileForEvent } from "./EventTile"; import SettingsStore from "../../../settings/SettingsStore"; -import {UIFeature} from "../../../settings/UIFeature"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import { UIFeature } from "../../../settings/UIFeature"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; +import DateSeparator from "../messages/DateSeparator"; +import EventTile from "./EventTile"; + +interface IProps { + // The details of this result + searchResult: SearchResult; + // Strings to be highlighted in the results + searchHighlights?: string[]; + // href for the highlights in this result + resultLink?: string; + onHeightChanged: () => void; + permalinkCreator: RoomPermalinkCreator; +} @replaceableComponent("views.rooms.SearchResultTile") -export default class SearchResultTile extends React.Component { - static propTypes = { - // a matrix-js-sdk SearchResult containing the details of this result - searchResult: PropTypes.object.isRequired, - - // a list of strings to be highlighted in the results - searchHighlights: PropTypes.array, - - // href for the highlights in this result - resultLink: PropTypes.string, - - onHeightChanged: PropTypes.func, - }; - +export default class SearchResultTile extends React.Component { static contextType = RoomContext; - render() { - const DateSeparator = sdk.getComponent('messages.DateSeparator'); - const EventTile = sdk.getComponent('rooms.EventTile'); + public render() { const result = this.props.searchResult; const mxEv = result.context.getEvent(); const eventId = mxEv.getId(); const ts1 = mxEv.getTs(); const ret = []; + const layout = SettingsStore.getValue("layout"); + const isTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); const alwaysShowTimestamps = SettingsStore.getValue("alwaysShowTimestamps"); const timeline = result.context.getTimeline(); @@ -61,25 +61,24 @@ export default class SearchResultTile extends React.Component { highlights = this.props.searchHighlights; } if (haveTileForEvent(ev, this.context?.showHiddenEventsInTimeline)) { - ret.push(( + ret.push( - )); + />, + ); } } - return ( -
  • - { ret } -
  • ); + + return
  • { ret }
  • ; } } From a921d32f44fdedc6489158ab69c43347da0bffcc Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 24 Jun 2021 18:51:46 -0400 Subject: [PATCH 07/11] Fix lint Signed-off-by: Robin Townsend --- src/components/structures/MessagePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index c7d9944435..19ef6b3350 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -56,7 +56,7 @@ const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; function shouldFormContinuation( prevEvent: MatrixEvent, mxEvent: MatrixEvent, - showHiddenEvents: boolean + showHiddenEvents: boolean, ): boolean { // sanity check inputs if (!prevEvent || !prevEvent.sender || !mxEvent.sender) return false; From c0e10218d9039a248974959e8965c7218493c67a Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 29 Jun 2021 22:42:46 -0400 Subject: [PATCH 08/11] Fix lints Signed-off-by: Robin Townsend --- src/TextForEvent.tsx | 6 +++--- src/components/views/messages/TextualEvent.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx index ee57f7dacb..c6ade33cbe 100644 --- a/src/TextForEvent.tsx +++ b/src/TextForEvent.tsx @@ -693,9 +693,9 @@ export function hasText(ev: MatrixEvent, showHiddenEvents?: boolean): boolean { * @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline * to avoid hitting the settings store */ -export function textForEvent( - ev: MatrixEvent, allowJSX: boolean = false, showHiddenEvents?: boolean -): string | JSX.Element { +export function textForEvent(ev: MatrixEvent): string; +export function textForEvent(ev: MatrixEvent, allowJSX: true, showHiddenEvents?: boolean): string | JSX.Element; +export function textForEvent(ev: MatrixEvent, allowJSX = false, showHiddenEvents?: boolean): string | JSX.Element { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return handler?.(ev, allowJSX, showHiddenEvents)?.() ?? ''; } diff --git a/src/components/views/messages/TextualEvent.tsx b/src/components/views/messages/TextualEvent.tsx index ab25b21323..beaf605e1f 100644 --- a/src/components/views/messages/TextualEvent.tsx +++ b/src/components/views/messages/TextualEvent.tsx @@ -32,7 +32,7 @@ export default class TextualEvent extends React.Component { public render() { const text = TextForEvent.textForEvent(this.props.mxEvent, true, this.context?.showHiddenEventsInTimeline); - if (text == null || text.length === 0) return null; + if (!text) return null; return
    { text }
    ; } } From 6c4f0526d7c2949ba4f39809dd51af03f5a0aae0 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 13 Jul 2021 23:26:09 -0400 Subject: [PATCH 09/11] Coalesce falsy values from TextForEvent handlers Signed-off-by: Robin Townsend --- src/TextForEvent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx index 3e3b5aa2e0..0056a37c85 100644 --- a/src/TextForEvent.tsx +++ b/src/TextForEvent.tsx @@ -705,5 +705,5 @@ export function textForEvent(ev: MatrixEvent): string; export function textForEvent(ev: MatrixEvent, allowJSX: true, showHiddenEvents?: boolean): string | JSX.Element; export function textForEvent(ev: MatrixEvent, allowJSX = false, showHiddenEvents?: boolean): string | JSX.Element { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; - return handler?.(ev, allowJSX, showHiddenEvents)?.() ?? ''; + return handler?.(ev, allowJSX, showHiddenEvents)?.() || ''; } From deab0407cb0d8f60ac6c5897d7b50db091207173 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 13 Jul 2021 23:27:49 -0400 Subject: [PATCH 10/11] Pull another settings lookup out of SearchResultTile loop Signed-off-by: Robin Townsend --- src/components/views/rooms/SearchResultTile.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/SearchResultTile.tsx b/src/components/views/rooms/SearchResultTile.tsx index 47e9849214..c033855eb5 100644 --- a/src/components/views/rooms/SearchResultTile.tsx +++ b/src/components/views/rooms/SearchResultTile.tsx @@ -50,6 +50,7 @@ export default class SearchResultTile extends React.Component { const layout = SettingsStore.getValue("layout"); const isTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); const alwaysShowTimestamps = SettingsStore.getValue("alwaysShowTimestamps"); + const enableFlair = SettingsStore.getValue(UIFeature.Flair); const timeline = result.context.getTimeline(); for (let j = 0; j < timeline.length; j++) { @@ -72,7 +73,7 @@ export default class SearchResultTile extends React.Component { onHeightChanged={this.props.onHeightChanged} isTwelveHour={isTwelveHour} alwaysShowTimestamps={alwaysShowTimestamps} - enableFlair={SettingsStore.getValue(UIFeature.Flair)} + enableFlair={enableFlair} />, ); } From 092fdf5e5e62abd8078a27bf7f695a1ed39f7629 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 16 Jul 2021 18:46:29 -0400 Subject: [PATCH 11/11] Be consistent about MessagePanel setting lookups Signed-off-by: Robin Townsend --- src/components/structures/MessagePanel.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index fce5040b70..8977549697 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -404,17 +404,21 @@ export default class MessagePanel extends React.Component { return !this.isMounted; }; + private get showHiddenEvents(): boolean { + return this.context?.showHiddenEventsInTimeline ?? this.showHiddenEventsInTimeline; + } + // TODO: Implement granular (per-room) hide options public shouldShowEvent(mxEv: MatrixEvent): boolean { if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender())) { return false; // ignored = no show (only happens if the ignore happens after an event was received) } - if (this.context?.showHiddenEventsInTimeline ?? this.showHiddenEventsInTimeline) { + if (this.showHiddenEvents) { return true; } - if (!haveTileForEvent(mxEv, this.context?.showHiddenEventsInTimeline)) { + if (!haveTileForEvent(mxEv, this.showHiddenEvents)) { return false; // no tile = no show } @@ -574,7 +578,7 @@ export default class MessagePanel extends React.Component { if (grouper) { if (grouper.shouldGroup(mxEv)) { - grouper.add(mxEv, this.context?.showHiddenEventsInTimeline); + grouper.add(mxEv, this.showHiddenEvents); continue; } else { // not part of group, so get the group tiles, close the @@ -655,7 +659,7 @@ export default class MessagePanel extends React.Component { // is this a continuation of the previous message? const continuation = !wantsDateSeparator && - shouldFormContinuation(prevEvent, mxEv, this.context?.showHiddenEventsInTimeline); + shouldFormContinuation(prevEvent, mxEv, this.showHiddenEvents); const eventId = mxEv.getId(); const highlight = (eventId === this.props.highlightedEventId);