- {floatingBadgeIcon &&
}
- {floatingBadgeText && (
-
- {floatingBadgeText}
+
+
+
+ {floatingBadgeIcon &&
}
+ {floatingBadgeText && (
+
+ {floatingBadgeText}
+
+ )}
- )}
+
-
)}
- {leftText}
+ {displayLeftText}
- {rightText}
+ {displayRightText}
-
-
- {leftText}
-
-
- {rightText}
-
+
+
+ {renderProgressLayer(
+ true,
+ positiveProgress,
+ buildClassName(hideMainLayer && styles.hidden),
+ disableMainProgressTransition,
+ )}
+
+ {renderProgressLayer(
+ false,
+ negativeProgress,
+ buildClassName(hideMainLayer && styles.hidden),
+ disableMainProgressTransition,
+ )}
+
+ {renderProgressLayer(
+ !isNegative,
+ layerProgress,
+ buildClassName(
+ isNegative ? styles.negativeLayer : styles.positiveLayer,
+ showLayer && styles.show,
+ ),
+ disableLayerProgressTransition,
+ )}
);
diff --git a/src/components/common/ProfileInfo.module.scss b/src/components/common/ProfileInfo.module.scss
index e05271164..6a5be5096 100644
--- a/src/components/common/ProfileInfo.module.scss
+++ b/src/components/common/ProfileInfo.module.scss
@@ -191,11 +191,64 @@
font-size: 0.875rem;
}
-.user-status {
+.userRatingNegativeWrapper,
+.userRatingWrapper {
+ pointer-events: all;
+ cursor: pointer;
+
+ position: relative;
+ z-index: 1;
+
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+
+ margin-right: 0.25rem;
+
+ font-size: 1rem;
+ color: #000000;
+}
+
+.userRatingWrapper {
+ width: 1rem;
+ font-size: 1.5rem;
+}
+
+.ratingNegativeIcon {
+ pointer-events: none;
+ font-size: 1rem;
+ color: var(--color-white);
+
+ -webkit-text-stroke: 1px #000000;
+}
+
+.ratingIcon {
+ pointer-events: none;
+ color: var(--color-white);
+
+ -webkit-text-stroke: 1px #000000;
+}
+
+.ratingLevel {
+ pointer-events: none;
+
+ position: absolute;
+ z-index: 1;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+
+ font-size: 0.625rem;
+ font-weight: var(--font-weight-bold);
+ line-height: 1;
+ color: #000000;
+}
+
+.userStatus {
opacity: 0.5;
}
-.get-status {
+.getStatus {
--blured-background-color: #c4c9cc42;
pointer-events: all;
diff --git a/src/components/common/ProfileInfo.tsx b/src/components/common/ProfileInfo.tsx
index e740047f0..7eccc6d7c 100644
--- a/src/components/common/ProfileInfo.tsx
+++ b/src/components/common/ProfileInfo.tsx
@@ -3,9 +3,10 @@ import { memo, useEffect, useState } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global';
import type {
- ApiChat, ApiPeerPhotos, ApiSticker, ApiTopic, ApiUser, ApiUserStatus,
+ ApiChat, ApiPeerPhotos, ApiSticker, ApiTopic, ApiUser, ApiUserFullInfo, ApiUserStatus,
} from '../../api/types';
import type { AnimationLevel } from '../../types';
+import type { IconName } from '../../types/icons';
import { MediaViewerOrigin } from '../../types';
import {
@@ -20,6 +21,7 @@ import {
selectThreadMessagesCount,
selectTopic,
selectUser,
+ selectUserFullInfo,
selectUserStatus,
} from '../../global/selectors';
import { selectSharedSettings } from '../../global/selectors/sharedState.ts';
@@ -40,12 +42,15 @@ import usePhotosPreload from './hooks/usePhotosPreload';
import Transition from '../ui/Transition';
import Avatar from './Avatar';
import FullNameTitle from './FullNameTitle';
+import Icon from './icons/Icon';
import ProfilePhoto from './ProfilePhoto';
import TopicIcon from './TopicIcon';
import './ProfileInfo.scss';
import styles from './ProfileInfo.module.scss';
+const MAX_LEVEL_ICON = 90;
+
type OwnProps = {
peerId: string;
forceShowSelf?: boolean;
@@ -56,6 +61,7 @@ type OwnProps = {
type StateProps =
{
user?: ApiUser;
+ userFullInfo?: ApiUserFullInfo;
userStatus?: ApiUserStatus;
chat?: ApiChat;
mediaIndex?: number;
@@ -78,6 +84,7 @@ const ProfileInfo: FC
= ({
forceShowSelf,
canPlayVideo,
user,
+ userFullInfo,
userStatus,
chat,
mediaIndex,
@@ -98,6 +105,7 @@ const ProfileInfo: FC = ({
openPrivacySettingsNoticeModal,
loadMoreProfilePhotos,
openUniqueGiftBySlug,
+ openProfileRatingModal,
} = getActions();
const oldLang = useOldLang();
@@ -182,6 +190,12 @@ const ProfileInfo: FC = ({
openPrivacySettingsNoticeModal({ chatId: chat!.id, isReadDate: false });
});
+ const handleRatingClick = useLastCallback((level: number) => {
+ if (user) {
+ openProfileRatingModal({ userId: user.id, level });
+ }
+ });
+
function handleSelectFallbackPhoto() {
if (!isFirst) return;
setHasSlideAnimation(true);
@@ -268,6 +282,43 @@ const ProfileInfo: FC = ({
);
}
+ function renderUserRating() {
+ if (!userFullInfo?.starsRating) return undefined;
+
+ const level = userFullInfo.starsRating.level;
+ const isNegative = level < 0;
+
+ const onRatingClick = () => handleRatingClick(level);
+
+ if (isNegative) {
+ return (
+
+
+ !
+
+ );
+ }
+
+ const safeLevel = Math.max(level, 1);
+ const iconLevel = Math.min(safeLevel, MAX_LEVEL_ICON);
+ const iconName = (iconLevel < 10
+ ? `rating-icons-level${iconLevel}`
+ : `rating-icons-level${Math.floor(iconLevel / 10) * 10}`) as IconName;
+
+ return (
+
+
+ {level}
+
+ );
+ }
+
function renderStatus() {
const isAnonymousForwards = isAnonymousForwardsChat(peerId);
const isSystemBotChat = isSystemBot(peerId);
@@ -290,6 +341,7 @@ const ProfileInfo: FC = ({
isUserOnline(user, userStatus) && 'online',
)}
>
+ {renderUserRating()}
{getUserStatus(oldLang, user, userStatus)}
@@ -400,6 +452,7 @@ const ProfileInfo: FC = ({
export default memo(withGlobal(
(global, { peerId }): StateProps => {
const user = selectUser(global, peerId);
+ const userFullInfo = user ? selectUserFullInfo(global, peerId) : undefined;
const userStatus = selectUserStatus(global, peerId);
const chat = selectChat(global, peerId);
const profilePhotos = selectPeerPhotos(global, peerId);
@@ -415,6 +468,7 @@ export default memo(withGlobal(
return {
user,
+ userFullInfo,
userStatus,
chat,
mediaIndex,
diff --git a/src/components/modals/ModalContainer.tsx b/src/components/modals/ModalContainer.tsx
index ba383a60a..c632c7994 100644
--- a/src/components/modals/ModalContainer.tsx
+++ b/src/components/modals/ModalContainer.tsx
@@ -35,6 +35,7 @@ import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
import PaidReactionModal from './paidReaction/PaidReactionModal.async';
import PreparedMessageModal from './preparedMessage/PreparedMessageModal.async';
import PriceConfirmModal from './priceConfirm/PriceConfirmModal.async';
+import ProfileRatingModal from './profileRating/ProfileRatingModal.async';
import ReportAdModal from './reportAd/ReportAdModal.async';
import ReportModal from './reportModal/ReportModal.async';
import SharePreparedMessageModal from './sharePreparedMessage/SharePreparedMessageModal.async';
@@ -93,7 +94,8 @@ type ModalKey = keyof Pick;
type StateProps = {
@@ -151,6 +153,7 @@ const MODALS: ModalRegistry = {
isFrozenAccountModalOpen: FrozenAccountModal,
deleteAccountModal: DeleteAccountModal,
isAgeVerificationModalOpen: AgeVerificationModal,
+ profileRatingModal: ProfileRatingModal,
};
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
const MODAL_ENTRIES = Object.entries(MODALS) as Entries;
diff --git a/src/components/modals/profileRating/ProfileRatingModal.async.tsx b/src/components/modals/profileRating/ProfileRatingModal.async.tsx
new file mode 100644
index 000000000..c57cd57a6
--- /dev/null
+++ b/src/components/modals/profileRating/ProfileRatingModal.async.tsx
@@ -0,0 +1,16 @@
+import type { FC } from '../../../lib/teact/teact';
+
+import type { OwnProps } from './ProfileRatingModal';
+
+import { Bundles } from '../../../util/moduleLoader';
+
+import useModuleLoader from '../../../hooks/useModuleLoader';
+
+const ProfileRatingModalAsync: FC = (props) => {
+ const { modal } = props;
+ const ProfileRatingModal = useModuleLoader(Bundles.Extra, 'ProfileRatingModal', !modal);
+
+ return ProfileRatingModal ? : undefined;
+};
+
+export default ProfileRatingModalAsync;
diff --git a/src/components/modals/profileRating/ProfileRatingModal.module.scss b/src/components/modals/profileRating/ProfileRatingModal.module.scss
new file mode 100644
index 000000000..3fa583751
--- /dev/null
+++ b/src/components/modals/profileRating/ProfileRatingModal.module.scss
@@ -0,0 +1,91 @@
+.header {
+ width: 100%;
+ padding-top: 0;
+ padding-bottom: 1rem;
+ text-align: center;
+}
+
+.description {
+ margin: 0;
+ padding-inline: 1rem;
+ line-height: 1.25;
+}
+
+.descriptionPreview {
+ margin-bottom: 1rem;
+ font-size: 0.875rem;
+ line-height: 1.25;
+ color: var(--color-text-secondary);
+
+ p {
+ margin-bottom: 0;
+ margin-inline: 1rem;
+ }
+}
+
+.previewLink,
+.backLink {
+ cursor: pointer;
+ color: var(--color-primary);
+ opacity: 1;
+ transition: opacity 0.15s;
+
+ &:hover {
+ opacity: 0.75;
+ }
+}
+
+.title {
+ margin-bottom: 4.75rem;
+ font-size: 1.5rem;
+ font-weight: var(--font-weight-medium);
+ line-height: 1.5rem;
+}
+
+.ratingProgress {
+ margin: 0 auto;
+ margin-bottom: 0.75rem;
+
+ &.withPreview {
+ margin-bottom: 0.5rem;
+ }
+}
+
+.subtitle {
+ line-height: 1.25;
+}
+
+.footer {
+ display: flex;
+ align-self: stretch;
+ margin-top: 1rem;
+}
+
+.badge {
+ transform: translateY(-1px);
+
+ display: inline-block;
+
+ margin-right: 0.25rem;
+ padding: 0.125rem 0.375rem;
+ border-radius: 0.375rem;
+
+ font-size: 0.6875rem;
+ font-weight: var(--font-weight-medium);
+ line-height: 0.875rem;
+ color: var(--color-white);
+ text-transform: uppercase;
+}
+
+.understoodIcon {
+ margin-right: 0.25rem;
+}
+
+.badgeAdded {
+ background-color: var(--color-primary);
+}
+
+.badgeDeducted {
+ color: var(--color-background);
+ background-color: var(--color-text-secondary);
+}
diff --git a/src/components/modals/profileRating/ProfileRatingModal.tsx b/src/components/modals/profileRating/ProfileRatingModal.tsx
new file mode 100644
index 000000000..c9334b552
--- /dev/null
+++ b/src/components/modals/profileRating/ProfileRatingModal.tsx
@@ -0,0 +1,260 @@
+import { memo, useEffect, useMemo, useState } from '../../../lib/teact/teact';
+import { getActions, withGlobal } from '../../../global';
+
+import type { ApiStarsRating, ApiUser } from '../../../api/types';
+import type { TabState } from '../../../global/types';
+
+import { getPeerTitle } from '../../../global/helpers/peers';
+import { selectUser, selectUserFullInfo } from '../../../global/selectors';
+import buildClassName from '../../../util/buildClassName';
+import { formatShortDuration } from '../../../util/dates/dateFormat';
+
+import useLang from '../../../hooks/useLang';
+import useLastCallback from '../../../hooks/useLastCallback';
+
+import Icon from '../../common/icons/Icon';
+import PremiumProgress, { type AnimationDirection } from '../../common/PremiumProgress';
+import Button from '../../ui/Button';
+import Transition from '../../ui/Transition';
+import TableAboutModal, { type TableAboutData } from '../common/TableAboutModal';
+
+import styles from './ProfileRatingModal.module.scss';
+
+export type OwnProps = {
+ modal: TabState['profileRatingModal'];
+};
+
+type StateProps = {
+ user?: ApiUser;
+ currentUserId?: string;
+ starsRating?: ApiStarsRating;
+ pendingRating?: ApiStarsRating;
+ pendingRatingDate?: number;
+};
+
+const ProfileRatingModal = ({
+ modal,
+ user,
+ currentUserId,
+ starsRating,
+ pendingRating,
+ pendingRatingDate,
+}: OwnProps & StateProps) => {
+ const {
+ closeProfileRatingModal,
+ } = getActions();
+ const lang = useLang();
+ const isOpen = Boolean(modal);
+ const [showFutureRating, setShowFutureRating] = useState(false);
+
+ const handleClose = useLastCallback(() => {
+ closeProfileRatingModal();
+ });
+
+ useEffect(() => {
+ if (!isOpen) {
+ setShowFutureRating(false);
+ }
+ }, [isOpen]);
+
+ const handleShowFuture = useLastCallback(() => {
+ setShowFutureRating(true);
+ });
+
+ const handleShowCurrent = useLastCallback(() => {
+ setShowFutureRating(false);
+ });
+
+ const renderBadge = (type: 'added' | 'deducted') => {
+ const isAdded = type === 'added';
+ const badgeText = isAdded ? lang('RatingBadgeAdded') : lang('RatingBadgeDeducted');
+ const badgeClass = isAdded ? styles.badgeAdded : styles.badgeDeducted;
+
+ return (
+
+ {badgeText}
+
+ );
+ };
+
+ const header = useMemo(() => {
+ if (!modal || !user || !starsRating || !isOpen) return undefined;
+
+ const rating = showFutureRating && pendingRating ? pendingRating : starsRating;
+ const currentStars = rating.stars;
+ const currentLevelStars = rating.currentLevelStars;
+ const nextLevelStars = rating.nextLevelStars;
+ const currentLevel = rating.level;
+ const nextLevel = currentLevel + 1;
+ const isNegative = currentLevel < 0;
+ const pendingLevel = !showFutureRating && pendingRating ? pendingRating.level : starsRating.level;
+
+ let levelProgress = 0;
+
+ if (!nextLevelStars) {
+ levelProgress = 1;
+ } else if (nextLevelStars > currentLevelStars) {
+ levelProgress = Math.max(0.03, (currentStars - currentLevelStars) / (nextLevelStars - currentLevelStars));
+ } else {
+ levelProgress = 1;
+ }
+
+ const progress = isNegative ? 0.5 : Math.max(0, Math.min(1, levelProgress));
+
+ const waitTime = pendingRatingDate ? pendingRatingDate - Math.floor(Date.now() / 1000) : 0;
+ const pendingPoints = pendingRating ? pendingRating.stars - starsRating.stars : 0;
+ const shouldShowPreview = pendingRating && pendingRatingDate;
+
+ const renderPreviewDescription = () => {
+ if (!shouldShowPreview) return undefined;
+
+ return (
+
+ {showFutureRating ? (
+
+ {lang('DescriptionFutureRating', {
+ time: formatShortDuration(lang, waitTime),
+ points: Math.abs(pendingPoints),
+ link: (
+
+ {lang('LinkDescriptionRatingBack')}
+
+ ),
+ }, {
+ pluralValue: Math.abs(pendingPoints),
+ withNodes: true,
+ })}
+
+ ) : (
+
+ {lang('DescriptionPendingRating', {
+ time: formatShortDuration(lang, waitTime),
+ points: Math.abs(pendingPoints),
+ link: (
+
+ {lang('LinkDescriptionRatingPreview')}
+
+ ),
+ }, {
+ pluralValue: Math.abs(pendingPoints),
+ withNodes: true,
+ })}
+
+ )}
+
+ );
+ };
+
+ let animationDirection: AnimationDirection = 'none';
+ if (currentLevel >= 0 && pendingLevel >= 0 && currentLevel !== pendingLevel) {
+ animationDirection = currentLevel > pendingLevel ? 'forward' : 'backward';
+ }
+
+ if (currentLevel < 0 && pendingLevel < 0 && currentLevel !== pendingLevel) {
+ animationDirection = currentLevel < pendingLevel ? 'backward' : 'forward';
+ }
+
+ const userFallbackText = lang('ActionFallbackUser');
+
+ return (
+
+
+ {lang('TitleRating')}
+
+
= 0}
+ isNegative={currentLevel < 0}
+ animationDirection={animationDirection}
+ className={buildClassName(styles.ratingProgress, shouldShowPreview && styles.withPreview)}
+ />
+ {renderPreviewDescription()}
+
+ {user?.id === currentUserId
+ ? lang('RatingYourReflectsActivity')
+ : lang('RatingReflectsActivity', { name: getPeerTitle(lang, user) || userFallbackText })}
+
+
+ );
+ }, [modal, user, currentUserId, starsRating,
+ pendingRating, pendingRatingDate, showFutureRating,
+ lang, handleShowFuture, handleShowCurrent, isOpen]);
+
+ const listItemData = [
+ ['gift', lang('RatingGiftsFromTelegram'), (
+
+ {renderBadge('added')}
+ {lang('RatingGiftsFromTelegramDesc')}
+
+ )],
+ ['user-stars', lang('RatingGiftsAndPostsFromUsers'), (
+
+ {renderBadge('added')}
+ {lang('RatingGiftsAndPostsFromUsersDesc')}
+
+ )],
+ ['refund', lang('RatingRefundsAndConversions'), (
+
+ {renderBadge('deducted')}
+ {lang('RatingRefundsAndConversionsDesc')}
+
+ )],
+ ] satisfies TableAboutData;
+
+ const footer = useMemo(() => {
+ if (!isOpen) return undefined;
+ return (
+
+
+
+ );
+ }, [lang, isOpen, handleClose]);
+
+ return (
+
+ );
+};
+
+export default memo(withGlobal(
+ (global, { modal }): StateProps => {
+ const currentUserId = global.currentUserId;
+ const user = modal?.userId ? selectUser(global, modal.userId) : undefined;
+ const userFullInfo = modal?.userId
+ ? selectUserFullInfo(global, modal.userId) : undefined;
+
+ const starsRating = userFullInfo?.starsRating;
+ const pendingRating = userFullInfo?.starsMyPendingRating;
+ const pendingRatingDate = userFullInfo?.starsMyPendingRatingDate;
+
+ return {
+ user,
+ currentUserId,
+ starsRating,
+ pendingRating,
+ pendingRatingDate,
+ };
+ },
+)(ProfileRatingModal));
diff --git a/src/global/actions/ui/users.ts b/src/global/actions/ui/users.ts
index da6006991..2c94d7495 100644
--- a/src/global/actions/ui/users.ts
+++ b/src/global/actions/ui/users.ts
@@ -64,3 +64,16 @@ addActionHandler('closeSuggestedStatusModal', (global, actions, payload): Action
});
addTabStateResetterAction('closeChatRefundModal', 'chatRefundModal');
+
+addActionHandler('openProfileRatingModal', (global, actions, payload): ActionReturnType => {
+ const { userId, level, tabId = getCurrentTabId() } = payload;
+
+ return updateTabState(global, {
+ profileRatingModal: {
+ userId,
+ level,
+ },
+ }, tabId);
+});
+
+addTabStateResetterAction('closeProfileRatingModal', 'profileRatingModal');
diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts
index db404b905..7c1ba9adb 100644
--- a/src/global/types/actions.ts
+++ b/src/global/types/actions.ts
@@ -1850,6 +1850,11 @@ export interface ActionPayloads {
userId: string;
} & WithTabId;
closeChatRefundModal: WithTabId | undefined;
+ openProfileRatingModal: {
+ userId: string;
+ level: number;
+ } & WithTabId;
+ closeProfileRatingModal: WithTabId | undefined;
loadMoreProfilePhotos: {
peerId: string;
isPreload?: boolean;
diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts
index 57362af1d..f10a1caab 100644
--- a/src/global/types/tabState.ts
+++ b/src/global/types/tabState.ts
@@ -852,6 +852,11 @@ export type TabState = {
duration?: number;
};
+ profileRatingModal?: {
+ userId: string;
+ level: number;
+ };
+
monetizationVerificationModal?: {
chatId: string;
isLoading?: boolean;
diff --git a/src/styles/icons.scss b/src/styles/icons.scss
index d828c8197..b89d13721 100644
--- a/src/styles/icons.scss
+++ b/src/styles/icons.scss
@@ -207,101 +207,124 @@ $icons-map: (
"quote-text": "\f1aa",
"quote": "\f1ab",
"radial-badge": "\f1ac",
- "readchats": "\f1ad",
- "recent": "\f1ae",
- "reload": "\f1af",
- "remove-quote": "\f1b0",
- "remove": "\f1b1",
- "reopen-topic": "\f1b2",
- "replace": "\f1b3",
- "replies": "\f1b4",
- "reply-filled": "\f1b5",
- "reply": "\f1b6",
- "revenue-split": "\f1b7",
- "revote": "\f1b8",
- "save-story": "\f1b9",
- "saved-messages": "\f1ba",
- "schedule": "\f1bb",
- "sd-photo": "\f1bc",
- "search": "\f1bd",
- "select": "\f1be",
- "sell-outline": "\f1bf",
- "sell": "\f1c0",
- "send-outline": "\f1c1",
- "send": "\f1c2",
- "settings-filled": "\f1c3",
- "settings": "\f1c4",
- "share-filled": "\f1c5",
- "share-screen-outlined": "\f1c6",
- "share-screen-stop": "\f1c7",
- "share-screen": "\f1c8",
- "show-message": "\f1c9",
- "sidebar": "\f1ca",
- "skip-next": "\f1cb",
- "skip-previous": "\f1cc",
- "smallscreen": "\f1cd",
- "smile": "\f1ce",
- "sort-by-date": "\f1cf",
- "sort-by-number": "\f1d0",
- "sort-by-price": "\f1d1",
- "sort": "\f1d2",
- "speaker-muted-story": "\f1d3",
- "speaker-outline": "\f1d4",
- "speaker-story": "\f1d5",
- "speaker": "\f1d6",
- "spoiler-disable": "\f1d7",
- "spoiler": "\f1d8",
- "sport": "\f1d9",
- "star": "\f1da",
- "stars-lock": "\f1db",
- "stats": "\f1dc",
- "stealth-future": "\f1dd",
- "stealth-past": "\f1de",
- "stickers": "\f1df",
- "stop-raising-hand": "\f1e0",
- "stop": "\f1e1",
- "story-caption": "\f1e2",
- "story-expired": "\f1e3",
- "story-priority": "\f1e4",
- "story-reply": "\f1e5",
- "strikethrough": "\f1e6",
- "tag-add": "\f1e7",
- "tag-crossed": "\f1e8",
- "tag-filter": "\f1e9",
- "tag-name": "\f1ea",
- "tag": "\f1eb",
- "timer": "\f1ec",
- "toncoin": "\f1ed",
- "trade": "\f1ee",
- "transcribe": "\f1ef",
- "truck": "\f1f0",
- "unarchive": "\f1f1",
- "underlined": "\f1f2",
- "unique-profile": "\f1f3",
- "unlist-outline": "\f1f4",
- "unlist": "\f1f5",
- "unlock-badge": "\f1f6",
- "unlock": "\f1f7",
- "unmute": "\f1f8",
- "unpin": "\f1f9",
- "unread": "\f1fa",
- "up": "\f1fb",
- "user-filled": "\f1fc",
- "user-online": "\f1fd",
- "user": "\f1fe",
- "video-outlined": "\f1ff",
- "video-stop": "\f200",
- "video": "\f201",
- "view-once": "\f202",
- "voice-chat": "\f203",
- "volume-1": "\f204",
- "volume-2": "\f205",
- "volume-3": "\f206",
- "web": "\f207",
- "webapp": "\f208",
- "word-wrap": "\f209",
- "zoom-in": "\f20a",
- "zoom-out": "\f20b",
+ "rating-icons-level1": "\f1ad",
+ "rating-icons-level10": "\f1ae",
+ "rating-icons-level2": "\f1af",
+ "rating-icons-level20": "\f1b0",
+ "rating-icons-level3": "\f1b1",
+ "rating-icons-level30": "\f1b2",
+ "rating-icons-level4": "\f1b3",
+ "rating-icons-level40": "\f1b4",
+ "rating-icons-level5": "\f1b5",
+ "rating-icons-level50": "\f1b6",
+ "rating-icons-level6": "\f1b7",
+ "rating-icons-level60": "\f1b8",
+ "rating-icons-level7": "\f1b9",
+ "rating-icons-level70": "\f1ba",
+ "rating-icons-level8": "\f1bb",
+ "rating-icons-level80": "\f1bc",
+ "rating-icons-level9": "\f1bd",
+ "rating-icons-level90": "\f1be",
+ "rating-icons-negative": "\f1bf",
+ "readchats": "\f1c0",
+ "recent": "\f1c1",
+ "refund": "\f1c2",
+ "reload": "\f1c3",
+ "remove-quote": "\f1c4",
+ "remove": "\f1c5",
+ "reopen-topic": "\f1c6",
+ "replace": "\f1c7",
+ "replies": "\f1c8",
+ "reply-filled": "\f1c9",
+ "reply": "\f1ca",
+ "revenue-split": "\f1cb",
+ "revote": "\f1cc",
+ "save-story": "\f1cd",
+ "saved-messages": "\f1ce",
+ "schedule": "\f1cf",
+ "sd-photo": "\f1d0",
+ "search": "\f1d1",
+ "select": "\f1d2",
+ "sell-outline": "\f1d3",
+ "sell": "\f1d4",
+ "send-outline": "\f1d5",
+ "send": "\f1d6",
+ "settings-filled": "\f1d7",
+ "settings": "\f1d8",
+ "share-filled": "\f1d9",
+ "share-screen-outlined": "\f1da",
+ "share-screen-stop": "\f1db",
+ "share-screen": "\f1dc",
+ "show-message": "\f1dd",
+ "sidebar": "\f1de",
+ "skip-next": "\f1df",
+ "skip-previous": "\f1e0",
+ "smallscreen": "\f1e1",
+ "smile": "\f1e2",
+ "sort-by-date": "\f1e3",
+ "sort-by-number": "\f1e4",
+ "sort-by-price": "\f1e5",
+ "sort": "\f1e6",
+ "speaker-muted-story": "\f1e7",
+ "speaker-outline": "\f1e8",
+ "speaker-story": "\f1e9",
+ "speaker": "\f1ea",
+ "spoiler-disable": "\f1eb",
+ "spoiler": "\f1ec",
+ "sport": "\f1ed",
+ "star": "\f1ee",
+ "stars-lock": "\f1ef",
+ "stats": "\f1f0",
+ "stealth-future": "\f1f1",
+ "stealth-past": "\f1f2",
+ "stickers": "\f1f3",
+ "stop-raising-hand": "\f1f4",
+ "stop": "\f1f5",
+ "story-caption": "\f1f6",
+ "story-expired": "\f1f7",
+ "story-priority": "\f1f8",
+ "story-reply": "\f1f9",
+ "strikethrough": "\f1fa",
+ "tag-add": "\f1fb",
+ "tag-crossed": "\f1fc",
+ "tag-filter": "\f1fd",
+ "tag-name": "\f1fe",
+ "tag": "\f1ff",
+ "timer": "\f200",
+ "toncoin": "\f201",
+ "trade": "\f202",
+ "transcribe": "\f203",
+ "truck": "\f204",
+ "unarchive": "\f205",
+ "underlined": "\f206",
+ "understood": "\f207",
+ "unique-profile": "\f208",
+ "unlist-outline": "\f209",
+ "unlist": "\f20a",
+ "unlock-badge": "\f20b",
+ "unlock": "\f20c",
+ "unmute": "\f20d",
+ "unpin": "\f20e",
+ "unread": "\f20f",
+ "up": "\f210",
+ "user-filled": "\f211",
+ "user-online": "\f212",
+ "user-stars": "\f213",
+ "user": "\f214",
+ "video-outlined": "\f215",
+ "video-stop": "\f216",
+ "video": "\f217",
+ "view-once": "\f218",
+ "voice-chat": "\f219",
+ "volume-1": "\f21a",
+ "volume-2": "\f21b",
+ "volume-3": "\f21c",
+ "warning": "\f21d",
+ "web": "\f21e",
+ "webapp": "\f21f",
+ "word-wrap": "\f220",
+ "zoom-in": "\f221",
+ "zoom-out": "\f222",
);
.icon-active-sessions::before {
@@ -820,12 +843,72 @@ $icons-map: (
.icon-radial-badge::before {
content: map.get($icons-map, "radial-badge");
}
+.icon-rating-icons-level1::before {
+ content: map.get($icons-map, "rating-icons-level1");
+}
+.icon-rating-icons-level10::before {
+ content: map.get($icons-map, "rating-icons-level10");
+}
+.icon-rating-icons-level2::before {
+ content: map.get($icons-map, "rating-icons-level2");
+}
+.icon-rating-icons-level20::before {
+ content: map.get($icons-map, "rating-icons-level20");
+}
+.icon-rating-icons-level3::before {
+ content: map.get($icons-map, "rating-icons-level3");
+}
+.icon-rating-icons-level30::before {
+ content: map.get($icons-map, "rating-icons-level30");
+}
+.icon-rating-icons-level4::before {
+ content: map.get($icons-map, "rating-icons-level4");
+}
+.icon-rating-icons-level40::before {
+ content: map.get($icons-map, "rating-icons-level40");
+}
+.icon-rating-icons-level5::before {
+ content: map.get($icons-map, "rating-icons-level5");
+}
+.icon-rating-icons-level50::before {
+ content: map.get($icons-map, "rating-icons-level50");
+}
+.icon-rating-icons-level6::before {
+ content: map.get($icons-map, "rating-icons-level6");
+}
+.icon-rating-icons-level60::before {
+ content: map.get($icons-map, "rating-icons-level60");
+}
+.icon-rating-icons-level7::before {
+ content: map.get($icons-map, "rating-icons-level7");
+}
+.icon-rating-icons-level70::before {
+ content: map.get($icons-map, "rating-icons-level70");
+}
+.icon-rating-icons-level8::before {
+ content: map.get($icons-map, "rating-icons-level8");
+}
+.icon-rating-icons-level80::before {
+ content: map.get($icons-map, "rating-icons-level80");
+}
+.icon-rating-icons-level9::before {
+ content: map.get($icons-map, "rating-icons-level9");
+}
+.icon-rating-icons-level90::before {
+ content: map.get($icons-map, "rating-icons-level90");
+}
+.icon-rating-icons-negative::before {
+ content: map.get($icons-map, "rating-icons-negative");
+}
.icon-readchats::before {
content: map.get($icons-map, "readchats");
}
.icon-recent::before {
content: map.get($icons-map, "recent");
}
+.icon-refund::before {
+ content: map.get($icons-map, "refund");
+}
.icon-reload::before {
content: map.get($icons-map, "reload");
}
@@ -1030,6 +1113,9 @@ $icons-map: (
.icon-underlined::before {
content: map.get($icons-map, "underlined");
}
+.icon-understood::before {
+ content: map.get($icons-map, "understood");
+}
.icon-unique-profile::before {
content: map.get($icons-map, "unique-profile");
}
@@ -1063,6 +1149,9 @@ $icons-map: (
.icon-user-online::before {
content: map.get($icons-map, "user-online");
}
+.icon-user-stars::before {
+ content: map.get($icons-map, "user-stars");
+}
.icon-user::before {
content: map.get($icons-map, "user");
}
@@ -1090,6 +1179,9 @@ $icons-map: (
.icon-volume-3::before {
content: map.get($icons-map, "volume-3");
}
+.icon-warning::before {
+ content: map.get($icons-map, "warning");
+}
.icon-web::before {
content: map.get($icons-map, "web");
}
diff --git a/src/styles/icons.woff b/src/styles/icons.woff
index 4844820ba..76c161972 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 8bd0da8ac..bc3e457fb 100644
Binary files a/src/styles/icons.woff2 and b/src/styles/icons.woff2 differ
diff --git a/src/types/icons/font.ts b/src/types/icons/font.ts
index fbd04f0d3..5e4364c6e 100644
--- a/src/types/icons/font.ts
+++ b/src/types/icons/font.ts
@@ -171,8 +171,28 @@ export type FontIconName =
| 'quote-text'
| 'quote'
| 'radial-badge'
+ | 'rating-icons-level1'
+ | 'rating-icons-level10'
+ | 'rating-icons-level2'
+ | 'rating-icons-level20'
+ | 'rating-icons-level3'
+ | 'rating-icons-level30'
+ | 'rating-icons-level4'
+ | 'rating-icons-level40'
+ | 'rating-icons-level5'
+ | 'rating-icons-level50'
+ | 'rating-icons-level6'
+ | 'rating-icons-level60'
+ | 'rating-icons-level7'
+ | 'rating-icons-level70'
+ | 'rating-icons-level8'
+ | 'rating-icons-level80'
+ | 'rating-icons-level9'
+ | 'rating-icons-level90'
+ | 'rating-icons-negative'
| 'readchats'
| 'recent'
+ | 'refund'
| 'reload'
| 'remove-quote'
| 'remove'
@@ -241,6 +261,7 @@ export type FontIconName =
| 'truck'
| 'unarchive'
| 'underlined'
+ | 'understood'
| 'unique-profile'
| 'unlist-outline'
| 'unlist'
@@ -252,6 +273,7 @@ export type FontIconName =
| 'up'
| 'user-filled'
| 'user-online'
+ | 'user-stars'
| 'user'
| 'video-outlined'
| 'video-stop'
@@ -261,6 +283,7 @@ export type FontIconName =
| 'volume-1'
| 'volume-2'
| 'volume-3'
+ | 'warning'
| 'web'
| 'webapp'
| 'word-wrap'
diff --git a/src/types/language.d.ts b/src/types/language.d.ts
index c82dcea22..7b93c7834 100644
--- a/src/types/language.d.ts
+++ b/src/types/language.d.ts
@@ -1642,6 +1642,19 @@ export interface LangPair {
'PublicPostsPremiumFeatureSubtitle': undefined;
'PublicPostsSubscribeToPremium': undefined;
'PostsSearchTransaction': undefined;
+ 'TitleRating': undefined;
+ 'RatingYourReflectsActivity': undefined;
+ 'RatingGiftsFromTelegram': undefined;
+ 'RatingGiftsFromTelegramDesc': undefined;
+ 'RatingGiftsAndPostsFromUsers': undefined;
+ 'RatingGiftsAndPostsFromUsersDesc': undefined;
+ 'RatingRefundsAndConversions': undefined;
+ 'RatingRefundsAndConversionsDesc': undefined;
+ 'RatingBadgeAdded': undefined;
+ 'RatingBadgeDeducted': undefined;
+ 'RatingNegativeLevel': undefined;
+ 'LinkDescriptionRatingBack': undefined;
+ 'LinkDescriptionRatingPreview': undefined;
}
export interface LangPairWithVariables {
@@ -2850,6 +2863,12 @@ export interface LangPairWithVariables {
'NotificationPaidExtraSearch': {
'stars': V;
};
+ 'RatingReflectsActivity': {
+ 'name': V;
+ };
+ 'RatingLevel': {
+ 'level': V;
+ };
}
export interface LangPairPlural {
@@ -3177,6 +3196,16 @@ export interface LangPairPluralWithVariables {
'HintPublicPostsSearchQuota': {
'count': V;
};
+ 'DescriptionPendingRating': {
+ 'time': V;
+ 'points': V;
+ 'link': V;
+ };
+ 'DescriptionFutureRating': {
+ 'time': V;
+ 'points': V;
+ 'link': V;
+ };
}
export type RegularLangKey = keyof LangPair;
export type RegularLangKeyWithVariables = keyof LangPairWithVariables;