Chat: Simplify styling (#6353)

This commit is contained in:
zubiden 2025-11-06 11:36:23 +01:00 committed by Alexander Zinchuk
parent e070063d74
commit 80ab048c29
29 changed files with 248 additions and 288 deletions

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M20.677 7.241c.171.103.329.226.47.367l3.245 3.245a2.441 2.441 0 0 1-.47 3.819l-2.552 1.531c.34 2.027-.485 4.026-2.371 5.912a1.107 1.107 0 0 1-1.565 0l-2.993-2.992-4.324 4.325a1.108 1.108 0 0 1-1.452.099l-.113-.099a1.108 1.108 0 0 1-.099-1.452l.099-.113 4.325-4.326-2.992-2.991a1.107 1.107 0 0 1 0-1.565c1.792-1.792 3.686-2.626 5.609-2.414l.304.042 1.531-2.552a2.44 2.44 0 0 1 3.349-.837zm-1.451 1.976-1.951 3.252a1.105 1.105 0 0 1-1.299.48c-1.197-.399-2.406-.135-3.699.878l5.896 5.896.193-.258c.853-1.197 1.058-2.325.686-3.442a1.108 1.108 0 0 1 .48-1.299l3.275-1.967.021-.018a.228.228 0 0 0 0-.322l-3.245-3.245-.044-.034a.228.228 0 0 0-.313.078z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M23.126 1.62q.458.277.836.653l5.767 5.768a4.34 4.34 0 0 1-.835 6.788l-4.536 2.72c.604 3.603-.862 7.156-4.214 10.509a1.97 1.97 0 0 1-2.782 0l-5.32-5.318-7.685 7.687a1.97 1.97 0 0 1-2.58.176l-.201-.176a1.97 1.97 0 0 1-.176-2.581l.176-.2 7.687-7.69-5.318-5.316a1.97 1.97 0 0 1 0-2.781c3.185-3.186 6.551-4.668 9.97-4.291l.54.075 2.72-4.536a4.337 4.337 0 0 1 5.953-1.488zm-2.579 3.513-3.467 5.78a1.964 1.964 0 0 1-2.31.853c-2.127-.71-4.275-.24-6.574 1.56l10.48 10.48.343-.458c1.516-2.128 1.88-4.133 1.22-6.118a1.97 1.97 0 0 1 .852-2.31l5.821-3.495.038-.032a.405.405 0 0 0 0-.572l-5.768-5.768-.078-.06a.405.405 0 0 0-.557.138z"/></svg>

Before

Width:  |  Height:  |  Size: 726 B

After

Width:  |  Height:  |  Size: 702 B

View File

@ -606,6 +606,7 @@
"ConversationOpenBotLinkLogin" = "Log in to **{url}** as {user}"; "ConversationOpenBotLinkLogin" = "Log in to **{url}** as {user}";
"ConversationOpenBotLinkAllowMessages" = "Allow **{bot}** to send me messages"; "ConversationOpenBotLinkAllowMessages" = "Allow **{bot}** to send me messages";
"BotWebViewOpenBot" = "Open Bot"; "BotWebViewOpenBot" = "Open Bot";
"BotChatMiniAppOpen" = "Open";
"WebAppReloadPage" = "Reload Page"; "WebAppReloadPage" = "Reload Page";
"WebAppRemoveBot" = "Remove From Menu"; "WebAppRemoveBot" = "Remove From Menu";
"WebAppAddToAttachmentAdd" = "Add"; "WebAppAddToAttachmentAdd" = "Add";

View File

@ -1,5 +1,4 @@
import type { FC } from '../../lib/teact/teact'; import type { TeactNode } from '../../lib/teact/teact';
import type React from '../../lib/teact/teact';
import { import {
memo, memo,
useEffect, useEffect,
@ -28,21 +27,21 @@ import styles from './ChatForumLastMessage.module.scss';
type OwnProps = { type OwnProps = {
chat: ApiChat; chat: ApiChat;
topics?: Record<number, ApiTopic>; topics?: Record<number, ApiTopic>;
renderLastMessage: () => React.ReactNode; hasTags?: boolean;
renderLastMessage: () => TeactNode | undefined;
observeIntersection?: ObserveFn; observeIntersection?: ObserveFn;
noForumTitle?: boolean;
}; };
const NO_CORNER_THRESHOLD = Number(REM); const NO_CORNER_THRESHOLD = Number(REM);
const MAX_TOPICS = 3; const MAX_TOPICS = 3;
const ChatForumLastMessage: FC<OwnProps> = ({ const ChatForumLastMessage = ({
chat, chat,
topics, topics,
hasTags,
renderLastMessage, renderLastMessage,
observeIntersection, observeIntersection,
noForumTitle, }: OwnProps) => {
}) => {
const { openThread } = getActions(); const { openThread } = getActions();
const lastMessageRef = useRef<HTMLDivElement>(); const lastMessageRef = useRef<HTMLDivElement>();
@ -81,7 +80,7 @@ const ChatForumLastMessage: FC<OwnProps> = ({
useEffect(() => { useEffect(() => {
const lastMessageElement = lastMessageRef.current; const lastMessageElement = lastMessageRef.current;
const mainColumnElement = mainColumnRef.current; const mainColumnElement = mainColumnRef.current;
if (!lastMessageElement || !mainColumnElement) return; if (!lastMessageElement || !mainColumnElement || hasTags) return;
const lastMessageWidth = lastMessageElement.offsetWidth; const lastMessageWidth = lastMessageElement.offsetWidth;
const mainColumnWidth = mainColumnElement.offsetWidth; const mainColumnWidth = mainColumnElement.offsetWidth;
@ -92,7 +91,7 @@ const ChatForumLastMessage: FC<OwnProps> = ({
setOverwrittenWidth(undefined); setOverwrittenWidth(undefined);
} }
setIsReversedCorner(lastMessageWidth > mainColumnWidth); setIsReversedCorner(lastMessageWidth > mainColumnWidth);
}, [lastActiveTopic, renderLastMessage]); }, [lastActiveTopic, renderLastMessage, hasTags]);
return ( return (
<div <div
@ -105,7 +104,7 @@ const ChatForumLastMessage: FC<OwnProps> = ({
style={overwrittenWidth ? `--overwritten-width: ${overwrittenWidth}px` : undefined} style={overwrittenWidth ? `--overwritten-width: ${overwrittenWidth}px` : undefined}
> >
{ {
!noForumTitle && ( !hasTags && (
<> <>
{lastActiveTopic && ( {lastActiveTopic && (
<div className={styles.titleRow}> <div className={styles.titleRow}>
@ -160,7 +159,7 @@ const ChatForumLastMessage: FC<OwnProps> = ({
) )
} }
<div <div
className={buildClassName(styles.lastMessage, lastActiveTopic?.unreadCount && !noForumTitle && styles.unread)} className={buildClassName(styles.lastMessage, lastActiveTopic?.unreadCount && !hasTags && styles.unread)}
ref={lastMessageRef} ref={lastMessageRef}
onClick={handleOpenTopicClick} onClick={handleOpenTopicClick}
onMouseDown={handleOpenTopicMouseDown} onMouseDown={handleOpenTopicMouseDown}

View File

@ -1,9 +1,8 @@
import type { FC } from '../../lib/teact/teact';
import { memo } from '../../lib/teact/teact'; import { memo } from '../../lib/teact/teact';
import type { ApiFakeType } from '../../api/types'; import type { ApiFakeType } from '../../api/types';
import useOldLang from '../../hooks/useOldLang'; import useLang from '../../hooks/useLang';
import './FakeIcon.scss'; import './FakeIcon.scss';
@ -11,10 +10,10 @@ type OwnProps = {
fakeType: ApiFakeType; fakeType: ApiFakeType;
}; };
const FakeIcon: FC<OwnProps> = ({ const FakeIcon = ({
fakeType, fakeType,
}) => { }: OwnProps) => {
const lang = useOldLang(); const lang = useLang();
return ( return (
<span className="FakeIcon"> <span className="FakeIcon">

View File

@ -4,8 +4,8 @@
align-items: center; align-items: center;
.statusTransition { .statusTransition {
width: 1.5em !important; width: 1.25em !important;
height: 1.5em !important; height: 1.25em !important;
} }
} }

View File

@ -2,9 +2,6 @@
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
align-items: center; align-items: center;
margin-right: 0.1875rem;
font-size: 0.75rem; font-size: 0.75rem;
.MessageOutgoingStatus { .MessageOutgoingStatus {

View File

@ -1,10 +1,9 @@
.VerifiedIcon { .VerifiedIcon {
--color-fill: var(--color-primary); --color-fill: var(--color-primary);
--color-checkmark: #fff; --color-checkmark: #fff;
display: inline-block; display: inline-block;
flex-shrink: 0; flex-shrink: 0;
width: 1.5rem; width: 1.25em;
height: 1.5rem; height: 1.25em;
} }

View File

@ -1,8 +1,6 @@
import type { FC } from '../../lib/teact/teact';
import './VerifiedIcon.scss'; import './VerifiedIcon.scss';
const VerifiedIcon: FC = () => { const VerifiedIcon = () => {
return ( return (
<svg className="VerifiedIcon" viewBox="0 0 24 24"> <svg className="VerifiedIcon" viewBox="0 0 24 24">
<path d="M12.3 2.9c.1.1.2.1.3.2.7.6 1.3 1.1 2 1.7.3.2.6.4.9.4.9.1 1.7.2 2.6.2.5 0 .6.1.7.7.1.9.1 1.8.2 2.6 0 .4.2.7.4 1 .6.7 1.1 1.3 1.7 2 .3.4.3.5 0 .8-.5.6-1.1 1.3-1.6 1.9-.3.3-.5.7-.5 1.2-.1.8-.2 1.7-.2 2.5 0 .4-.2.5-.6.6-.8 0-1.6.1-2.5.2-.5 0-1 .2-1.4.5-.6.5-1.3 1.1-1.9 1.6-.3.3-.5.3-.8 0-.7-.6-1.4-1.2-2-1.8-.3-.2-.6-.4-.9-.4-.9-.1-1.8-.2-2.7-.2-.4 0-.5-.2-.6-.5 0-.9-.1-1.7-.2-2.6 0-.4-.2-.8-.4-1.1-.6-.6-1.1-1.3-1.6-2-.4-.4-.3-.5 0-1 .6-.6 1.1-1.3 1.7-1.9.3-.3.4-.6.4-1 0-.8.1-1.6.2-2.5 0-.5.1-.6.6-.6.9-.1 1.7-.1 2.6-.2.4 0 .7-.2 1-.4.7-.6 1.4-1.2 2.1-1.7.1-.2.3-.3.5-.2z" style="fill: var(--color-fill)" /> <path d="M12.3 2.9c.1.1.2.1.3.2.7.6 1.3 1.1 2 1.7.3.2.6.4.9.4.9.1 1.7.2 2.6.2.5 0 .6.1.7.7.1.9.1 1.8.2 2.6 0 .4.2.7.4 1 .6.7 1.1 1.3 1.7 2 .3.4.3.5 0 .8-.5.6-1.1 1.3-1.6 1.9-.3.3-.5.7-.5 1.2-.1.8-.2 1.7-.2 2.5 0 .4-.2.5-.6.6-.8 0-1.6.1-2.5.2-.5 0-1 .2-1.4.5-.6.5-1.3 1.1-1.9 1.6-.3.3-.5.3-.8 0-.7-.6-1.4-1.2-2-1.8-.3-.2-.6-.4-.9-.4-.9-.1-1.8-.2-2.7-.2-.4 0-.5-.2-.6-.5 0-.9-.1-1.7-.2-2.6 0-.4-.2-.8-.4-1.1-.6-.6-1.1-1.3-1.6-2-.4-.4-.3-.5 0-1 .6-.6 1.1-1.3 1.7-1.9.3-.3.4-.6.4-1 0-.8.1-1.6.2-2.5 0-.5.1-.6.6-.6.9-.1 1.7-.1 2.6-.2.4 0 .7-.2 1-.4.7-.6 1.4-1.2 2.1-1.7.1-.2.3-.3.5-.2z" style="fill: var(--color-fill)" />

View File

@ -65,13 +65,6 @@
} }
} }
body.is-ios &,
body.is-android & {
.ListItem-button {
border-radius: 0 !important;
}
}
&.forum { &.forum {
--color-chat-username: var(--color-text); --color-chat-username: var(--color-text);
@ -103,14 +96,15 @@
padding-bottom: 0.3125rem; padding-bottom: 0.3125rem;
} }
.title {
line-height: 1.375rem;
}
.status { .status {
background-color: var(--background-color); background-color: var(--background-color);
} }
.info.has-tags .subtitle {
height: unset;
line-height: inherit;
}
@media (max-width: 600px) { @media (max-width: 600px) {
&::before { &::before {
left: 0; left: 0;
@ -118,6 +112,13 @@
} }
} }
&.chat-item-with-tags {
.ListItem-button {
align-items: flex-start;
padding: 0.375rem 0.5625rem;
}
}
.ripple-container { .ripple-container {
z-index: var(--z-chat-ripple); z-index: var(--z-chat-ripple);
} }
@ -133,7 +134,6 @@
} }
.avatar-badge-wrapper { .avatar-badge-wrapper {
--outline-color: var(--color-background); --outline-color: var(--color-background);
position: absolute; position: absolute;
@ -141,11 +141,7 @@
right: 0.5rem; right: 0.5rem;
bottom: 0; bottom: 0;
.ChatBadge { .chat-badge-transition {
box-shadow: 0 0 0 2px var(--outline-color);
}
.ChatBadge-transition {
position: relative; position: relative;
transition: opacity var(--layer-transition), transform var(--layer-transition); transition: opacity var(--layer-transition), transform var(--layer-transition);
@ -190,12 +186,25 @@
} }
.info { .info {
display: flex;
flex-direction: column;
justify-content: center;
transition: opacity 300ms ease, transform var(--layer-transition); transition: opacity 300ms ease, transform var(--layer-transition);
body.no-page-transitions & { body.no-page-transitions & {
transition: opacity 300ms ease; transition: opacity 300ms ease;
} }
&.has-tags {
justify-content: flex-start;
height: 3.75rem;
.info-row, .subtitle {
height: 1.25rem;
line-height: 1.25rem;
}
}
.title .custom-emoji { .title .custom-emoji {
color: var(--color-primary); color: var(--color-primary);
} }
@ -204,11 +213,6 @@
margin-left: 0.25rem; margin-left: 0.25rem;
font-size: 1.125rem; font-size: 1.125rem;
color: var(--color-list-icon); color: var(--color-list-icon);
body.is-ios & {
margin-top: 0;
margin-right: 0.5rem;
}
} }
.general-forum-icon { .general-forum-icon {
@ -216,13 +220,6 @@
color: var(--color-text-secondary); color: var(--color-text-secondary);
} }
.LastMessageMeta {
body.is-ios & {
margin-right: 0;
font-size: 0.875rem;
}
}
.last-message, .last-message,
.typing-status { .typing-status {
unicode-bidi: plaintext; unicode-bidi: plaintext;
@ -284,7 +281,6 @@
width: 1.25rem; width: 1.25rem;
height: 1.25rem; height: 1.25rem;
margin-inline-start: 0.125rem;
margin-inline-end: 0.25rem; margin-inline-end: 0.25rem;
border-radius: 0.125rem; border-radius: 0.125rem;
@ -292,12 +288,6 @@
object-fit: cover; object-fit: cover;
body.is-ios & {
width: 1.125rem;
height: 1.125rem;
vertical-align: -0.1875rem;
}
&.round { &.round {
border-radius: 0.625rem; border-radius: 0.625rem;
} }
@ -319,11 +309,6 @@
font-size: 0.75rem; font-size: 0.75rem;
color: #fff; color: #fff;
body.is-ios & {
bottom: 0;
margin-inline-start: -1.125rem;
}
} }
} }
@ -332,21 +317,6 @@
line-height: initial; line-height: initial;
vertical-align: text-top; vertical-align: text-top;
} }
&.has-tags {
line-height: 1.375rem;
.ChatBadge {
min-width: 1.375rem;
height: 1.375rem;
line-height: 1.5rem;
.is-ios &,
.is-macos & {
line-height: 1.375rem;
}
}
}
} }
&[dir="rtl"] { &[dir="rtl"] {
@ -446,30 +416,12 @@
color: var(--color-white) !important; color: var(--color-white) !important;
} }
.ChatBadge:not(.pinned):not(.muted) {
color: var(--color-chat-active);
background: var(--color-white);
}
.ChatBadge:not(.pinned).muted {
color: var(--color-white);
background: #FFFFFF33;
}
.avatar-badge-wrapper .ChatBadge:not(.pinned) {
--outline-color: transparent;
}
.avatar-badge-wrapper .ChatBadge:not(.pinned).muted {
background: var(--color-gray);
}
.monoforum-badge { .monoforum-badge {
color: var(--color-chat-active); color: var(--color-chat-active);
background-color: #fff; background-color: #fff;
} }
.ChatTags { .chat-tag {
color: var(--color-chat-active); color: var(--color-chat-active);
background-color: #ffffffab; background-color: #ffffffab;
} }

View File

@ -221,7 +221,8 @@ const Chat: FC<OwnProps & StateProps> = ({
}); });
}, [orderedFolderIds, folderId, chatFoldersById, chatFolderIds]); }, [orderedFolderIds, folderId, chatFoldersById, chatFolderIds]);
const shouldRenderTags = areTagsEnabled && withTags && Boolean(tagFolderIds?.length); const isTagsMode = areTagsEnabled && withTags;
const shouldRenderTags = isTagsMode && Boolean(tagFolderIds?.length);
const { renderSubtitle, ref } = useChatListEntry({ const { renderSubtitle, ref } = useChatListEntry({
chat, chat,
@ -240,7 +241,7 @@ const Chat: FC<OwnProps & StateProps> = ({
isPreview, isPreview,
onReorderAnimationEnd, onReorderAnimationEnd,
topics, topics,
noForumTitle: shouldRenderTags, hasTags: shouldRenderTags,
}); });
const getIsForumPanelClosed = useSelectorSignal(selectIsForumPanelClosed); const getIsForumPanelClosed = useSelectorSignal(selectIsForumPanelClosed);
@ -441,13 +442,14 @@ const Chat: FC<OwnProps & StateProps> = ({
forceHidden={getIsForumPanelClosed} forceHidden={getIsForumPanelClosed}
topics={topics} topics={topics}
isSelected={isSelected} isSelected={isSelected}
isOnAvatar
/> />
</div> </div>
{chat.isCallActive && chat.isCallNotEmpty && ( {chat.isCallActive && chat.isCallNotEmpty && (
<ChatCallStatus isMobile={isMobile} isSelected={isSelected} isActive={withInterfaceAnimations} /> <ChatCallStatus isMobile={isMobile} isSelected={isSelected} isActive={withInterfaceAnimations} />
)} )}
</div> </div>
<div className={buildClassName('info', areTagsEnabled && withTags && 'has-tags')}> <div className={buildClassName('info', isTagsMode && 'has-tags')}>
<div className="info-row"> <div className="info-row">
<FullNameTitle <FullNameTitle
peer={isMonoforum ? monoforumChannel! : peer} peer={isMonoforum ? monoforumChannel! : peer}
@ -480,11 +482,13 @@ const Chat: FC<OwnProps & StateProps> = ({
hasMiniApp={user?.hasMainMiniApp} hasMiniApp={user?.hasMainMiniApp}
topics={topics} topics={topics}
isSelected={isSelected} isSelected={isSelected}
transitionClassName="chat-badge-transition"
/> />
)} )}
</div> </div>
{shouldRenderTags && ( {shouldRenderTags && (
<ChatTags <ChatTags
itemClassName="chat-tag"
orderedFolderIds={tagFolderIds} orderedFolderIds={tagFolderIds}
chatFoldersById={chatFoldersById} chatFoldersById={chatFoldersById}
/> />

View File

@ -0,0 +1,120 @@
.badge {
flex-shrink: 0;
min-width: 1.375rem;
height: 1.375rem;
margin-inline-start: 0.5rem;
padding: 0 0.4375rem;
border-radius: 0.75rem;
font-size: 0.875rem;
font-weight: var(--font-weight-medium);
line-height: 1.4375rem;
color: white;
text-align: center;
background: var(--color-gray);
:global(body.is-ios) &,
:global(body.is-macos) & {
line-height: 1.375rem;
}
}
.transition {
display: flex;
align-items: center;
justify-content: center;
height: 1.25rem;
opacity: 1;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
&:not(:global(.open)) {
transform: scale(0);
opacity: 0;
}
&:not(:global(.shown)) {
display: none;
}
&:global(.closing) {
transition:
transform 0.2s ease-out,
opacity 0.2s ease-out;
}
}
.wrapper {
display: flex;
}
.mention,
.unread:not(.muted),
.unopened:not(.muted) {
color: var(--color-white);
background: var(--color-green);
}
.unopened {
align-self: center;
width: 0.5rem;
min-width: auto;
height: 0.5rem;
min-height: auto;
padding: 0;
}
.pinned {
display: flex;
justify-content: flex-end;
padding: 0;
font-size: 1rem;
color: var(--color-list-icon);
background: transparent;
}
.reaction:not(.muted) {
background: #ed504f;
}
.round {
display: grid;
place-content: center;
width: 1.375rem;
height: 1.375rem;
padding: 0.25rem;
font-size: 1rem;
}
.miniapp {
z-index: calc(var(--z-chat-ripple) + 1);
height: auto !important;
padding: 0 0.4375rem !important;
font-size: 0.875rem !important;
}
.selected {
.badge:not(.pinned) {
color: var(--color-chat-active);
background: var(--color-white);
&.muted {
color: var(--color-white);
background: #ffffff33;
}
}
}
.onAvatar .badge {
box-shadow: 0 0 0 2px var(--outline-color);
}

View File

@ -1,122 +0,0 @@
.ChatBadge-transition {
opacity: 1;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
&:not(.open) {
transform: scale(0);
opacity: 0;
}
&:not(.shown) {
display: none;
}
&.closing {
transition: transform 0.2s ease-out, opacity 0.2s ease-out;
}
body.is-macos & {
height: 1.375rem;
}
}
.ChatBadge-wrapper {
display: flex;
.ChatBadge {
margin-inline-start: 0.5rem;
}
}
.ChatBadge {
flex-shrink: 0;
min-width: 1.5rem;
height: 1.5rem;
padding: 0 0.4375rem;
border-radius: 0.75rem;
font-size: 0.875rem;
font-weight: var(--font-weight-medium);
line-height: 1.5625rem;
color: white;
text-align: center;
background: var(--color-gray);
body.is-macos & {
line-height: 1.5rem;
}
body.is-ios &:not(.unopened) {
min-width: 1.375rem;
height: 1.375rem;
padding: 0 0.375rem;
line-height: 1.375rem;
}
body.is-ios,
body.is-macos,
&.miniapp {
min-width: 1.5rem;
height: 1.5rem !important;
line-height: 1.5rem !important;
}
&.mention,
&.unread:not(.muted),
&.unopened:not(.muted) {
color: var(--color-white);
background: var(--color-green);
}
&.unopened {
align-self: center;
width: 0.5rem;
min-width: auto;
height: 0.5rem;
min-height: auto;
padding: 0;
}
&.pinned {
width: 1.5rem;
padding: 0;
color: var(--color-list-icon);
background: transparent;
.icon {
font-size: 1.5rem;
}
}
&.reaction:not(.muted) {
background: #ed504f;
}
&.mention, &.reaction {
width: 1.5rem;
padding: 0.25rem !important;
.icon {
font-size: 1rem;
vertical-align: super;
}
body.is-ios & {
width: 1.375rem;
padding: 0.25rem;
.icon {
font-size: 0.875rem;
}
}
}
&.miniapp {
z-index: calc(var(--z-chat-ripple) + 1);
padding: 0 0.4375rem !important;
font-size: 0.875rem !important;
}
}

View File

@ -1,5 +1,3 @@
import type { FC } from '../../../lib/teact/teact';
import type React from '../../../lib/teact/teact';
import { memo, useMemo } from '../../../lib/teact/teact'; import { memo, useMemo } from '../../../lib/teact/teact';
import { getActions } from '../../../global'; import { getActions } from '../../../global';
@ -15,14 +13,13 @@ import { extractCurrentThemeParams } from '../../../util/themeStyle';
import useDerivedState from '../../../hooks/useDerivedState'; import useDerivedState from '../../../hooks/useDerivedState';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import AnimatedCounter from '../../common/AnimatedCounter'; import AnimatedCounter from '../../common/AnimatedCounter';
import Icon from '../../common/icons/Icon'; import Icon from '../../common/icons/Icon';
import Button from '../../ui/Button'; import Button from '../../ui/Button';
import ShowTransition from '../../ui/ShowTransition'; import ShowTransition from '../../ui/ShowTransition';
import './ChatBadge.scss'; import styles from './ChatBadge.module.scss';
type OwnProps = { type OwnProps = {
chat: ApiChat; chat: ApiChat;
@ -36,9 +33,12 @@ type OwnProps = {
forceHidden?: boolean | Signal<boolean>; forceHidden?: boolean | Signal<boolean>;
topics?: Record<number, ApiTopic>; topics?: Record<number, ApiTopic>;
isSelected?: boolean; isSelected?: boolean;
isOnAvatar?: boolean;
transitionClassName?: string;
badgeClassName?: string;
}; };
const ChatBadge: FC<OwnProps> = ({ const ChatBadge = ({
topic, topic,
topics, topics,
chat, chat,
@ -50,10 +50,12 @@ const ChatBadge: FC<OwnProps> = ({
isSavedDialog, isSavedDialog,
hasMiniApp, hasMiniApp,
isSelected, isSelected,
}) => { isOnAvatar,
transitionClassName,
badgeClassName,
}: OwnProps) => {
const { requestMainWebView } = getActions(); const { requestMainWebView } = getActions();
const oldLang = useOldLang();
const lang = useLang(); const lang = useLang();
const { const {
@ -101,14 +103,6 @@ const ChatBadge: FC<OwnProps> = ({
|| isTopicUnopened || hasMiniApp, || isTopicUnopened || hasMiniApp,
); );
const isUnread = Boolean((unreadCount || hasUnreadMark) && !isSavedDialog);
const className = buildClassName(
'ChatBadge',
!shouldBeUnMuted && 'muted',
!isUnread && isPinned && 'pinned',
isUnread && 'unread',
);
const handleOpenApp = useLastCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { const handleOpenApp = useLastCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.stopPropagation(); e.stopPropagation();
@ -121,30 +115,32 @@ const ChatBadge: FC<OwnProps> = ({
}); });
function renderContent() { function renderContent() {
const baseClassName = buildClassName(styles.badge, !shouldBeUnMuted && styles.muted, badgeClassName);
const unreadReactionsElement = unreadReactionsCount && ( const unreadReactionsElement = unreadReactionsCount && (
<div className={buildClassName('ChatBadge reaction', !shouldBeUnMuted && 'muted')}> <div className={buildClassName(baseClassName, styles.reaction, styles.round)}>
<Icon name="heart" /> <Icon name="heart" />
</div> </div>
); );
const unreadMentionsElement = unreadMentionsCount && ( const unreadMentionsElement = unreadMentionsCount && (
<div className="ChatBadge mention"> <div className={buildClassName(baseClassName, styles.mention, styles.round)}>
<Icon name="mention" /> <Icon name="mention" />
</div> </div>
); );
const unopenedTopicElement = isTopicUnopened && ( const unopenedTopicElement = isTopicUnopened && (
<div className={buildClassName('ChatBadge unopened', !shouldBeUnMuted && 'muted')} /> <div className={buildClassName(baseClassName, styles.unopened)} />
); );
const unreadCountElement = (hasUnreadMark || unreadCount) ? ( const unreadCountElement = (hasUnreadMark || unreadCount) ? (
<div className={className}> <div className={baseClassName}>
{!hasUnreadMark && <AnimatedCounter text={formatIntegerCompact(lang, unreadCount!)} />} {!hasUnreadMark && <AnimatedCounter text={formatIntegerCompact(lang, unreadCount!)} />}
</div> </div>
) : undefined; ) : undefined;
const pinnedElement = isPinned && ( const pinnedElement = isPinned && (
<div className={className}> <div className={buildClassName(baseClassName, styles.pinned)}>
<Icon name="pinned-chat" /> <Icon name="pinned-chat" />
</div> </div>
); );
@ -152,12 +148,12 @@ const ChatBadge: FC<OwnProps> = ({
const miniAppButton = hasMiniApp && ( const miniAppButton = hasMiniApp && (
<Button <Button
color={isSelected ? 'secondary' : 'primary'} color={isSelected ? 'secondary' : 'primary'}
className="ChatBadge miniapp" className={buildClassName(baseClassName, styles.miniapp)}
pill pill
size="tiny" size="tiny"
onClick={handleOpenApp} onClick={handleOpenApp}
> >
{oldLang('BotOpen')} {lang('BotChatMiniAppOpen')}
</Button> </Button>
); );
@ -185,14 +181,23 @@ const ChatBadge: FC<OwnProps> = ({
} }
return ( return (
<div className="ChatBadge-wrapper"> <div className={styles.wrapper}>
{elements} {elements}
</div> </div>
); );
} }
return ( return (
<ShowTransition isCustom className="ChatBadge-transition" isOpen={isShown}> <ShowTransition
isCustom
className={buildClassName(
styles.transition,
isSelected && styles.selected,
isOnAvatar && styles.onAvatar,
transitionClassName,
)}
isOpen={isShown}
>
{renderContent()} {renderContent()}
</ShowTransition> </ShowTransition>
); );

View File

@ -1,13 +1,12 @@
.wrapper { .wrapper {
display: flex; display: flex;
gap: 0.25rem; gap: 0.25rem;
height: 0.875rem; padding-top: 0.125rem;
margin-top: 0.125rem;
} }
.tag { .tag {
--emoji-size: 0.75rem; --emoji-size: 0.875rem;
--custom-emoji-size: 0.75rem; --custom-emoji-size: 0.875rem;
position: relative; position: relative;
@ -17,10 +16,11 @@
justify-content: center; justify-content: center;
min-width: 1.5rem; min-width: 1.5rem;
padding: 0 0.25rem; height: 1.125rem;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem; border-radius: 0.25rem;
font-size: 0.6875rem; font-size: 0.75rem;
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
line-height: 1; line-height: 1;
color: var(--accent-color); color: var(--accent-color);

View File

@ -3,21 +3,25 @@ import { memo } from '../../../lib/teact/teact';
import type { ApiChatFolder } from '../../../api/types'; import type { ApiChatFolder } from '../../../api/types';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { REM } from '../../common/helpers/mediaDimensions';
import { getApiPeerColorClass } from '../../common/helpers/peerColor'; import { getApiPeerColorClass } from '../../common/helpers/peerColor';
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities'; import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
import styles from './ChatTags.module.scss'; import styles from './ChatTags.module.scss';
const MAX_VISIBLE_TAGS = 3; const MAX_VISIBLE_TAGS = 3;
const CUSTOM_EMOJI_SIZE = 0.875 * REM;
type OwnProps = { type OwnProps = {
orderedFolderIds?: number[]; orderedFolderIds?: number[];
chatFoldersById?: Record<number, ApiChatFolder>; chatFoldersById?: Record<number, ApiChatFolder>;
itemClassName?: string;
}; };
const ChatTags = ({ const ChatTags = ({
orderedFolderIds, orderedFolderIds,
chatFoldersById, chatFoldersById,
itemClassName,
}: OwnProps) => { }: OwnProps) => {
if (!orderedFolderIds) { if (!orderedFolderIds) {
return undefined; return undefined;
@ -34,22 +38,22 @@ const ChatTags = ({
<div <div
key={folder.id} key={folder.id}
className={buildClassName( className={buildClassName(
'ChatTags',
styles.tag, styles.tag,
getApiPeerColorClass({ color: folder.color }), getApiPeerColorClass({ color: folder.color }),
itemClassName,
)} )}
> >
{renderTextWithEntities({ {renderTextWithEntities({
text: folder.title.text, text: folder.title.text,
entities: folder.title.entities, entities: folder.title.entities,
noCustomEmojiPlayback: folder.noTitleAnimations, noCustomEmojiPlayback: folder.noTitleAnimations,
emojiSize: 12, emojiSize: CUSTOM_EMOJI_SIZE,
})} })}
</div> </div>
); );
})} })}
{remainingCount > 0 && ( {remainingCount > 0 && (
<div className={`ChatTags ${styles.tag} ${styles.tagColorMore}`}> <div className={buildClassName(styles.tag, styles.tagColorMore, itemClassName)}>
+ +
{remainingCount} {remainingCount}
</div> </div>

View File

@ -14,7 +14,6 @@
:global(.LastMessageMeta) { :global(.LastMessageMeta) {
margin-top: -0.5rem; margin-top: -0.5rem;
margin-right: 0 !important;
padding-top: 0; padding-top: 0;
} }
} }

View File

@ -225,6 +225,7 @@ const Topic: FC<OwnProps & StateProps> = ({
topic={topic} topic={topic}
wasTopicOpened={wasTopicOpened} wasTopicOpened={wasTopicOpened}
topics={topics} topics={topics}
isSelected={isSelected}
/> />
</div> </div>
</div> </div>

View File

@ -51,7 +51,7 @@ export default function useChatListEntry({
isTopic, isTopic,
isSavedDialog, isSavedDialog,
isPreview, isPreview,
noForumTitle, hasTags,
onReorderAnimationEnd, onReorderAnimationEnd,
}: { }: {
chat?: ApiChat; chat?: ApiChat;
@ -67,11 +67,11 @@ export default function useChatListEntry({
isTopic?: boolean; isTopic?: boolean;
isSavedDialog?: boolean; isSavedDialog?: boolean;
isPreview?: boolean; isPreview?: boolean;
hasTags?: boolean;
animationType: ChatAnimationTypes; animationType: ChatAnimationTypes;
orderDiff: number; orderDiff: number;
withInterfaceAnimations?: boolean; withInterfaceAnimations?: boolean;
noForumTitle?: boolean;
onReorderAnimationEnd?: NoneToVoidFunction; onReorderAnimationEnd?: NoneToVoidFunction;
}) { }) {
const lang = useLang(); const lang = useLang();
@ -156,7 +156,7 @@ export default function useChatListEntry({
renderLastMessage={renderLastMessageOrTyping} renderLastMessage={renderLastMessageOrTyping}
observeIntersection={observeIntersection} observeIntersection={observeIntersection}
topics={topics} topics={topics}
noForumTitle={noForumTitle} hasTags={hasTags}
/> />
); );
} }

View File

@ -197,6 +197,13 @@
filter: brightness(1.1); filter: brightness(1.1);
} }
} }
.search-result-miniapp-button {
height: auto !important;
padding: 0 0.4375rem !important;
font-size: 0.875rem !important;
line-height: 1.5rem;
}
} }
.search-section { .search-section {

View File

@ -20,8 +20,8 @@ import { extractCurrentThemeParams } from '../../../util/themeStyle';
import useChatContextActions from '../../../hooks/useChatContextActions'; import useChatContextActions from '../../../hooks/useChatContextActions';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import useSelectWithEnter from '../../../hooks/useSelectWithEnter'; import useSelectWithEnter from '../../../hooks/useSelectWithEnter';
import GroupChatInfo from '../../common/GroupChatInfo'; import GroupChatInfo from '../../common/GroupChatInfo';
@ -58,7 +58,7 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
onClick, onClick,
}) => { }) => {
const { requestMainWebView, updateChatMutedState, openQuickPreview } = getActions(); const { requestMainWebView, updateChatMutedState, openQuickPreview } = getActions();
const oldLang = useOldLang(); const lang = useLang();
const [isMuteModalOpen, openMuteModal, closeMuteModal] = useFlag(); const [isMuteModalOpen, openMuteModal, closeMuteModal] = useFlag();
const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag(); const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag();
@ -150,13 +150,13 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
)} )}
{withOpenAppButton && user?.hasMainMiniApp && ( {withOpenAppButton && user?.hasMainMiniApp && (
<Button <Button
className="ChatBadge miniapp" className="search-result-miniapp-button"
pill pill
fluid fluid
size="tiny" size="tiny"
onClick={handleOpenApp} onClick={handleOpenApp}
> >
{oldLang('BotOpen')} {lang('BotChatMiniAppOpen')}
</Button> </Button>
)} )}
{shouldRenderMuteModal && ( {shouldRenderMuteModal && (

View File

@ -214,7 +214,7 @@
.settings-folders-color-circle { .settings-folders-color-circle {
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 2rem; right: 2.5rem;
transform: translateY(-50%); transform: translateY(-50%);
width: 1.25rem; width: 1.25rem;

View File

@ -250,7 +250,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
const draggedTop = (state.orderedFolderIds?.indexOf(folder.id) ?? 0) * FOLDER_HEIGHT_PX; const draggedTop = (state.orderedFolderIds?.indexOf(folder.id) ?? 0) * FOLDER_HEIGHT_PX;
const top = (state.dragOrderIds?.indexOf(folder.id) ?? 0) * FOLDER_HEIGHT_PX; const top = (state.dragOrderIds?.indexOf(folder.id) ?? 0) * FOLDER_HEIGHT_PX;
const shouldRenderColor = folder?.color !== undefined && folder.color !== -1 && isPremium; const shouldRenderColor = folder?.color !== undefined && folder.color !== -1 && areTagsEnabled;
if (folder.id === ALL_FOLDER_ID) { if (folder.id === ALL_FOLDER_ID) {
return ( return (
@ -292,7 +292,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
onDrag={handleDrag} onDrag={handleDrag}
onDragEnd={handleDragEnd} onDragEnd={handleDragEnd}
style={`top: ${isDragged ? draggedTop : top}px;`} style={`top: ${isDragged ? draggedTop : top}px;`}
knobStyle={`${lang.isRtl ? 'left' : 'right'}: ${shouldRenderColor ? '3.5rem' : '3rem'};`} knobStyle={`${lang.isRtl ? 'left' : 'right'}: ${shouldRenderColor ? '4rem' : '2.5rem'};`}
isDisabled={isBlocked || !isActive} isDisabled={isBlocked || !isActive}
> >
<ListItem <ListItem

View File

@ -1,15 +1,20 @@
.root { .root {
flex-shrink: 0; flex-shrink: 0;
min-width: 1.5rem; min-width: 1.375rem;
height: 1.5rem; height: 1.375rem;
padding: 0 0.4375rem; padding: 0 0.4375rem;
border-radius: 0.75rem; border-radius: 0.75rem;
font-size: 0.875rem; font-size: 0.875rem;
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
line-height: 1.5625rem; line-height: 1.4375rem;
text-align: center; text-align: center;
:global(body.is-ios) &,
:global(body.is-macos) & {
line-height: 1.375rem;
}
} }
.default { .default {

View File

@ -1,4 +1,3 @@
import type { FC } from '../../lib/teact/teact';
import { memo } from '../../lib/teact/teact'; import { memo } from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
@ -14,11 +13,11 @@ type OwnProps = {
isAlternateColor?: boolean; isAlternateColor?: boolean;
}; };
const Badge: FC<OwnProps> = ({ const Badge = ({
text, text,
className, className,
isAlternateColor, isAlternateColor,
}) => { }: OwnProps) => {
return ( return (
<ShowTransition <ShowTransition
className={buildClassName(styles.root, isAlternateColor ? styles.alternate : styles.default, className)} className={buildClassName(styles.root, isAlternateColor ? styles.alternate : styles.default, className)}

View File

@ -223,14 +223,6 @@
padding: 0.5625rem; padding: 0.5625rem;
} }
&.chat-item-with-tags {
.ListItem-button {
align-items: flex-start;
height: 72px;
padding: 0.375rem 0.5625rem;
}
}
&.contact-list-item { &.contact-list-item {
.ListItem-button { .ListItem-button {
padding: 0.5rem; padding: 0.5rem;

View File

@ -3,8 +3,8 @@
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: block; font-display: block;
src: url("./icons.woff2?2c0ef26912799b44f650248acc1b48ec") format("woff2"), src: url("./icons.woff2?c4762f96fd2b9a4edb1c0206e3cf5af6") format("woff2"),
url("./icons.woff?2c0ef26912799b44f650248acc1b48ec") format("woff"); url("./icons.woff?c4762f96fd2b9a4edb1c0206e3cf5af6") format("woff");
} }
.icon-char::before { .icon-char::before {

Binary file not shown.

Binary file not shown.

View File

@ -539,6 +539,7 @@ export interface LangPair {
'OpenUrlTitle': undefined; 'OpenUrlTitle': undefined;
'OpenUrlConfirm': undefined; 'OpenUrlConfirm': undefined;
'BotWebViewOpenBot': undefined; 'BotWebViewOpenBot': undefined;
'BotChatMiniAppOpen': undefined;
'WebAppReloadPage': undefined; 'WebAppReloadPage': undefined;
'WebAppRemoveBot': undefined; 'WebAppRemoveBot': undefined;
'WebAppAddToAttachmentAdd': undefined; 'WebAppAddToAttachmentAdd': undefined;