diff --git a/src/assets/font-icons/closed-gift.svg b/src/assets/font-icons/closed-gift.svg index 2fefc3363..bd36be106 100644 --- a/src/assets/font-icons/closed-gift.svg +++ b/src/assets/font-icons/closed-gift.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/frozen-time.svg b/src/assets/font-icons/frozen-time.svg index 15a7c2b13..8f3eebc57 100644 --- a/src/assets/font-icons/frozen-time.svg +++ b/src/assets/font-icons/frozen-time.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/hand-stop.svg b/src/assets/font-icons/hand-stop.svg index 540546b9f..478e9198d 100644 --- a/src/assets/font-icons/hand-stop.svg +++ b/src/assets/font-icons/hand-stop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/lock.svg b/src/assets/font-icons/lock.svg index ba83cc0a5..9a23a397c 100644 --- a/src/assets/font-icons/lock.svg +++ b/src/assets/font-icons/lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/stars-refund.svg b/src/assets/font-icons/stars-refund.svg index fc5ab8be0..4e3fb9d27 100644 --- a/src/assets/font-icons/stars-refund.svg +++ b/src/assets/font-icons/stars-refund.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/stealth-future.svg b/src/assets/font-icons/stealth-future.svg index 7f66ceae4..5b7b0fd35 100644 --- a/src/assets/font-icons/stealth-future.svg +++ b/src/assets/font-icons/stealth-future.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/stealth-past.svg b/src/assets/font-icons/stealth-past.svg index 4df90dfb3..e2d9cad54 100644 --- a/src/assets/font-icons/stealth-past.svg +++ b/src/assets/font-icons/stealth-past.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/user-stars.svg b/src/assets/font-icons/user-stars.svg index 12e7ec236..08943452a 100644 --- a/src/assets/font-icons/user-stars.svg +++ b/src/assets/font-icons/user-stars.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index d39dbb91e..b89002795 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -2108,6 +2108,7 @@ "StarGiftPurchaseTransaction" = "Gift Sale"; "GiftBuyConfirmDescription" = "Do you want to buy **{gift}** for **{stars}**?"; "GiftBuyForPeerConfirmDescription" = "Do you want to buy **{gift}** for **{stars}** and gift it to **{peer}**?"; +"GiftBuyEqualsTo" = "Equals to {stars}"; "ComposerTitleForwardFrom" = "From: **{users}**"; "ContextMenuItemMention" = "Mention"; "GiftRibbonResale" = "resale"; diff --git a/src/components/App.tsx b/src/components/App.tsx index df3066990..f6275a85c 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -8,7 +8,7 @@ import type { UiLoaderPage } from './common/UiLoader'; import { DARK_THEME_BG_COLOR, INACTIVE_MARKER, LIGHT_THEME_BG_COLOR, PAGE_TITLE, PAGE_TITLE_TAURI } from '../config'; import { forceMutation } from '../lib/fasterdom/stricterdom.ts'; -import { selectTabState, selectTheme } from '../global/selectors'; +import { selectActionMessageBg, selectTabState, selectTheme } from '../global/selectors'; import { IS_TAURI } from '../util/browser/globalEnvironment'; import { IS_INSTALL_PROMPT_SUPPORTED, PLATFORM_ENV } from '../util/browser/windowEnvironment'; import buildClassName from '../util/buildClassName'; @@ -43,6 +43,7 @@ type StateProps = { hasWebAuthTokenFailed?: boolean; isTestServer?: boolean; theme: ThemeKey; + actionMessageBg?: string; }; enum AppScreens { @@ -64,6 +65,7 @@ const App: FC = ({ hasWebAuthTokenFailed, isTestServer, theme, + actionMessageBg, }) => { const { isMobile } = useAppLayout(); const isMobileOs = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android'; @@ -226,6 +228,12 @@ const App: FC = ({ ); }, [theme]); + useLayoutEffect(() => { + if (actionMessageBg) { + document.body.style.setProperty('--action-message-bg', actionMessageBg); + } + }, [actionMessageBg]); + const getIsInBackgroundLocal = getIsInBackground; useSignalEffect(() => { // Mutation forced to avoid RAF throttling in background @@ -263,6 +271,7 @@ export default withGlobal( hasWebAuthTokenFailed: global.hasWebAuthTokenFailed || global.hasWebAuthTokenPasswordRequired, theme: selectTheme(global), isTestServer: global.config?.isTestServer, + actionMessageBg: selectActionMessageBg(global), }; }, )(App); diff --git a/src/components/common/CalendarModal.scss b/src/components/common/CalendarModal.scss index 7f9725fe9..57df4ef90 100644 --- a/src/components/common/CalendarModal.scss +++ b/src/components/common/CalendarModal.scss @@ -70,13 +70,17 @@ } } + .container { + margin-top: 0.1875rem; + } + .month-selector { display: flex; align-items: center; h4 { flex: 1; - margin: 0 0 0 1.25rem; + margin: 0 0 0 2.25rem; font-size: 1.25rem; & ~ .Button { diff --git a/src/components/common/CalendarModal.tsx b/src/components/common/CalendarModal.tsx index f8d202895..2f7affb3a 100644 --- a/src/components/common/CalendarModal.tsx +++ b/src/components/common/CalendarModal.tsx @@ -370,16 +370,10 @@ const CalendarModal: FC = ({ className="CalendarModal" onEnter={handleSubmit} dialogRef={dialogRef} + hasAbsoluteCloseButton > - {oldLang(`lng_month${currentMonth + 1}`)} diff --git a/src/components/common/CountryPickerModal.module.scss b/src/components/common/CountryPickerModal.module.scss index 4894708d5..97cf46506 100644 --- a/src/components/common/CountryPickerModal.module.scss +++ b/src/components/common/CountryPickerModal.module.scss @@ -9,8 +9,9 @@ .pickerTitle { flex: 1; - margin: 0 0 0 1.25rem; + margin: 0 0 0 2.25rem; font-size: 1.25rem; + line-height: 1.25; @media (max-width: 600px) { margin-left: 0.75rem; diff --git a/src/components/common/CountryPickerModal.tsx b/src/components/common/CountryPickerModal.tsx index 571650339..a749f0e27 100644 --- a/src/components/common/CountryPickerModal.tsx +++ b/src/components/common/CountryPickerModal.tsx @@ -76,16 +76,10 @@ const CountryPickerModal: FC = ({ isOpen={isOpen} onClose={onClose} onEnter={handleSubmit} + hasAbsoluteCloseButton > - {lang('BoostingSelectCountry')} diff --git a/src/components/common/PrivacySettingsNoticeModal.module.scss b/src/components/common/PrivacySettingsNoticeModal.module.scss index dd5847457..bd1b8cd51 100644 --- a/src/components/common/PrivacySettingsNoticeModal.module.scss +++ b/src/components/common/PrivacySettingsNoticeModal.module.scss @@ -38,6 +38,6 @@ .closeButton { position: absolute; - top: 0.5rem; - right: 0.5rem; + top: 0.875rem; + right: 0.875rem; } diff --git a/src/components/common/PrivacySettingsNoticeModal.tsx b/src/components/common/PrivacySettingsNoticeModal.tsx index caf2747e0..2eb5ffcf4 100644 --- a/src/components/common/PrivacySettingsNoticeModal.tsx +++ b/src/components/common/PrivacySettingsNoticeModal.tsx @@ -87,7 +87,7 @@ const PrivacySettingsNoticeModal = ({ isOpen, isReadDate, user }: OwnProps & Sta className={styles.closeButton} color="translucent" round - size="smaller" + size="tiny" onClick={handleClose} ariaLabel="Close" iconName="close" diff --git a/src/components/common/StickerSetModal.tsx b/src/components/common/StickerSetModal.tsx index 2541642bc..a38c66d4a 100644 --- a/src/components/common/StickerSetModal.tsx +++ b/src/components/common/StickerSetModal.tsx @@ -195,7 +195,7 @@ const StickerSetModal: FC = ({ = ({ = ({ { const lang = useOldLang(); const hasButton = Boolean(confirmButtonText || onConfirm); + const dialogRef = useRef(); + + useScrollNotch({ + containerRef: dialogRef, + selector: `.modal-content ${itemsContainerSelector}`, + isBottomNotch: true, + }, [modalProps.isOpen]); + return ( { @@ -78,7 +82,7 @@ const RadialPatternBackground = ({ for (let ring = 1; ring <= ringsCount; ring++) { const ringItemCount = Math.floor(BASE_RING_ITEM_COUNT * (1 + (ring - 1) * RING_INCREMENT)); const ringProgress = ring / ringsCount; - const ringRadius = centerEmptiness + (MAX_RADIUS - centerEmptiness) * ringProgress; + const ringRadius = centerEmptiness + (maxRadius - centerEmptiness) * ringProgress; const angleShift = ring % 2 === 0 ? Math.PI / ringItemCount : 0; for (let i = 0; i < ringItemCount; i++) { @@ -100,7 +104,7 @@ const RadialPatternBackground = ({ } } return coordinates; - }, [centerEmptiness, clearBottomSector, ovalFactor, ringsCount]); + }, [centerEmptiness, clearBottomSector, maxRadius, ovalFactor, ringsCount]); useResizeObserver(containerRef, (entry) => { setContainerSize({ @@ -148,7 +152,7 @@ const RadialPatternBackground = ({ ctx.globalCompositeOperation = 'source-in'; ctx.fillRect(0, 0, width, height); - const radialGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, width / 2); + const radialGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, width * maxRadius); radialGradient.addColorStop(0, 'rgb(255 255 255 / 0.4)'); radialGradient.addColorStop(1, 'rgb(255 255 255 / 0.9)'); @@ -200,7 +204,7 @@ const RadialPatternBackground = ({ > diff --git a/src/components/left/settings/SettingsActiveSession.tsx b/src/components/left/settings/SettingsActiveSession.tsx index 7104e83ad..5173f7ebc 100644 --- a/src/components/left/settings/SettingsActiveSession.tsx +++ b/src/components/left/settings/SettingsActiveSession.tsx @@ -62,7 +62,14 @@ const SettingsActiveSession: FC = ({ function renderHeader() { return ( - + {lang('SessionPreviewTitle')} = ({ function renderHeader() { return ( - + {lang('WebSessionsTitle')} = ({ isOpen={isOpen} dialogRef={dialogRef} onEnter={(dataPrepaidGiveaway || dataStarsPrepaidGiveaway) ? openConfirmModal : handleClick} + hasAbsoluteCloseButton > - {renderText(lang('BoostingBoostsViaGifts'))} diff --git a/src/components/main/premium/PremiumFeatureModal.module.scss b/src/components/main/premium/PremiumFeatureModal.module.scss index a8c379cbb..959496533 100644 --- a/src/components/main/premium/PremiumFeatureModal.module.scss +++ b/src/components/main/premium/PremiumFeatureModal.module.scss @@ -17,8 +17,9 @@ .back-button { position: absolute; - z-index: 2; - margin: -0.5rem; + z-index: 3; + top: 0.875rem; + left: 0.875rem; } .white-back-button { diff --git a/src/components/main/premium/PremiumFeatureModal.tsx b/src/components/main/premium/PremiumFeatureModal.tsx index 6474d6661..4779b8758 100644 --- a/src/components/main/premium/PremiumFeatureModal.tsx +++ b/src/components/main/premium/PremiumFeatureModal.tsx @@ -227,7 +227,7 @@ const PremiumFeatureModal: FC = ({ = ({ onClose={closePremiumModal} isOpen={isOpen} dialogRef={dialogRef} + hasAbsoluteCloseButton > {!currentSection ? ( - closePremiumModal()} - ariaLabel={oldLang('Close')} - iconName="close" - /> {renderHeader()} {!isPremium && !isGift && renderSubscriptionOptions()} diff --git a/src/components/middle/ContactGreeting.module.scss b/src/components/middle/ContactGreeting.module.scss index cb71457fe..421fdd198 100644 --- a/src/components/middle/ContactGreeting.module.scss +++ b/src/components/middle/ContactGreeting.module.scss @@ -21,7 +21,7 @@ color: #fff; - background: var(--pattern-color); + background: var(--action-message-bg); } .explainer { @@ -33,7 +33,7 @@ color: #fff; text-wrap: balance; - background: var(--pattern-color); + background: var(--action-message-bg); } .title { diff --git a/src/components/middle/MessageList.scss b/src/components/middle/MessageList.scss index 5806b2324..176dec950 100644 --- a/src/components/middle/MessageList.scss +++ b/src/components/middle/MessageList.scss @@ -1,6 +1,4 @@ .MessageList { - --action-message-bg: var(--pattern-color); - overflow-x: hidden; overflow-y: scroll; flex: 1; diff --git a/src/components/middle/MiddleColumn.scss b/src/components/middle/MiddleColumn.scss index c2e3eae3c..0a3922130 100644 --- a/src/components/middle/MiddleColumn.scss +++ b/src/components/middle/MiddleColumn.scss @@ -218,18 +218,11 @@ } &.with-notch::before { - opacity: 1; + opacity: 0.75; } html.theme-dark &::before { - background: - linear-gradient( - 90deg, - rgba(127, 127, 127, 0) 0%, - rgba(127, 127, 127, 0.4) 2%, - rgba(127, 127, 127, 0.4) 98%, - rgba(127, 127, 127, 0) 100% - ); + background: var(--action-message-bg); } @media (min-width: 1276px) { diff --git a/src/components/middle/MiddleColumn.tsx b/src/components/middle/MiddleColumn.tsx index de5cc0a9d..1d7998a04 100644 --- a/src/components/middle/MiddleColumn.tsx +++ b/src/components/middle/MiddleColumn.tsx @@ -30,6 +30,7 @@ import { isUserRightBanned, } from '../../global/helpers'; import { + selectActionMessageBg, selectBot, selectCanAnimateInterface, selectCanAnimateRightColumn, selectChat, @@ -125,6 +126,7 @@ type StateProps = { customBackground?: string; backgroundColor?: string; patternColor?: string; + actionMessageBg?: string; isLeftColumnShown?: boolean; isRightColumnShown?: boolean; isBackgroundBlurred?: boolean; @@ -191,6 +193,7 @@ function MiddleColumn({ theme, backgroundColor, patternColor, + actionMessageBg, isLeftColumnShown, isRightColumnShown, isBackgroundBlurred, @@ -486,7 +489,7 @@ function MiddleColumn({ }); // Prepare filter beforehand to avoid flickering - useFluidBackgroundFilter(patternColor); + useFluidBackgroundFilter(actionMessageBg); const isMessagingDisabled = Boolean( !isPinnedMessageList && !isSavedDialog && !renderingCanPost && !renderingCanRestartBot && !renderingCanStartBot @@ -760,6 +763,7 @@ export default memo(withGlobal( customBackground, backgroundColor, patternColor, + actionMessageBg: selectActionMessageBg(global), isLeftColumnShown, isRightColumnShown: selectIsRightColumnShown(global, isMobile), isBackgroundBlurred, diff --git a/src/components/middle/NoMessages.scss b/src/components/middle/NoMessages.scss index 275c77a9f..84acbf322 100644 --- a/src/components/middle/NoMessages.scss +++ b/src/components/middle/NoMessages.scss @@ -29,7 +29,7 @@ color: #fff; - background: var(--pattern-color); + background: var(--action-message-bg); &[dir="rtl"] { text-align: right; diff --git a/src/components/middle/ReactorListModal.scss b/src/components/middle/ReactorListModal.scss index c4d3f14aa..fdb13b47f 100644 --- a/src/components/middle/ReactorListModal.scss +++ b/src/components/middle/ReactorListModal.scss @@ -5,15 +5,6 @@ --hover-color-reaction: var(--color-message-reaction-hover); --accent-color: var(--color-primary); - .modal-header { - padding-top: 0.5rem; - padding-inline: 0.5rem; - - .modal-title { - margin-inline: 1.3125rem; - } - } - .modal-dialog { overflow: clip; } @@ -46,20 +37,27 @@ gap: 0.4375rem; width: auto; - min-height: 2.25rem; - padding-inline: 0.4375rem; + min-height: 2rem; + margin-top: 0; + padding-inline: 0.75rem; } .reaction-filter-emoji { margin-inline-end: 0.25rem; } + .not-chosen-button { + &:hover { + background-color: var(--hover-color-reaction) !important; + } + } + .reactor-list { overflow: auto; overflow-x: hidden; max-height: 100%; - padding-top: 0.5rem; + padding-top: 0.25rem; padding-inline: 0.5rem; } diff --git a/src/components/middle/ReactorListModal.tsx b/src/components/middle/ReactorListModal.tsx index e838f6865..aeeded8ea 100644 --- a/src/components/middle/ReactorListModal.tsx +++ b/src/components/middle/ReactorListModal.tsx @@ -7,6 +7,7 @@ import { import { getActions, getGlobal, withGlobal } from '../../global'; import type { ApiAvailableReaction, ApiMessage, ApiReaction } from '../../api/types'; +import type { AnimationLevel } from '../../types'; import { LoadMoreDirection } from '../../types'; import { getReactionKey, isSameReaction } from '../../global/helpers'; @@ -14,9 +15,11 @@ import { selectChatMessage, selectTabState, } from '../../global/selectors'; +import { selectSharedSettings } from '../../global/selectors/sharedState'; import buildClassName from '../../util/buildClassName'; import { formatDateAtTime } from '../../util/dates/dateFormat'; import { unique } from '../../util/iteratees'; +import { resolveTransitionName } from '../../util/resolveTransitionName'; import { formatIntegerCompact } from '../../util/textFormat'; import { REM } from '../common/helpers/mediaDimensions'; @@ -52,6 +55,7 @@ export type StateProps = Pick = ({ messageId, seenByDates, availableReactions, + animationLevel, }) => { const { loadReactors, @@ -169,6 +174,7 @@ const ReactorListModal: FC = ({ className="ReactorListModal narrow" title={oldLang('Reactions')} onCloseAnimationEnd={handleCloseAnimationEnd} + isCondensedHeader hasCloseButton > {canShowFilters && ( @@ -182,6 +188,7 @@ const ReactorListModal: FC = ({ size="tiny" ripple iconName="heart" + className={chosenTab ? 'not-chosen-button' : undefined} pill fluid onClick={() => setChosenTab(undefined)} @@ -191,10 +198,12 @@ const ReactorListModal: FC = ({ {allReactions.map((reaction) => { const count = reactions?.results .find((reactionsCount) => isSameReaction(reactionsCount.reaction, reaction))?.count; + const isChosen = isSameReaction(chosenTab, reaction); return ( = ({ dir={lang.isRtl ? 'rtl' : undefined} className="reactor-list-wrapper" > - + {viewportIds?.length ? ( ( reactors: message?.reactors, seenByDates: message?.seenByDates, availableReactions: global.reactions.availableReactions, + animationLevel: selectSharedSettings(global).animationLevel, }; }, )(ReactorListModal)); diff --git a/src/components/middle/RequirementToContactMessage.module.scss b/src/components/middle/RequirementToContactMessage.module.scss index 4ad03126f..3da8d8f46 100644 --- a/src/components/middle/RequirementToContactMessage.module.scss +++ b/src/components/middle/RequirementToContactMessage.module.scss @@ -17,12 +17,12 @@ color: var(--color-white); text-transform: none; - background: var(--pattern-color); + background: var(--action-message-bg); transition: filter 150ms ease-in-out; &:not(.disabled):not(:disabled):hover { - background-color: var(--pattern-color); + background-color: var(--action-message-bg); filter: brightness(1.05); } } @@ -37,7 +37,7 @@ padding: 1.0625rem 0; border-radius: 1.5rem; - background: var(--pattern-color); + background: var(--action-message-bg); &[dir="rtl"] { text-align: right; diff --git a/src/components/middle/composer/AttachmentModal.tsx b/src/components/middle/composer/AttachmentModal.tsx index de48e8bb8..12752ce25 100644 --- a/src/components/middle/composer/AttachmentModal.tsx +++ b/src/components/middle/composer/AttachmentModal.tsx @@ -451,7 +451,7 @@ const AttachmentModal: FC = ({ = ({ {lang(modalTitle)} diff --git a/src/components/middle/message/ActionMessage.tsx b/src/components/middle/message/ActionMessage.tsx index 0112685d5..10627316c 100644 --- a/src/components/middle/message/ActionMessage.tsx +++ b/src/components/middle/message/ActionMessage.tsx @@ -17,6 +17,7 @@ import { MESSAGE_APPEARANCE_DELAY } from '../../../config'; import { getMessageHtmlId } from '../../../global/helpers'; import { getMessageReplyInfo } from '../../../global/helpers/replies'; import { + selectActionMessageBg, selectChat, selectChatMessage, selectIsCurrentUserFrozen, @@ -25,7 +26,6 @@ import { selectIsMessageFocused, selectSender, selectTabState, - selectTheme, } from '../../../global/selectors'; import { IS_TAURI } from '../../../util/browser/globalEnvironment'; import { IS_ANDROID, IS_FLUID_BACKGROUND_SUPPORTED } from '../../../util/browser/windowEnvironment'; @@ -83,7 +83,7 @@ type StateProps = { focusDirection?: FocusDirection; noFocusHighlight?: boolean; replyMessage?: ApiMessage; - patternColor?: string; + actionMessageBg?: string; isCurrentUserPremium?: boolean; isInSelectMode?: boolean; hasUnreadReaction?: boolean; @@ -118,7 +118,7 @@ const ActionMessage = ({ focusDirection, noFocusHighlight, replyMessage, - patternColor, + actionMessageBg, isCurrentUserPremium, isInSelectMode, hasUnreadReaction, @@ -245,7 +245,7 @@ const ActionMessage = ({ } }, [action.type, id, isLocal, memoFirstUnreadIdRef]); - const fluidBackgroundStyle = useFluidBackgroundFilter(isFluidMultiline ? patternColor : undefined); + const fluidBackgroundStyle = useFluidBackgroundFilter(isFluidMultiline ? actionMessageBg : undefined); const handleClick = useLastCallback(() => { switch (action.type) { @@ -515,7 +515,6 @@ const ActionMessage = ({ export default memo(withGlobal( (global, { message, threadId }): Complete => { const tabState = selectTabState(global); - const { themes } = global.settings; const chat = selectChat(global, message.chatId); @@ -549,7 +548,7 @@ export default memo(withGlobal( isInsideTopic, replyMessage, isInSelectMode: selectIsInSelectMode(global), - patternColor: themes[selectTheme(global)]?.patternColor, + actionMessageBg: selectActionMessageBg(global), hasUnreadReaction, isResizingContainer, scrollTargetPosition, diff --git a/src/components/middle/message/InlineButtons.scss b/src/components/middle/message/InlineButtons.scss index 60d8c6ad2..543a69307 100644 --- a/src/components/middle/message/InlineButtons.scss +++ b/src/components/middle/message/InlineButtons.scss @@ -19,13 +19,13 @@ font-weight: var(--font-weight-medium); text-transform: none; - background: var(--pattern-color); + background: var(--action-message-bg); transition: background-color 150ms, color 150ms, backdrop-filter 150ms, filter 150ms; &:hover, &:focus { - background: var(--pattern-color) !important; + background: var(--action-message-bg) !important; backdrop-filter: brightness(115%); @supports not (backdrop-filter: brightness(115%)) { diff --git a/src/components/middle/message/Message.scss b/src/components/middle/message/Message.scss index 4938f32d4..f5836b4a0 100644 --- a/src/components/middle/message/Message.scss +++ b/src/components/middle/message/Message.scss @@ -719,7 +719,7 @@ border-radius: 1.125rem; opacity: 0; - background-color: var(--pattern-color); + background-color: var(--action-message-bg); transition: opacity 150ms; @@ -729,6 +729,8 @@ } .message-action-button { + width: 2.25rem; + height: 2.25rem; color: white; } diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index ef05a089e..d1b99bb65 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -1820,7 +1820,6 @@ const Message = ({ className="message-action-button" color="translucent-white" round - size="tiny" ariaLabel={oldLang('lng_context_forward_msg')} onClick={isLastInDocumentGroup ? handleGroupForward : handleForward} iconName="share-filled" @@ -1831,7 +1830,6 @@ const Message = ({ className="message-action-button" color="translucent-white" round - size="tiny" ariaLabel={lang('FocusMessage')} onClick={isPinnedList ? handleFocus : handleFocusForwarded} iconName="arrow-right" diff --git a/src/components/middle/message/MessageMeta.scss b/src/components/middle/message/MessageMeta.scss index a6c0a3ada..8904b5d9d 100644 --- a/src/components/middle/message/MessageMeta.scss +++ b/src/components/middle/message/MessageMeta.scss @@ -137,7 +137,7 @@ } .Message .custom-shape & { - background: var(--pattern-color); + background: var(--action-message-bg); } .voice &[dir="rtl"] { diff --git a/src/components/middle/message/RoundVideo.scss b/src/components/middle/message/RoundVideo.scss index e25337838..c2b4bb152 100644 --- a/src/components/middle/message/RoundVideo.scss +++ b/src/components/middle/message/RoundVideo.scss @@ -98,10 +98,10 @@ height: 1.3125rem; border-radius: 0.5rem; - background: var(--pattern-color); + background: var(--action-message-bg); &:hover { opacity: 0.8; - background: var(--pattern-color) !important; + background: var(--action-message-bg) !important; } } diff --git a/src/components/middle/message/SponsoredMessage.tsx b/src/components/middle/message/SponsoredMessage.tsx index c36a3385b..ae4c4b745 100644 --- a/src/components/middle/message/SponsoredMessage.tsx +++ b/src/components/middle/message/SponsoredMessage.tsx @@ -298,7 +298,6 @@ const SponsoredMessage: FC = ({ className="message-action-button" color="translucent-white" round - size="tiny" iconName="close" iconClassName="sponsored-action-icon" ariaLabel={lang('Close')} @@ -309,7 +308,6 @@ const SponsoredMessage: FC = ({ className="message-action-button" color="translucent-white" round - size="tiny" iconName="more" iconClassName="sponsored-action-icon" ariaLabel={lang('More')} diff --git a/src/components/middle/message/_message-content.scss b/src/components/middle/message/_message-content.scss index ae1234eac..0420b4d1e 100644 --- a/src/components/middle/message/_message-content.scss +++ b/src/components/middle/message/_message-content.scss @@ -12,7 +12,7 @@ font-size: calc(var(--message-text-size, 1rem) - 0.125rem); - background-color: var(--pattern-color); + background-color: var(--action-message-bg); box-shadow: 0 1px 2px var(--color-default-shadow); @media (max-width: 600px) { diff --git a/src/components/modals/collectible/CollectibleInfoModal.tsx b/src/components/modals/collectible/CollectibleInfoModal.tsx index ae35b981a..0097bc2eb 100644 --- a/src/components/modals/collectible/CollectibleInfoModal.tsx +++ b/src/components/modals/collectible/CollectibleInfoModal.tsx @@ -101,16 +101,8 @@ const CollectibleInfoModal: FC = ({ isSlim contentClassName={styles.content} onClose={closeCollectibleInfoModal} + hasAbsoluteCloseButton > - - {title} + {title} {subtitle} ); diff --git a/src/components/modals/common/TableInfoModal.module.scss b/src/components/modals/common/TableInfoModal.module.scss index 7a1df5ebf..c539a85fc 100644 --- a/src/components/modals/common/TableInfoModal.module.scss +++ b/src/components/modals/common/TableInfoModal.module.scss @@ -28,7 +28,7 @@ gap: 1px; border: 1px solid var(--color-borders); - border-radius: 0.3125rem; + border-radius: 1rem; background-color: var(--color-borders); } diff --git a/src/components/modals/frozenAccount/FrozenAccountModal.module.scss b/src/components/modals/frozenAccount/FrozenAccountModal.module.scss index bd55624f3..f27bb5267 100644 --- a/src/components/modals/frozenAccount/FrozenAccountModal.module.scss +++ b/src/components/modals/frozenAccount/FrozenAccountModal.module.scss @@ -4,13 +4,13 @@ align-items: center; width: 100%; - margin-top: 0.5rem; - margin-bottom: 0.75rem; + margin-top: 0.125rem; + margin-bottom: 0.25rem; } .title { - padding-top: 0.5rem; - font-size: 1.25rem; + padding-top: 0.25rem; + font-size: 1.5rem; font-weight: var(--font-weight-medium); text-align: center; } diff --git a/src/components/modals/frozenAccount/FrozenAccountModal.tsx b/src/components/modals/frozenAccount/FrozenAccountModal.tsx index 639b44ea6..bcfda0733 100644 --- a/src/components/modals/frozenAccount/FrozenAccountModal.tsx +++ b/src/components/modals/frozenAccount/FrozenAccountModal.tsx @@ -29,6 +29,8 @@ type StateProps = { freezeUntilDate?: number; }; +const ICON_SIZE = 130; + const FrozenAccountModal = ({ modal, freezeUntilDate, @@ -58,7 +60,7 @@ const FrozenAccountModal = ({ return ( @@ -76,14 +78,12 @@ const FrozenAccountModal = ({ {lang('ButtonAppeal')} {lang('ButtonUnderstood')} @@ -123,7 +123,6 @@ const FrozenAccountModal = ({ header={header} listItemData={listItemData} footer={footer} - hasBackdrop onClose={handleClose} /> ); diff --git a/src/components/modals/gift/GiftItemPremium.tsx b/src/components/modals/gift/GiftItemPremium.tsx index e4550a8eb..dd0fee7de 100644 --- a/src/components/modals/gift/GiftItemPremium.tsx +++ b/src/components/modals/gift/GiftItemPremium.tsx @@ -83,7 +83,7 @@ function GiftItemPremium({ {lang('PremiumGiftDescription')} - + {formatCurrencyAsString(amount, currency)} {optionByStars && ( diff --git a/src/components/modals/gift/GiftModal.module.scss b/src/components/modals/gift/GiftModal.module.scss index 73c2b75c7..04e19d692 100644 --- a/src/components/modals/gift/GiftModal.module.scss +++ b/src/components/modals/gift/GiftModal.module.scss @@ -119,7 +119,11 @@ background: var(--color-background); /* stylelint-disable-next-line plugin/no-low-performance-animation-properties */ - transition: height 0.25s ease-out, transform 0.25s ease-out; + transition: height 0.25s ease-out, transform 0.25s ease-out, border-color 0.2s ease; + + &.noBorder { + border-bottom-color: transparent; + } } .resaleHeader { @@ -174,8 +178,8 @@ .closeButton { position: absolute; z-index: 3; - top: 0.375rem; - left: 0.375rem; + top: 0.875rem; + left: 0.875rem; } .balance { @@ -209,9 +213,12 @@ } .logoBackground { + pointer-events: none; + position: absolute; left: 50%; transform: translateX(-50%); + height: 7rem; } diff --git a/src/components/modals/gift/GiftModal.tsx b/src/components/modals/gift/GiftModal.tsx index 80f16c52b..53c83af3b 100644 --- a/src/components/modals/gift/GiftModal.tsx +++ b/src/components/modals/gift/GiftModal.tsx @@ -1,7 +1,7 @@ import type { FC } from '@teact'; import { - memo, useEffect, useMemo, useRef, useState, -} from '@teact'; + memo, useEffect, + useMemo, useRef, useState } from '@teact'; import type React from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; @@ -24,8 +24,10 @@ import { selectTabState } from '../../../global/selectors'; import { selectPeer, selectUserFullInfo } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { throttle } from '../../../util/schedulers'; +import { REM } from '../../common/helpers/mediaDimensions'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; +import useFlag from '../../../hooks/useFlag'; import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; @@ -74,6 +76,7 @@ const AVATAR_SIZE = 100; const INTERSECTION_THROTTLE = 200; const SCROLL_THROTTLE = 200; const AVATAR_SPARKLES_CENTER_SHIFT = [0, -50] as const; +const CATEGORY_LIST_STICKY_TOP = 3.5 * REM; const runThrottledForScroll = throttle((cb) => cb(), SCROLL_THROTTLE, true); @@ -105,6 +108,7 @@ const GiftModal: FC = ({ const dialogRef = useRef(); const transitionRef = useRef(); const giftHeaderRef = useRef(); + const categoryListRef = useRef(); const scrollerRef = useRef(); @@ -118,8 +122,8 @@ const GiftModal: FC = ({ const [shouldShowMainScreenHeader, setShouldShowMainScreenHeader] = useState(false); const [isMainScreenHeaderForStarGifts, setIsMainScreenHeaderForStarGifts] = useState(false); const [isGiftScreenHeaderForStarGifts, setIsGiftScreenHeaderForStarGifts] = useState(false); - const [selectedCategory, setSelectedCategory] = useState('all'); + const [isCategoryListPinned, pinCategoryList, unpinCategoryList] = useFlag(false); const triggerSparklesRef = useRef<(() => void) | undefined>(); const areAllGiftsDisallowed = useMemo(() => { @@ -207,6 +211,17 @@ const GiftModal: FC = ({ const { top: transitionTop } = transitionRef.current.getBoundingClientRect(); setIsMainScreenHeaderForStarGifts(headerTop - transitionTop <= 0); } + + if (categoryListRef.current && scrollerRef.current) { + const { top: listTop } = categoryListRef.current.getBoundingClientRect(); + const { top: scrollerTop } = scrollerRef.current.getBoundingClientRect(); + const isPinned = listTop - scrollerTop <= CATEGORY_LIST_STICKY_TOP; + if (isPinned) { + pinCategoryList(); + } else { + unpinCategoryList(); + } + } }); }); @@ -463,10 +478,12 @@ const GiftModal: FC = ({ {renderStarGiftsHeader()} {renderStarGiftsDescription()} = ({ className={styles.closeButton} round color="translucent" - size="smaller" + size="tiny" onClick={handleCloseButtonClick} ariaLabel={isBackButton ? oldLang('Common.Back') : oldLang('Common.Close')} > @@ -547,7 +564,8 @@ const GiftModal: FC = ({ ; areUniqueStarGiftsDisallowed?: boolean; areLimitedStarGiftsDisallowed?: boolean; isSelf?: boolean; hasMyUnique?: boolean; + isPinned?: boolean; onCategoryChanged: (category: StarGiftCategory) => void; }; @@ -25,14 +28,19 @@ type StateProps = { }; const StarGiftCategoryList = ({ + ref: externalRef, idsByCategory, onCategoryChanged, areUniqueStarGiftsDisallowed, areLimitedStarGiftsDisallowed, isSelf, hasMyUnique, + isPinned, }: StateProps & OwnProps) => { - const ref = useRef(); + let ref = useRef(); + if (externalRef) { + ref = externalRef; + } const lang = useLang(); @@ -42,9 +50,7 @@ const StarGiftCategoryList = ({ function handleItemClick(category: StarGiftCategory) { setSelectedCategory(category); - onCategoryChanged( - category, - ); + onCategoryChanged(category); } function renderCategoryName(category: StarGiftCategory) { @@ -71,7 +77,7 @@ const StarGiftCategoryList = ({ useHorizontalScroll(ref, undefined, true); return ( - + {renderCategoryItem('all')} {!areUniqueStarGiftsDisallowed && !isSelf && hasMyUnique && renderCategoryItem('myUnique')} {(!areUniqueStarGiftsDisallowed || !areLimitedStarGiftsDisallowed) diff --git a/src/components/modals/gift/UniqueGiftHeader.module.scss b/src/components/modals/gift/UniqueGiftHeader.module.scss index 0d40fd2a0..5e93e11f4 100644 --- a/src/components/modals/gift/UniqueGiftHeader.module.scss +++ b/src/components/modals/gift/UniqueGiftHeader.module.scss @@ -12,15 +12,22 @@ padding-bottom: 1rem; &.withManageButtons { - --_height: 18.5rem; + --_height: 19.25rem; .sticker { - margin-top: 2.5rem; + margin-top: 2.625rem; + } + + :global { + canvas { + transform-origin: center 32%; + } } } :global { canvas { + transform-origin: center 45%; transition: 250ms transform; } } @@ -48,7 +55,7 @@ .radialPattern { position: absolute; z-index: -1; - inset: 0; + inset: -5%; } .amount { @@ -65,7 +72,7 @@ } .sticker { - margin-top: 2rem; + margin-top: 2.875rem; } .transition { @@ -83,14 +90,16 @@ .title { margin-top: auto; - font-size: 1.25rem; + + font-size: 1.5rem; + font-weight: var(--font-weight-semibold); + line-height: 1.75rem; color: white; } .subtitle { - max-width: 85%; - - font-size: 0.875rem; + font-size: 1rem; + line-height: 1.375rem; text-align: center; text-wrap: balance; diff --git a/src/components/modals/gift/UniqueGiftHeader.tsx b/src/components/modals/gift/UniqueGiftHeader.tsx index 80b2974dd..298aafd65 100644 --- a/src/components/modals/gift/UniqueGiftHeader.tsx +++ b/src/components/modals/gift/UniqueGiftHeader.tsx @@ -17,6 +17,7 @@ import buildStyle from '../../../util/buildStyle'; import { REM } from '../../common/helpers/mediaDimensions.ts'; import { useTransitionActiveKey } from '../../../hooks/animations/useTransitionActiveKey'; +import useAppLayout from '../../../hooks/useAppLayout'; import useFlag from '../../../hooks/useFlag'; import useLang from '../../../hooks/useLang'; @@ -60,6 +61,8 @@ const UniqueGiftHeader = ({ openChat, } = getActions(); + const { isMobile } = useAppLayout(); + const lang = useLang(); const [isGiftHover, markGiftHover, unmarkGiftHover] = useFlag(false); const activeKey = useTransitionActiveKey([modelAttribute, backdropAttribute, patternAttribute]); @@ -71,16 +74,20 @@ const UniqueGiftHeader = ({ return ( ); - }, [backdropAttribute, patternAttribute]); + }, [backdropAttribute, patternAttribute, isMobile]); return ( diff --git a/src/components/modals/gift/UniqueGiftManageButtons.module.scss b/src/components/modals/gift/UniqueGiftManageButtons.module.scss index a8eee242f..71781b4a9 100644 --- a/src/components/modals/gift/UniqueGiftManageButtons.module.scss +++ b/src/components/modals/gift/UniqueGiftManageButtons.module.scss @@ -5,7 +5,7 @@ gap: 0.5rem; width: 100%; - margin-top: 1rem; + margin-top: 0.375rem; } .manageButton { diff --git a/src/components/modals/gift/info/GiftInfoModal.module.scss b/src/components/modals/gift/info/GiftInfoModal.module.scss index 7e96cb047..86ed1a108 100644 --- a/src/components/modals/gift/info/GiftInfoModal.module.scss +++ b/src/components/modals/gift/info/GiftInfoModal.module.scss @@ -63,31 +63,44 @@ backdrop-filter: blur(0.5rem); } -.headerButton, +.moreMenuButton { + position: absolute; + z-index: 1; + top: 0.875rem; + right: 0.875rem; +} + +.closeButton { + position: absolute; + z-index: 1; + top: 0.875rem; + left: 0.875rem; +} + .giftResalePriceContainer { pointer-events: auto; + position: absolute; + top: 0.875rem; + right: 3.25rem; + display: flex; align-items: center; justify-content: center; width: fit-content; - height: 1.75rem; + height: 1.875rem; padding: 0.25rem; padding-inline: 0.625rem; border-radius: 1rem; - font-size: 1rem; + font-size: 0.875rem; font-weight: var(--font-weight-medium); color: white; - background-color: rgba(0, 0, 0, 0.2); - outline: none !important; -} - -.giftResalePriceContainer { - font-size: 0.75rem; + background-color: rgba(255, 255, 255, 0.1); backdrop-filter: blur(25px); + outline: none !important; } .starAmountIcon, @@ -137,6 +150,24 @@ text-align: center; } +.footerHint { + display: flex; + align-items: center; + + font-size: 0.75rem; + line-height: 1.125rem; + color: var(--color-white); + text-align: center; + text-transform: none; + + opacity: 0.6; +} + +.buyButton { + display: flex; + flex-direction: column; +} + .unknown { margin-inline-start: 0.25rem; } diff --git a/src/components/modals/gift/info/GiftInfoModal.tsx b/src/components/modals/gift/info/GiftInfoModal.tsx index c2742f4db..019cca15a 100644 --- a/src/components/modals/gift/info/GiftInfoModal.tsx +++ b/src/components/modals/gift/info/GiftInfoModal.tsx @@ -108,7 +108,7 @@ const GiftInfoModal = ({ const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false); const [shouldPayInTon, setShouldPayInTon] = useState(false); - const splitButtonRef = useRef(); + const moreButtonRef = useRef(); const menuRef = useRef(); const uniqueGiftHeaderRef = useRef(); const { @@ -117,7 +117,7 @@ const GiftInfoModal = ({ handleContextMenu, handleContextMenuClose, handleContextMenuHide, - } = useContextMenuHandlers(splitButtonRef); + } = useContextMenuHandlers(moreButtonRef); const handleSymbolClick = useLastCallback(() => { if (!gift || !giftAttributes?.pattern) return; @@ -245,6 +245,9 @@ const GiftInfoModal = ({ const resellPrice = getResalePrice(); const confirmPrice = getResalePrice(shouldPayInTon); + const resellPriceInStars = resellPrice?.currency === TON_CURRENCY_CODE && isGiftUnique + ? gift.resellPrice?.find((amount) => amount.currency === STARS_CURRENCY_CODE) + : undefined; const canBuyGift = !isSelfUnique && gift?.type === 'starGiftUnique' && gift.ownerId !== currentUserId && Boolean(resellPrice); @@ -330,37 +333,24 @@ const GiftInfoModal = ({ return gift && getGiftAttributes(gift); }, [gift]); - const SettingsMenuButton = useMemo(() => { - return ( - - - - ); - }, [lang, handleContextMenu]); - const renderFooterButton = useLastCallback(() => { if (canBuyGift) { return ( - - {lang('ButtonBuyGift', { - stars: resellPrice?.currency === TON_CURRENCY_CODE - ? formatTonAsIcon(lang, resellPrice.amount, { shouldConvertFromNanos: true }) - : formatStarsAsIcon(lang, resellPrice?.amount, { asFont: true }), - }, { withNodes: true })} + + + {lang('ButtonBuyGift', { + stars: resellPrice?.currency === TON_CURRENCY_CODE + ? formatTonAsIcon(lang, resellPrice.amount, { shouldConvertFromNanos: true }) + : formatStarsAsIcon(lang, resellPrice?.amount, { asFont: true }), + }, { withNodes: true })} + + {resellPrice?.currency === TON_CURRENCY_CODE && Boolean(resellPriceInStars) && ( + + {lang('GiftBuyEqualsTo', { + stars: formatStarsAsIcon(lang, resellPriceInStars.amount, { asFont: true }), + }, { withNodes: true })} + + )} ); } @@ -536,6 +526,30 @@ const GiftInfoModal = ({ + + + + + {Boolean(resellPrice?.amount) && ( {resellPrice.currency === TON_CURRENCY_CODE @@ -549,28 +563,6 @@ const GiftInfoModal = ({ })} )} - - {SettingsMenuButton} - - - - ); @@ -883,13 +875,14 @@ const GiftInfoModal = ({ typeGift, savedGift, renderingTargetPeer, giftSticker, lang, canManage, hasConvertOption, isSender, oldLang, tonExplorerUrl, gift, giftAttributes, renderFooterButton, isTargetChat, - SettingsMenuButton, isGiftUnique, saleDateInfo, + isGiftUnique, saleDateInfo, canBuyGift, giftOwnerTitle, resellPrice, giftSubtitle, releasedByPeer, handleSymbolClick, handleBackdropClick, handleModelClick, + handleContextMenu, ]); const getRootElement = useLastCallback(() => uniqueGiftHeaderRef.current); - const getTriggerElement = useLastCallback(() => splitButtonRef.current); + const getTriggerElement = useLastCallback(() => moreButtonRef.current); const getMenuElement = useLastCallback(() => menuRef.current); const getLayout = useLastCallback(() => ({ withPortal: true })); diff --git a/src/components/modals/gift/status/GiftStatusInfoModal.module.scss b/src/components/modals/gift/status/GiftStatusInfoModal.module.scss index 29947073e..0bcd97599 100644 --- a/src/components/modals/gift/status/GiftStatusInfoModal.module.scss +++ b/src/components/modals/gift/status/GiftStatusInfoModal.module.scss @@ -28,7 +28,7 @@ } .avatar { - margin-top: 2rem; + margin-top: 2.375rem; } .userTitle { @@ -36,7 +36,7 @@ width: 100%; margin-top: auto; - padding-top: 1rem; + padding-top: 1.75rem; font-size: 1.25rem; color: white; diff --git a/src/components/modals/gift/status/GiftStatusInfoModal.tsx b/src/components/modals/gift/status/GiftStatusInfoModal.tsx index d70fd65dd..50feff433 100644 --- a/src/components/modals/gift/status/GiftStatusInfoModal.tsx +++ b/src/components/modals/gift/status/GiftStatusInfoModal.tsx @@ -11,6 +11,7 @@ import buildClassName from '../../../../util/buildClassName'; import buildStyle from '../../../../util/buildStyle'; import { REM } from '../../../common/helpers/mediaDimensions'; +import useAppLayout from '../../../../hooks/useAppLayout'; import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev'; import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; @@ -34,6 +35,8 @@ type StateProps = { isCurrentUserPremium?: boolean; }; +const AVATAR_SIZE = 7 * REM; + const GiftStatusInfoModal = ({ modal, currentUser, @@ -44,6 +47,8 @@ const GiftStatusInfoModal = ({ setEmojiStatus, } = getActions(); const lang = useLang(); + const { isMobile } = useAppLayout(); + const isOpen = Boolean(modal); const renderingModal = useCurrentOrPrev(modal); @@ -74,10 +79,13 @@ const GiftStatusInfoModal = ({ className={styles.radialPattern} backgroundColors={backdropColors} patternIcon={patternIcon.customEmoji} - yPosition={6.5 * REM} + yPosition={6.875 * REM} + maxRadius={0.31} + patternSize={isMobile ? 13 : 15} + ovalFactor={1} /> ); - }, [emojiStatus, isOpen, patternIcon]); + }, [emojiStatus, isOpen, isMobile, patternIcon]); const mockPeerWithStatus = useMemo(() => { return { @@ -95,7 +103,7 @@ const GiftStatusInfoModal = ({ > {radialPatternBackdrop} - + + {renderBadge('added')} {lang('RatingGiftsFromTelegramDesc')} )], ['user-stars', lang('RatingGiftsAndPostsFromUsers'), ( - + {renderBadge('added')} {lang('RatingGiftsAndPostsFromUsersDesc')} )], ['stars-refund', lang('RatingRefundsAndConversions'), ( - + {renderBadge('deducted')} {lang('RatingRefundsAndConversionsDesc')} diff --git a/src/components/modals/quickPreview/QuickPreviewModalHeader.module.scss b/src/components/modals/quickPreview/QuickPreviewModalHeader.module.scss index d03656d6d..f445bdeb4 100644 --- a/src/components/modals/quickPreview/QuickPreviewModalHeader.module.scss +++ b/src/components/modals/quickPreview/QuickPreviewModalHeader.module.scss @@ -21,8 +21,8 @@ .closeButton { position: absolute !important; - top: 0.5rem; - right: 0.5rem; + top: 0.875rem; + right: 0.875rem; } .markAsReadButton { diff --git a/src/components/modals/quickPreview/QuickPreviewModalHeader.tsx b/src/components/modals/quickPreview/QuickPreviewModalHeader.tsx index a92f27542..81e6b4987 100644 --- a/src/components/modals/quickPreview/QuickPreviewModalHeader.tsx +++ b/src/components/modals/quickPreview/QuickPreviewModalHeader.tsx @@ -84,7 +84,7 @@ const QuickPreviewModalHeader: FC = ({ (); + const isOpen = Boolean(modal && (starsBalanceState || tonBalanceState)); const { @@ -160,6 +165,7 @@ const StarsBalanceModal = ({ setHeaderHidden(true); setSelectedTabIndex(0); hideBuyOptions(); + unpinTabs(); } }, [isOpen]); @@ -261,6 +267,12 @@ const StarsBalanceModal = ({ > {lang('ButtonTopUpViaFragment')} + + {currency === TON_CURRENCY_CODE && ( + + {lang('TonModalHint')} + + )} > ); }; @@ -269,6 +281,17 @@ const StarsBalanceModal = ({ const { scrollTop } = e.currentTarget; setHeaderHidden(scrollTop <= 150); + + if (tabsRef.current) { + const { top: tabsTop } = tabsRef.current.getBoundingClientRect(); + const { top: scrollerTop } = e.currentTarget.getBoundingClientRect(); + const isPinned = tabsTop - scrollerTop <= HEADER_HEIGHT; + if (isPinned) { + pinTabs(); + } else { + unpinTabs(); + } + } } const handleLoadMoreTransactions = useLastCallback(() => { @@ -305,19 +328,15 @@ const StarsBalanceModal = ({ isOpen={isOpen} onClose={closeStarsBalanceModal} dialogStyle={`--modal-height: ${modalHeight}`} + hasAbsoluteCloseButton > - closeStarsBalanceModal()} - ariaLabel={lang('Close')} - iconName="close" - /> {currency !== TON_CURRENCY_CODE && } - + {oldLang('TelegramStars')} @@ -330,11 +349,6 @@ const StarsBalanceModal = ({ {tosText} )} - {currency === TON_CURRENCY_CODE && ( - - {lang('TonModalHint')} - - )} {shouldShowItems && Boolean(subscriptions?.list.length) && ( {oldLang('StarMySubscriptions')} @@ -391,6 +405,7 @@ const StarsBalanceModal = ({ = ({ isSlim onClose={handleClose} isOpen={isOpen} + hasAbsoluteCloseButton > - closeStarsGiftModal()} - ariaLabel={oldLang('Close')} - iconName="close" - /> {user ? oldLang('GiftStarsTitle') : oldLang('Star.List.GetStars')} diff --git a/src/components/modals/webApp/WebAppModal.module.scss b/src/components/modals/webApp/WebAppModal.module.scss index 6d26ca6d5..e93311298 100644 --- a/src/components/modals/webApp/WebAppModal.module.scss +++ b/src/components/modals/webApp/WebAppModal.module.scss @@ -157,7 +157,7 @@ .modal-header { padding: 0; - padding-inline: 0.5rem; + padding-inline: 0.875rem; border-bottom: 0; } diff --git a/src/components/story/StorySettings.tsx b/src/components/story/StorySettings.tsx index 9ff747fd7..df7d45149 100644 --- a/src/components/story/StorySettings.tsx +++ b/src/components/story/StorySettings.tsx @@ -378,7 +378,7 @@ function StorySettings({ className={buildClassName(styles.closeButton, 'close-button')} round color="translucent" - size="smaller" + size="tiny" onClick={handleCloseButtonClick} ariaLabel={isBackButton ? lang('Common.Back') : lang('Common.Close')} > diff --git a/src/components/story/StoryViewer.tsx b/src/components/story/StoryViewer.tsx index 57e5e59eb..4eae66dc3 100644 --- a/src/components/story/StoryViewer.tsx +++ b/src/components/story/StoryViewer.tsx @@ -151,7 +151,7 @@ function StoryViewer({ = ({ className={buildClassName(hasAbsoluteCloseButton && 'modal-absolute-close-button')} round color={absoluteCloseButtonColor} - size="smaller" + size="tiny" iconName="close" ariaLabel={lang('Close')} onClick={onClose} diff --git a/src/components/ui/TabList.tsx b/src/components/ui/TabList.tsx index 0c6a15ecb..4652efe9e 100644 --- a/src/components/ui/TabList.tsx +++ b/src/components/ui/TabList.tsx @@ -1,4 +1,4 @@ -import type { TeactNode } from '../../lib/teact/teact'; +import type { ElementRef, TeactNode } from '../../lib/teact/teact'; import { memo, useEffect, useRef } from '../../lib/teact/teact'; import type { ApiMessageEntityCustomEmoji } from '../../api/types'; @@ -33,6 +33,7 @@ type OwnProps = { className?: string; tabClassName?: string; contextRootElementSelector?: string; + ref?: ElementRef; onSwitchTab: (index: number) => void; }; @@ -46,9 +47,13 @@ const TabList = ({ className, tabClassName, contextRootElementSelector, + ref, onSwitchTab, }: OwnProps) => { - const containerRef = useRef(); + let containerRef = useRef(); + if (ref) { + containerRef = ref; + } const previousActiveTab = usePreviousDeprecated(activeTab); const lang = useLang(); diff --git a/src/config.ts b/src/config.ts index 1bc8875b9..fdb1378b5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -372,7 +372,7 @@ export const BOT_VERIFICATION_PEERS_LIMIT = 20; export const LIGHT_THEME_BG_COLOR = '#99BA92'; export const DARK_THEME_BG_COLOR = '#0F0F0F'; export const DEFAULT_PATTERN_COLOR = '#4A8E3A8C'; -export const DARK_THEME_PATTERN_COLOR = '#0A0A0A8C'; +export const DARK_THEME_PATTERN_COLOR = '#48576166'; export const PEER_COLOR_BG_OPACITY = '1a'; export const PEER_COLOR_BG_ACTIVE_OPACITY = '2b'; export const PEER_COLOR_GRADIENT_STEP = 5; // px diff --git a/src/global/selectors/ui.ts b/src/global/selectors/ui.ts index 775355bd5..dc42c1efa 100644 --- a/src/global/selectors/ui.ts +++ b/src/global/selectors/ui.ts @@ -80,6 +80,11 @@ export function selectThemeValues(global: T, themeKey: Th return global.settings.themes[themeKey]; } +export function selectActionMessageBg(global: T) { + const theme = selectTheme(global); + return global.settings.themes[theme]?.patternColor; +} + export function selectIsForumPanelOpen( global: T, ...[tabId = getCurrentTabId()]: TabArgs diff --git a/src/hooks/useScrollNotch.ts b/src/hooks/useScrollNotch.ts index 4e8ca8451..bd71db1de 100644 --- a/src/hooks/useScrollNotch.ts +++ b/src/hooks/useScrollNotch.ts @@ -6,13 +6,16 @@ import { requestMutation } from '../lib/fasterdom/fasterdom.ts'; import { throttle } from '../util/schedulers.ts'; const THROTTLE_DELAY = 100; +const SCROLL_THRESHOLD = 5; const useScrollNotch = ({ containerRef, selector, + isBottomNotch, }: { containerRef: ElementRef; selector: string; + isBottomNotch?: boolean; }, deps: unknown[]) => { useLayoutEffect(() => { const elements = containerRef.current?.querySelectorAll(selector); @@ -21,14 +24,24 @@ const useScrollNotch = ({ const handleScroll = throttle((event: Event) => { const target = event.target as HTMLElement; const isScrolled = target.scrollTop > 0; + const { scrollHeight, scrollTop, clientHeight } = target; + const isAtEnd = scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD; requestMutation(() => { - toggleExtraClass(target, 'scrolled', isScrolled); + if (isBottomNotch) { + toggleExtraClass(target, 'scrolled-to-end', isAtEnd); + } else { + toggleExtraClass(target, 'scrolled', isScrolled); + } }); }, THROTTLE_DELAY); elements.forEach((el) => { - addExtraClass(el, 'with-notch'); + if (isBottomNotch) { + addExtraClass(el, 'with-bottom-notch'); + } else { + addExtraClass(el, 'with-notch'); + } el.addEventListener('scroll', handleScroll, { passive: true }); }); @@ -36,10 +49,13 @@ const useScrollNotch = ({ elements.forEach((el) => { el.removeEventListener('scroll', handleScroll); removeExtraClass(el, 'with-notch'); + removeExtraClass(el, 'with-bottom-notch'); + removeExtraClass(el, 'scrolled'); + removeExtraClass(el, 'scrolled-to-end'); }); }; // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps - }, [containerRef, selector, ...deps]); + }, [containerRef, selector, isBottomNotch, ...deps]); useEffect(() => { const elements = containerRef.current?.querySelectorAll(selector); @@ -47,12 +63,19 @@ const useScrollNotch = ({ elements.forEach((el) => { const isScrolled = el.scrollTop > 0; + const { scrollHeight, scrollTop, clientHeight } = el; + const isAtEnd = scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD; + requestMutation(() => { - toggleExtraClass(el, 'scrolled', isScrolled); + if (isBottomNotch) { + toggleExtraClass(el, 'scrolled-to-end', isAtEnd); + } else { + toggleExtraClass(el, 'scrolled', isScrolled); + } }); }); // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps - }, [containerRef, selector, ...deps]); + }, [containerRef, selector, isBottomNotch, ...deps]); }; export default useScrollNotch; diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 7173e35f6..99e631730 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -212,7 +212,9 @@ $color-message-story-mention-to: #74bcff; --vh: 1vh; - --border-radius-modal: 1rem; + --border-radius-button: 1rem; + --border-radius-button-tiny: 0.875rem; + --border-radius-modal: 2rem; --border-radius-default: 0.75rem; --border-radius-default-small: 0.625rem; --border-radius-default-tiny: 0.375rem; diff --git a/src/styles/icons.css b/src/styles/icons.css index 361d17744..5c280a21d 100644 --- a/src/styles/icons.css +++ b/src/styles/icons.css @@ -3,8 +3,8 @@ font-weight: normal; font-style: normal; font-display: block; - src: url("./icons.woff2?cc3fadda4d577575d1b441ec5d6c8994") format("woff2"), -url("./icons.woff?cc3fadda4d577575d1b441ec5d6c8994") format("woff"); + src: url("./icons.woff2?a07bab7d97a4c6540cca18b1d787228b") format("woff2"), +url("./icons.woff?a07bab7d97a4c6540cca18b1d787228b") format("woff"); } .icon-char::before { diff --git a/src/styles/icons.woff b/src/styles/icons.woff index f0e528d00..cdbd4c2e2 100644 Binary files a/src/styles/icons.woff and b/src/styles/icons.woff differ diff --git a/src/styles/icons.woff2 b/src/styles/icons.woff2 index c01b062f8..e993561d9 100644 Binary files a/src/styles/icons.woff2 and b/src/styles/icons.woff2 differ diff --git a/src/styles/index.scss b/src/styles/index.scss index 574473782..e9fda82cc 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -396,6 +396,15 @@ body:not(.is-ios) { } } +.with-bottom-notch { + border-bottom: 1px solid var(--color-borders); + transition: border-color 0.2s ease-in-out; + + &.scrolled-to-end { + border-bottom-color: transparent; + } +} + @keyframes grow-icon { 0% { transform: scale(0.5); diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 798050d01..cd93de1a6 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -2830,6 +2830,9 @@ export interface LangPairWithVariables { 'stars': V; 'peer': V; }; + 'GiftBuyEqualsTo': { + 'stars': V; + }; 'ComposerTitleForwardFrom': { 'users': V; }; diff --git a/src/util/colors.ts b/src/util/colors.ts index 7bf0d6b29..02a790aec 100644 --- a/src/util/colors.ts +++ b/src/util/colors.ts @@ -234,10 +234,9 @@ export function getPatternColor(rgb: Number3) { ? Math.max(0, v * 0.65) : Math.max(0, Math.min(1, 1 - v * 0.65)); - const newRgb = hsl2rgb([h * 360, s, v]); - const mappedRgb = newRgb.map((c) => Math.floor(c * 255)) as Number3; + const newRgb = hsv2rgb([h, s, v]); - return rgba2hex([...mappedRgb, 102]); + return rgba2hex([...newRgb, 102]); } export function int2cssRgba(color: number): string {