Avatar: Update sizes (#5337)
This commit is contained in:
parent
5e4924189a
commit
7b2956cb3f
@ -2,12 +2,14 @@
|
||||
--premium-gradient: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%);
|
||||
--color-user: var(--color-primary);
|
||||
--radius: 50%;
|
||||
--_size: 0px;
|
||||
--_font-size: max(calc(var(--_size) / 2 - 2px), 0.75rem);
|
||||
|
||||
flex: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 3.375rem;
|
||||
height: 3.375rem;
|
||||
width: var(--_size);
|
||||
height: var(--_size);
|
||||
border-radius: var(--radius);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
@ -16,6 +18,17 @@
|
||||
user-select: none;
|
||||
position: relative;
|
||||
|
||||
font-size: var(--_font-size);
|
||||
|
||||
.emoji {
|
||||
width: var(--_font-size);
|
||||
height: var(--_font-size);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: calc(var(--_size) / 2);
|
||||
}
|
||||
|
||||
> .inner {
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius);
|
||||
@ -40,123 +53,6 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
&.size-micro {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
font-size: 0.5rem;
|
||||
|
||||
.emoji {
|
||||
width: 0.5625rem;
|
||||
height: 0.5625rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-tiny {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.emoji {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-mini {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
font-size: 0.75rem;
|
||||
|
||||
.emoji {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-small {
|
||||
width: 2.125rem;
|
||||
height: 2.125rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.emoji {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-small-mobile {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.emoji {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-medium {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
font-size: 1.1875rem;
|
||||
|
||||
.emoji {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-large {
|
||||
font-size: 1.3125rem;
|
||||
|
||||
.emoji {
|
||||
width: 1.3125rem;
|
||||
height: 1.3125rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-giant {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
font-size: 2.5rem;
|
||||
|
||||
.emoji {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-huge {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
font-size: 3rem;
|
||||
|
||||
.emoji {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-jumbo {
|
||||
width: 7.5rem;
|
||||
height: 7.5rem;
|
||||
font-size: 3.5rem;
|
||||
|
||||
.emoji {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.interactive {
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
}
|
||||
@ -173,15 +69,15 @@
|
||||
}
|
||||
|
||||
&.with-story-solid {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
width: calc(var(--_size) - 0.25rem);
|
||||
height: calc(var(--_size) - 0.25rem);
|
||||
margin: 0.1875rem;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
width: calc(var(--_size) + 0.25rem);
|
||||
height: calc(var(--_size) + 0.25rem);
|
||||
left: -0.25rem;
|
||||
top: -0.25rem;
|
||||
border-radius: 50%;
|
||||
@ -191,8 +87,8 @@
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 3.25rem;
|
||||
height: 3.25rem;
|
||||
width: var(--_size);
|
||||
height: var(--_size);
|
||||
left: -0.125rem;
|
||||
top: -0.125rem;
|
||||
border-radius: 50%;
|
||||
@ -200,51 +96,6 @@
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
&.size-tiny {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
|
||||
&::before {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
&::after {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-medium {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
|
||||
&::before {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
&::after {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-small-mobile {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
|
||||
&::before {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
|
||||
&::after {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.online::after {
|
||||
bottom: -0.125rem;
|
||||
right: -0.125rem;
|
||||
|
||||
@ -28,6 +28,7 @@ import {
|
||||
import buildClassName, { createClassNameBuilder } from '../../util/buildClassName';
|
||||
import buildStyle from '../../util/buildStyle';
|
||||
import { getFirstLetters } from '../../util/textFormat';
|
||||
import { REM } from './helpers/mediaDimensions';
|
||||
import { getPeerColorClass } from './helpers/peerColor';
|
||||
import renderText from './helpers/renderText';
|
||||
|
||||
@ -45,8 +46,19 @@ import './Avatar.scss';
|
||||
|
||||
const LOOP_COUNT = 3;
|
||||
|
||||
export const AVATAR_SIZES = {
|
||||
micro: REM,
|
||||
mini: 1.5 * REM,
|
||||
tiny: 2 * REM,
|
||||
small: 2.125 * REM,
|
||||
medium: 2.75 * REM,
|
||||
large: 3.375 * REM,
|
||||
giant: 5.625 * REM,
|
||||
jumbo: 7.5 * REM,
|
||||
};
|
||||
|
||||
export type AvatarSize =
|
||||
'micro' | 'tiny' | 'mini' | 'small' | 'small-mobile' | 'medium' | 'large' | 'giant' | 'huge' | 'jumbo';
|
||||
'micro' | 'mini' | 'tiny' | 'small' | 'medium' | 'large' | 'giant' | 'jumbo' | number;
|
||||
|
||||
const cn = createClassNameBuilder('Avatar');
|
||||
cn.media = cn('media');
|
||||
@ -114,12 +126,14 @@ const Avatar: FC<OwnProps> = ({
|
||||
let imageHash: string | undefined;
|
||||
let videoHash: string | undefined;
|
||||
|
||||
const pxSize = typeof size === 'number' ? size : AVATAR_SIZES[size];
|
||||
|
||||
const shouldLoadVideo = withVideo && photo?.isVideo;
|
||||
|
||||
const shouldFetchBig = size === 'jumbo';
|
||||
const isBig = pxSize >= AVATAR_SIZES.jumbo;
|
||||
if (!isSavedMessages && !isDeleted) {
|
||||
if ((user && !noPersonalPhoto) || chat) {
|
||||
imageHash = getChatAvatarHash(peer as ApiPeer, shouldFetchBig ? 'big' : undefined);
|
||||
imageHash = getChatAvatarHash(peer as ApiPeer, isBig ? 'big' : undefined);
|
||||
} else if (photo) {
|
||||
imageHash = `photo${photo.id}?size=m`;
|
||||
if (photo.isVideo && withVideo) {
|
||||
@ -233,7 +247,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
const customColor = isCustomPeer && peer.customPeerAvatarColor;
|
||||
|
||||
const fullClassName = buildClassName(
|
||||
`Avatar size-${size}`,
|
||||
'Avatar',
|
||||
className,
|
||||
getPeerColorClass(peer),
|
||||
!peer && text && 'hidden-user',
|
||||
@ -279,15 +293,15 @@ const Avatar: FC<OwnProps> = ({
|
||||
data-peer-id={realPeer?.id}
|
||||
data-test-sender-id={IS_TEST ? realPeer?.id : undefined}
|
||||
aria-label={typeof content === 'string' ? author : undefined}
|
||||
style={buildStyle(customColor && `--color-user: ${customColor}`)}
|
||||
style={buildStyle(`--_size: ${pxSize}px;`, customColor && `--color-user: ${customColor}`)}
|
||||
onClick={handleClick}
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
<div className="inner">
|
||||
{typeof content === 'string' ? renderText(content, [size === 'jumbo' ? 'hq_emoji' : 'emoji']) : content}
|
||||
{typeof content === 'string' ? renderText(content, [isBig ? 'hq_emoji' : 'emoji']) : content}
|
||||
</div>
|
||||
{withStory && realPeer?.hasStories && (
|
||||
<AvatarStoryCircle peerId={realPeer.id} size={size} withExtraGap={withStoryGap} />
|
||||
<AvatarStoryCircle peerId={realPeer.id} size={pxSize} withExtraGap={withStoryGap} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,47 +1,11 @@
|
||||
.root {
|
||||
--_size: 0px;
|
||||
--half-size: calc(var(--_size) / 2);
|
||||
--spacing: calc(var(--_size) * 0.4);
|
||||
--spacing-gap: calc(var(--_size) * 0.04);
|
||||
|
||||
display: flex;
|
||||
position: relative;
|
||||
--spacing: calc(var(--size) * 0.4);
|
||||
--spacing-gap: calc(var(--size) * 0.04);
|
||||
--size: 0px;
|
||||
|
||||
--half-size: calc(var(--size) / 2);
|
||||
}
|
||||
|
||||
.size-micro {
|
||||
--size: 1rem;
|
||||
}
|
||||
|
||||
.size-mini {
|
||||
--size: 1.5rem;
|
||||
}
|
||||
|
||||
.size-tiny {
|
||||
--size: 2rem;
|
||||
}
|
||||
|
||||
.size-small {
|
||||
--size: 2.125rem;
|
||||
}
|
||||
|
||||
.size-small-mobile {
|
||||
--size: 2.5rem;
|
||||
}
|
||||
|
||||
.size-medium {
|
||||
--size: 2.75rem;
|
||||
}
|
||||
|
||||
.size-large {
|
||||
--size: 3.375rem;
|
||||
}
|
||||
|
||||
.size-huge {
|
||||
--size: 6.5rem;
|
||||
}
|
||||
|
||||
.size-jumbo {
|
||||
--size: 7.5rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
|
||||
@ -8,7 +8,7 @@ import buildClassName from '../../util/buildClassName';
|
||||
|
||||
import useOldLang from '../../hooks/useOldLang';
|
||||
|
||||
import Avatar from './Avatar';
|
||||
import Avatar, { AVATAR_SIZES } from './Avatar';
|
||||
|
||||
import styles from './AvatarList.module.scss';
|
||||
|
||||
@ -30,6 +30,9 @@ const AvatarList: FC<OwnProps> = ({
|
||||
badgeText,
|
||||
}) => {
|
||||
const lang = useOldLang();
|
||||
|
||||
const pxSize = typeof size === 'number' ? size : AVATAR_SIZES[size];
|
||||
|
||||
const renderingBadgeText = useMemo(() => {
|
||||
if (badgeText) return badgeText;
|
||||
if (!peers?.length || peers.length <= limit) return undefined;
|
||||
@ -38,7 +41,8 @@ const AvatarList: FC<OwnProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={buildClassName(className, styles.root, styles[`size-${size}`])}
|
||||
className={buildClassName(className, styles.root)}
|
||||
style={`--_size: ${pxSize}px;`}
|
||||
dir={lang.isRtl ? 'rtl' : 'ltr'}
|
||||
>
|
||||
{peers?.slice(0, limit).map((peer) => <Avatar size={size} peer={peer} className={styles.avatar} />)}
|
||||
|
||||
@ -5,7 +5,6 @@ import { withGlobal } from '../../global';
|
||||
|
||||
import type { ApiTypeStory } from '../../api/types';
|
||||
import type { ThemeKey } from '../../types';
|
||||
import type { AvatarSize } from './Avatar';
|
||||
|
||||
import { selectPeerStories, selectTheme } from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -17,7 +16,7 @@ interface OwnProps {
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
peerId: string;
|
||||
className?: string;
|
||||
size: AvatarSize;
|
||||
size: number;
|
||||
withExtraGap?: boolean;
|
||||
}
|
||||
|
||||
@ -28,19 +27,6 @@ interface StateProps {
|
||||
appTheme: ThemeKey;
|
||||
}
|
||||
|
||||
const SIZES: Record<AvatarSize, number> = {
|
||||
micro: 1.125 * REM,
|
||||
tiny: 2.125 * REM,
|
||||
mini: 1.625 * REM,
|
||||
small: 2.25 * REM,
|
||||
'small-mobile': 2.625 * REM,
|
||||
medium: 2.875 * REM,
|
||||
large: 3.5 * REM,
|
||||
giant: 5.125 * REM,
|
||||
huge: 6.125 * REM,
|
||||
jumbo: 7.625 * REM,
|
||||
};
|
||||
|
||||
const BLUE = ['#34C578', '#3CA3F3'];
|
||||
const GREEN = ['#C9EB38', '#09C167'];
|
||||
const PURPLE = ['#A667FF', '#55A5FF'];
|
||||
@ -50,6 +36,7 @@ const STROKE_WIDTH = 0.125 * REM;
|
||||
const STROKE_WIDTH_READ = 0.0625 * REM;
|
||||
const GAP_PERCENT = 2;
|
||||
const SEGMENTS_MAX = 45; // More than this breaks rendering in Safari and Chrome
|
||||
const LARGE_AVATAR_SIZE = 3.5 * REM;
|
||||
|
||||
const GAP_PERCENT_EXTRA = 10;
|
||||
const EXTRA_GAP_ANGLE = Math.PI / 4;
|
||||
@ -58,7 +45,7 @@ const EXTRA_GAP_START = EXTRA_GAP_ANGLE - EXTRA_GAP_SIZE / 2;
|
||||
const EXTRA_GAP_END = EXTRA_GAP_ANGLE + EXTRA_GAP_SIZE / 2;
|
||||
|
||||
function AvatarStoryCircle({
|
||||
size = 'large',
|
||||
size,
|
||||
className,
|
||||
peerStories,
|
||||
storyIds,
|
||||
@ -71,6 +58,8 @@ function AvatarStoryCircle({
|
||||
|
||||
const dpr = useDevicePixelRatio();
|
||||
|
||||
const adaptedSize = size + STROKE_WIDTH;
|
||||
|
||||
const values = useMemo(() => {
|
||||
return (storyIds || []).reduce((acc, id) => {
|
||||
acc.total += 1;
|
||||
@ -104,7 +93,7 @@ function AvatarStoryCircle({
|
||||
|
||||
drawGradientCircle({
|
||||
canvas: ref.current,
|
||||
size: SIZES[size] * dpr,
|
||||
size: adaptedSize * dpr,
|
||||
segmentsCount: values.total,
|
||||
color: isCloseFriend ? 'green' : 'blue',
|
||||
readSegmentsCount: values.read,
|
||||
@ -112,19 +101,17 @@ function AvatarStoryCircle({
|
||||
readSegmentColor: appTheme === 'dark' ? DARK_GRAY : GRAY,
|
||||
dpr,
|
||||
});
|
||||
}, [appTheme, isCloseFriend, size, values.read, values.total, withExtraGap, dpr]);
|
||||
}, [appTheme, isCloseFriend, adaptedSize, values.read, values.total, withExtraGap, dpr]);
|
||||
|
||||
if (!values.total) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const maxSize = SIZES[size];
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={ref}
|
||||
className={buildClassName('story-circle', size, className)}
|
||||
style={`max-width: ${maxSize}px; max-height: ${maxSize}px;`}
|
||||
className={buildClassName('story-circle', className)}
|
||||
style={`max-width: ${adaptedSize}px; max-height: ${adaptedSize}px;`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -166,7 +153,7 @@ export function drawGradientCircle({
|
||||
segmentsCount = SEGMENTS_MAX;
|
||||
}
|
||||
|
||||
const strokeModifier = Math.max(Math.max(size - SIZES.large * dpr, 0) / dpr / REM / 1.5, 1) * dpr;
|
||||
const strokeModifier = Math.max(Math.max(size - LARGE_AVATAR_SIZE * dpr, 0) / dpr / REM / 1.5, 1) * dpr;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
|
||||
@ -3,6 +3,7 @@ import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, getGlobal } from '../../../global';
|
||||
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import type { CustomPeer } from '../../../types';
|
||||
|
||||
import { ARCHIVED_FOLDER_ID } from '../../../config';
|
||||
import { getChatTitle } from '../../../global/helpers';
|
||||
@ -14,6 +15,8 @@ import renderText from '../../common/helpers/renderText';
|
||||
import { useFolderManagerForOrderedIds, useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import Avatar from '../../common/Avatar';
|
||||
import Icon from '../../common/icons/Icon';
|
||||
import Badge from '../../ui/Badge';
|
||||
import ListItem, { type MenuItemContextAction } from '../../ui/ListItem';
|
||||
|
||||
@ -26,6 +29,12 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
const PREVIEW_SLICE = 5;
|
||||
const ARCHIVE_CUSTOM_PEER: CustomPeer = {
|
||||
isCustomPeer: true,
|
||||
title: 'Archived Chats',
|
||||
avatarIcon: 'archive-filled',
|
||||
customPeerAvatarColor: '#9EAAB5',
|
||||
};
|
||||
|
||||
const Archive: FC<OwnProps> = ({
|
||||
archiveSettings,
|
||||
@ -103,7 +112,7 @@ const Archive: FC<OwnProps> = ({
|
||||
<div className="info-row">
|
||||
<div className={buildClassName('title', styles.title)}>
|
||||
<h3 dir="auto" className={buildClassName(styles.name, 'fullName')}>
|
||||
<i className={buildClassName(styles.icon, 'icon', 'icon-archive-filled')} />
|
||||
<Icon name="archive-filled" className={styles.icon} />
|
||||
{lang('ArchivedChats')}
|
||||
</h3>
|
||||
</div>
|
||||
@ -120,9 +129,7 @@ const Archive: FC<OwnProps> = ({
|
||||
return (
|
||||
<>
|
||||
<div className={buildClassName('status', styles.avatarWrapper)}>
|
||||
<div className={buildClassName('Avatar', styles.avatar)}>
|
||||
<i className="icon icon-archive-filled" />
|
||||
</div>
|
||||
<Avatar peer={ARCHIVE_CUSTOM_PEER} />
|
||||
</div>
|
||||
<div className={buildClassName(styles.info, 'info')}>
|
||||
<div className="info-row">
|
||||
|
||||
@ -11,7 +11,7 @@ import useOldLang from '../../../../hooks/useOldLang';
|
||||
import useScrolledState from '../../../../hooks/useScrolledState';
|
||||
import useDevicePixelRatio from '../../../../hooks/window/useDevicePixelRatio';
|
||||
|
||||
import Avatar from '../../../common/Avatar';
|
||||
import Avatar, { AVATAR_SIZES } from '../../../common/Avatar';
|
||||
import { drawGradientCircle } from '../../../common/AvatarStoryCircle';
|
||||
import PremiumFeatureItem from '../PremiumFeatureItem';
|
||||
|
||||
@ -53,7 +53,7 @@ const STORY_FEATURE_ICONS = {
|
||||
|
||||
const STORY_FEATURE_ORDER = Object.keys(STORY_FEATURE_TITLES) as (keyof typeof STORY_FEATURE_TITLES)[];
|
||||
|
||||
const CIRCLE_SIZE = 5.25 * REM;
|
||||
const CIRCLE_SIZE = AVATAR_SIZES.giant + 0.25 * REM;
|
||||
const CIRCLE_SEGMENTS = 8;
|
||||
const CIRCLE_READ_SEGMENTS = 0;
|
||||
|
||||
|
||||
@ -979,7 +979,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
size={isMobile ? 'small-mobile' : 'small'}
|
||||
size="small"
|
||||
peer={avatarPeer}
|
||||
text={hiddenName}
|
||||
onClick={avatarPeer ? handleAvatarClick : undefined}
|
||||
|
||||
@ -51,6 +51,8 @@ type StateProps = {
|
||||
user?: ApiUser;
|
||||
};
|
||||
|
||||
const AVATAR_SIZE = 100;
|
||||
|
||||
const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
||||
modal,
|
||||
starGiftsById,
|
||||
@ -234,7 +236,7 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
||||
<div className={buildClassName(styles.main, 'custom-scroll')} onScroll={handleScroll}>
|
||||
<div className={styles.avatars}>
|
||||
<Avatar
|
||||
size="huge"
|
||||
size={AVATAR_SIZE}
|
||||
peer={user}
|
||||
/>
|
||||
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
.root :global(.modal-content) {
|
||||
padding: 0;
|
||||
@use "../../../../styles/mixins";
|
||||
|
||||
.modalDialog :global(.modal-dialog) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.root :global(.modal-dialog) {
|
||||
overflow: hidden;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
@ -48,6 +53,8 @@
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 0.5rem;
|
||||
|
||||
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
||||
}
|
||||
|
||||
.header {
|
||||
@ -85,6 +92,7 @@
|
||||
|
||||
.avatar {
|
||||
margin: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.center {
|
||||
|
||||
@ -41,6 +41,8 @@ type StateProps = {
|
||||
user?: ApiUser;
|
||||
};
|
||||
|
||||
const AVATAR_SIZE = 100;
|
||||
|
||||
const StarsGiftModal: FC<OwnProps & StateProps> = ({
|
||||
modal,
|
||||
user,
|
||||
@ -136,20 +138,24 @@ const StarsGiftModal: FC<OwnProps & StateProps> = ({
|
||||
const parts = text.split('{link}');
|
||||
return [
|
||||
parts[0],
|
||||
<SafeLink url={oldLang('StarsTOSLink')} text={oldLang('lng_credits_summary_options_about_link')} />,
|
||||
<SafeLink
|
||||
url={oldLang('StarsTOSLink')}
|
||||
text={oldLang('lng_credits_summary_options_about_link')}
|
||||
/>,
|
||||
parts[1],
|
||||
];
|
||||
}, [oldLang]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={buildClassName(styles.modalDialog, styles.root)}
|
||||
className={buildClassName(styles.modalDialog)}
|
||||
contentClassName={styles.content}
|
||||
dialogRef={dialogRef}
|
||||
isSlim
|
||||
onClose={handleClose}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
<div className={styles.main} onScroll={handleScroll}>
|
||||
<div className={buildClassName(styles.main, 'custom-scroll')} onScroll={handleScroll}>
|
||||
<Button
|
||||
round
|
||||
size="smaller"
|
||||
@ -170,7 +176,7 @@ const StarsGiftModal: FC<OwnProps & StateProps> = ({
|
||||
{user ? (
|
||||
<>
|
||||
<Avatar
|
||||
size="huge"
|
||||
size={AVATAR_SIZE}
|
||||
peer={user}
|
||||
className={styles.avatar}
|
||||
/>
|
||||
|
||||
@ -8,15 +8,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.25rem;
|
||||
margin-block: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.starsHeader {
|
||||
gap: normal;
|
||||
}
|
||||
|
||||
.title, .amount {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@ -24,7 +20,9 @@
|
||||
.title {
|
||||
text-align: center;
|
||||
text-wrap: balance;
|
||||
font-size: 1.75rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
margin-top: 1rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
@ -44,8 +42,8 @@
|
||||
|
||||
.starsBackground {
|
||||
position: absolute;
|
||||
height: 8rem;
|
||||
top: 0;
|
||||
height: 7rem;
|
||||
top: -0.75rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: -1;
|
||||
@ -60,10 +58,10 @@
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
font-size: 2rem;
|
||||
font-size: 1.75rem;
|
||||
z-index: 1;
|
||||
|
||||
@include mixins.filter-outline(2px, var(--color-background));
|
||||
@include mixins.filter-outline(1px, var(--color-background));
|
||||
}
|
||||
|
||||
.amountStar {
|
||||
|
||||
@ -125,9 +125,9 @@ const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
|
||||
const isBotSubscription = isApiPeerUser(peer);
|
||||
|
||||
const header = (
|
||||
<div className={buildClassName(styles.header, styles.starsHeader)}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.avatarWrapper}>
|
||||
<Avatar peer={!photo ? peer : undefined} webPhoto={photo} size="jumbo" />
|
||||
<Avatar peer={!photo ? peer : undefined} webPhoto={photo} size="giant" />
|
||||
<StarIcon className={styles.subscriptionStar} type="gold" size="adaptive" />
|
||||
</div>
|
||||
<img
|
||||
|
||||
@ -16,15 +16,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.25rem;
|
||||
margin-block: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.starsHeader {
|
||||
gap: normal;
|
||||
}
|
||||
|
||||
.amount {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
@ -42,7 +38,8 @@
|
||||
text-wrap: balance;
|
||||
font-size: 1.5rem;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
margin: 0.5rem 0 0.25rem;
|
||||
margin-top: 1rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.tid {
|
||||
@ -69,8 +66,8 @@
|
||||
|
||||
.starsBackground {
|
||||
position: absolute;
|
||||
height: 8rem;
|
||||
top: 0;
|
||||
height: 7rem;
|
||||
top: -0.75rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: -1;
|
||||
|
||||
@ -104,7 +104,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
||||
const avatarPeer = !photo ? (peer || customPeer) : undefined;
|
||||
|
||||
const header = (
|
||||
<div className={buildClassName(styles.header, styles.starsHeader)}>
|
||||
<div className={styles.header}>
|
||||
{media && (
|
||||
<PaidMediaThumb
|
||||
className={buildClassName(styles.mediaPreview, 'transaction-media-preview')}
|
||||
@ -124,12 +124,14 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
||||
{shouldDisplayAvatar && (
|
||||
<Avatar peer={avatarPeer} webPhoto={photo} size="giant" />
|
||||
)}
|
||||
<img
|
||||
className={buildClassName(styles.starsBackground)}
|
||||
src={StarsBackground}
|
||||
alt=""
|
||||
draggable={false}
|
||||
/>
|
||||
{!topSticker && (
|
||||
<img
|
||||
className={buildClassName(styles.starsBackground)}
|
||||
src={StarsBackground}
|
||||
alt=""
|
||||
draggable={false}
|
||||
/>
|
||||
)}
|
||||
{title && <h1 className={styles.title}>{title}</h1>}
|
||||
<p className={styles.description}>{description}</p>
|
||||
<p className={styles.amount}>
|
||||
|
||||
@ -80,7 +80,7 @@ function renderSummary(lang: OldLangFn, chat: ApiChat, blobUrl?: string) {
|
||||
) : (
|
||||
<Avatar
|
||||
peer={chat}
|
||||
size="small-mobile"
|
||||
size="small"
|
||||
className={styles.image}
|
||||
withStorySolid
|
||||
forceUnreadStorySolid
|
||||
|
||||
@ -217,8 +217,8 @@
|
||||
|
||||
.modal-absolute-close-button {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
left: 0.5rem;
|
||||
top: 0.375rem;
|
||||
left: 0.375rem;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,21 +133,26 @@ const Modal: FC<OwnProps> = ({
|
||||
}
|
||||
|
||||
if (!title && !withCloseButton) return undefined;
|
||||
const closeButton = (
|
||||
<Button
|
||||
className={buildClassName(hasAbsoluteCloseButton && 'modal-absolute-close-button')}
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Close')}
|
||||
onClick={onClose}
|
||||
>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
if (hasAbsoluteCloseButton) {
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={buildClassName('modal-header', headerClassName)}>
|
||||
{withCloseButton && (
|
||||
<Button
|
||||
className={buildClassName(hasAbsoluteCloseButton && 'modal-absolute-close-button')}
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Close')}
|
||||
onClick={onClose}
|
||||
>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
)}
|
||||
{withCloseButton && closeButton}
|
||||
<div className="modal-title">{title}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user