Linkify untruncated text
This commit is contained in:
parent
8ea030074e
commit
6297e12803
8 changed files with 48 additions and 6 deletions
|
@ -704,6 +704,7 @@ export function StoryViewer({
|
||||||
onExpandSpoiler={data => setIsSpoilerExpanded(data)}
|
onExpandSpoiler={data => setIsSpoilerExpanded(data)}
|
||||||
renderLocation={RenderLocation.StoryViewer}
|
renderLocation={RenderLocation.StoryViewer}
|
||||||
text={caption.text}
|
text={caption.text}
|
||||||
|
originalText={caption.text}
|
||||||
/>
|
/>
|
||||||
{caption.hasReadMore && !hasExpandedCaption && (
|
{caption.hasReadMore && !hasExpandedCaption && (
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -40,6 +40,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
showConversation:
|
showConversation:
|
||||||
overrideProps.showConversation || action('showConversation'),
|
overrideProps.showConversation || action('showConversation'),
|
||||||
text: overrideProps.text || '',
|
text: overrideProps.text || '',
|
||||||
|
originalText: overrideProps.originalText || overrideProps.text || '',
|
||||||
textAttachment: overrideProps.textAttachment || {
|
textAttachment: overrideProps.textAttachment || {
|
||||||
pending: false,
|
pending: false,
|
||||||
},
|
},
|
||||||
|
@ -525,3 +526,15 @@ export function ZalgoText(): JSX.Element {
|
||||||
|
|
||||||
return <MessageBody {...props} />;
|
return <MessageBody {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function LinkOverReadMoreBoundary(): JSX.Element {
|
||||||
|
const text = 'https://hello.me';
|
||||||
|
const originalText = 'https://hello.me123';
|
||||||
|
|
||||||
|
const props = createProps({
|
||||||
|
text,
|
||||||
|
originalText,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <MessageBody {...props} />;
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ export type Props = {
|
||||||
AttachmentType,
|
AttachmentType,
|
||||||
'pending' | 'digest' | 'key' | 'wasTooBig' | 'path'
|
'pending' | 'digest' | 'key' | 'wasTooBig' | 'path'
|
||||||
>;
|
>;
|
||||||
|
originalText: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,8 +85,10 @@ export function MessageBody({
|
||||||
showConversation,
|
showConversation,
|
||||||
text,
|
text,
|
||||||
textAttachment,
|
textAttachment,
|
||||||
|
originalText,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const shouldDisableLinks = disableLinks || !shouldLinkifyMessage(text);
|
const shouldDisableLinks =
|
||||||
|
disableLinks || !shouldLinkifyMessage(originalText);
|
||||||
const textWithSuffix =
|
const textWithSuffix =
|
||||||
textAttachment?.pending || onIncreaseTextLength || textAttachment?.wasTooBig
|
textAttachment?.pending || onIncreaseTextLength || textAttachment?.wasTooBig
|
||||||
? `${text}...`
|
? `${text}...`
|
||||||
|
@ -177,6 +180,7 @@ export function MessageBody({
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
isSpoilerExpanded={isSpoilerExpanded}
|
isSpoilerExpanded={isSpoilerExpanded}
|
||||||
messageText={textWithSuffix}
|
messageText={textWithSuffix}
|
||||||
|
originalMessageText={originalText}
|
||||||
onMentionTrigger={conversationId =>
|
onMentionTrigger={conversationId =>
|
||||||
showConversation?.({ conversationId })
|
showConversation?.({ conversationId })
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ export function MessageBodyReadMore({
|
||||||
showConversation={showConversation}
|
showConversation={showConversation}
|
||||||
text={slicedText}
|
text={slicedText}
|
||||||
textAttachment={textAttachment}
|
textAttachment={textAttachment}
|
||||||
|
originalText={text}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ type Props = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isSpoilerExpanded: Record<number, boolean>;
|
isSpoilerExpanded: Record<number, boolean>;
|
||||||
messageText: string;
|
messageText: string;
|
||||||
|
originalMessageText: string;
|
||||||
onExpandSpoiler?: (data: Record<number, boolean>) => void;
|
onExpandSpoiler?: (data: Record<number, boolean>) => void;
|
||||||
onMentionTrigger: (conversationId: string) => void;
|
onMentionTrigger: (conversationId: string) => void;
|
||||||
renderLocation: RenderLocation;
|
renderLocation: RenderLocation;
|
||||||
|
@ -64,9 +65,12 @@ export function MessageTextRenderer({
|
||||||
onMentionTrigger,
|
onMentionTrigger,
|
||||||
renderLocation,
|
renderLocation,
|
||||||
textLength,
|
textLength,
|
||||||
|
originalMessageText,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const finalNodes = React.useMemo(() => {
|
const finalNodes = React.useMemo(() => {
|
||||||
const links = disableLinks ? [] : extractLinks(messageText);
|
const links = disableLinks
|
||||||
|
? []
|
||||||
|
: extractLinks(messageText, originalMessageText);
|
||||||
|
|
||||||
// We need mentions to come last; they can't have children for proper rendering
|
// We need mentions to come last; they can't have children for proper rendering
|
||||||
const sortedRanges = sortBy(bodyRanges, range =>
|
const sortedRanges = sortBy(bodyRanges, range =>
|
||||||
|
@ -104,7 +108,7 @@ export function MessageTextRenderer({
|
||||||
|
|
||||||
// Group all contigusous spoilers to create one parent spoiler element in the DOM
|
// Group all contigusous spoilers to create one parent spoiler element in the DOM
|
||||||
return groupContiguousSpoilers(nodes);
|
return groupContiguousSpoilers(nodes);
|
||||||
}, [bodyRanges, disableLinks, messageText, textLength]);
|
}, [bodyRanges, disableLinks, messageText, originalMessageText, textLength]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -424,19 +428,34 @@ function renderText({
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractLinks(
|
export function extractLinks(
|
||||||
messageText: string
|
messageText: string,
|
||||||
|
// Full, untruncated message text
|
||||||
|
originalMessageText: string
|
||||||
): ReadonlyArray<BodyRange<{ url: string }>> {
|
): ReadonlyArray<BodyRange<{ url: string }>> {
|
||||||
// to support emojis immediately before links
|
// to support emojis immediately before links
|
||||||
// we replace emojis with a space for each byte
|
// we replace emojis with a space for each byte
|
||||||
const matches = linkify.match(
|
const matches = linkify.match(
|
||||||
messageText.replace(EMOJI_REGEXP, s => ' '.repeat(s.length))
|
originalMessageText.replace(EMOJI_REGEXP, s => ' '.repeat(s.length))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (matches == null) {
|
if (matches == null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches.map(match => {
|
// Only return matches present in the `messageText`
|
||||||
|
const currentMatches = matches.filter(({ index, lastIndex, url }) => {
|
||||||
|
if (index >= messageText.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastIndex > messageText.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageText.slice(index, lastIndex) === url;
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentMatches.map(match => {
|
||||||
return {
|
return {
|
||||||
start: match.index,
|
start: match.index,
|
||||||
length: match.lastIndex - match.index,
|
length: match.lastIndex - match.index,
|
||||||
|
|
|
@ -378,6 +378,7 @@ export function Quote(props: Props): JSX.Element | null {
|
||||||
isSpoilerExpanded={EMPTY_OBJECT}
|
isSpoilerExpanded={EMPTY_OBJECT}
|
||||||
renderLocation={RenderLocation.Quote}
|
renderLocation={RenderLocation.Quote}
|
||||||
text={text}
|
text={text}
|
||||||
|
originalText={text}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -170,6 +170,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
|
||||||
prefix={draftPreview.prefix}
|
prefix={draftPreview.prefix}
|
||||||
renderLocation={RenderLocation.ConversationList}
|
renderLocation={RenderLocation.ConversationList}
|
||||||
text={draftPreview.text}
|
text={draftPreview.text}
|
||||||
|
originalText={draftPreview.text}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -191,6 +192,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
|
||||||
prefix={lastMessage.prefix}
|
prefix={lastMessage.prefix}
|
||||||
renderLocation={RenderLocation.ConversationList}
|
renderLocation={RenderLocation.ConversationList}
|
||||||
text={lastMessage.text}
|
text={lastMessage.text}
|
||||||
|
originalText={lastMessage.text}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
if (lastMessage.status) {
|
if (lastMessage.status) {
|
||||||
|
|
|
@ -168,6 +168,7 @@ export const MessageSearchResult: FunctionComponent<PropsType> = React.memo(
|
||||||
const messageText = (
|
const messageText = (
|
||||||
<MessageTextRenderer
|
<MessageTextRenderer
|
||||||
messageText={cleanedSnippet}
|
messageText={cleanedSnippet}
|
||||||
|
originalMessageText={cleanedSnippet}
|
||||||
bodyRanges={displayBodyRanges}
|
bodyRanges={displayBodyRanges}
|
||||||
direction={undefined}
|
direction={undefined}
|
||||||
disableLinks
|
disableLinks
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue