Fix search results view for layouts other than Group/Modern (#7648)
This commit is contained in:
parent
31e6b942f2
commit
50f8c61fa8
6 changed files with 69 additions and 26 deletions
|
@ -209,6 +209,10 @@ limitations under the License.
|
||||||
.mx_RoomView_searchResultsPanel {
|
.mx_RoomView_searchResultsPanel {
|
||||||
.mx_RoomView_messageListWrapper {
|
.mx_RoomView_messageListWrapper {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> .mx_RoomView_MessageList > li > ol {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
|
|
@ -52,6 +52,7 @@ import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
|
||||||
import EditorStateTransfer from "../../utils/EditorStateTransfer";
|
import EditorStateTransfer from "../../utils/EditorStateTransfer";
|
||||||
import { Action } from '../../dispatcher/actions';
|
import { Action } from '../../dispatcher/actions';
|
||||||
import { getEventDisplayInfo } from "../../utils/EventUtils";
|
import { getEventDisplayInfo } from "../../utils/EventUtils";
|
||||||
|
import { IReadReceiptInfo } from "../views/rooms/ReadReceiptMarker";
|
||||||
|
|
||||||
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
||||||
const continuedTypes = [EventType.Sticker, EventType.RoomMessage];
|
const continuedTypes = [EventType.Sticker, EventType.RoomMessage];
|
||||||
|
@ -72,7 +73,7 @@ export function shouldFormContinuation(
|
||||||
): boolean {
|
): boolean {
|
||||||
if (timelineRenderingType === TimelineRenderingType.ThreadsList) return false;
|
if (timelineRenderingType === TimelineRenderingType.ThreadsList) return false;
|
||||||
// sanity check inputs
|
// sanity check inputs
|
||||||
if (!prevEvent || !prevEvent.sender || !mxEvent.sender) return false;
|
if (!prevEvent?.sender || !mxEvent.sender) return false;
|
||||||
// check if within the max continuation period
|
// check if within the max continuation period
|
||||||
if (mxEvent.getTs() - prevEvent.getTs() > CONTINUATION_MAX_INTERVAL) return false;
|
if (mxEvent.getTs() - prevEvent.getTs() > CONTINUATION_MAX_INTERVAL) return false;
|
||||||
|
|
||||||
|
@ -208,7 +209,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
||||||
// to manage its animations
|
// to manage its animations
|
||||||
private readonly readReceiptMap: Record<string, object> = {};
|
private readonly readReceiptMap: { [userId: string]: IReadReceiptInfo } = {};
|
||||||
|
|
||||||
// Track read receipts by event ID. For each _shown_ event ID, we store
|
// Track read receipts by event ID. For each _shown_ event ID, we store
|
||||||
// the list of read receipts to display:
|
// the list of read receipts to display:
|
||||||
|
|
|
@ -1783,6 +1783,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private get messagePanelClassNames(): string {
|
||||||
|
return classNames("mx_RoomView_messagePanel", {
|
||||||
|
mx_IRCLayout: this.state.layout === Layout.IRC,
|
||||||
|
mx_GroupLayout: this.state.layout === Layout.Group,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading;
|
const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading;
|
||||||
|
@ -2068,7 +2075,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
searchResultsPanel = (
|
searchResultsPanel = (
|
||||||
<ScrollPanel
|
<ScrollPanel
|
||||||
ref={this.searchResultsPanel}
|
ref={this.searchResultsPanel}
|
||||||
className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel mx_GroupLayout"
|
className={"mx_RoomView_searchResultsPanel " + this.messagePanelClassNames}
|
||||||
onFillRequest={this.onSearchResultsFillRequest}
|
onFillRequest={this.onSearchResultsFillRequest}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
>
|
>
|
||||||
|
@ -2085,13 +2092,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
highlightedEventId = this.state.initialEventId;
|
highlightedEventId = this.state.initialEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagePanelClassNames = classNames(
|
|
||||||
"mx_RoomView_messagePanel",
|
|
||||||
{
|
|
||||||
"mx_IRCLayout": this.state.layout == Layout.IRC,
|
|
||||||
"mx_GroupLayout": this.state.layout == Layout.Group,
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
|
// console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
|
||||||
const messagePanel = (
|
const messagePanel = (
|
||||||
<TimelinePanel
|
<TimelinePanel
|
||||||
|
@ -2109,7 +2109,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
onUserScroll={this.onUserScroll}
|
onUserScroll={this.onUserScroll}
|
||||||
onReadMarkerUpdated={this.updateTopUnreadMessagesBar}
|
onReadMarkerUpdated={this.updateTopUnreadMessagesBar}
|
||||||
showUrlPreview={this.state.showUrlPreview}
|
showUrlPreview={this.state.showUrlPreview}
|
||||||
className={messagePanelClassNames}
|
className={this.messagePanelClassNames}
|
||||||
membersLoaded={this.state.membersLoaded}
|
membersLoaded={this.state.membersLoaded}
|
||||||
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
|
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
|
|
@ -55,7 +55,7 @@ import MemberAvatar from '../avatars/MemberAvatar';
|
||||||
import SenderProfile from '../messages/SenderProfile';
|
import SenderProfile from '../messages/SenderProfile';
|
||||||
import MessageTimestamp from '../messages/MessageTimestamp';
|
import MessageTimestamp from '../messages/MessageTimestamp';
|
||||||
import TooltipButton from '../elements/TooltipButton';
|
import TooltipButton from '../elements/TooltipButton';
|
||||||
import ReadReceiptMarker from "./ReadReceiptMarker";
|
import ReadReceiptMarker, { IReadReceiptInfo } from "./ReadReceiptMarker";
|
||||||
import MessageActionBar from "../messages/MessageActionBar";
|
import MessageActionBar from "../messages/MessageActionBar";
|
||||||
import ReactionsRow from '../messages/ReactionsRow';
|
import ReactionsRow from '../messages/ReactionsRow';
|
||||||
import { getEventDisplayInfo } from '../../../utils/EventUtils';
|
import { getEventDisplayInfo } from '../../../utils/EventUtils';
|
||||||
|
@ -262,8 +262,7 @@ interface IProps {
|
||||||
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
||||||
// to manage its animations. Should be an empty object when the room
|
// to manage its animations. Should be an empty object when the room
|
||||||
// first loads
|
// first loads
|
||||||
// TODO: Proper typing for RR info
|
readReceiptMap?: { [userId: string]: IReadReceiptInfo };
|
||||||
readReceiptMap?: any;
|
|
||||||
|
|
||||||
// A function which is used to check if the parent panel is being
|
// A function which is used to check if the parent panel is being
|
||||||
// unmounted, to avoid unnecessary work. Should return true if we
|
// unmounted, to avoid unnecessary work. Should return true if we
|
||||||
|
@ -929,7 +928,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset;
|
left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset;
|
||||||
|
|
||||||
const userId = receipt.userId;
|
const userId = receipt.userId;
|
||||||
let readReceiptInfo;
|
let readReceiptInfo: IReadReceiptInfo;
|
||||||
|
|
||||||
if (this.props.readReceiptMap) {
|
if (this.props.readReceiptMap) {
|
||||||
readReceiptInfo = this.props.readReceiptMap[userId];
|
readReceiptInfo = this.props.readReceiptMap[userId];
|
||||||
|
|
|
@ -26,6 +26,12 @@ import { toPx } from "../../../utils/units";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import MemberAvatar from '../avatars/MemberAvatar';
|
import MemberAvatar from '../avatars/MemberAvatar';
|
||||||
|
|
||||||
|
export interface IReadReceiptInfo {
|
||||||
|
top?: number;
|
||||||
|
left?: number;
|
||||||
|
parent?: Element;
|
||||||
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
// the RoomMember to show the RR for
|
// the RoomMember to show the RR for
|
||||||
member?: RoomMember;
|
member?: RoomMember;
|
||||||
|
@ -43,10 +49,8 @@ interface IProps {
|
||||||
// don't animate this RR into position
|
// don't animate this RR into position
|
||||||
suppressAnimation?: boolean;
|
suppressAnimation?: boolean;
|
||||||
|
|
||||||
// an opaque object for storing information about this user's RR in
|
// an opaque object for storing information about this user's RR in this room
|
||||||
// this room
|
readReceiptInfo: IReadReceiptInfo;
|
||||||
// TODO: proper typing for RR info
|
|
||||||
readReceiptInfo: any;
|
|
||||||
|
|
||||||
// A function which is used to check if the parent panel is being
|
// A function which is used to check if the parent panel is being
|
||||||
// unmounted, to avoid unnecessary work. Should return true if we
|
// unmounted, to avoid unnecessary work. Should return true if we
|
||||||
|
|
|
@ -25,6 +25,8 @@ import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import DateSeparator from "../messages/DateSeparator";
|
import DateSeparator from "../messages/DateSeparator";
|
||||||
import EventTile, { haveTileForEvent } from "./EventTile";
|
import EventTile, { haveTileForEvent } from "./EventTile";
|
||||||
|
import { shouldFormContinuation } from "../../structures/MessagePanel";
|
||||||
|
import { wantsDateSeparator } from "../../../DateUtils";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
// a matrix-js-sdk SearchResult containing the details of this result
|
// a matrix-js-sdk SearchResult containing the details of this result
|
||||||
|
@ -43,10 +45,10 @@ export default class SearchResultTile extends React.Component<IProps> {
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const result = this.props.searchResult;
|
const result = this.props.searchResult;
|
||||||
const mxEv = result.context.getEvent();
|
const resultEvent = result.context.getEvent();
|
||||||
const eventId = mxEv.getId();
|
const eventId = resultEvent.getId();
|
||||||
|
|
||||||
const ts1 = mxEv.getTs();
|
const ts1 = resultEvent.getTs();
|
||||||
const ret = [<DateSeparator key={ts1 + "-search"} ts={ts1} />];
|
const ret = [<DateSeparator key={ts1 + "-search"} ts={ts1} />];
|
||||||
const layout = SettingsStore.getValue("layout");
|
const layout = SettingsStore.getValue("layout");
|
||||||
const isTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
const isTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
||||||
|
@ -55,17 +57,46 @@ export default class SearchResultTile extends React.Component<IProps> {
|
||||||
|
|
||||||
const timeline = result.context.getTimeline();
|
const timeline = result.context.getTimeline();
|
||||||
for (let j = 0; j < timeline.length; j++) {
|
for (let j = 0; j < timeline.length; j++) {
|
||||||
const ev = timeline[j];
|
const mxEv = timeline[j];
|
||||||
let highlights;
|
let highlights;
|
||||||
const contextual = (j != result.context.getOurEventIndex());
|
const contextual = (j != result.context.getOurEventIndex());
|
||||||
if (!contextual) {
|
if (!contextual) {
|
||||||
highlights = this.props.searchHighlights;
|
highlights = this.props.searchHighlights;
|
||||||
}
|
}
|
||||||
if (haveTileForEvent(ev, this.context?.showHiddenEventsInTimeline)) {
|
|
||||||
|
if (haveTileForEvent(mxEv, this.context?.showHiddenEventsInTimeline)) {
|
||||||
|
// do we need a date separator since the last event?
|
||||||
|
const prevEv = timeline[j - 1];
|
||||||
|
// is this a continuation of the previous message?
|
||||||
|
const continuation = prevEv &&
|
||||||
|
!wantsDateSeparator(prevEv.getDate(), mxEv.getDate()) &&
|
||||||
|
shouldFormContinuation(
|
||||||
|
prevEv,
|
||||||
|
mxEv,
|
||||||
|
this.context?.showHiddenEventsInTimeline,
|
||||||
|
TimelineRenderingType.Search,
|
||||||
|
);
|
||||||
|
|
||||||
|
let lastInSection = true;
|
||||||
|
const nextEv = timeline[j + 1];
|
||||||
|
if (nextEv) {
|
||||||
|
const willWantDateSeparator = wantsDateSeparator(mxEv.getDate(), nextEv.getDate());
|
||||||
|
lastInSection = (
|
||||||
|
willWantDateSeparator ||
|
||||||
|
mxEv.getSender() !== nextEv.getSender() ||
|
||||||
|
!shouldFormContinuation(
|
||||||
|
mxEv,
|
||||||
|
nextEv,
|
||||||
|
this.context?.showHiddenEventsInTimeline,
|
||||||
|
TimelineRenderingType.Search,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ret.push(
|
ret.push(
|
||||||
<EventTile
|
<EventTile
|
||||||
key={`${eventId}+${j}`}
|
key={`${eventId}+${j}`}
|
||||||
mxEvent={ev}
|
mxEvent={mxEv}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
contextual={contextual}
|
contextual={contextual}
|
||||||
highlights={highlights}
|
highlights={highlights}
|
||||||
|
@ -76,11 +107,15 @@ export default class SearchResultTile extends React.Component<IProps> {
|
||||||
alwaysShowTimestamps={alwaysShowTimestamps}
|
alwaysShowTimestamps={alwaysShowTimestamps}
|
||||||
enableFlair={enableFlair}
|
enableFlair={enableFlair}
|
||||||
timelineRenderingType={TimelineRenderingType.Search}
|
timelineRenderingType={TimelineRenderingType.Search}
|
||||||
|
lastInSection={lastInSection}
|
||||||
|
continuation={continuation}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <li data-scroll-tokens={eventId}>{ ret }</li>;
|
return <li data-scroll-tokens={eventId}>
|
||||||
|
<ol>{ ret }</ol>
|
||||||
|
</li>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue