Merge pull request #1466 from Bilb/fix-link-previews

fix previews sent on next message if they are resolved too late
pull/1469/head
Audric Ackermann 4 years ago committed by GitHub
commit 859cbfbe36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -165,6 +165,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
this.onChooseAttachment = this.onChooseAttachment.bind(this); this.onChooseAttachment = this.onChooseAttachment.bind(this);
this.onClickAttachment = this.onClickAttachment.bind(this); this.onClickAttachment = this.onClickAttachment.bind(this);
this.renderCaptionEditor = this.renderCaptionEditor.bind(this); this.renderCaptionEditor = this.renderCaptionEditor.bind(this);
this.abortLinkPreviewFetch = this.abortLinkPreviewFetch.bind(this);
// On Sending // On Sending
this.onSendMessage = this.onSendMessage.bind(this); this.onSendMessage = this.onSendMessage.bind(this);
@ -183,7 +184,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
} }
public componentWillUnmount() { public componentWillUnmount() {
this.linkPreviewAbortController?.abort(); this.abortLinkPreviewFetch();
this.linkPreviewAbortController = undefined; this.linkPreviewAbortController = undefined;
} }
public componentDidUpdate(prevProps: Props, _prevState: State) { public componentDidUpdate(prevProps: Props, _prevState: State) {
@ -566,7 +567,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
}, },
}); });
const abortController = new AbortController(); const abortController = new AbortController();
this.linkPreviewAbortController?.abort(); this.abortLinkPreviewFetch();
this.linkPreviewAbortController = abortController; this.linkPreviewAbortController = abortController;
setTimeout(() => { setTimeout(() => {
abortController.abort(); abortController.abort();
@ -590,6 +591,12 @@ export class SessionCompositionBox extends React.Component<Props, State> {
} }
} }
} }
// we finished loading the preview, and checking the abortConrtoller, we are still not aborted.
// => update the staged preview
if (
this.linkPreviewAbortController &&
!this.linkPreviewAbortController.signal.aborted
) {
this.setState({ this.setState({
stagedLinkPreview: { stagedLinkPreview: {
isLoaded: true, isLoaded: true,
@ -597,24 +604,51 @@ export class SessionCompositionBox extends React.Component<Props, State> {
description: ret?.description || '', description: ret?.description || '',
url: ret?.url || null, url: ret?.url || null,
domain: domain:
(ret?.url && window.Signal.LinkPreviews.getDomain(ret.url)) || '', (ret?.url && window.Signal.LinkPreviews.getDomain(ret.url)) ||
'',
image, image,
}, },
}); });
} else if (this.linkPreviewAbortController) {
this.setState({
stagedLinkPreview: {
isLoaded: false,
title: null,
description: null,
url: null,
domain: null,
image: undefined,
},
});
this.linkPreviewAbortController = undefined;
}
}) })
.catch(err => { .catch(err => {
window.log.warn('fetch link preview: ', err); window.log.warn('fetch link preview: ', err);
abortController.abort(); const aborted = this.linkPreviewAbortController?.signal.aborted;
this.linkPreviewAbortController = undefined;
// if we were aborted, it either means the UI was unmount, or more probably,
// than the message was sent without the link preview.
// So be sure to reset the staged link preview so it is not sent with the next message.
// if we were not aborted, it's probably just an error on the fetch. Nothing to do excpet mark the fetch as done (with errors)
if (aborted) {
this.setState({
stagedLinkPreview: undefined,
});
} else {
this.setState({ this.setState({
stagedLinkPreview: { stagedLinkPreview: {
isLoaded: true, isLoaded: true,
title: null, title: null,
domain: null,
description: null, description: null,
url: firstLink, url: firstLink,
domain: null,
image: undefined, image: undefined,
}, },
}); });
}
}); });
} }
@ -751,6 +785,8 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// tslint:disable-next-line: cyclomatic-complexity // tslint:disable-next-line: cyclomatic-complexity
private async onSendMessage() { private async onSendMessage() {
this.abortLinkPreviewFetch();
// this is dirty but we have to replace all @(xxx) by @xxx manually here // this is dirty but we have to replace all @(xxx) by @xxx manually here
const cleanMentions = (text: string): string => { const cleanMentions = (text: string): string => {
const matches = text.match(this.mentionsRegex); const matches = text.match(this.mentionsRegex);
@ -835,8 +871,11 @@ export class SessionCompositionBox extends React.Component<Props, State> {
'attachments' 'attachments'
); );
// we consider that a link previews without a title at least is not a preview
const linkPreviews = const linkPreviews =
(stagedLinkPreview && [ (stagedLinkPreview &&
stagedLinkPreview.isLoaded &&
stagedLinkPreview.title?.length && [
_.pick(stagedLinkPreview, 'url', 'image', 'title'), _.pick(stagedLinkPreview, 'url', 'image', 'title'),
]) || ]) ||
[]; [];
@ -854,20 +893,15 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// Message sending sucess // Message sending sucess
this.props.onMessageSuccess(); this.props.onMessageSuccess();
this.props.clearAttachments();
// Empty composition box // Empty composition box and stagedAttachments
this.setState({ this.setState({
message: '', message: '',
showEmojiPanel: false, showEmojiPanel: false,
});
// Empty stagedAttachments
this.props.clearAttachments();
if (stagedLinkPreview && stagedLinkPreview.url) {
this.setState({
stagedLinkPreview: undefined, stagedLinkPreview: undefined,
ignoredLink: undefined, ignoredLink: undefined,
}); });
}
} catch (e) { } catch (e) {
// Message sending failed // Message sending failed
window.log.error(e); window.log.error(e);
@ -983,4 +1017,8 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// Focus the textarea when user clicks anywhere in the composition box // Focus the textarea when user clicks anywhere in the composition box
this.textarea.current?.focus(); this.textarea.current?.focus();
} }
private abortLinkPreviewFetch() {
this.linkPreviewAbortController?.abort();
}
} }

@ -131,3 +131,11 @@ export async function timeout<T>(
return Promise.race([timeoutPromise, promise]); return Promise.race([timeoutPromise, promise]);
} }
export async function delay(timeoutMs: number = 2000): Promise<Boolean> {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, timeoutMs);
});
}

@ -9,6 +9,7 @@ import {
IMAGE_WEBP, IMAGE_WEBP,
MIMEType, MIMEType,
} from '../types/MIME'; } from '../types/MIME';
import { PromiseUtils } from '../session/utils';
const MAX_REQUEST_COUNT_WITH_REDIRECTS = 20; const MAX_REQUEST_COUNT_WITH_REDIRECTS = 20;
// tslint:disable: prefer-for-of // tslint:disable: prefer-for-of

Loading…
Cancel
Save