Composer / Web Page Preview: Add animation, fix links with _, do not show title
This commit is contained in:
parent
987dfacaf5
commit
ca65c04884
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 && (
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user