Composer / Web Page Preview: Add animation, fix links with _, do not show title

This commit is contained in:
Alexander Zinchuk 2021-05-06 01:47:40 +03:00
parent 987dfacaf5
commit ca65c04884
6 changed files with 64 additions and 27 deletions

View File

@ -700,9 +700,12 @@ const Composer: FC<OwnProps & StateProps & DispatchProps> = ({
<div id="message-compose"> <div id="message-compose">
<div className="svg-appendix" ref={appendixRef} /> <div className="svg-appendix" ref={appendixRef} />
<ComposerEmbeddedMessage /> <ComposerEmbeddedMessage />
{allowedAttachmentOptions.canAttachEmbedLinks && ( <WebPagePreview
<WebPagePreview chatId={chatId} threadId={threadId} messageText={!attachments.length ? html : ''} /> chatId={chatId}
)} threadId={threadId}
messageText={!attachments.length ? html : ''}
disabled={!allowedAttachmentOptions.canAttachEmbedLinks}
/>
<div className="message-input-wrapper"> <div className="message-input-wrapper">
{IS_MOBILE_SCREEN ? ( {IS_MOBILE_SCREEN ? (
<Button <Button

View File

@ -1,9 +1,17 @@
.WebPagePreview { .WebPagePreview {
display: flex; height: 2.625rem;
align-items: center; transition: height 150ms ease-out, opacity 150ms ease-out;
margin-bottom: -.75rem;
padding-right: 0.625rem; &:not(.open) {
padding-top: 0.45rem; height: 0 !important;
}
& > div {
display: flex;
align-items: center;
padding-right: 0.625rem;
padding-top: 0.45rem;
}
--accent-color: var(--color-primary); --accent-color: var(--color-primary);
@ -11,7 +19,7 @@
margin-top: 0.75rem; margin-top: 0.75rem;
} }
& > .Button { & > div > .Button {
flex-shrink: 0; flex-shrink: 0;
background: none !important; background: none !important;
width: 2.125rem; width: 2.125rem;
@ -26,13 +34,18 @@
margin: 0.1875rem 0 0.1875rem 0.125rem; margin: 0.1875rem 0 0.1875rem 0.125rem;
&::before { &::before {
top: .125rem; bottom: .125rem;
bottom: .0625rem;
} }
.site-title { .site-title {
margin-top: .125rem; margin-top: .125rem;
margin-bottom: 0.1875rem; margin-bottom: 0.1875rem;
} }
.site-description {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
} }
} }

View File

@ -6,10 +6,14 @@ import { withGlobal } from '../../../lib/teact/teactn';
import { GlobalActions } from '../../../global/types'; import { GlobalActions } from '../../../global/types';
import { ApiMessage, ApiMessageEntityTypes, ApiWebPage } from '../../../api/types'; import { ApiMessage, ApiMessageEntityTypes, ApiWebPage } from '../../../api/types';
import { RE_LINK_TEMPLATE } from '../../../config';
import { selectNoWebPage } from '../../../modules/selectors'; import { selectNoWebPage } from '../../../modules/selectors';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import parseMessageInput from './helpers/parseMessageInput'; import parseMessageInput from './helpers/parseMessageInput';
import useOnChange from '../../../hooks/useOnChange'; import useOnChange from '../../../hooks/useOnChange';
import useShowTransition from '../../../hooks/useShowTransition';
import usePrevForAnimation from '../../../hooks/usePrevForAnimation';
import buildClassName from '../../../util/buildClassName';
import WebPage from '../message/WebPage'; import WebPage from '../message/WebPage';
import Button from '../../ui/Button'; import Button from '../../ui/Button';
@ -20,6 +24,7 @@ type OwnProps = {
chatId: number; chatId: number;
threadId: number; threadId: number;
messageText: string; messageText: string;
disabled?: boolean;
}; };
type StateProps = { type StateProps = {
@ -28,12 +33,13 @@ type StateProps = {
}; };
type DispatchProps = Pick<GlobalActions, 'loadWebPagePreview' | 'clearWebPagePreview' | 'toggleMessageWebPage'>; type DispatchProps = Pick<GlobalActions, 'loadWebPagePreview' | 'clearWebPagePreview' | 'toggleMessageWebPage'>;
const RE_LINK = /https?:\/\/(www.)?([a-zA-Z0-9.-]{2,256})([a-zA-Z/.-]{1,256})([?|#][=&#a-zA-Z0-9]{2,128})?/; const RE_LINK = new RegExp(RE_LINK_TEMPLATE, 'i');
const WebPagePreview: FC<OwnProps & StateProps & DispatchProps> = ({ const WebPagePreview: FC<OwnProps & StateProps & DispatchProps> = ({
chatId, chatId,
threadId, threadId,
messageText, messageText,
disabled,
webPagePreview, webPagePreview,
noWebPage, noWebPage,
loadWebPagePreview, loadWebPagePreview,
@ -70,16 +76,21 @@ const WebPagePreview: FC<OwnProps & StateProps & DispatchProps> = ({
toggleMessageWebPage({ chatId, threadId }); toggleMessageWebPage({ chatId, threadId });
}, [chatId]); }, [chatId]);
const isShown = Boolean(webPagePreview && messageText.length && !noWebPage && !disabled);
const { shouldRender, transitionClassNames } = useShowTransition(isShown);
const renderingWebPage = usePrevForAnimation(webPagePreview);
if (!shouldRender || !renderingWebPage) {
return undefined;
}
const handleClearWebpagePreview = () => { const handleClearWebpagePreview = () => {
toggleMessageWebPage({ chatId, threadId, noWebPage: true }); toggleMessageWebPage({ chatId, threadId, noWebPage: true });
}; };
if (!webPagePreview || !messageText.length || noWebPage) {
return undefined;
}
// TODO Refactor so `WebPage` can be used without message // TODO Refactor so `WebPage` can be used without message
const { photo, ...webPageWithoutPhoto } = webPagePreview; const { photo, ...webPageWithoutPhoto } = renderingWebPage;
const messageStub = { const messageStub = {
content: { content: {
webPage: webPageWithoutPhoto, webPage: webPageWithoutPhoto,
@ -87,11 +98,13 @@ const WebPagePreview: FC<OwnProps & StateProps & DispatchProps> = ({
} as ApiMessage; } as ApiMessage;
return ( return (
<div className="WebPagePreview"> <div className={buildClassName('WebPagePreview', transitionClassNames)}>
<Button round color="translucent" ariaLabel="Clear Webpage Preview" onClick={handleClearWebpagePreview}> <div>
<i className="icon-close" /> <Button round color="translucent" ariaLabel="Clear Webpage Preview" onClick={handleClearWebpagePreview}>
</Button> <i className="icon-close" />
<WebPage message={messageStub} inPreview /> </Button>
<WebPage message={messageStub} inPreview />
</div>
</div> </div>
); );
}; };

View File

@ -19,6 +19,12 @@
border-radius: 2px; border-radius: 2px;
} }
&-text {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.media-inner { .media-inner {
margin: 0 !important; margin: 0 !important;
margin-bottom: 0.375rem !important; margin-bottom: 0.375rem !important;
@ -77,6 +83,7 @@
.site-name { .site-name {
color: var(--accent-color); color: var(--accent-color);
font-weight: 500; font-weight: 500;
margin-bottom: 0.125rem;
} }
.site-title { .site-title {

View File

@ -1,12 +1,13 @@
import React, { FC, memo, useCallback } from '../../../lib/teact/teact'; import React, { FC, memo, useCallback } from '../../../lib/teact/teact';
import { ApiMessage } from '../../../api/types'; import { ApiMessage } from '../../../api/types';
import { ObserveFn } from '../../../hooks/useIntersectionObserver';
import { getMessageWebPage } from '../../../modules/helpers'; import { getMessageWebPage } from '../../../modules/helpers';
import { calculateMediaDimensions } from './helpers/mediaDimensions'; import { calculateMediaDimensions } from './helpers/mediaDimensions';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
import trimText from '../../../util/trimText'; import trimText from '../../../util/trimText';
import { ObserveFn } from '../../../hooks/useIntersectionObserver'; import buildClassName from '../../../util/buildClassName';
import SafeLink from '../../common/SafeLink'; import SafeLink from '../../common/SafeLink';
import Photo from './Photo'; import Photo from './Photo';
@ -63,12 +64,12 @@ const WebPage: FC<OwnProps> = ({
const truncatedDescription = trimText(description, MAX_TEXT_LENGTH); const truncatedDescription = trimText(description, MAX_TEXT_LENGTH);
const className = [ const className = buildClassName(
'WebPage', 'WebPage',
photo photo
? (isSquarePhoto && 'with-square-photo') ? (isSquarePhoto && 'with-square-photo')
: (!inPreview && 'without-photo'), : (!inPreview && 'without-photo'),
].filter(Boolean).join(' '); );
return ( return (
<div <div
@ -87,7 +88,7 @@ const WebPage: FC<OwnProps> = ({
)} )}
<div className="WebPage-text"> <div className="WebPage-text">
<SafeLink className="site-name" url={url} text={siteName || displayUrl} /> <SafeLink className="site-name" url={url} text={siteName || displayUrl} />
{title && ( {!inPreview && title && (
<p className="site-title">{renderText(title)}</p> <p className="site-title">{renderText(title)}</p>
)} )}
{truncatedDescription && ( {truncatedDescription && (

View File

@ -26,5 +26,5 @@ export default function usePrevForAnimation(current: any, duration?: number) {
} }
}, [current]); }, [current]);
return !timeoutRef.current || !duration || isCurrentPresent ? current : prev; return isCurrentPresent || (duration && !timeoutRef.current) ? current : prev;
} }