diff --git a/stylesheets/components/StagedLinkPreview.scss b/stylesheets/components/StagedLinkPreview.scss index 5524e414ec52..240cf8e309ab 100644 --- a/stylesheets/components/StagedLinkPreview.scss +++ b/stylesheets/components/StagedLinkPreview.scss @@ -52,6 +52,10 @@ display: flex; flex-direction: column; margin-right: 20px; + + &--only-url { + justify-content: center; + } } .module-staged-link-preview__title { @include font-body-1-bold; diff --git a/ts/components/TextStoryCreator.tsx b/ts/components/TextStoryCreator.tsx index 22bbd33efb65..67709dbe3a67 100644 --- a/ts/components/TextStoryCreator.tsx +++ b/ts/components/TextStoryCreator.tsx @@ -14,6 +14,7 @@ import type { TextAttachmentType } from '../types/Attachment'; import { Button, ButtonVariant } from './Button'; import { ContextMenu } from './ContextMenu'; import { LinkPreviewSourceType, findLinks } from '../types/LinkPreview'; +import type { MaybeGrabLinkPreviewOptionsType } from '../types/LinkPreview'; import { Input } from './Input'; import { Slider } from './Slider'; import { StagedLinkPreview } from './conversation/StagedLinkPreview'; @@ -31,7 +32,8 @@ import { handleOutsideClick } from '../util/handleOutsideClick'; export type PropsType = { debouncedMaybeGrabLinkPreview: ( message: string, - source: LinkPreviewSourceType + source: LinkPreviewSourceType, + options?: MaybeGrabLinkPreviewOptionsType ) => unknown; i18n: LocalizerType; linkPreview?: LinkPreviewType; @@ -178,7 +180,10 @@ export const TextStoryCreator = ({ } debouncedMaybeGrabLinkPreview( linkPreviewInputValue, - LinkPreviewSourceType.StoryCreator + LinkPreviewSourceType.StoryCreator, + { + mode: 'story', + } ); }, [ debouncedMaybeGrabLinkPreview, @@ -525,12 +530,9 @@ export const TextStoryCreator = ({ {linkPreview ? ( <> = ({ moduleClassName ); + let maybeContent: JSX.Element | undefined; + if (isLoaded) { + // No title, no description - display only domain + if (!title && !description) { + maybeContent = ( + + {domain} + + ); + } else { + maybeContent = ( + + {title} + {description && ( + + {unescape(description)} + + )} + + {domain} + + + + ); + } + } + return ( = ({ ) : null} {isLoaded && !image && } - {isLoaded ? ( - - {title} - {description && ( - - {unescape(description)} - - )} - - {domain} - - - - ) : null} + {maybeContent} {onClose && ( { if (currentlyMatchedLink === url) { log.warn('addLinkPreview should not be called with the same URL like this'); @@ -153,7 +159,17 @@ export async function addLinkPreview( ); try { - const result = await getPreview(url, thisRequestAbortController.signal); + let result: LinkPreviewResult | null; + if (disableFetch) { + result = { + title: null, + url, + description: null, + date: null, + }; + } else { + result = await getPreview(url, thisRequestAbortController.signal); + } if (!result) { log.info( @@ -179,7 +195,7 @@ export async function addLinkPreview( type: result.image.contentType, }); result.image.url = URL.createObjectURL(blob); - } else if (!result.title) { + } else if (!result.title && !disableFetch) { // A link preview isn't worth showing unless we have either a title or an image removeLinkPreview(); return; @@ -188,6 +204,7 @@ export async function addLinkPreview( window.reduxActions.linkPreviews.addLinkPreview( { ...result, + title: dropNull(result.title), description: dropNull(result.description), date: dropNull(result.date), domain: LinkPreview.getDomain(result.url), @@ -232,6 +249,7 @@ export function getLinkPreviewForSend(message: string): Array { return { ...item, image: omit(item.image, 'url'), + title: dropNull(item.title), description: dropNull(item.description), date: dropNull(item.date), domain: LinkPreview.getDomain(item.url), @@ -241,6 +259,7 @@ export function getLinkPreviewForSend(message: string): Array { return { ...item, + title: dropNull(item.title), description: dropNull(item.description), date: dropNull(item.date), domain: LinkPreview.getDomain(item.url), diff --git a/ts/state/ducks/linkPreviews.ts b/ts/state/ducks/linkPreviews.ts index a19b9e33065a..d06753cadfc1 100644 --- a/ts/state/ducks/linkPreviews.ts +++ b/ts/state/ducks/linkPreviews.ts @@ -6,7 +6,10 @@ import type { ThunkAction } from 'redux-thunk'; import type { NoopActionType } from './noop'; import type { StateType as RootStateType } from '../reducer'; import type { LinkPreviewType } from '../../types/message/LinkPreviews'; -import type { LinkPreviewSourceType } from '../../types/LinkPreview'; +import type { + LinkPreviewSourceType, + MaybeGrabLinkPreviewOptionsType, +} from '../../types/LinkPreview'; import { assignWithNoUnnecessaryAllocation } from '../../util/assignWithNoUnnecessaryAllocation'; import { maybeGrabLinkPreview } from '../../services/LinkPreview'; import { useBoundActions } from '../../hooks/useBoundActions'; @@ -43,10 +46,11 @@ type LinkPreviewsActionType = function debouncedMaybeGrabLinkPreview( message: string, - source: LinkPreviewSourceType + source: LinkPreviewSourceType, + options?: MaybeGrabLinkPreviewOptionsType ): ThunkAction { return dispatch => { - maybeGrabLinkPreview(message, source); + maybeGrabLinkPreview(message, source, options); dispatch({ type: 'NOOP', diff --git a/ts/state/smart/ForwardMessageModal.tsx b/ts/state/smart/ForwardMessageModal.tsx index ef3f0b7c4215..aea8a55c4557 100644 --- a/ts/state/smart/ForwardMessageModal.tsx +++ b/ts/state/smart/ForwardMessageModal.tsx @@ -128,7 +128,7 @@ export function SmartForwardMessageModal(): JSX.Element | null { maybeGrabLinkPreview( messageText, LinkPreviewSourceType.ForwardMessageModal, - caretLocation + { caretLocation } ); } }} diff --git a/ts/types/LinkPreview.ts b/ts/types/LinkPreview.ts index 7b2964a16633..77415f94cd03 100644 --- a/ts/types/LinkPreview.ts +++ b/ts/types/LinkPreview.ts @@ -15,7 +15,7 @@ export type LinkPreviewImage = AttachmentType & { }; export type LinkPreviewResult = { - title: string; + title: string | null; url: string; image?: LinkPreviewImage; description: string | null; @@ -32,6 +32,15 @@ export enum LinkPreviewSourceType { StoryCreator, } +export type MaybeGrabLinkPreviewOptionsType = Readonly<{ + caretLocation?: number; + mode?: 'conversation' | 'story'; +}>; + +export type AddLinkPreviewOptionsType = Readonly<{ + disableFetch?: boolean; +}>; + const linkify = LinkifyIt(); export function shouldPreviewHref(href: string): boolean { diff --git a/ts/views/conversation_view.tsx b/ts/views/conversation_view.tsx index 0fa4c74329b3..927fca350719 100644 --- a/ts/views/conversation_view.tsx +++ b/ts/views/conversation_view.tsx @@ -2575,11 +2575,9 @@ export class ConversationView extends window.Backbone.View { // If we have attachments, don't add link preview if (!this.hasFiles({ includePending: true })) { - maybeGrabLinkPreview( - messageText, - LinkPreviewSourceType.Composer, - caretLocation - ); + maybeGrabLinkPreview(messageText, LinkPreviewSourceType.Composer, { + caretLocation, + }); } }