Support url-only link previews in stories

Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
automated-signal 2022-10-07 12:00:59 -07:00 committed by GitHub
parent 28b9d84a9f
commit 8e09f761fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 37 deletions

View File

@ -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;

View File

@ -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 ? (
<>
<StagedLinkPreview
domain={linkPreview.domain}
{...linkPreview}
i18n={i18n}
image={linkPreview.image}
moduleClassName="StoryCreator__link-preview"
title={linkPreview.title}
url={linkPreview.url}
/>
<Button
className="StoryCreator__link-preview-button"

View File

@ -39,6 +39,38 @@ export const StagedLinkPreview: React.FC<Props> = ({
moduleClassName
);
let maybeContent: JSX.Element | undefined;
if (isLoaded) {
// No title, no description - display only domain
if (!title && !description) {
maybeContent = (
<div
className={classNames(
getClassName('__content'),
getClassName('__content--only-url')
)}
>
<div className={getClassName('__title')}>{domain}</div>
</div>
);
} else {
maybeContent = (
<div className={getClassName('__content')}>
<div className={getClassName('__title')}>{title}</div>
{description && (
<div className={getClassName('__description')}>
{unescape(description)}
</div>
)}
<div className={getClassName('__footer')}>
<div className={getClassName('__location')}>{domain}</div>
<LinkPreviewDate date={date} className={getClassName('__date')} />
</div>
</div>
);
}
}
return (
<div
className={classNames(
@ -68,20 +100,7 @@ export const StagedLinkPreview: React.FC<Props> = ({
</div>
) : null}
{isLoaded && !image && <div className={getClassName('__no-image')} />}
{isLoaded ? (
<div className={getClassName('__content')}>
<div className={getClassName('__title')}>{title}</div>
{description && (
<div className={getClassName('__description')}>
{unescape(description)}
</div>
)}
<div className={getClassName('__footer')}>
<div className={getClassName('__location')}>{domain}</div>
<LinkPreviewDate date={date} className={getClassName('__date')} />
</div>
</div>
) : null}
{maybeContent}
{onClose && (
<button
aria-label={i18n('close')}

View File

@ -8,6 +8,8 @@ import type {
LinkPreviewImage,
LinkPreviewResult,
LinkPreviewSourceType,
MaybeGrabLinkPreviewOptionsType,
AddLinkPreviewOptionsType,
} from '../types/LinkPreview';
import type { StickerPackType as StickerPackDBType } from '../sql/Interface';
import type { MIMEType } from '../types/MIME';
@ -45,10 +47,11 @@ export const maybeGrabLinkPreview = debounce(_maybeGrabLinkPreview, 200);
function _maybeGrabLinkPreview(
message: string,
source: LinkPreviewSourceType,
caretLocation?: number
{ caretLocation, mode = 'conversation' }: MaybeGrabLinkPreviewOptionsType = {}
): void {
// Don't generate link previews if user has turned them off
if (!window.Events.getLinkPreviewSetting()) {
// Don't generate link previews if user has turned them off. When posting a
// story we should return minimal (url-only) link previews.
if (!window.Events.getLinkPreviewSetting() && mode === 'conversation') {
return;
}
@ -88,7 +91,9 @@ function _maybeGrabLinkPreview(
return;
}
addLinkPreview(link, source);
addLinkPreview(link, source, {
disableFetch: !window.Events.getLinkPreviewSetting(),
});
}
export function resetLinkPreview(): void {
@ -113,7 +118,8 @@ export function removeLinkPreview(): void {
export async function addLinkPreview(
url: string,
source: LinkPreviewSourceType
source: LinkPreviewSourceType,
{ disableFetch }: AddLinkPreviewOptionsType = {}
): Promise<void> {
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<LinkPreviewType> {
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<LinkPreviewType> {
return {
...item,
title: dropNull(item.title),
description: dropNull(item.description),
date: dropNull(item.date),
domain: LinkPreview.getDomain(item.url),

View File

@ -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<void, RootStateType, unknown, NoopActionType> {
return dispatch => {
maybeGrabLinkPreview(message, source);
maybeGrabLinkPreview(message, source, options);
dispatch({
type: 'NOOP',

View File

@ -128,7 +128,7 @@ export function SmartForwardMessageModal(): JSX.Element | null {
maybeGrabLinkPreview(
messageText,
LinkPreviewSourceType.ForwardMessageModal,
caretLocation
{ caretLocation }
);
}
}}

View File

@ -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 {

View File

@ -2575,11 +2575,9 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
// If we have attachments, don't add link preview
if (!this.hasFiles({ includePending: true })) {
maybeGrabLinkPreview(
messageText,
LinkPreviewSourceType.Composer,
caretLocation
);
maybeGrabLinkPreview(messageText, LinkPreviewSourceType.Composer, {
caretLocation,
});
}
}