Management: Fix layout in management boost list (#4645)

This commit is contained in:
Alexander Zinchuk 2024-06-12 18:11:18 +02:00
parent 1997008ba5
commit 86ddd5bf0b
4 changed files with 89 additions and 42 deletions

View File

@ -12,17 +12,13 @@
} }
.badgeContainer { .badgeContainer {
--min-shift: calc(10% + var(--digits-count, 1) * 0.4ch);
--max-shift: calc(85% - var(--digits-count, 1) * 0.4ch);
--shift-x: calc(clamp(var(--min-shift), var(--percent), var(--max-shift)) - 50%);
display: flex; display: flex;
justify-content: center; justify-content: center;
position: absolute; position: absolute;
top: -1.5rem; top: -1.5rem;
left: 0; left: 0;
right: 0; right: 0;
transform: translate(var(--shift-x), -20px); transform: translate(calc(var(--shift-x) * 100% - 50%), -20px);
transition: transform 0.2s ease-in-out; transition: transform 0.2s ease-in-out;
animation: slide-in 0.5s ease-in-out; animation: slide-in 0.5s ease-in-out;
} }
@ -33,7 +29,7 @@
} }
to { to {
transform: translate(var(--shift-x), -20px); transform: translate(calc(var(--shift-x) * 100% - 50%), -20px);
} }
} }
@ -75,7 +71,7 @@
.floating-badge-triangle { .floating-badge-triangle {
display: inline-block; display: inline-block;
position: absolute; position: absolute;
bottom: -5px; bottom: -4px;
left: calc(var(--tail-position, 0.5) * 100%); left: calc(var(--tail-position, 0.5) * 100%);
transform: translateX(-50%); transform: translateX(-50%);
} }

View File

@ -1,5 +1,7 @@
import type { FC } from '../../lib/teact/teact'; import type { FC } from '../../lib/teact/teact';
import React, { memo } from '../../lib/teact/teact'; import React, {
memo, useEffect, useRef, useState,
} from '../../lib/teact/teact';
import type { IconName } from '../../types/icons'; import type { IconName } from '../../types/icons';
@ -7,6 +9,7 @@ import buildClassName from '../../util/buildClassName';
import buildStyle from '../../util/buildStyle'; import buildStyle from '../../util/buildStyle';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
import useResizeObserver from '../../hooks/useResizeObserver';
import Icon from './Icon'; import Icon from './Icon';
@ -21,8 +24,6 @@ type OwnProps = {
className?: string; className?: string;
}; };
const PROGRESS_LOCK = 0.1;
const LimitPreview: FC<OwnProps> = ({ const LimitPreview: FC<OwnProps> = ({
leftText, leftText,
rightText, rightText,
@ -32,15 +33,48 @@ const LimitPreview: FC<OwnProps> = ({
className, className,
}) => { }) => {
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null
const floatingBadgeRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line no-null/no-null
const parentContainerRef = useRef<HTMLDivElement>(null);
const [shiftX, setShiftX] = useState(0);
const [tailPosition, setTailPosition] = useState(0);
const updateBadgePosition = () => {
if (floatingBadgeRef.current && parentContainerRef.current && progress !== undefined) {
const badgeWidth = floatingBadgeRef.current.offsetWidth;
const parentWidth = parentContainerRef.current.offsetWidth;
const minShift = badgeWidth / 2;
const maxShift = parentWidth - badgeWidth / 2;
const currentShift = progress * parentWidth;
const safeShift = Math.max(minShift, Math.min(currentShift, maxShift));
setShiftX(safeShift / parentWidth);
let newTailPosition;
if (currentShift < minShift) {
newTailPosition = (progress * parentWidth) / (minShift * 2);
} else if (currentShift > maxShift) {
const progressMapped = (progress - (maxShift / parentWidth)) / (1 - maxShift / parentWidth);
newTailPosition = 0.5 + (progressMapped * 0.4);
} else {
newTailPosition = 0.5;
}
setTailPosition(newTailPosition);
}
};
useEffect(updateBadgePosition, [progress]);
useResizeObserver(parentContainerRef, updateBadgePosition);
const hasFloatingBadge = Boolean(floatingBadgeIcon || floatingBadgeText); const hasFloatingBadge = Boolean(floatingBadgeIcon || floatingBadgeText);
const isProgressFull = Boolean(progress) && progress > 0.99; const isProgressFull = Boolean(progress) && progress > 0.99;
const digitsCount = floatingBadgeText?.length;
const tailPosition = progress && (progress < PROGRESS_LOCK ? 0 : progress > 1 - PROGRESS_LOCK ? 1 : 0.5);
return ( return (
<div <div
ref={parentContainerRef}
className={buildClassName( className={buildClassName(
styles.root, styles.root,
hasFloatingBadge && styles.withBadge, hasFloatingBadge && styles.withBadge,
@ -49,13 +83,13 @@ const LimitPreview: FC<OwnProps> = ({
style={buildStyle( style={buildStyle(
progress !== undefined && `--progress: ${progress}`, progress !== undefined && `--progress: ${progress}`,
tailPosition !== undefined && `--tail-position: ${tailPosition}`, tailPosition !== undefined && `--tail-position: ${tailPosition}`,
digitsCount !== undefined && `--digits-count: ${digitsCount}`, `--shift-x: ${shiftX}`,
)} )}
> >
{hasFloatingBadge && ( {hasFloatingBadge && (
<div className={styles.badgeContainer}> <div className={styles.badgeContainer}>
<div className={styles.floatingBadgeWrapper}> <div className={styles.floatingBadgeWrapper}>
<div className={styles.floatingBadge}> <div className={styles.floatingBadge} ref={floatingBadgeRef}>
{floatingBadgeIcon && <Icon name={floatingBadgeIcon} className={styles.floatingBadgeIcon} />} {floatingBadgeIcon && <Icon name={floatingBadgeIcon} className={styles.floatingBadgeIcon} />}
{floatingBadgeText && ( {floatingBadgeText && (
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}> <div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>

View File

@ -44,7 +44,7 @@
.status { .status {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1rem; gap: 0.5rem;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
} }
@ -111,3 +111,8 @@
.giveawayButton { .giveawayButton {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.giveawayIcon {
width: 2.75rem;
height: 2.75rem;
}

View File

@ -177,7 +177,7 @@ const BoostStatistics = ({
> >
<Icon name="gift" className={styles.floatingBadgeIcon} /> <Icon name="gift" className={styles.floatingBadgeIcon} />
<div className={styles.floatingBadgeValue}>{lang(boost.isFromGiveaway <div className={styles.floatingBadgeValue}>{lang(boost.isFromGiveaway
? 'lng_prizes_results_link' : 'BoostingGift')} ? 'BoostingGiveaway' : 'BoostingGift')}
</div> </div>
</div> </div>
</div> </div>
@ -218,11 +218,13 @@ const BoostStatistics = ({
); );
}); });
const handleGiveawayClick = useLastCallback(() => { const handleGiveawayClick = useLastCallback((e) => {
e.preventDefault();
openGiveawayModal({ chatId }); openGiveawayModal({ chatId });
}); });
const handleLoadMore = useLastCallback(() => { const handleLoadMore = useLastCallback((e) => {
e.preventDefault();
loadMoreBoosters({ isGifts: tabType === 'giftedBoostList' }); loadMoreBoosters({ isGifts: tabType === 'giftedBoostList' });
}); });
@ -243,7 +245,7 @@ const BoostStatistics = ({
} }
return ( return (
<div className={styles.content}> <div className={styles.section}>
{listToRender?.map((boost) => renderBoostList(boost))} {listToRender?.map((boost) => renderBoostList(boost))}
</div> </div>
); );
@ -278,7 +280,11 @@ const BoostStatistics = ({
> >
<div className={buildClassName(styles.status, 'status-clickable')}> <div className={buildClassName(styles.status, 'status-clickable')}>
<div> <div>
<img src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months]} alt="Giveaway" /> <img
src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months]}
className={styles.giveawayIcon}
alt={lang('Giveaway')}
/>
</div> </div>
<div className={styles.info}> <div className={styles.info}>
<h3> <h3>
@ -303,13 +309,12 @@ const BoostStatistics = ({
<p className="text-muted hint" key="links-hint">{lang('BoostingSelectPaidGiveaway')}</p> <p className="text-muted hint" key="links-hint">{lang('BoostingSelectPaidGiveaway')}</p>
</div> </div>
)} )}
<div className={styles.section}> <div>
{shouldDisplayGiftList ? ( {shouldDisplayGiftList ? (
<div <div
className={styles.boostSection} className={buildClassName(styles.boostSection, styles.content)}
> >
<Transition <Transition
key={activeKey}
ref={transitionRef} ref={transitionRef}
name={lang.isRtl ? 'slideOptimizedRtl' : 'slideOptimized'} name={lang.isRtl ? 'slideOptimizedRtl' : 'slideOptimized'}
activeKey={activeKey} activeKey={activeKey}
@ -322,7 +327,7 @@ const BoostStatistics = ({
<TabList big activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={setActiveTab} /> <TabList big activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={setActiveTab} />
</div> </div>
) : ( ) : (
<> <div className={styles.section}>
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}> <h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
{lang('BoostingBoostsCount', boostStatistics?.boosts?.count)} {lang('BoostingBoostsCount', boostStatistics?.boosts?.count)}
</h4> </h4>
@ -331,28 +336,35 @@ const BoostStatistics = ({
</div> </div>
)} )}
{boostStatistics?.boosts?.list?.map((boost) => renderBoostList(boost))} {boostStatistics?.boosts?.list?.map((boost) => renderBoostList(boost))}
</> </div>
)}
{Boolean(boostersToLoadCount) && (
<ListItem
key="load-more"
className={styles.showMore}
disabled={boostStatistics?.isLoadingBoosters}
onClick={handleLoadMore}
>
{boostStatistics?.isLoadingBoosters ? (
<Spinner className={styles.loadMoreSpinner} />
) : (
<Icon name="down" className={styles.down} />
)}
{lang('ShowVotes', boostersToLoadCount, 'i')}
</ListItem>
)} )}
<div className={styles.section}>
{Boolean(boostersToLoadCount) && (
<ListItem
key="load-more"
className={styles.showMore}
disabled={boostStatistics?.isLoadingBoosters}
onClick={handleLoadMore}
>
{boostStatistics?.isLoadingBoosters ? (
<Spinner className={styles.loadMoreSpinner} />
) : (
<Icon name="down" className={styles.down} />
)}
{lang('ShowVotes', boostersToLoadCount, 'i')}
</ListItem>
)}
</div>
</div> </div>
<LinkField className={styles.section} link={status!.boostUrl} withShare title={lang('LinkForBoosting')} /> <LinkField className={styles.section} link={status!.boostUrl} withShare title={lang('LinkForBoosting')} />
{isGiveawayAvailable && ( {isGiveawayAvailable && (
<div className={styles.section}> <div className={styles.section}>
<ListItem icon="gift" ripple onClick={handleGiveawayClick} className={styles.giveawayButton}> <ListItem
key="load-more"
icon="gift"
onClick={handleGiveawayClick}
className={styles.giveawayButton}
>
{lang('BoostingGetBoostsViaGifts')} {lang('BoostingGetBoostsViaGifts')}
</ListItem> </ListItem>
<p className="text-muted hint" key="links-hint">{lang( <p className="text-muted hint" key="links-hint">{lang(