diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index ae3fa0a97..6a98aaf39 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -27,6 +27,7 @@ export interface ApiOnProgress { export interface ApiAttachment { blobUrl: string; + compressedBlobUrl?: string; filename: string; mimeType: string; size: number; diff --git a/src/components/common/MediaSpoiler.module.scss b/src/components/common/MediaSpoiler.module.scss index acbf100c4..5ed69854c 100644 --- a/src/components/common/MediaSpoiler.module.scss +++ b/src/components/common/MediaSpoiler.module.scss @@ -14,7 +14,11 @@ mask-composite: exclude; mask-position: calc(50% + var(--click-shift-x)) calc(50% + var(--click-shift-y)), center center; mask-repeat: no-repeat; - animation: 500ms ease-in circle-cut forwards; + mask-size: 0%; + + :global(body.animation-level-2) & { + animation: 500ms ease-in circle-cut forwards; + } .dots { transform: scale(1.2); @@ -52,7 +56,6 @@ --x-direction: var(--background-size); --y-direction: 0; - animation: 20s linear infinite dots; &::before { content: ""; @@ -66,7 +69,8 @@ --x-direction: 0; --y-direction: var(--background-size); - animation: 20s linear -7s infinite dots; + animation: 2s linear -0.8s infinite opacity-breath; + background-position: calc(var(--x-direction) / 2) calc(var(--y-direction) / 2); } &::after { @@ -81,6 +85,19 @@ --x-direction: calc(-1 * var(--background-size)); --y-direction: calc(-1 * var(--background-size)); + animation: 2s linear -1.5s infinite opacity-breath; + background-position: calc(var(--x-direction) / 2) calc(var(--y-direction) / 2); + } +} + +:global(body.animation-level-2) .dots { + animation: 20s linear infinite dots; + + &::before { + animation: 20s linear -7s infinite dots; + } + + &::after { animation: 20s linear -14s infinite dots; } } @@ -103,3 +120,17 @@ mask-size: 350%, 100%; } } + +@keyframes opacity-breath { + 0% { + opacity: 1; + } + + 50% { + opacity: 0.1; + } + + 100% { + opacity: 1; + } +} diff --git a/src/components/middle/MiddleHeader.scss b/src/components/middle/MiddleHeader.scss index b2564a9f8..d96f81a22 100644 --- a/src/components/middle/MiddleHeader.scss +++ b/src/components/middle/MiddleHeader.scss @@ -584,6 +584,8 @@ } .pinned-thumb-image { + width: 100%; + height: 100%; object-fit: cover; } } diff --git a/src/components/middle/composer/AttachmentModal.module.scss b/src/components/middle/composer/AttachmentModal.module.scss new file mode 100644 index 000000000..7eaf6c644 --- /dev/null +++ b/src/components/middle/composer/AttachmentModal.module.scss @@ -0,0 +1,162 @@ +.root { + --border-radius-default: 0.625rem; + + :global { + .modal-dialog { + max-width: 26.25rem; + + @media (max-width: 600px) { + max-height: 100%; + } + } + + .modal-header-condensed { + padding: 0.3125rem 0.5rem !important; + border-bottom: 1px solid transparent; + + transition: border-color 250ms ease-in-out; + } + + .modal-content { + display: flex; + flex-direction: column; + padding: 0; + // Full height - header - margins + max-height: calc(100vh - 3.25rem - 5rem); + + overflow-x: auto; + } + } + + &.header-border :global(.modal-header-condensed) { + border-bottom-color: var(--color-borders); + } +} + +.attachments { + max-height: 26rem; + min-height: 5rem; + overflow: auto; + flex-shrink: 1; + + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0.25rem; + padding: 0.5rem 0.25rem 0; + + @media (max-width: 600px) { + max-height: 80vh; + } +} + +.attachments-bottom-padding { + padding-bottom: 0.5rem; +} + +.caption-wrapper { + position: relative; + padding: 0 0.5rem; + + border-top: 1px solid transparent; + transition: border-color 250ms ease-in-out; + + :global { + .form-control { + background: var(--color-background); + } + + .MentionTooltip { + right: 0 !important; + z-index: 0; + } + } +} + +.caption-top-border { + border-top-color: var(--color-borders); +} + +.caption { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.drop-target { + display: flex; + flex-direction: column; + + position: relative; + overflow: hidden; + + &::before, + &::after { + content: ""; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + border-radius: var(--border-radius-default); + pointer-events: none; + + opacity: 0; + transition: 250ms opacity; + z-index: 1; + } + + &::before { + background-image: var(--drag-target-border-hovered); + background-color: var(--color-background); + } + + &::after { + content: attr(data-attach-description); + display: flex; + justify-content: center; + align-items: center; + color: var(--color-primary); + } +} + +.hovered { + .drop-target::before { + opacity: 0.95; + } + + .drop-target::after { + opacity: 1; + } + + .caption-wrapper, + .attachments, + :global(.input-scroller) { + pointer-events: none; + } +} + +.send-wrapper { + align-self: flex-end; + position: relative; + padding-bottom: 0.5rem; + margin-right: 0.375rem; +} + +.send { + height: 2.5rem; + padding: 0 1rem; +} + +:global { + .CustomSendMenu { + bottom: 2.25rem; + + .is-pointer-env & > .backdrop { + width: 100%; + top: -2rem; + bottom: auto; + height: 3.5rem; + } + } +} diff --git a/src/components/middle/composer/AttachmentModal.scss b/src/components/middle/composer/AttachmentModal.scss deleted file mode 100644 index 98b768726..000000000 --- a/src/components/middle/composer/AttachmentModal.scss +++ /dev/null @@ -1,172 +0,0 @@ -.AttachmentModal { - --border-radius-default: 0.625rem; - - .modal-dialog { - max-width: 26.25rem; - - @media (max-width: 600px) { - max-height: 100%; - } - } - - .modal-header-condensed { - padding: 0.3125rem 0.5rem !important; - border-bottom: 1px solid transparent; - - transition: border-color 250ms ease-in-out; - } - - &.modal-header-border .modal-header-condensed { - border-bottom-color: var(--color-borders); - } - - .modal-content { - display: flex; - flex-direction: column; - padding: 0 0 0.5rem; - // Full height - header - margins - max-height: calc(100vh - 3.25rem - 5rem); - - overflow-x: auto; - } - - .attachments-wrapper { - max-height: 26rem; - min-height: 13rem; - overflow: auto; - flex-shrink: 1; - - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin: 0 0.25rem; - padding: 0.5rem 0.25rem; - - @media (max-width: 600px) { - max-height: 80vh; - } - } - - .attachment-caption-wrapper { - position: relative; - margin: 0 0.5rem; - - &::before { - content: ''; - position: absolute; - left: -0.5rem; - top: 0; - right: -0.5rem; - border-top: 1px solid transparent; - - transition: border-color 250ms ease-in-out; - } - - .form-control { - background: var(--color-background); - } - - .MentionTooltip { - right: 0 !important; - z-index: 0; - } - } - - .caption-top-border::before { - border-top-color: var(--color-borders); - } - - .attachment-caption { - display: flex; - align-items: center; - gap: 0.5rem; - } - - .attachment-checkbox { - margin-left: -1rem; - } - - .drop-target { - display: flex; - flex-direction: column; - - position: relative; - overflow: hidden; - - &::before, - &::after { - content: ""; - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - border-radius: var(--border-radius-default); - pointer-events: none; - - opacity: 0; - transition: 250ms opacity; - z-index: 1; - } - - &::before { - background-image: var(--drag-target-border-hovered); - background-color: var(--color-background); - } - - &::after { - content: attr(data-attach-description); - display: flex; - justify-content: center; - align-items: center; - color: var(--color-primary); - } - } - - &.hovered { - .drop-target::before { - opacity: 0.95; - } - - .drop-target::after { - opacity: 1; - } - - .attachment-caption-wrapper, - .attachments-wrapper, - .input-scroller { - pointer-events: none; - } - - .document-wrapper, - .media-wrapper { - border-radius: var(--border-radius-default); - } - } - - &--send-wrapper { - position: relative; - } - - .CustomSendMenu { - bottom: 2.25rem; - - .is-pointer-env & > .backdrop { - width: 100%; - top: -2rem; - bottom: auto; - height: 3.5rem; - } - } - - .AttachmentModal--send-wrapper { - align-self: flex-end; - padding-bottom: 0.25rem; - margin-right: 0.375rem; - } - - .AttachmentModal--send { - height: 2.5rem; - padding: 0 1rem; - } -} diff --git a/src/components/middle/composer/AttachmentModal.tsx b/src/components/middle/composer/AttachmentModal.tsx index baa47d376..ffaa99015 100644 --- a/src/components/middle/composer/AttachmentModal.tsx +++ b/src/components/middle/composer/AttachmentModal.tsx @@ -46,7 +46,7 @@ import AttachmentModalItem from './AttachmentModalItem'; import DropdownMenu from '../../ui/DropdownMenu'; import MenuItem from '../../ui/MenuItem'; -import './AttachmentModal.scss'; +import styles from './AttachmentModal.module.scss'; export type OwnProps = { chatId: string; @@ -233,12 +233,12 @@ const AttachmentModal: FC = ({ const { relatedTarget: toTarget, target: fromTarget } = e; // Esc button pressed during drag event - if ((fromTarget as HTMLDivElement).matches('.drop-target') && !toTarget) { + if ((fromTarget as HTMLDivElement).matches(styles.dropTarget) && !toTarget) { hideTimeoutRef.current = window.setTimeout(unmarkHovered, DROP_LEAVE_TIMEOUT_MS); } // Prevent DragLeave event from firing when the pointer moves inside the AttachmentModal drop target - if (fromTarget && (fromTarget as HTMLElement).closest('.AttachmentModal.hovered')) { + if (fromTarget && (fromTarget as HTMLElement).closest(styles.hovered)) { return; } @@ -422,20 +422,22 @@ const AttachmentModal: FC = ({ ); } + const isBottomDividerShown = !areAttachmentsScrolledToBottom || !isCaptionNotScrolled; + return (
= ({ data-dropzone >
{renderingAttachments.map((attachment, i) => ( = ({
= ({ onCustomEmojiSelect={insertCustomEmoji} addRecentCustomEmoji={addRecentCustomEmoji} /> -
+
= ({ canAutoFocus={Boolean(isReady && attachments.length)} captionLimit={leftChars} /> -
+