Moves link previews into the composer

This commit is contained in:
Josh Perez 2023-01-30 15:16:09 -05:00 committed by GitHub
parent 270804d62d
commit de1564fd37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 277 additions and 214 deletions

View file

@ -6341,198 +6341,6 @@ button.module-image__border-overlay:focus {
@include emoji-size(66px); @include emoji-size(66px);
} }
// Module: CompositionInput
.module-composition-input {
&__quill {
height: 100%;
.ql-editor {
caret-color: transparent;
padding: 0;
text-align: start;
white-space: break-spaces;
line-height: inherit;
&--loaded {
caret-color: auto;
}
&.ql-blank::before {
left: 0;
right: 0;
font-style: normal;
}
.emoji-blot {
width: 20px;
height: 20px;
vertical-align: text-bottom;
}
}
}
&__at-mention {
background-color: $color-gray-20;
border-radius: 4px;
display: inline;
padding-left: 4px;
padding-right: 4px;
height: 22px;
line-height: 22px;
@include dark-theme {
background-color: $color-gray-60;
}
}
&__input {
$border-size: 1px;
border-radius: 18px;
overflow: hidden;
word-break: break-word;
// Override Quill styles
.ql-container {
@include font-body-1;
}
.ql-blank::before {
@include light-theme() {
color: $color-gray-45;
}
@include dark-theme() {
color: $color-gray-25;
}
}
@include light-theme() {
// Same as background color
background-color: $color-gray-05;
color: $color-gray-90;
}
@include dark-theme() {
// Same as background color
background-color: $color-gray-75;
color: $color-gray-05;
}
&__scroller {
$padding-top: 5px;
padding: $padding-top 10px $padding-top 10px;
min-height: calc(32px - 2 * $border-size);
max-height: calc(72px - 2 * $border-size);
overflow: auto;
&::-webkit-scrollbar-thumb {
@include light-theme {
border: 2px solid $color-gray-05;
}
@include dark-theme {
border: 2px solid $color-gray-75;
}
}
&--large {
max-height: calc(212px - 2 * $border-size);
min-height: calc(212px - 2 * $border-size);
.DraftEditor-root {
height: calc(212px - 2 * $padding-top - 2 * $border-size);
}
}
}
border: $border-size solid transparent;
&:focus-within {
outline: 0;
@include keyboard-mode {
border: $border-size solid $color-ultramarine;
}
}
}
&__suggestions {
padding: 0;
margin-bottom: 6px;
border-radius: 8px;
z-index: $z-index-above-popup;
overflow: hidden;
&--scroller {
max-height: 300px;
overflow-y: auto;
}
@include popper-shadow();
@include light-theme() {
background: $color-white;
}
@include dark-theme() {
background: $color-gray-75;
}
&__row {
height: 34px;
padding: 0 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
background: none;
border: none;
width: 100%;
&--mention {
height: 40px;
}
&:focus {
outline: 0;
}
@include font-body-2;
@include light-theme() {
color: $color-gray-60;
}
@include dark-theme() {
color: $color-gray-25;
}
&__short-name {
margin-left: 4px;
}
&--selected,
&:hover {
@include light-theme() {
background: $color-gray-05;
color: $color-gray-90;
}
@include dark-theme() {
background: $color-gray-60;
color: $color-gray-05;
}
}
}
&__title {
padding-left: 8px;
}
stroke: $color-white;
}
}
// Module: Last Seen Indicator // Module: Last Seen Indicator
.module-last-seen-indicator { .module-last-seen-indicator {

View file

@ -0,0 +1,242 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
.module-composition-input {
&__quill {
height: 100%;
padding-left: 6px;
.ql-editor {
caret-color: transparent;
padding: 0;
text-align: start;
white-space: break-spaces;
line-height: inherit;
&--loaded {
caret-color: auto;
}
&.ql-blank::before {
left: 0;
right: 0;
font-style: normal;
}
.emoji-blot {
width: 20px;
height: 20px;
vertical-align: text-bottom;
}
}
}
&__at-mention {
background-color: $color-gray-20;
border-radius: 4px;
display: inline;
padding-left: 4px;
padding-right: 4px;
height: 22px;
line-height: 22px;
@include dark-theme {
background-color: $color-gray-60;
}
}
&__input {
$border-size: 1px;
border-radius: 18px;
overflow: hidden;
word-break: break-word;
// Override Quill styles
.ql-container {
@include font-body-1;
}
.ql-blank::before {
@include light-theme() {
color: $color-gray-45;
}
@include dark-theme() {
color: $color-gray-25;
}
}
@include light-theme() {
// Same as background color
background-color: $color-gray-05;
color: $color-gray-90;
}
@include dark-theme() {
// Same as background color
background-color: $color-gray-75;
color: $color-gray-05;
}
&__scroller {
$padding-top: 6px;
padding: $padding-top;
min-height: calc(32px - 2 * $border-size);
max-height: calc(72px - 2 * $border-size);
overflow: auto;
&::-webkit-scrollbar-thumb {
@include light-theme {
border: 2px solid $color-gray-05;
}
@include dark-theme {
border: 2px solid $color-gray-75;
}
}
&--large {
max-height: calc(212px - 2 * $border-size);
min-height: calc(212px - 2 * $border-size);
.DraftEditor-root {
height: calc(212px - 2 * $padding-top - 2 * $border-size);
}
}
&--link-preview {
min-height: 110px;
max-height: 200px;
}
}
border: $border-size solid transparent;
&:focus-within {
outline: 0;
@include keyboard-mode {
border: $border-size solid $color-ultramarine;
}
}
}
&__suggestions {
padding: 0;
margin-bottom: 6px;
border-radius: 8px;
z-index: $z-index-above-popup;
overflow: hidden;
&--scroller {
max-height: 300px;
overflow-y: auto;
}
@include popper-shadow();
@include light-theme() {
background: $color-white;
}
@include dark-theme() {
background: $color-gray-75;
}
&__row {
height: 34px;
padding: 0 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
background: none;
border: none;
width: 100%;
&--mention {
height: 40px;
}
&:focus {
outline: 0;
}
@include font-body-2;
@include light-theme() {
color: $color-gray-60;
}
@include dark-theme() {
color: $color-gray-25;
}
&__short-name {
margin-left: 4px;
}
&--selected,
&:hover {
@include light-theme() {
background: $color-gray-05;
color: $color-gray-90;
}
@include dark-theme() {
background: $color-gray-60;
color: $color-gray-05;
}
}
}
&__title {
padding-left: 8px;
}
stroke: $color-white;
}
}
div.CompositionInput__link-preview {
background: $color-gray-02;
border-radius: 8px;
flex-direction: row-reverse;
margin-bottom: 4px;
overflow: hidden;
&__icon-container {
align-items: center;
display: flex;
margin-left: 8px;
margin-right: 0;
}
&__content {
margin-right: 0;
padding-bottom: 8px;
padding-left: 12px;
padding-top: 8px;
}
}
button.CompositionInput__link-preview__close-button {
-webkit-mask: none;
@include rounded-corners;
align-items: center;
backdrop-filter: blur(32px);
background: $color-white-alpha-80;
display: flex;
height: 20px;
justify-content: center;
right: 6px;
top: 6px;
width: 20px;
&::before {
@include color-svg('../images/icons/v2/x-24.svg', $color-gray-75);
content: '';
height: 16px;
width: 16px;
}
}

View file

@ -51,6 +51,7 @@
@import './components/Checkbox.scss'; @import './components/Checkbox.scss';
@import './components/CircleCheckbox.scss'; @import './components/CircleCheckbox.scss';
@import './components/CompositionArea.scss'; @import './components/CompositionArea.scss';
@import './components/CompositionInput.scss';
@import './components/CompositionTextArea.scss'; @import './components/CompositionTextArea.scss';
@import './components/ContactModal.scss'; @import './components/ContactModal.scss';
@import './components/ContactName.scss'; @import './components/ContactName.scss';

View file

@ -52,7 +52,6 @@ import { MandatoryProfileSharingActions } from './conversation/MandatoryProfileS
import { MediaQualitySelector } from './MediaQualitySelector'; import { MediaQualitySelector } from './MediaQualitySelector';
import type { Props as QuoteProps } from './conversation/Quote'; import type { Props as QuoteProps } from './conversation/Quote';
import { Quote } from './conversation/Quote'; import { Quote } from './conversation/Quote';
import { StagedLinkPreview } from './conversation/StagedLinkPreview';
import { countStickers } from './stickers/lib'; import { countStickers } from './stickers/lib';
import { import {
useAttachFileShortcut, useAttachFileShortcut,
@ -691,15 +690,6 @@ export function CompositionArea({
/> />
</div> </div>
)} )}
{linkPreviewLoading && linkPreviewResult && (
<div className="preview-wrapper">
<StagedLinkPreview
{...linkPreviewResult}
i18n={i18n}
onClose={() => onCloseLinkPreview(conversationId)}
/>
</div>
)}
{draftAttachments.length ? ( {draftAttachments.length ? (
<div className="CompositionArea__attachment-list"> <div className="CompositionArea__attachment-list">
<AttachmentList <AttachmentList
@ -732,8 +722,8 @@ export function CompositionArea({
)} )}
> >
<CompositionInput <CompositionInput
conversationId={conversationId}
clearQuotedMessage={clearQuotedMessage} clearQuotedMessage={clearQuotedMessage}
conversationId={conversationId}
disabled={isDisabled} disabled={isDisabled}
draftBodyRanges={draftBodyRanges} draftBodyRanges={draftBodyRanges}
draftText={draftText} draftText={draftText}
@ -742,6 +732,9 @@ export function CompositionArea({
i18n={i18n} i18n={i18n}
inputApi={inputApiRef} inputApi={inputApiRef}
large={large} large={large}
linkPreviewLoading={linkPreviewLoading}
linkPreviewResult={linkPreviewResult}
onCloseLinkPreview={onCloseLinkPreview}
onDirtyChange={setDirty} onDirtyChange={setDirty}
onEditorStateChange={onEditorStateChange} onEditorStateChange={onEditorStateChange}
onPickEmoji={onPickEmoji} onPickEmoji={onPickEmoji}

View file

@ -44,6 +44,8 @@ import { DirectionalBlot } from '../quill/block/blot';
import { getClassNamesFor } from '../util/getClassNamesFor'; import { getClassNamesFor } from '../util/getClassNamesFor';
import * as log from '../logging/log'; import * as log from '../logging/log';
import { useRefMerger } from '../hooks/useRefMerger'; import { useRefMerger } from '../hooks/useRefMerger';
import type { LinkPreviewType } from '../types/message/LinkPreviews';
import { StagedLinkPreview } from './conversation/StagedLinkPreview';
Quill.register('formats/emoji', EmojiBlot); Quill.register('formats/emoji', EmojiBlot);
Quill.register('formats/mention', MentionBlot); Quill.register('formats/mention', MentionBlot);
@ -102,6 +104,9 @@ export type Props = Readonly<{
onScroll?: (ev: React.UIEvent<HTMLElement>) => void; onScroll?: (ev: React.UIEvent<HTMLElement>) => void;
getQuotedMessage?(): unknown; getQuotedMessage?(): unknown;
clearQuotedMessage?(): unknown; clearQuotedMessage?(): unknown;
linkPreviewLoading?: boolean;
linkPreviewResult?: LinkPreviewType;
onCloseLinkPreview?(conversationId: string): unknown;
}>; }>;
const MAX_LENGTH = 64 * 1024; const MAX_LENGTH = 64 * 1024;
@ -110,22 +115,25 @@ const BASE_CLASS_NAME = 'module-composition-input';
export function CompositionInput(props: Props): React.ReactElement { export function CompositionInput(props: Props): React.ReactElement {
const { const {
children, children,
clearQuotedMessage,
conversationId, conversationId,
i18n,
disabled, disabled,
large,
inputApi,
moduleClassName,
onPickEmoji,
onSubmit,
onScroll,
placeholder,
skinTone,
draftText,
draftBodyRanges, draftBodyRanges,
draftText,
getPreferredBadge, getPreferredBadge,
getQuotedMessage, getQuotedMessage,
clearQuotedMessage, i18n,
inputApi,
large,
linkPreviewLoading,
linkPreviewResult,
moduleClassName,
onCloseLinkPreview,
onPickEmoji,
onScroll,
onSubmit,
placeholder,
skinTone,
sortedGroupMembers, sortedGroupMembers,
theme, theme,
} = props; } = props;
@ -698,10 +706,21 @@ export function CompositionInput(props: Props): React.ReactElement {
onScroll={onScroll} onScroll={onScroll}
className={classNames( className={classNames(
getClassName('__input__scroller'), getClassName('__input__scroller'),
linkPreviewResult
? getClassName('__input__scroller--link-preview')
: null,
large ? getClassName('__input__scroller--large') : null, large ? getClassName('__input__scroller--large') : null,
children ? getClassName('__input--with-children') : null children ? getClassName('__input--with-children') : null
)} )}
> >
{conversationId && linkPreviewLoading && linkPreviewResult && (
<StagedLinkPreview
{...linkPreviewResult}
moduleClassName="CompositionInput__link-preview"
i18n={i18n}
onClose={() => onCloseLinkPreview?.(conversationId)}
/>
)}
{reactQuill} {reactQuill}
{emojiCompletionElement} {emojiCompletionElement}
{mentionCompletionElement} {mentionCompletionElement}