From 28bdd9a0fa9a63140f1ea0fabbb952ac40b17391 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Wed, 27 Nov 2024 22:22:09 +0800 Subject: [PATCH] Adjustments to composer footer buttons - Make it one-liner - Make the add-action buttons scrollable - Introduce 'Add' button that shows a menu of the actions to allow more actions in the future --- src/components/compose.css | 140 +++++++++++++++-- src/components/compose.jsx | 310 ++++++++++++++++++++++++------------- 2 files changed, 335 insertions(+), 115 deletions(-) diff --git a/src/components/compose.css b/src/components/compose.css index d62de470..08d30c51 100644 --- a/src/components/compose.css +++ b/src/components/compose.css @@ -20,11 +20,15 @@ justify-content: space-between; gap: 8px; align-items: center; - padding: 16px; + padding: 8px; position: sticky; top: 0; z-index: 100; white-space: nowrap; + + @media (min-width: 480px) { + padding: 16px; + } } #compose-container .compose-top .account-block { text-align: start; @@ -110,10 +114,10 @@ } #compose-container form { - --form-padding-inline: 8px; - --form-padding-block: 0; + --form-spacing-inline: 4px; + --form-spacing-block: 0; /* border-radius: 16px; */ - padding: var(--form-padding-block) var(--form-padding-inline); + padding: var(--form-spacing-block) var(--form-spacing-inline); background-color: var(--bg-blur-color); /* background-image: linear-gradient(var(--bg-color) 85%, transparent); */ position: relative; @@ -121,6 +125,10 @@ --drop-shadow: 0 3px 6px -3px var(--drop-shadow-color); box-shadow: var(--drop-shadow); + @media (min-width: 480px) { + --form-spacing-inline: 8px; + } + @media (min-width: 40em) { border-radius: 16px; } @@ -153,8 +161,8 @@ display: flex; justify-content: space-between; align-items: center; - padding: 8px 0; - gap: 8px; + padding: var(--form-spacing-inline) 0; + gap: var(--form-spacing-inline); } #compose-container .toolbar.wrap { flex-wrap: wrap; @@ -181,6 +189,11 @@ white-space: nowrap; border: 2px solid transparent; vertical-align: middle; + + &.active { + filter: brightness(0.8); + background-color: var(--bg-color); + } } #compose-container .toolbar-button > * { vertical-align: middle; @@ -248,6 +261,38 @@ max-width: 100%; } +#compose-container .compose-footer { + .add-toolbar-button-group { + display: flex; + overflow: auto; + } + .add-sub-toolbar-button-group { + flex-grow: 1; + display: flex; + overflow: auto; + transition: 0.5s ease-in-out; + transition-property: opacity, width; + scrollbar-width: none; + padding-inline-end: 16px; + mask-image: linear-gradient( + var(--to-backward), + transparent 0, + black 16px, + black 100% + ); + + &::-webkit-scrollbar { + display: none; + } + + &[hidden] { + opacity: 0; + pointer-events: none; + width: 0; + } + } +} + #compose-container text-expander { position: relative; display: block; @@ -516,6 +561,37 @@ color: var(--red-color); } +.compose-menu-add-media { + position: relative; + + .compose-menu-add-media-field { + position: absolute; + inset: 0; + opacity: 0; + cursor: inherit; + } +} + +.icon-gif { + display: inline-block !important; + min-width: 16px; + height: 16px; + font-size: 10px !important; + letter-spacing: -0.5px; + font-size-adjust: none; + overflow: hidden; + white-space: nowrap; + text-align: center; + line-height: 16px; + font-weight: bold; + text-rendering: optimizeSpeed; + + &:after { + display: block; + content: 'GIF'; + } +} + @media (display-mode: standalone) { /* No popping in standalone mode */ #compose-container .pop-button { @@ -525,8 +601,10 @@ #compose-container button[type='submit'] { border-radius: 8px; + @media (min-width: 480px) { padding-inline: 24px; + font-size: 125%; } } @@ -820,8 +898,8 @@ .compose-field-container { display: grid !important; - @media (width < 30em) { - margin-inline: calc(-1 * var(--form-padding-inline)); + @media (width < 480px) { + margin-inline: calc(-1 * var(--form-spacing-inline)); width: 100vw !important; max-width: 100vw; @@ -929,15 +1007,55 @@ } } +@keyframes jump-scare { + from { + opacity: 0.5; + transform: scale(0.25) translateX(80px); + } + to { + opacity: 1; + transform: scale(1) translateX(0); + } +} +@keyframes jump-scare-rtl { + from { + opacity: 0.5; + transform: scale(0.25) translateX(-80px); + } + to { + opacity: 1; + transform: scale(1) translateX(0); + } +} + +.add-button { + transform-origin: var(--forward) center; + background-color: var(--bg-blur-color) !important; + animation: jump-scare 0.2s ease-in-out both; + :dir(rtl) & { + animation-name: jump-scare-rtl; + } + + .icon { + transition: transform 0.3s ease-in-out; + } + &.active { + .icon { + transform: rotate(135deg); + } + } +} + .gif-picker-button { - span { + /* span { font-weight: bold; font-size: 11.5px; display: block; - } + line-height: 1; + } */ &:is(:hover, :focus) { - span { + .icon { animation: gif-shake 0.3s 3; } } diff --git a/src/components/compose.jsx b/src/components/compose.jsx index 4786ae9e..a2eb6db9 100644 --- a/src/components/compose.jsx +++ b/src/components/compose.jsx @@ -19,6 +19,7 @@ import stringLength from 'string-length'; // import { detectAll } from 'tinyld/light'; import { uid } from 'uid/single'; import { useDebouncedCallback, useThrottledCallback } from 'use-debounce'; +import useResizeObserver from 'use-resize-observer'; import { useSnapshot } from 'valtio'; import poweredByGiphyURL from '../assets/powered-by-giphy.svg'; @@ -201,6 +202,13 @@ const LF = mem((locale) => new Intl.ListFormat(locale || undefined)); const CUSTOM_EMOJIS_COUNT = 100; +const ADD_LABELS = { + media: msg`Add media`, + customEmoji: msg`Add custom emoji`, + gif: msg`Add GIF`, + poll: msg`Add poll`, +}; + function Compose({ onClose, replyToStatus, @@ -209,7 +217,7 @@ function Compose({ standalone, hasOpener, }) { - const { i18n } = useLingui(); + const { i18n, _ } = useLingui(); const rtf = RTF(i18n.locale); const lf = LF(i18n.locale); @@ -732,6 +740,39 @@ function Compose({ states.composerState.minimized = true; }; + const gifPickerDisabled = + uiState === 'loading' || + (maxMediaAttachments !== undefined && + mediaAttachments.length >= maxMediaAttachments) || + !!poll; + + // If maxOptions is not defined or defined and is greater than 1, show poll button + const showPollButton = maxOptions == null || maxOptions > 1; + const pollButtonDisabled = + uiState === 'loading' || !!poll || !!mediaAttachments.length; + const onPollButtonClick = () => { + setPoll({ + options: ['', ''], + expiresIn: 24 * 60 * 60, // 1 day + multiple: false, + }); + }; + + const addSubToolbarRef = useRef(); + const [showAddButton, setShowAddButton] = useState(false); + useResizeObserver({ + ref: addSubToolbarRef, + box: 'border-box', + onResize: ({ width }) => { + // If scrollable, it's truncated + const { scrollWidth } = addSubToolbarRef.current; + const truncated = scrollWidth > width; + const overTruncated = width < 84; // roughly two buttons width + setShowAddButton(overTruncated || truncated); + addSubToolbarRef.current.hidden = overTruncated; + }, + }); + return (
@@ -1318,87 +1359,88 @@ function Compose({ }} /> )} -
- -