mirror of https://github.com/vector-im/riot-web
Fix pinned messages not re-linkifying on edit (#9042)
* Fix pinned messages not re-linkifying on edit * Fix testspull/28788/head^2
parent
2587aa1369
commit
e576347b62
|
@ -26,6 +26,7 @@ import { split } from 'lodash';
|
||||||
import katex from 'katex';
|
import katex from 'katex';
|
||||||
import { AllHtmlEntities } from 'html-entities';
|
import { AllHtmlEntities } from 'html-entities';
|
||||||
import { IContent } from 'matrix-js-sdk/src/models/event';
|
import { IContent } from 'matrix-js-sdk/src/models/event';
|
||||||
|
import { Optional } from 'matrix-events-sdk';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
_linkifyElement,
|
_linkifyElement,
|
||||||
|
@ -456,9 +457,9 @@ function formatEmojis(message: string, isHtmlMessage: boolean): (JSX.Element | s
|
||||||
* opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
|
* opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
|
||||||
* opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString)
|
* opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString)
|
||||||
*/
|
*/
|
||||||
export function bodyToHtml(content: IContent, highlights: string[], opts: IOptsReturnString): string;
|
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnString): string;
|
||||||
export function bodyToHtml(content: IContent, highlights: string[], opts: IOptsReturnNode): ReactNode;
|
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnNode): ReactNode;
|
||||||
export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts = {}) {
|
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOpts = {}) {
|
||||||
const isFormattedBody = content.format === "org.matrix.custom.html" && content.formatted_body;
|
const isFormattedBody = content.format === "org.matrix.custom.html" && content.formatted_body;
|
||||||
let bodyHasEmoji = false;
|
let bodyHasEmoji = false;
|
||||||
let isHtmlMessage = false;
|
let isHtmlMessage = false;
|
||||||
|
|
|
@ -199,7 +199,11 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
case RightPanelPhases.PinnedMessages:
|
case RightPanelPhases.PinnedMessages:
|
||||||
if (SettingsStore.getValue("feature_pinning")) {
|
if (SettingsStore.getValue("feature_pinning")) {
|
||||||
card = <PinnedMessagesCard room={this.props.room} onClose={this.onClose} />;
|
card = <PinnedMessagesCard
|
||||||
|
room={this.props.room}
|
||||||
|
onClose={this.onClose}
|
||||||
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.Timeline:
|
case RightPanelPhases.Timeline:
|
||||||
|
|
|
@ -26,10 +26,10 @@ export interface IBodyProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
|
||||||
/* a list of words to highlight */
|
/* a list of words to highlight */
|
||||||
highlights: string[];
|
highlights?: string[];
|
||||||
|
|
||||||
/* link URL for the highlights */
|
/* link URL for the highlights */
|
||||||
highlightLink: string;
|
highlightLink?: string;
|
||||||
|
|
||||||
/* callback called when dynamic content in events are loaded */
|
/* callback called when dynamic content in events are loaded */
|
||||||
onHeightChanged: () => void;
|
onHeightChanged: () => void;
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
||||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||||
|
|
||||||
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||||
super(props);
|
super(props, context);
|
||||||
|
|
||||||
if (MediaEventHelper.isEligible(this.props.mxEvent)) {
|
if (MediaEventHelper.isEligible(this.props.mxEvent)) {
|
||||||
this.mediaHelper = new MediaEventHelper(this.props.mxEvent);
|
this.mediaHelper = new MediaEventHelper(this.props.mxEvent);
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { createRef, SyntheticEvent, MouseEvent } from 'react';
|
import React, { createRef, SyntheticEvent, MouseEvent, ReactNode } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import highlight from 'highlight.js';
|
import highlight from 'highlight.js';
|
||||||
import { MsgType } from "matrix-js-sdk/src/@types/event";
|
import { MsgType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
@ -565,7 +565,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
// only strip reply if this is the original replying event, edits thereafter do not have the fallback
|
// only strip reply if this is the original replying event, edits thereafter do not have the fallback
|
||||||
const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent);
|
const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent);
|
||||||
let body;
|
let body: ReactNode;
|
||||||
if (SettingsStore.isEnabled("feature_extensible_events")) {
|
if (SettingsStore.isEnabled("feature_extensible_events")) {
|
||||||
const extev = this.props.mxEvent.unstableExtensibleEvent as MessageEvent;
|
const extev = this.props.mxEvent.unstableExtensibleEvent as MessageEvent;
|
||||||
if (extev?.isEquivalentTo(M_MESSAGE)) {
|
if (extev?.isEquivalentTo(M_MESSAGE)) {
|
||||||
|
|
|
@ -33,9 +33,11 @@ import { useRoomState } from "../../../hooks/useRoomState";
|
||||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||||
import { ReadPinsEventId } from "./types";
|
import { ReadPinsEventId } from "./types";
|
||||||
import Heading from '../typography/Heading';
|
import Heading from '../typography/Heading';
|
||||||
|
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
onClose(): void;
|
onClose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +80,7 @@ export const useReadPinnedEvents = (room: Room): Set<string> => {
|
||||||
return readPinnedEvents;
|
return readPinnedEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PinnedMessagesCard = ({ room, onClose }: IProps) => {
|
const PinnedMessagesCard = ({ room, onClose, permalinkCreator }: IProps) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const roomContext = useContext(RoomContext);
|
const roomContext = useContext(RoomContext);
|
||||||
const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli));
|
const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli));
|
||||||
|
@ -152,6 +154,7 @@ const PinnedMessagesCard = ({ room, onClose }: IProps) => {
|
||||||
key={ev.getId()}
|
key={ev.getId()}
|
||||||
event={ev}
|
event={ev}
|
||||||
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
|
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
|
||||||
|
permalinkCreator={permalinkCreator}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -33,9 +33,11 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
|
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
event: MatrixEvent;
|
event: MatrixEvent;
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
onUnpinClicked?(): void;
|
onUnpinClicked?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +142,8 @@ export default class PinnedEventTile extends React.Component<IProps> {
|
||||||
className="mx_PinnedEventTile_body"
|
className="mx_PinnedEventTile_body"
|
||||||
maxImageHeight={150}
|
maxImageHeight={150}
|
||||||
onHeightChanged={() => {}} // we need to give this, apparently
|
onHeightChanged={() => {}} // we need to give this, apparently
|
||||||
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
|
replacingEventId={this.props.event.replacingEventId()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,8 @@ import { shouldDisplayAsBeaconTile } from "../utils/beacon/timeline";
|
||||||
export interface EventTileTypeProps {
|
export interface EventTileTypeProps {
|
||||||
ref?: React.RefObject<any>; // `any` because it's effectively impossible to convince TS of a reasonable type
|
ref?: React.RefObject<any>; // `any` because it's effectively impossible to convince TS of a reasonable type
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
highlights: string[];
|
highlights?: string[];
|
||||||
highlightLink: string;
|
highlightLink?: string;
|
||||||
showUrlPreview?: boolean;
|
showUrlPreview?: boolean;
|
||||||
onHeightChanged: () => void;
|
onHeightChanged: () => void;
|
||||||
forExport?: boolean;
|
forExport?: boolean;
|
||||||
|
|
|
@ -42,6 +42,7 @@ import PinnedMessagesCard from "../../../../src/components/views/right_panel/Pin
|
||||||
import PinnedEventTile from "../../../../src/components/views/rooms/PinnedEventTile";
|
import PinnedEventTile from "../../../../src/components/views/rooms/PinnedEventTile";
|
||||||
import MPollBody from "../../../../src/components/views/messages/MPollBody";
|
import MPollBody from "../../../../src/components/views/messages/MPollBody";
|
||||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||||
|
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||||
|
|
||||||
describe("<PinnedMessagesCard />", () => {
|
describe("<PinnedMessagesCard />", () => {
|
||||||
stubClient();
|
stubClient();
|
||||||
|
@ -85,7 +86,11 @@ describe("<PinnedMessagesCard />", () => {
|
||||||
const mountPins = async (room: Room): Promise<ReactWrapper<ComponentProps<typeof PinnedMessagesCard>>> => {
|
const mountPins = async (room: Room): Promise<ReactWrapper<ComponentProps<typeof PinnedMessagesCard>>> => {
|
||||||
let pins;
|
let pins;
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
pins = mount(<PinnedMessagesCard room={room} onClose={jest.fn()} />, {
|
pins = mount(<PinnedMessagesCard
|
||||||
|
room={room}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||||
|
/>, {
|
||||||
wrappingComponent: MatrixClientContext.Provider,
|
wrappingComponent: MatrixClientContext.Provider,
|
||||||
wrappingComponentProps: { value: cli },
|
wrappingComponentProps: { value: cli },
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue