Settings: Performance mode (#3045)
Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>
This commit is contained in:
parent
8535fcff66
commit
4f42b676ce
1
package-lock.json
generated
1
package-lock.json
generated
@ -107,6 +107,7 @@
|
||||
}
|
||||
},
|
||||
"dev/eslint-multitab": {
|
||||
"name": "eslint-plugin-eslint-multitab-tt",
|
||||
"version": "0.0.0",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
|
||||
@ -5,7 +5,6 @@ import React, {
|
||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiGroupCall } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import { selectChatGroupCall } from '../../../global/selectors/calls';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -26,7 +25,6 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
groupCall?: ApiGroupCall;
|
||||
isActive: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const GroupCallTopPane: FC<OwnProps & StateProps> = ({
|
||||
@ -35,7 +33,6 @@ const GroupCallTopPane: FC<OwnProps & StateProps> = ({
|
||||
className,
|
||||
groupCall,
|
||||
hasPinnedOffset,
|
||||
animationLevel,
|
||||
}) => {
|
||||
const {
|
||||
requestMasterAndJoinGroupCall,
|
||||
@ -112,12 +109,12 @@ const GroupCallTopPane: FC<OwnProps & StateProps> = ({
|
||||
<div className="avatars">
|
||||
{fetchedParticipants.map((p) => {
|
||||
if (!p) return undefined;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
key={p.user ? p.user.id : p.chat.id}
|
||||
chat={p.chat}
|
||||
user={p.user}
|
||||
animationLevel={animationLevel}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -142,7 +139,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
? groupCall.participantsCount > 0 && groupCall.isLoaded
|
||||
: chat && chat.isCallNotEmpty && chat.isCallActive,
|
||||
),
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(GroupCallTopPane));
|
||||
|
||||
@ -6,7 +6,6 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import '../../../global/actions/calls';
|
||||
|
||||
import type { ApiPhoneCall, ApiUser } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import {
|
||||
IS_ANDROID,
|
||||
@ -41,7 +40,6 @@ type StateProps = {
|
||||
phoneCall?: ApiPhoneCall;
|
||||
isOutgoing: boolean;
|
||||
isCallPanelVisible?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const PhoneCall: FC<StateProps> = ({
|
||||
@ -49,7 +47,6 @@ const PhoneCall: FC<StateProps> = ({
|
||||
isOutgoing,
|
||||
phoneCall,
|
||||
isCallPanelVisible,
|
||||
animationLevel,
|
||||
}) => {
|
||||
const lang = useLang();
|
||||
const {
|
||||
@ -240,9 +237,6 @@ const PhoneCall: FC<StateProps> = ({
|
||||
user={user}
|
||||
size="jumbo"
|
||||
className={hasVideo || hasPresentation ? styles.blurred : ''}
|
||||
withVideo
|
||||
noLoop={phoneCall?.state !== 'requesting'}
|
||||
animationLevel={animationLevel}
|
||||
/>
|
||||
{phoneCall?.screencastState === 'active' && streams?.presentation
|
||||
&& <video className={styles.mainVideo} muted autoPlay playsInline srcObject={streams.presentation} />}
|
||||
@ -379,7 +373,6 @@ export default memo(withGlobal(
|
||||
user,
|
||||
isOutgoing: phoneCall?.adminId === currentUserId,
|
||||
phoneCall: isMasterTab ? phoneCall : undefined,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(PhoneCall));
|
||||
|
||||
@ -4,8 +4,8 @@ import React, {
|
||||
} from '../../lib/teact/teact';
|
||||
import { getGlobal } from '../../global';
|
||||
|
||||
import { ANIMATION_LEVEL_MAX } from '../../config';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { selectCanAnimateInterface } from '../../global/selectors';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
|
||||
@ -25,7 +25,7 @@ const AnimatedCounter: FC<OwnProps> = ({
|
||||
const prevTextRef = useRef<string>();
|
||||
const [isAnimating, markAnimating, unmarkAnimating] = useFlag(false);
|
||||
|
||||
const shouldAnimate = getGlobal().settings.byKey.animationLevel === ANIMATION_LEVEL_MAX;
|
||||
const shouldAnimate = selectCanAnimateInterface(getGlobal());
|
||||
|
||||
const textElement = useMemo(() => {
|
||||
if (!shouldAnimate) {
|
||||
|
||||
@ -8,11 +8,9 @@ import type {
|
||||
ApiChat, ApiPhoto, ApiUser, ApiUserStatus,
|
||||
} from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import { ApiMediaFormat } from '../../api/types';
|
||||
|
||||
import { ANIMATION_LEVEL_MAX, IS_TEST } from '../../config';
|
||||
import { VIDEO_AVATARS_DISABLED } from '../../util/windowEnvironment';
|
||||
import { IS_TEST } from '../../config';
|
||||
import {
|
||||
getChatAvatarHash,
|
||||
getChatTitle,
|
||||
@ -30,7 +28,6 @@ import renderText from './helpers/renderText';
|
||||
import useMedia from '../../hooks/useMedia';
|
||||
import useMediaTransition from '../../hooks/useMediaTransition';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
|
||||
|
||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||
|
||||
@ -52,12 +49,9 @@ type OwnProps = {
|
||||
text?: string;
|
||||
isSavedMessages?: boolean;
|
||||
withVideo?: boolean;
|
||||
noLoop?: boolean;
|
||||
loopIndefinitely?: boolean;
|
||||
animationLevel?: AnimationLevel;
|
||||
noPersonalPhoto?: boolean;
|
||||
lastSyncTime?: number;
|
||||
forceVideo?: boolean;
|
||||
observeIntersection?: ObserveFn;
|
||||
onClick?: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>, hasMedia: boolean) => void;
|
||||
};
|
||||
@ -72,13 +66,9 @@ const Avatar: FC<OwnProps> = ({
|
||||
text,
|
||||
isSavedMessages,
|
||||
withVideo,
|
||||
noLoop,
|
||||
loopIndefinitely,
|
||||
lastSyncTime,
|
||||
forceVideo,
|
||||
animationLevel,
|
||||
noPersonalPhoto,
|
||||
observeIntersection,
|
||||
onClick,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -90,14 +80,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
let imageHash: string | undefined;
|
||||
let videoHash: string | undefined;
|
||||
|
||||
const canShowVideo = (
|
||||
withVideo && !VIDEO_AVATARS_DISABLED && animationLevel === ANIMATION_LEVEL_MAX
|
||||
&& user?.isPremium && user?.hasVideoAvatar
|
||||
);
|
||||
const isIntersectingForVideo = useIsIntersecting(
|
||||
ref, canShowVideo ? observeIntersection : undefined,
|
||||
);
|
||||
const shouldLoadVideo = isIntersectingForVideo && (canShowVideo || (forceVideo && photo?.isVideo));
|
||||
const shouldLoadVideo = withVideo && photo?.isVideo;
|
||||
|
||||
const shouldFetchBig = size === 'jumbo';
|
||||
if (!isSavedMessages && !isDeleted) {
|
||||
@ -117,7 +100,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
const videoBlobUrl = useMedia(videoHash, !shouldLoadVideo, ApiMediaFormat.BlobUrl, lastSyncTime);
|
||||
const hasBlobUrl = Boolean(imgBlobUrl || videoBlobUrl);
|
||||
// `videoBlobUrl` can be taken from memory cache, so we need to check `shouldLoadVideo` again
|
||||
const shouldPlayVideo = Boolean(isIntersectingForVideo && videoBlobUrl && shouldLoadVideo);
|
||||
const shouldPlayVideo = Boolean(videoBlobUrl && shouldLoadVideo);
|
||||
|
||||
const transitionClassNames = useMediaTransition(hasBlobUrl);
|
||||
|
||||
@ -134,10 +117,10 @@ const Avatar: FC<OwnProps> = ({
|
||||
if (loopIndefinitely) return;
|
||||
|
||||
videoLoopCountRef.current += 1;
|
||||
if (videoLoopCountRef.current >= LOOP_COUNT || noLoop) {
|
||||
if (videoLoopCountRef.current >= LOOP_COUNT) {
|
||||
video.style.display = 'none';
|
||||
}
|
||||
}, [loopIndefinitely, noLoop, videoBlobUrl]);
|
||||
}, [loopIndefinitely, videoBlobUrl]);
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ const CustomEmoji: FC<OwnProps> = ({
|
||||
}
|
||||
|
||||
// An alternative to `withGlobal` to avoid adding numerous global containers
|
||||
const customEmoji = useCustomEmoji(documentId);
|
||||
const { customEmoji, canPlay } = useCustomEmoji(documentId);
|
||||
|
||||
const loopCountRef = useRef(0);
|
||||
const [shouldLoop, setShouldLoop] = useState(true);
|
||||
@ -131,13 +131,13 @@ const CustomEmoji: FC<OwnProps> = ({
|
||||
sticker={customEmoji}
|
||||
isSmall={!isBig}
|
||||
size={size}
|
||||
noPlay={noPlay}
|
||||
noPlay={noPlay || !canPlay}
|
||||
customColor={customColor}
|
||||
thumbClassName={styles.thumb}
|
||||
fullMediaClassName={styles.media}
|
||||
shouldLoop={shouldLoop}
|
||||
loopLimit={loopLimit}
|
||||
shouldPreloadPreview={shouldPreloadPreview}
|
||||
shouldPreloadPreview={shouldPreloadPreview || noPlay || !canPlay}
|
||||
forceOnHeavyAnimation={forceOnHeavyAnimation}
|
||||
observeIntersectionForLoading={observeIntersectionForLoading}
|
||||
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
||||
|
||||
@ -28,6 +28,7 @@ import animateHorizontalScroll from '../../util/animateHorizontalScroll';
|
||||
import { pickTruthy, unique } from '../../util/iteratees';
|
||||
import { isSameReaction } from '../../global/helpers';
|
||||
import {
|
||||
selectCanPlayAnimatedEmojis,
|
||||
selectIsAlwaysHighPriorityEmoji,
|
||||
selectIsChatWithSelf,
|
||||
selectIsCurrentUserPremium,
|
||||
@ -316,7 +317,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
) : (
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
noPlay={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
sharedCanvasRef={withSharedCanvas ? (isHq ? sharedCanvasHqRef : sharedCanvasRef) : undefined}
|
||||
/>
|
||||
@ -332,7 +333,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
size={STICKER_SIZE_PICKER_HEADER}
|
||||
title={stickerSet.title}
|
||||
className={buttonClassName}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
noPlay={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
noContextMenu
|
||||
isCurrentUserPremium
|
||||
@ -451,7 +452,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
recentStatusEmojis: isStatusPicker ? recentStatusEmojis : undefined,
|
||||
stickerSetsById,
|
||||
addedCustomEmojiIds: global.customEmojis.added.setIds,
|
||||
canAnimate: global.settings.byKey.shouldLoopStickers,
|
||||
canAnimate: selectCanPlayAnimatedEmojis(global),
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
customEmojiFeaturedIds,
|
||||
|
||||
@ -7,7 +7,7 @@ import type { FC } from '../../lib/teact/teact';
|
||||
import type { ApiSticker, ApiStickerSet } from '../../api/types';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../global/selectors';
|
||||
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -24,10 +24,12 @@ export type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
customEmojiSets?: ApiStickerSet[];
|
||||
canPlayAnimatedEmojis?: boolean;
|
||||
};
|
||||
|
||||
const CustomEmojiSetsModal: FC<OwnProps & StateProps> = ({
|
||||
customEmojiSets,
|
||||
canPlayAnimatedEmojis,
|
||||
onClose,
|
||||
}) => {
|
||||
const { openStickerSet } = getActions();
|
||||
@ -64,6 +66,7 @@ const CustomEmojiSetsModal: FC<OwnProps & StateProps> = ({
|
||||
stickerSet={customEmojiSet}
|
||||
onClick={handleSetClick}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
noPlay={!canPlayAnimatedEmojis}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -77,6 +80,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
customEmojiSets,
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
},
|
||||
)(CustomEmojiSetsModal));
|
||||
|
||||
@ -3,7 +3,6 @@ import React, { useCallback, memo } from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiChat } from '../../api/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
|
||||
import { selectIsChatWithSelf, selectUser } from '../../global/selectors';
|
||||
import {
|
||||
@ -42,7 +41,6 @@ type StateProps = {
|
||||
currentUserId: string | undefined;
|
||||
canDeleteForAll?: boolean;
|
||||
contactName?: string;
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const DeleteChatModal: FC<OwnProps & StateProps> = ({
|
||||
@ -57,7 +55,6 @@ const DeleteChatModal: FC<OwnProps & StateProps> = ({
|
||||
currentUserId,
|
||||
canDeleteForAll,
|
||||
contactName,
|
||||
animationLevel,
|
||||
onClose,
|
||||
onCloseAnimationEnd,
|
||||
}) => {
|
||||
@ -128,8 +125,6 @@ const DeleteChatModal: FC<OwnProps & StateProps> = ({
|
||||
size="tiny"
|
||||
chat={chat}
|
||||
isSavedMessages={isChatWithSelf}
|
||||
animationLevel={animationLevel}
|
||||
withVideo
|
||||
/>
|
||||
<h3 className="modal-title">{lang(renderTitle())}</h3>
|
||||
</div>
|
||||
@ -243,7 +238,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
currentUserId: global.currentUserId,
|
||||
canDeleteForAll,
|
||||
contactName,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(DeleteChatModal));
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
body.animation-level-1 & {
|
||||
body.no-page-transitions & {
|
||||
.ripple-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -74,6 +74,8 @@
|
||||
position: absolute;
|
||||
|
||||
.bubble {
|
||||
--offset-y: 0;
|
||||
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +152,7 @@ const GifButton: FC<OwnProps> = ({
|
||||
className="gif-unsave-button"
|
||||
color="dark"
|
||||
pill
|
||||
noFastClick
|
||||
onClick={handleUnsaveClick}
|
||||
>
|
||||
<i className="icon icon-close gif-unsave-button-icon" />
|
||||
|
||||
@ -9,7 +9,6 @@ import type {
|
||||
ApiChat, ApiTopic, ApiThreadInfo, ApiTypingStatus,
|
||||
} from '../../api/types';
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import type { LangFn } from '../../hooks/useLang';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
@ -20,7 +19,11 @@ import {
|
||||
isChatSuperGroup,
|
||||
} from '../../global/helpers';
|
||||
import {
|
||||
selectChat, selectChatMessages, selectChatOnlineCount, selectThreadInfo, selectThreadMessagesCount,
|
||||
selectChat,
|
||||
selectChatMessages,
|
||||
selectChatOnlineCount,
|
||||
selectThreadInfo,
|
||||
selectThreadMessagesCount,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import renderText from './helpers/renderText';
|
||||
@ -47,7 +50,6 @@ type OwnProps = {
|
||||
withFullInfo?: boolean;
|
||||
withUpdatingStatus?: boolean;
|
||||
withChatType?: boolean;
|
||||
withVideoAvatar?: boolean;
|
||||
noRtl?: boolean;
|
||||
noAvatar?: boolean;
|
||||
onClick?: VoidFunction;
|
||||
@ -60,7 +62,6 @@ type StateProps =
|
||||
topic?: ApiTopic;
|
||||
onlineCount?: number;
|
||||
areMessagesLoaded: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
messagesCount?: number;
|
||||
}
|
||||
& Pick<GlobalState, 'lastSyncTime'>;
|
||||
@ -77,13 +78,11 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
withFullInfo,
|
||||
withUpdatingStatus,
|
||||
withChatType,
|
||||
withVideoAvatar,
|
||||
threadInfo,
|
||||
noRtl,
|
||||
chat,
|
||||
onlineCount,
|
||||
areMessagesLoaded,
|
||||
animationLevel,
|
||||
lastSyncTime,
|
||||
topic,
|
||||
messagesCount,
|
||||
@ -187,12 +186,14 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
size={avatarSize}
|
||||
chat={chat}
|
||||
onClick={withMediaViewer ? handleAvatarViewerOpen : undefined}
|
||||
withVideo={withVideoAvatar}
|
||||
animationLevel={animationLevel}
|
||||
/>
|
||||
)}
|
||||
{isTopic && (
|
||||
<TopicIcon topic={topic!} className="topic-header-icon" size={TOPIC_ICON_SIZE} />
|
||||
<TopicIcon
|
||||
topic={topic!}
|
||||
className="topic-header-icon"
|
||||
size={TOPIC_ICON_SIZE}
|
||||
/>
|
||||
)}
|
||||
<div className="info">
|
||||
{topic
|
||||
@ -238,7 +239,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
onlineCount,
|
||||
topic,
|
||||
areMessagesLoaded,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
messagesCount,
|
||||
};
|
||||
},
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: 0%;
|
||||
|
||||
:global(body.animation-level-2) & {
|
||||
:global(body:not(.no-page-transitions)) & {
|
||||
animation: 500ms ease-in circle-cut forwards;
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
:global(body.animation-level-2) .dots {
|
||||
:global(body:not(.no-page-transitions)) .dots {
|
||||
animation: 20s linear infinite dots;
|
||||
|
||||
&::before {
|
||||
|
||||
@ -79,13 +79,13 @@ function MessageText({
|
||||
{[
|
||||
withSharedCanvas && <canvas ref={sharedCanvasRef} className="shared-canvas" />,
|
||||
withSharedCanvas && <canvas ref={sharedCanvasHqRef} className="shared-canvas" />,
|
||||
renderTextWithEntities(
|
||||
trimText(text!, truncateLength),
|
||||
renderTextWithEntities({
|
||||
text: trimText(text!, truncateLength),
|
||||
entities,
|
||||
highlight,
|
||||
emojiSize,
|
||||
shouldRenderAsHtml,
|
||||
message.id,
|
||||
messageId: message.id,
|
||||
isSimple,
|
||||
isProtected,
|
||||
observeIntersectionForLoading,
|
||||
@ -93,8 +93,8 @@ function MessageText({
|
||||
withTranslucentThumbs,
|
||||
sharedCanvasRef,
|
||||
sharedCanvasHqRef,
|
||||
textCacheBusterRef.current.toString(),
|
||||
),
|
||||
cacheBuster: textCacheBusterRef.current.toString(),
|
||||
}),
|
||||
].flat().filter(Boolean)}
|
||||
</>
|
||||
);
|
||||
|
||||
@ -8,10 +8,13 @@ import type {
|
||||
ApiUser, ApiTypingStatus, ApiUserStatus, ApiChatMember,
|
||||
} from '../../api/types';
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import { selectChatMessages, selectUser, selectUserStatus } from '../../global/selectors';
|
||||
import {
|
||||
selectChatMessages,
|
||||
selectUser,
|
||||
selectUserStatus,
|
||||
} from '../../global/selectors';
|
||||
import { getMainUsername, getUserStatus, isUserOnline } from '../../global/helpers';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import renderText from './helpers/renderText';
|
||||
@ -34,7 +37,6 @@ type OwnProps = {
|
||||
withUsername?: boolean;
|
||||
withFullInfo?: boolean;
|
||||
withUpdatingStatus?: boolean;
|
||||
withVideoAvatar?: boolean;
|
||||
emojiStatusSize?: number;
|
||||
noStatusOrTyping?: boolean;
|
||||
noRtl?: boolean;
|
||||
@ -46,7 +48,6 @@ type StateProps =
|
||||
user?: ApiUser;
|
||||
userStatus?: ApiUserStatus;
|
||||
isSavedMessages?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
areMessagesLoaded: boolean;
|
||||
}
|
||||
& Pick<GlobalState, 'lastSyncTime'>;
|
||||
@ -60,7 +61,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
withUsername,
|
||||
withFullInfo,
|
||||
withUpdatingStatus,
|
||||
withVideoAvatar,
|
||||
emojiStatusSize,
|
||||
noStatusOrTyping,
|
||||
noRtl,
|
||||
@ -68,7 +68,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
userStatus,
|
||||
isSavedMessages,
|
||||
areMessagesLoaded,
|
||||
animationLevel,
|
||||
lastSyncTime,
|
||||
adminMember,
|
||||
}) => {
|
||||
@ -173,8 +172,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
user={user}
|
||||
isSavedMessages={isSavedMessages}
|
||||
onClick={withMediaViewer ? handleAvatarViewerOpen : undefined}
|
||||
withVideo={withVideoAvatar}
|
||||
animationLevel={animationLevel}
|
||||
/>
|
||||
<div className="info">
|
||||
{renderNameTitle()}
|
||||
@ -198,7 +195,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
userStatus,
|
||||
isSavedMessages,
|
||||
areMessagesLoaded,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(PrivateChatInfo));
|
||||
|
||||
@ -8,7 +8,6 @@ import type {
|
||||
ApiUser, ApiChat, ApiUserStatus, ApiTopic, ApiPhoto,
|
||||
} from '../../api/types';
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
@ -55,7 +54,6 @@ type StateProps =
|
||||
userStatus?: ApiUserStatus;
|
||||
chat?: ApiChat;
|
||||
isSavedMessages?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
mediaId?: number;
|
||||
avatarOwnerId?: string;
|
||||
topic?: ApiTopic;
|
||||
@ -78,7 +76,6 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
chat,
|
||||
isSavedMessages,
|
||||
connectionState,
|
||||
animationLevel,
|
||||
mediaId,
|
||||
avatarOwnerId,
|
||||
topic,
|
||||
@ -103,7 +100,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
const prevAvatarOwnerId = usePrevious(avatarOwnerId);
|
||||
const [hasSlideAnimation, setHasSlideAnimation] = useState(true);
|
||||
const slideAnimation = hasSlideAnimation
|
||||
? animationLevel >= 1 ? (lang.isRtl ? 'slideOptimizedRtl' : 'slideOptimized') : 'none'
|
||||
? (lang.isRtl ? 'slideOptimizedRtl' : 'slideOptimized')
|
||||
: 'none';
|
||||
|
||||
const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0);
|
||||
@ -355,7 +352,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const userStatus = selectUserStatus(global, userId);
|
||||
const chat = selectChat(global, userId);
|
||||
const isSavedMessages = !forceShowSelf && user && user.isSelf;
|
||||
const { animationLevel } = global.settings.byKey;
|
||||
const { mediaId, avatarOwnerId } = selectTabState(global).mediaViewer;
|
||||
const isForum = chat?.isForum;
|
||||
const { threadId: currentTopicId } = selectCurrentMessageList(global) || {};
|
||||
@ -373,7 +369,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
userFallbackPhoto: userFullInfo?.fallbackPhoto,
|
||||
chatProfilePhoto: chatFullInfo?.profilePhoto,
|
||||
isSavedMessages,
|
||||
animationLevel,
|
||||
mediaId,
|
||||
avatarOwnerId,
|
||||
...(topic && {
|
||||
|
||||
@ -28,7 +28,7 @@ import './StickerButton.scss';
|
||||
type OwnProps<T> = {
|
||||
sticker: ApiSticker;
|
||||
size: number;
|
||||
noAnimate?: boolean;
|
||||
noPlay?: boolean;
|
||||
title?: string;
|
||||
className?: string;
|
||||
noContextMenu?: boolean;
|
||||
@ -63,7 +63,7 @@ const contentForStatusMenuContext = [
|
||||
const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult | undefined = undefined>({
|
||||
sticker,
|
||||
size,
|
||||
noAnimate,
|
||||
noPlay,
|
||||
title,
|
||||
className,
|
||||
noContextMenu,
|
||||
@ -102,7 +102,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
|
||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||
const shouldLoad = isIntersecting;
|
||||
const shouldPlay = isIntersecting && !noAnimate;
|
||||
const shouldPlay = isIntersecting && !noPlay;
|
||||
|
||||
const isIntesectingForShowing = useIsIntersecting(ref, observeIntersectionForShowing);
|
||||
|
||||
@ -326,6 +326,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
className="sticker-remove-button"
|
||||
color="dark"
|
||||
round
|
||||
noFastClick
|
||||
onClick={handleRemoveClick}
|
||||
>
|
||||
<i className="icon icon-close" />
|
||||
|
||||
@ -339,7 +339,7 @@ const StickerSet: FC<OwnProps> = ({
|
||||
size={itemSize}
|
||||
observeIntersection={observeIntersectionForPlayingItems}
|
||||
observeIntersectionForShowing={observeIntersectionForShowingItems}
|
||||
noAnimate={!loadAndPlay}
|
||||
noPlay={!loadAndPlay}
|
||||
isSavedMessages={isSavedMessages}
|
||||
isStatusPicker={isStatusPicker}
|
||||
canViewSet
|
||||
|
||||
@ -18,7 +18,7 @@ import './StickerSetCard.scss';
|
||||
|
||||
type OwnProps = {
|
||||
stickerSet?: ApiStickerSet;
|
||||
noAnimate?: boolean;
|
||||
noPlay?: boolean;
|
||||
className?: string;
|
||||
observeIntersection: ObserveFn;
|
||||
onClick: (value: ApiSticker) => void;
|
||||
@ -26,7 +26,7 @@ type OwnProps = {
|
||||
|
||||
const StickerSetCard: FC<OwnProps> = ({
|
||||
stickerSet,
|
||||
noAnimate,
|
||||
noPlay,
|
||||
className,
|
||||
observeIntersection,
|
||||
onClick,
|
||||
@ -55,7 +55,7 @@ const StickerSetCard: FC<OwnProps> = ({
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet}
|
||||
size={STICKER_SIZE_GENERAL_SETTINGS}
|
||||
noAnimate={noAnimate}
|
||||
noPlay={noPlay}
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
</Button>
|
||||
@ -66,7 +66,7 @@ const StickerSetCard: FC<OwnProps> = ({
|
||||
sticker={firstSticker}
|
||||
size={STICKER_SIZE_GENERAL_SETTINGS}
|
||||
title={stickerSet.title}
|
||||
noAnimate={noAnimate}
|
||||
noPlay={noPlay}
|
||||
observeIntersection={observeIntersection}
|
||||
noContextMenu
|
||||
isCurrentUserPremium
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { memo, useMemo, useState } from '../../lib/teact/teact';
|
||||
import React, { memo, useMemo } from '../../lib/teact/teact';
|
||||
import { getGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
@ -95,19 +95,22 @@ const StickerView: FC<OwnProps> = ({
|
||||
const shouldPlay = isIntersectingForPlaying && !noPlay;
|
||||
|
||||
const thumbDataUri = useThumbnail(sticker);
|
||||
// Use preview instead of thumb but only if it's already loaded
|
||||
const [preloadedPreviewData] = useState(mediaLoader.getFromMemory(previewMediaHash));
|
||||
const thumbData = customColor ? thumbDataUri : (preloadedPreviewData || thumbDataUri);
|
||||
// Use preview instead of thumb but only if it's already loaded or when playing an animation is disabled
|
||||
const previewMediaDataFromCache: string | undefined = mediaLoader.getFromMemory(previewMediaHash);
|
||||
const previewMediaData = useMedia(
|
||||
previewMediaHash, Boolean(previewMediaDataFromCache || !noPlay), undefined, cacheBuster,
|
||||
);
|
||||
const thumbData = customColor ? thumbDataUri : (previewMediaData || thumbDataUri);
|
||||
|
||||
const shouldForcePreview = isUnsupportedVideo || (isStatic && isSmall);
|
||||
fullMediaHash ||= shouldForcePreview ? previewMediaHash : `sticker${id}`;
|
||||
|
||||
// If preloaded preview is forced, it will render as thumb, so no need to load it again
|
||||
const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && preloadedPreviewData);
|
||||
const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && previewMediaData);
|
||||
|
||||
const fullMediaData = useMedia(fullMediaHash, !shouldLoad || shouldSkipFullMedia, undefined, cacheBuster);
|
||||
// If Lottie data is loaded we will only render thumb if it's good enough (from preview)
|
||||
const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData && !preloadedPreviewData));
|
||||
const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData && !previewMediaData));
|
||||
// Delay mounting on Android until heavy animation ends
|
||||
const [isReadyToMount, markReadyToMount, unmarkReadyToMount] = useFlag(!IS_ANDROID || !isHeavyAnimating());
|
||||
useHeavyAnimationCheck(unmarkReadyToMount, markReadyToMount, isReadyToMount);
|
||||
@ -116,7 +119,7 @@ const StickerView: FC<OwnProps> = ({
|
||||
const isThumbOpaque = sharedCanvasRef && !withTranslucentThumb;
|
||||
const thumbClassNames = useMediaTransition(thumbData && !isFullMediaReady);
|
||||
const fullMediaClassNames = useMediaTransition(isFullMediaReady);
|
||||
const noTransition = isLottie && preloadedPreviewData;
|
||||
const noTransition = isLottie && previewMediaData;
|
||||
|
||||
const coords = useCoordsInSharedCanvas(containerRef, sharedCanvasRef);
|
||||
|
||||
|
||||
@ -97,7 +97,9 @@ export function renderActionMessageText(
|
||||
content.push(...processed);
|
||||
|
||||
if (unprocessed.includes('%action_topic%')) {
|
||||
const topicEmoji = topic?.iconEmojiId ? <CustomEmoji documentId={topic.iconEmojiId} /> : '';
|
||||
const topicEmoji = topic?.iconEmojiId
|
||||
? <CustomEmoji documentId={topic.iconEmojiId} />
|
||||
: '';
|
||||
const topicString = topic ? `${topic.title}` : 'a topic';
|
||||
processed = processPlaceholder(
|
||||
unprocessed,
|
||||
|
||||
@ -30,16 +30,16 @@ export function renderMessageText(
|
||||
return contentNotSupportedText ? [trimText(contentNotSupportedText, truncateLength)] : undefined;
|
||||
}
|
||||
|
||||
return renderTextWithEntities(
|
||||
trimText(text, truncateLength),
|
||||
return renderTextWithEntities({
|
||||
text: trimText(text, truncateLength),
|
||||
entities,
|
||||
highlight,
|
||||
emojiSize,
|
||||
shouldRenderAsHtml,
|
||||
message.id,
|
||||
messageId: message.id,
|
||||
isSimple,
|
||||
isProtected,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO Use Message Summary component instead
|
||||
|
||||
@ -27,22 +27,37 @@ interface IOrganizedEntity {
|
||||
|
||||
const HQ_EMOJI_THRESHOLD = 64;
|
||||
|
||||
export function renderTextWithEntities(
|
||||
text: string,
|
||||
entities?: ApiMessageEntity[],
|
||||
highlight?: string,
|
||||
emojiSize?: number,
|
||||
shouldRenderAsHtml?: boolean,
|
||||
messageId?: number,
|
||||
isSimple?: boolean,
|
||||
isProtected?: boolean,
|
||||
observeIntersectionForLoading?: ObserveFn,
|
||||
observeIntersectionForPlaying?: ObserveFn,
|
||||
withTranslucentThumbs?: boolean,
|
||||
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>,
|
||||
sharedCanvasHqRef?: React.RefObject<HTMLCanvasElement>,
|
||||
cacheBuster?: string,
|
||||
) {
|
||||
export function renderTextWithEntities({
|
||||
text,
|
||||
entities,
|
||||
highlight,
|
||||
emojiSize,
|
||||
shouldRenderAsHtml,
|
||||
messageId,
|
||||
isSimple,
|
||||
isProtected,
|
||||
observeIntersectionForLoading,
|
||||
observeIntersectionForPlaying,
|
||||
withTranslucentThumbs,
|
||||
sharedCanvasRef,
|
||||
sharedCanvasHqRef,
|
||||
cacheBuster,
|
||||
}: {
|
||||
text: string;
|
||||
entities?: ApiMessageEntity[];
|
||||
highlight?: string;
|
||||
emojiSize?: number;
|
||||
shouldRenderAsHtml?: boolean;
|
||||
messageId?: number;
|
||||
isSimple?: boolean;
|
||||
isProtected?: boolean;
|
||||
observeIntersectionForLoading?: ObserveFn;
|
||||
observeIntersectionForPlaying?: ObserveFn;
|
||||
withTranslucentThumbs?: boolean;
|
||||
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>;
|
||||
sharedCanvasHqRef?: React.RefObject<HTMLCanvasElement>;
|
||||
cacheBuster?: string;
|
||||
}) {
|
||||
if (!entities || !entities.length) {
|
||||
return renderMessagePart(text, highlight, emojiSize, shouldRenderAsHtml, isSimple);
|
||||
}
|
||||
@ -183,13 +198,11 @@ export function getTextWithEntitiesAsHtml(formattedText?: ApiFormattedText) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const result = renderTextWithEntities(
|
||||
const result = renderTextWithEntities({
|
||||
text,
|
||||
entities,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
shouldRenderAsHtml: true,
|
||||
});
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return result.join('');
|
||||
|
||||
@ -1,28 +1,35 @@
|
||||
import { useCallback, useEffect, useState } from '../../../lib/teact/teact';
|
||||
import { getGlobal } from '../../../global';
|
||||
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import type { ApiSticker } from '../../../api/types';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
import { addCustomEmojiCallback, removeCustomEmojiCallback } from '../../../util/customEmojiManager';
|
||||
|
||||
import useEnsureCustomEmoji from '../../../hooks/useEnsureCustomEmoji';
|
||||
|
||||
export default function useCustomEmoji(documentId?: string) {
|
||||
const global = getGlobal();
|
||||
const [customEmoji, setCustomEmoji] = useState<ApiSticker | undefined>(
|
||||
documentId ? getGlobal().customEmojis.byId[documentId] : undefined,
|
||||
documentId ? global.customEmojis.byId[documentId] : undefined,
|
||||
);
|
||||
const [canPlay, setCanPlay] = useState(selectCanPlayAnimatedEmojis(global));
|
||||
|
||||
useEnsureCustomEmoji(documentId);
|
||||
|
||||
const handleGlobalChange = useCallback(() => {
|
||||
const handleGlobalChange = useCallback((customEmojis?: GlobalState['customEmojis']) => {
|
||||
if (!documentId) return;
|
||||
setCustomEmoji(getGlobal().customEmojis.byId[documentId]);
|
||||
|
||||
const newGlobal = getGlobal();
|
||||
setCustomEmoji((customEmojis ?? newGlobal.customEmojis).byId[documentId]);
|
||||
setCanPlay(selectCanPlayAnimatedEmojis(newGlobal));
|
||||
}, [documentId]);
|
||||
|
||||
useEffect(handleGlobalChange, [documentId, handleGlobalChange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (customEmoji || !documentId) return undefined;
|
||||
if (!documentId) return undefined;
|
||||
|
||||
addCustomEmojiCallback(handleGlobalChange, documentId);
|
||||
|
||||
@ -31,5 +38,5 @@ export default function useCustomEmoji(documentId?: string) {
|
||||
};
|
||||
}, [customEmoji, documentId, handleGlobalChange]);
|
||||
|
||||
return customEmoji;
|
||||
return { customEmoji, canPlay };
|
||||
}
|
||||
|
||||
@ -157,6 +157,7 @@ const LeftColumn: FC<StateProps> = ({
|
||||
case SettingsScreens.Notifications:
|
||||
case SettingsScreens.DataStorage:
|
||||
case SettingsScreens.Privacy:
|
||||
case SettingsScreens.Performance:
|
||||
case SettingsScreens.ActiveSessions:
|
||||
case SettingsScreens.Language:
|
||||
case SettingsScreens.Stickers:
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transform: none !important;
|
||||
opacity: 0;
|
||||
|
||||
|
||||
@ -53,8 +53,8 @@
|
||||
.info {
|
||||
transition: opacity 0.3s ease, transform var(--layer-transition);
|
||||
|
||||
:global(body.animation-level-0) & {
|
||||
transition: none;
|
||||
:global(body.no-page-transitions) & {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
// Super specific selector to override the same in `ListItem`
|
||||
@media (min-width: 600px) {
|
||||
&:not(.has-ripple):not(.is-static),
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
.ListItem-button:active {
|
||||
--background-color: var(--color-chat-hover) !important;
|
||||
}
|
||||
@ -156,7 +156,7 @@
|
||||
transform: translateX(-0.375rem) scaleY(0.5);
|
||||
transition: transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -211,8 +211,8 @@
|
||||
.Badge-transition {
|
||||
transition: opacity var(--layer-transition), transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none;
|
||||
body.no-page-transitions & {
|
||||
transition: opacity var(--layer-transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,8 +220,8 @@
|
||||
.info {
|
||||
transition: opacity 300ms ease, transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none;
|
||||
body.no-page-transitions & {
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
|
||||
.title .custom-emoji {
|
||||
|
||||
@ -13,7 +13,6 @@ import type {
|
||||
ApiUser,
|
||||
ApiUserStatus,
|
||||
} from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
import type { ChatAnimationTypes } from './hooks';
|
||||
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
@ -25,6 +24,7 @@ import {
|
||||
selectIsChatMuted,
|
||||
} from '../../../global/helpers';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectChat,
|
||||
selectChatMessage,
|
||||
selectCurrentMessageList,
|
||||
@ -82,7 +82,6 @@ type StateProps = {
|
||||
lastMessageSender?: ApiUser | ApiChat;
|
||||
lastMessageOutgoingStatus?: ApiMessageOutgoingStatus;
|
||||
draft?: ApiFormattedText;
|
||||
animationLevel?: AnimationLevel;
|
||||
isSelected?: boolean;
|
||||
isSelectedForum?: boolean;
|
||||
canScrollDown?: boolean;
|
||||
@ -90,6 +89,7 @@ type StateProps = {
|
||||
lastSyncTime?: number;
|
||||
lastMessageTopic?: ApiTopic;
|
||||
typingStatus?: ApiTypingStatus;
|
||||
withInterfaceAnimations?: boolean;
|
||||
};
|
||||
|
||||
const Chat: FC<OwnProps & StateProps> = ({
|
||||
@ -110,7 +110,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
actionTargetChatId,
|
||||
offsetTop,
|
||||
draft,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
isSelected,
|
||||
isSelectedForum,
|
||||
canScrollDown,
|
||||
@ -150,7 +150,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
lastMessageSender,
|
||||
observeIntersection,
|
||||
animationType,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
orderDiff,
|
||||
});
|
||||
|
||||
@ -239,13 +239,10 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
userStatus={userStatus}
|
||||
isSavedMessages={user?.isSelf}
|
||||
lastSyncTime={lastSyncTime}
|
||||
animationLevel={animationLevel}
|
||||
withVideo
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
<AvatarBadge chatId={chatId} />
|
||||
{chat.isCallActive && chat.isCallNotEmpty && (
|
||||
<ChatCallStatus isMobile={isMobile} isSelected={isSelected} isActive={animationLevel !== 0} />
|
||||
<ChatCallStatus isMobile={isMobile} isSelected={isSelected} isActive={withInterfaceAnimations} />
|
||||
)}
|
||||
</div>
|
||||
<div className="info">
|
||||
@ -337,7 +334,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
actionTargetChatId,
|
||||
actionTargetMessage,
|
||||
draft: selectDraft(global, chatId, MAIN_THREAD_ID),
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
isSelected,
|
||||
isSelectedForum,
|
||||
canScrollDown: isSelected && messageListType === 'thread',
|
||||
@ -350,6 +346,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
userStatus,
|
||||
lastMessageTopic,
|
||||
typingStatus,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
};
|
||||
},
|
||||
)(Chat));
|
||||
|
||||
@ -9,12 +9,11 @@ import type { ApiChat } from '../../../api/types';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
|
||||
import {
|
||||
GENERAL_TOPIC_ID,
|
||||
TOPICS_SLICE, TOPIC_HEIGHT_PX, TOPIC_LIST_SENSITIVE_AREA, ANIMATION_LEVEL_MIN,
|
||||
GENERAL_TOPIC_ID, TOPICS_SLICE, TOPIC_HEIGHT_PX, TOPIC_LIST_SENSITIVE_AREA,
|
||||
} from '../../../config';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import {
|
||||
selectChat, selectCurrentMessageList, selectIsForumPanelOpen, selectTabState,
|
||||
selectCanAnimateInterface, selectChat, selectCurrentMessageList, selectIsForumPanelOpen, selectTabState,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { getOrderedTopics } from '../../../global/helpers';
|
||||
@ -54,7 +53,7 @@ type StateProps = {
|
||||
chat?: ApiChat;
|
||||
currentTopicId?: number;
|
||||
lastSyncTime?: number;
|
||||
animationLevel?: number;
|
||||
withInterfaceAnimations?: boolean;
|
||||
};
|
||||
|
||||
const INTERSECTION_THROTTLE = 200;
|
||||
@ -68,7 +67,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
onTopicSearch,
|
||||
onCloseAnimationEnd,
|
||||
onOpenAnimationStart,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
}) => {
|
||||
const {
|
||||
closeForumPanel, openChatWithInfo, loadTopics,
|
||||
@ -97,10 +96,10 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
}, [closeForumPanel]);
|
||||
|
||||
useEffect(() => {
|
||||
if (animationLevel === ANIMATION_LEVEL_MIN && !isOpen) {
|
||||
if (!withInterfaceAnimations && !isOpen) {
|
||||
onCloseAnimationEnd?.();
|
||||
}
|
||||
}, [animationLevel, isOpen, onCloseAnimationEnd]);
|
||||
}, [withInterfaceAnimations, isOpen, onCloseAnimationEnd]);
|
||||
|
||||
const handleToggleChatInfo = useCallback(() => {
|
||||
if (!chat) return;
|
||||
@ -212,7 +211,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
styles.root,
|
||||
isScrolled && styles.scrolled,
|
||||
lang.isRtl && styles.rtl,
|
||||
animationLevel === ANIMATION_LEVEL_MIN && styles.noAnimation,
|
||||
!withInterfaceAnimations && styles.noAnimation,
|
||||
)}
|
||||
onTransitionEnd={!isOpen ? onCloseAnimationEnd : undefined}
|
||||
>
|
||||
@ -294,7 +293,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
chat,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
currentTopicId: chatId === currentChatId ? currentThreadId : undefined,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
};
|
||||
},
|
||||
)(ForumPanel));
|
||||
|
||||
@ -4,13 +4,15 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { GlobalState, TabState } from '../../../global/types';
|
||||
import type { AnimationLevel, ISettings } from '../../../types';
|
||||
import type { TabState, GlobalState } from '../../../global/types';
|
||||
|
||||
import { LeftColumnContent, SettingsScreens } from '../../../types';
|
||||
|
||||
import {
|
||||
APP_NAME, APP_VERSION, ARCHIVED_FOLDER_ID,
|
||||
ANIMATION_LEVEL_MAX,
|
||||
ANIMATION_LEVEL_MIN,
|
||||
APP_NAME, APP_VERSION,
|
||||
ARCHIVED_FOLDER_ID,
|
||||
BETA_CHANGELOG_URL,
|
||||
DEBUG,
|
||||
FEEDBACK_URL,
|
||||
@ -18,6 +20,11 @@ import {
|
||||
IS_TEST,
|
||||
PRODUCTION_HOSTNAME,
|
||||
} from '../../../config';
|
||||
import {
|
||||
INITIAL_PERFORMANCE_STATE_MAX,
|
||||
INITIAL_PERFORMANCE_STATE_MID,
|
||||
INITIAL_PERFORMANCE_STATE_MIN,
|
||||
} from '../../../global/initialState';
|
||||
import { IS_PWA } from '../../../util/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatDateToString } from '../../../util/dateFormat';
|
||||
@ -32,6 +39,7 @@ import { useHotkeys } from '../../../hooks/useHotkeys';
|
||||
import { getPromptInstall } from '../../../util/installPrompt';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import useLeftHeaderButtonRtlForumTransition from './hooks/useLeftHeaderButtonRtlForumTransition';
|
||||
import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager';
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
|
||||
import DropdownMenu from '../../ui/DropdownMenu';
|
||||
@ -43,9 +51,9 @@ import Switcher from '../../ui/Switcher';
|
||||
import ShowTransition from '../../ui/ShowTransition';
|
||||
import ConnectionStatusOverlay from '../ConnectionStatusOverlay';
|
||||
import StatusButton from './StatusButton';
|
||||
import Toggle from '../../ui/Toggle';
|
||||
|
||||
import './LeftMainHeader.scss';
|
||||
import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager';
|
||||
|
||||
type OwnProps = {
|
||||
shouldHideSearch?: boolean;
|
||||
@ -79,7 +87,6 @@ type StateProps =
|
||||
& Pick<GlobalState, 'connectionState' | 'isSyncing' | 'archiveSettings'>
|
||||
& Pick<TabState, 'canInstall'>;
|
||||
|
||||
const ANIMATION_LEVEL_OPTIONS = [0, 1, 2];
|
||||
const WEBK_VERSION_URL = 'https://web.telegram.org/k/';
|
||||
|
||||
const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
@ -121,6 +128,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
requestNextSettingsScreen,
|
||||
skipLockOnUnload,
|
||||
openUrl,
|
||||
updatePerformanceSettings,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
@ -206,12 +214,16 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
const handleAnimationLevelChange = useCallback((e: React.SyntheticEvent<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const newLevel = animationLevel === 0 ? 2 : 0;
|
||||
ANIMATION_LEVEL_OPTIONS.forEach((_, i) => {
|
||||
document.body.classList.toggle(`animation-level-${i}`, newLevel === i);
|
||||
});
|
||||
let newLevel = animationLevel + 1;
|
||||
if (newLevel > ANIMATION_LEVEL_MAX) {
|
||||
newLevel = ANIMATION_LEVEL_MIN;
|
||||
}
|
||||
const performanceSettings = newLevel === ANIMATION_LEVEL_MIN
|
||||
? INITIAL_PERFORMANCE_STATE_MIN
|
||||
: (newLevel === ANIMATION_LEVEL_MAX ? INITIAL_PERFORMANCE_STATE_MAX : INITIAL_PERFORMANCE_STATE_MID);
|
||||
|
||||
setSettingOption({ animationLevel: newLevel });
|
||||
setSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
updatePerformanceSettings(performanceSettings);
|
||||
}, [animationLevel, setSettingOption]);
|
||||
|
||||
const handleChangelogClick = useCallback(() => {
|
||||
@ -249,6 +261,9 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
: lang('Search');
|
||||
|
||||
const versionString = IS_BETA ? `${APP_VERSION} Beta (${APP_REVISION})` : (DEBUG ? APP_REVISION : APP_VERSION);
|
||||
const animationLevelValue = animationLevel !== ANIMATION_LEVEL_MIN
|
||||
? (animationLevel === ANIMATION_LEVEL_MAX ? 'max' : 'mid')
|
||||
: 'min';
|
||||
|
||||
// Disable dropdown menu RTL animation for resize
|
||||
const {
|
||||
@ -304,11 +319,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
onClick={handleAnimationLevelChange}
|
||||
>
|
||||
<span className="menu-item-name capitalize">{lang('Appearance.Animations').toLowerCase()}</span>
|
||||
<Switcher
|
||||
id="animations"
|
||||
label="Toggle Animations"
|
||||
checked={animationLevel > 0}
|
||||
/>
|
||||
<Toggle value={animationLevelValue} />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon="help"
|
||||
@ -350,7 +361,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
)}
|
||||
</>
|
||||
), [
|
||||
animationLevel, archivedUnreadChatsCount, canInstall, handleAnimationLevelChange, handleBugReportClick, lang,
|
||||
animationLevelValue, archivedUnreadChatsCount, canInstall, handleAnimationLevelChange, handleBugReportClick, lang,
|
||||
handleChangelogClick, handleDarkModeToggle, handleOpenTipsChat, handleSelectSaved, handleSwitchToWebK,
|
||||
onSelectArchived, onSelectContacts, onSelectSettings, theme, withOtherVersions, archiveSettings,
|
||||
]);
|
||||
|
||||
@ -93,7 +93,7 @@ const StatusButton: FC<StateProps> = ({ emojiStatus }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal((global) => {
|
||||
export default memo(withGlobal((global): StateProps => {
|
||||
const { currentUserId } = global;
|
||||
const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined;
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
--offset-y: 3.25rem !important;
|
||||
--offset-x: auto !important;
|
||||
--color-text: var(--color-primary);
|
||||
--color-background: var(--color-background-compact-menu);
|
||||
--border-radius-default: 1.25rem;
|
||||
|
||||
left: 0.5rem;
|
||||
@ -10,7 +9,12 @@
|
||||
max-width: calc(var(--symbol-menu-width) + 0.25rem); // Reserve width for scrollbar
|
||||
height: var(--symbol-menu-height);
|
||||
padding: 0 !important;
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
:global(body:not(.no-menu-blur)) & {
|
||||
--color-background: var(--color-background-compact-menu);
|
||||
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
@supports (overflow: overlay) {
|
||||
width: var(--symbol-menu-width);
|
||||
|
||||
@ -7,6 +7,7 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiSticker } from '../../../api/types';
|
||||
|
||||
import { selectIsContextMenuTranslucent } from '../../../global/selectors';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
|
||||
import Menu from '../../ui/Menu';
|
||||
@ -24,12 +25,14 @@ export type OwnProps = {
|
||||
|
||||
interface StateProps {
|
||||
areFeaturedStickersLoaded?: boolean;
|
||||
isTranslucent?: boolean;
|
||||
}
|
||||
|
||||
const StatusPickerMenu: FC<OwnProps & StateProps> = ({
|
||||
isOpen,
|
||||
statusButtonRef,
|
||||
areFeaturedStickersLoaded,
|
||||
isTranslucent,
|
||||
onEmojiStatusSelect,
|
||||
onClose,
|
||||
}) => {
|
||||
@ -68,7 +71,7 @@ const StatusPickerMenu: FC<OwnProps & StateProps> = ({
|
||||
loadAndPlay={isOpen}
|
||||
isHidden={!isOpen}
|
||||
isStatusPicker
|
||||
isTranslucent
|
||||
isTranslucent={isTranslucent}
|
||||
onContextMenuOpen={markContextMenuShown}
|
||||
onContextMenuClose={unmarkContextMenuShown}
|
||||
onCustomEmojiSelect={handleEmojiSelect}
|
||||
@ -82,5 +85,6 @@ const StatusPickerMenu: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
return {
|
||||
areFeaturedStickersLoaded: Boolean(global.customEmojis.featuredIds?.length),
|
||||
isTranslucent: selectIsContextMenuTranslucent(global),
|
||||
};
|
||||
})(StatusPickerMenu));
|
||||
|
||||
@ -9,15 +9,19 @@ import type {
|
||||
} from '../../../api/types';
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
import type { ChatAnimationTypes } from './hooks';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectCanDeleteTopic,
|
||||
selectChat,
|
||||
selectChatMessage, selectCurrentMessageList,
|
||||
selectChatMessage,
|
||||
selectCurrentMessageList,
|
||||
selectDraft,
|
||||
selectOutgoingStatus, selectThreadInfo, selectThreadParam, selectUser,
|
||||
selectOutgoingStatus,
|
||||
selectThreadInfo,
|
||||
selectThreadParam,
|
||||
selectUser,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { createLocationHash } from '../../../util/routing';
|
||||
@ -57,11 +61,11 @@ type StateProps = {
|
||||
actionTargetUserIds?: string[];
|
||||
lastMessageSender?: ApiUser | ApiChat;
|
||||
actionTargetChatId?: string;
|
||||
animationLevel?: AnimationLevel;
|
||||
typingStatus?: ApiTypingStatus;
|
||||
draft?: ApiFormattedText;
|
||||
canScrollDown?: boolean;
|
||||
wasTopicOpened?: boolean;
|
||||
withInterfaceAnimations?: boolean;
|
||||
};
|
||||
|
||||
const Topic: FC<OwnProps & StateProps> = ({
|
||||
@ -80,7 +84,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
actionTargetChatId,
|
||||
lastMessageSender,
|
||||
animationType,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
orderDiff,
|
||||
typingStatus,
|
||||
draft,
|
||||
@ -122,7 +126,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
typingStatus,
|
||||
|
||||
animationType,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
orderDiff,
|
||||
});
|
||||
|
||||
@ -229,7 +233,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
lastMessageSender,
|
||||
typingStatus,
|
||||
canDelete: selectCanDeleteTopic(global, chatId, topic.id),
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
draft,
|
||||
...(isOutgoing && lastMessage && {
|
||||
lastMessageOutgoingStatus: selectOutgoingStatus(global, lastMessage),
|
||||
|
||||
@ -2,7 +2,6 @@ import React, { useLayoutEffect, useMemo, useRef } from '../../../../lib/teact/t
|
||||
import { requestMutation } from '../../../../lib/fasterdom/fasterdom';
|
||||
import { getGlobal } from '../../../../global';
|
||||
|
||||
import type { AnimationLevel } from '../../../../types';
|
||||
import type { LangFn } from '../../../../hooks/useLang';
|
||||
import type {
|
||||
ApiChat, ApiTopic, ApiMessage, ApiTypingStatus, ApiUser,
|
||||
@ -46,7 +45,7 @@ export default function useChatListEntry({
|
||||
observeIntersection,
|
||||
animationType,
|
||||
orderDiff,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
isTopic,
|
||||
}: {
|
||||
chat?: ApiChat;
|
||||
@ -64,7 +63,7 @@ export default function useChatListEntry({
|
||||
|
||||
animationType: ChatAnimationTypes;
|
||||
orderDiff: number;
|
||||
animationLevel?: AnimationLevel;
|
||||
withInterfaceAnimations?: boolean;
|
||||
}) {
|
||||
const lang = useLang();
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -113,7 +112,12 @@ export default function useChatListEntry({
|
||||
return (
|
||||
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||
<span className="draft">{lang('Draft')}</span>
|
||||
{renderTextWithEntities(draft.text, draft.entities, undefined, undefined, undefined, undefined, true)}
|
||||
{renderTextWithEntities({
|
||||
text: draft.text,
|
||||
entities: draft.entities,
|
||||
isSimple: true,
|
||||
withTranslucentThumbs: true,
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@ -137,6 +141,8 @@ export default function useChatListEntry({
|
||||
actionTargetChatId,
|
||||
lastMessageTopic,
|
||||
{ isEmbedded: true },
|
||||
undefined,
|
||||
undefined,
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
@ -161,7 +167,7 @@ export default function useChatListEntry({
|
||||
useLayoutEffect(() => {
|
||||
const element = ref.current;
|
||||
|
||||
if (animationLevel === 0 || !element) {
|
||||
if (!withInterfaceAnimations || !element) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -191,7 +197,7 @@ export default function useChatListEntry({
|
||||
element.style.transform = '';
|
||||
});
|
||||
}, ANIMATION_DURATION + ANIMATION_END_DELAY);
|
||||
}, [animationLevel, orderDiff, animationType]);
|
||||
}, [withInterfaceAnimations, orderDiff, animationType]);
|
||||
|
||||
return {
|
||||
renderSubtitle,
|
||||
|
||||
@ -5,7 +5,6 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import type {
|
||||
ApiChat, ApiUser, ApiMessage, ApiMessageOutgoingStatus,
|
||||
} from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
import type { LangFn } from '../../../hooks/useLang';
|
||||
|
||||
import {
|
||||
@ -45,7 +44,6 @@ type StateProps = {
|
||||
privateChatUser?: ApiUser;
|
||||
lastMessageOutgoingStatus?: ApiMessageOutgoingStatus;
|
||||
lastSyncTime?: number;
|
||||
animationLevel?: AnimationLevel;
|
||||
};
|
||||
|
||||
const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
@ -54,7 +52,6 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
chatId,
|
||||
chat,
|
||||
privateChatUser,
|
||||
animationLevel,
|
||||
lastSyncTime,
|
||||
}) => {
|
||||
const { focusMessage } = getActions();
|
||||
@ -88,8 +85,6 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
user={privateChatUser}
|
||||
isSavedMessages={privateChatUser?.isSelf}
|
||||
lastSyncTime={lastSyncTime}
|
||||
withVideo
|
||||
animationLevel={animationLevel}
|
||||
/>
|
||||
<div className="info">
|
||||
<div className="info-row">
|
||||
@ -152,7 +147,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
chat,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
...(privateChatUserId && { privateChatUser }),
|
||||
};
|
||||
},
|
||||
|
||||
@ -73,9 +73,9 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
||||
buttonRef={buttonRef}
|
||||
>
|
||||
{isUserId(chatId) ? (
|
||||
<PrivateChatInfo userId={chatId} withUsername={withUsername} avatarSize="large" withVideoAvatar />
|
||||
<PrivateChatInfo userId={chatId} withUsername={withUsername} avatarSize="large" />
|
||||
) : (
|
||||
<GroupChatInfo chatId={chatId} withUsername={withUsername} avatarSize="large" withVideoAvatar />
|
||||
<GroupChatInfo chatId={chatId} withUsername={withUsername} avatarSize="large" />
|
||||
)}
|
||||
<DeleteChatModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
|
||||
@ -4,10 +4,8 @@ import { withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiTopic } from '../../../api/types';
|
||||
|
||||
import {
|
||||
selectChat,
|
||||
} from '../../../global/selectors';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
import { selectChat } from '../../../global/selectors';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import useSelectWithEnter from '../../../hooks/useSelectWithEnter';
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ import React, {
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiUser } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import { getUserFirstOrLastName } from '../../../global/helpers';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
@ -14,8 +13,8 @@ import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
|
||||
import Button from '../../ui/Button';
|
||||
import LeftSearchResultChat from './LeftSearchResultChat';
|
||||
import Avatar from '../../common/Avatar';
|
||||
import LeftSearchResultChat from './LeftSearchResultChat';
|
||||
|
||||
import './RecentContacts.scss';
|
||||
|
||||
@ -27,7 +26,6 @@ type StateProps = {
|
||||
topUserIds?: string[];
|
||||
usersById: Record<string, ApiUser>;
|
||||
recentlyFoundChatIds?: string[];
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const SEARCH_CLOSE_TIMEOUT_MS = 250;
|
||||
@ -39,7 +37,6 @@ const RecentContacts: FC<OwnProps & StateProps> = ({
|
||||
topUserIds,
|
||||
usersById,
|
||||
recentlyFoundChatIds,
|
||||
animationLevel,
|
||||
onReset,
|
||||
}) => {
|
||||
const {
|
||||
@ -86,7 +83,7 @@ const RecentContacts: FC<OwnProps & StateProps> = ({
|
||||
onClick={() => handleClick(userId)}
|
||||
dir={lang.isRtl ? 'rtl' : undefined}
|
||||
>
|
||||
<Avatar user={usersById[userId]} animationLevel={animationLevel} withVideo />
|
||||
<Avatar user={usersById[userId]} />
|
||||
<div className="top-peer-name">{renderText(getUserFirstOrLastName(usersById[userId]) || NBSP)}</div>
|
||||
</div>
|
||||
))}
|
||||
@ -126,13 +123,11 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { userIds: topUserIds } = global.topPeers;
|
||||
const usersById = global.users.byId;
|
||||
const { recentlyFoundChatIds } = global;
|
||||
const { animationLevel } = global.settings.byKey;
|
||||
|
||||
return {
|
||||
topUserIds,
|
||||
usersById,
|
||||
recentlyFoundChatIds,
|
||||
animationLevel,
|
||||
};
|
||||
},
|
||||
)(RecentContacts));
|
||||
|
||||
@ -124,12 +124,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.settings-item-simple,
|
||||
.settings-item {
|
||||
background-color: var(--color-background);
|
||||
padding: 1.5rem 1.5rem 1rem;
|
||||
box-shadow: inset 0 -0.0625rem 0 0 var(--color-background-secondary-accent);
|
||||
margin-bottom: 0.625rem;
|
||||
}
|
||||
|
||||
.settings-item {
|
||||
&.no-border {
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
@ -360,6 +363,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.settings-dropdown-section {
|
||||
margin: 0 -0.75rem 1rem -1rem;
|
||||
|
||||
.DropdownList {
|
||||
position: relative;
|
||||
|
||||
&--open {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.SettingsDefaultReaction {
|
||||
.current-default-reaction {
|
||||
margin-inline-end: 2rem;
|
||||
|
||||
@ -31,6 +31,7 @@ import SettingsStickers from './SettingsStickers';
|
||||
import SettingsCustomEmoji from './SettingsCustomEmoji';
|
||||
import SettingsDoNotTranslate from './SettingsDoNotTranslate';
|
||||
import SettingsExperimental from './SettingsExperimental';
|
||||
import SettingsPerformance from './SettingsPerformance';
|
||||
|
||||
import './Settings.scss';
|
||||
|
||||
@ -425,6 +426,14 @@ const Settings: FC<OwnProps> = ({
|
||||
/>
|
||||
);
|
||||
|
||||
case SettingsScreens.Performance:
|
||||
return (
|
||||
<SettingsPerformance
|
||||
isActive={isScreenActive}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import React, { memo, useCallback } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
import type { ApiUser, ApiWebSession } from '../../../api/types';
|
||||
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -26,14 +25,12 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
session?: ApiWebSession;
|
||||
bot?: ApiUser;
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const SettingsActiveWebsite: FC<OwnProps & StateProps> = ({
|
||||
isOpen,
|
||||
session,
|
||||
bot,
|
||||
animationLevel,
|
||||
onClose,
|
||||
}) => {
|
||||
const { terminateWebAuthorization } = getActions();
|
||||
@ -80,8 +77,6 @@ const SettingsActiveWebsite: FC<OwnProps & StateProps> = ({
|
||||
className={styles.avatar}
|
||||
user={renderingBot}
|
||||
size="large"
|
||||
animationLevel={animationLevel}
|
||||
withVideo
|
||||
/>
|
||||
{renderingBot && <FullNameTitle className={styles.title} peer={renderingBot} />}
|
||||
<div className={styles.note}>
|
||||
@ -112,6 +107,5 @@ export default memo(withGlobal<OwnProps>((global, { hash }): StateProps => {
|
||||
return {
|
||||
session,
|
||||
bot,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
})(SettingsActiveWebsite));
|
||||
|
||||
@ -5,7 +5,6 @@ import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiWebSession } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import { formatPastTimeShort } from '../../../util/dateFormat';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -30,14 +29,12 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
byHash: Record<string, ApiWebSession>;
|
||||
orderedHashes: string[];
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
byHash,
|
||||
orderedHashes,
|
||||
animationLevel,
|
||||
onReset,
|
||||
}) => {
|
||||
const {
|
||||
@ -113,7 +110,7 @@ const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleOpenSessionModal(session.hash)}
|
||||
>
|
||||
<Avatar className={styles.avatar} user={bot} size="tiny" withVideo animationLevel={animationLevel} />
|
||||
<Avatar className={styles.avatar} user={bot} size="tiny" />
|
||||
<div className="multiline-menu-item full-size" dir="auto">
|
||||
<span className="date">{formatPastTimeShort(lang, session.dateActive * 1000)}</span>
|
||||
{bot && <FullNameTitle className={styles.title} peer={bot} />}
|
||||
@ -164,7 +161,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
byHash,
|
||||
orderedHashes,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(SettingsActiveWebsites));
|
||||
|
||||
@ -10,6 +10,7 @@ import type { ISettings } from '../../../types';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -27,6 +28,7 @@ type StateProps = Pick<ISettings, (
|
||||
)> & {
|
||||
customEmojiSetIds?: string[];
|
||||
stickerSetsById: Record<string, ApiStickerSet>;
|
||||
canPlayAnimatedEmojis: boolean;
|
||||
};
|
||||
|
||||
const SettingsCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
@ -34,6 +36,7 @@ const SettingsCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
customEmojiSetIds,
|
||||
stickerSetsById,
|
||||
shouldSuggestCustomEmoji,
|
||||
canPlayAnimatedEmojis,
|
||||
onReset,
|
||||
}) => {
|
||||
const { openStickerSet, setSettingOption } = getActions();
|
||||
@ -78,6 +81,7 @@ const SettingsCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
stickerSet={stickerSet}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
onClick={handleStickerSetClick}
|
||||
noPlay={!canPlayAnimatedEmojis}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -91,13 +95,14 @@ const SettingsCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global) => {
|
||||
(global): StateProps => {
|
||||
return {
|
||||
...pick(global.settings.byKey, [
|
||||
'shouldSuggestCustomEmoji',
|
||||
]),
|
||||
customEmojiSetIds: global.customEmojis.added.setIds,
|
||||
stickerSetsById: global.stickers.setsById,
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
},
|
||||
)(SettingsCustomEmoji));
|
||||
|
||||
@ -30,8 +30,6 @@ type StateProps = Pick<ISettings, (
|
||||
'canAutoLoadFileInPrivateChats' |
|
||||
'canAutoLoadFileInGroups' |
|
||||
'canAutoLoadFileInChannels' |
|
||||
'canAutoPlayGifs' |
|
||||
'canAutoPlayVideos' |
|
||||
'autoLoadFileMaxSizeMb'
|
||||
)>;
|
||||
|
||||
@ -50,8 +48,6 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||
canAutoLoadFileInPrivateChats,
|
||||
canAutoLoadFileInGroups,
|
||||
canAutoLoadFileInChannels,
|
||||
canAutoPlayGifs,
|
||||
canAutoPlayVideos,
|
||||
autoLoadFileMaxSizeMb,
|
||||
}) => {
|
||||
const { setSettingOption } = getActions();
|
||||
@ -71,14 +67,6 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||
setSettingOption({ autoLoadFileMaxSizeMb: AUTODOWNLOAD_FILESIZE_MB_LIMITS[value] });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleCanAutoPlayGifsChange = useCallback((value: boolean) => {
|
||||
setSettingOption({ canAutoPlayGifs: value });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleCanAutoPlayVideosChange = useCallback((value: boolean) => {
|
||||
setSettingOption({ canAutoPlayVideos: value });
|
||||
}, [setSettingOption]);
|
||||
|
||||
function renderContentSizeSlider() {
|
||||
const value = AUTODOWNLOAD_FILESIZE_MB_LIMITS.indexOf(autoLoadFileMaxSizeMb);
|
||||
|
||||
@ -165,21 +153,6 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||
canAutoLoadFileInGroups,
|
||||
canAutoLoadFileInChannels,
|
||||
)}
|
||||
|
||||
<div className="settings-item">
|
||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('AutoplayMedia')}</h4>
|
||||
|
||||
<Checkbox
|
||||
label={lang('GifsTab2')}
|
||||
checked={canAutoPlayGifs}
|
||||
onCheck={handleCanAutoPlayGifsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('DataAndStorage.Autoplay.Videos')}
|
||||
checked={canAutoPlayVideos}
|
||||
onCheck={handleCanAutoPlayVideosChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -199,8 +172,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
'canAutoLoadFileInPrivateChats',
|
||||
'canAutoLoadFileInGroups',
|
||||
'canAutoLoadFileInChannels',
|
||||
'canAutoPlayGifs',
|
||||
'canAutoPlayVideos',
|
||||
'autoLoadFileMaxSizeMb',
|
||||
]);
|
||||
},
|
||||
|
||||
@ -4,7 +4,7 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { AnimationLevel, ISettings, TimeFormat } from '../../../types';
|
||||
import type { ISettings, TimeFormat } from '../../../types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import {
|
||||
@ -37,12 +37,6 @@ type StateProps =
|
||||
shouldUseSystemTheme: boolean;
|
||||
};
|
||||
|
||||
const ANIMATION_LEVEL_OPTIONS = [
|
||||
'Solid and Steady',
|
||||
'Nice and Fast',
|
||||
'Lots of Stuff',
|
||||
];
|
||||
|
||||
const TIME_FORMAT_OPTIONS: IRadioOption[] = [{
|
||||
label: '12-hour',
|
||||
value: '12h',
|
||||
@ -56,7 +50,6 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
onScreenSelect,
|
||||
onReset,
|
||||
messageTextSize,
|
||||
animationLevel,
|
||||
messageSendKeyCombo,
|
||||
timeFormat,
|
||||
theme,
|
||||
@ -88,14 +81,6 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
},
|
||||
] : undefined;
|
||||
|
||||
const handleAnimationLevelChange = useCallback((newLevel: number) => {
|
||||
ANIMATION_LEVEL_OPTIONS.forEach((_, i) => {
|
||||
document.body.classList.toggle(`animation-level-${i}`, newLevel === i);
|
||||
});
|
||||
|
||||
setSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleMessageTextSizeChange = useCallback((newSize: number) => {
|
||||
document.documentElement.style.setProperty(
|
||||
'--composer-text-size', `${Math.max(newSize, IS_IOS ? 16 : 15)}px`,
|
||||
@ -176,21 +161,6 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="settings-item">
|
||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
Animation Level
|
||||
</h4>
|
||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
Choose the desired animations amount.
|
||||
</p>
|
||||
|
||||
<RangeSlider
|
||||
options={ANIMATION_LEVEL_OPTIONS}
|
||||
value={animationLevel}
|
||||
onChange={handleAnimationLevelChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{KEYBOARD_SEND_OPTIONS && (
|
||||
<div className="settings-item">
|
||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('VoiceOver.Keyboard')}</h4>
|
||||
|
||||
@ -145,6 +145,9 @@ const SettingsHeader: FC<OwnProps> = ({
|
||||
case SettingsScreens.PrivacyPhoneP2PDeniedContacts:
|
||||
return <h3>{lang('NeverShareWith')}</h3>;
|
||||
|
||||
case SettingsScreens.Performance:
|
||||
return <h3>{lang('Animations and Performance')}</h3>;
|
||||
|
||||
case SettingsScreens.ActiveSessions:
|
||||
return <h3>{lang('SessionsTitle')}</h3>;
|
||||
case SettingsScreens.ActiveWebsites:
|
||||
|
||||
@ -85,6 +85,13 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
{lang('Telegram.GeneralSettingsViewController')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="animations"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.Performance)}
|
||||
>
|
||||
{lang('Animations and Performance')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="unmute"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
|
||||
221
src/components/left/settings/SettingsPerformance.tsx
Normal file
221
src/components/left/settings/SettingsPerformance.tsx
Normal file
@ -0,0 +1,221 @@
|
||||
import React, {
|
||||
memo, useCallback, useMemo, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { AnimationLevel, PerformanceType, PerformanceTypeKey } from '../../../types';
|
||||
|
||||
import { IS_BACKDROP_BLUR_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import {
|
||||
ANIMATION_LEVEL_CUSTOM, ANIMATION_LEVEL_MAX, ANIMATION_LEVEL_MED, ANIMATION_LEVEL_MIN,
|
||||
} from '../../../config';
|
||||
import {
|
||||
INITIAL_PERFORMANCE_STATE_MAX,
|
||||
INITIAL_PERFORMANCE_STATE_MID,
|
||||
INITIAL_PERFORMANCE_STATE_MIN,
|
||||
} from '../../../global/initialState';
|
||||
import { selectPerformanceSettings } from '../../../global/selectors';
|
||||
import { areDeepEqual } from '../../../util/areDeepEqual';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
|
||||
import RangeSlider from '../../ui/RangeSlider';
|
||||
import Checkbox from '../../ui/Checkbox';
|
||||
|
||||
type PerformanceSection = [string, PerformanceOption[]];
|
||||
type PerformanceOption = {
|
||||
key: PerformanceTypeKey;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
isActive?: boolean;
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
performanceSettings: PerformanceType;
|
||||
};
|
||||
|
||||
const ANIMATION_LEVEL_OPTIONS = [
|
||||
'Power Saving',
|
||||
'Nice and Fast',
|
||||
'Lots of Stuff',
|
||||
];
|
||||
|
||||
const ANIMATION_LEVEL_CUSTOM_OPTIONS = [
|
||||
'Power Saving',
|
||||
'Custom',
|
||||
'Lots of Stuff',
|
||||
];
|
||||
|
||||
const PERFORMANCE_OPTIONS: PerformanceSection[] = [
|
||||
['LiteMode.Key.animations.Title', [
|
||||
{ key: 'pageTransitions', label: 'Page Transitions' },
|
||||
{ key: 'messageSendingAnimations', label: 'Message Sending Animation' },
|
||||
{ key: 'mediaViewerAnimations', label: 'Media Viewer Animations' },
|
||||
{ key: 'messageComposerAnimations', label: 'Message Composer Animations' },
|
||||
{ key: 'contextMenuAnimations', label: 'Context Menu Animation' },
|
||||
{ key: 'contextMenuBlur', label: 'Context Menu Blur', disabled: !IS_BACKDROP_BLUR_SUPPORTED },
|
||||
{ key: 'rightColumnAnimations', label: 'Right Column Animation' },
|
||||
]],
|
||||
['Stickers and Emoji', [
|
||||
{ key: 'animatedEmoji', label: 'Allow Animated Emoji' },
|
||||
{ key: 'loopAnimatedStickers', label: 'Loop Animated Stickers' },
|
||||
{ key: 'reactionEffects', label: 'Reaction Effects' },
|
||||
{ key: 'stickerEffects', label: 'Full-Screen Sticker and Emoji Effects' },
|
||||
]],
|
||||
['AutoplayMedia', [
|
||||
{ key: 'autoplayGifs', label: 'AutoplayGIF' },
|
||||
{ key: 'autoplayVideos', label: 'AutoplayVideo' },
|
||||
]],
|
||||
];
|
||||
|
||||
function SettingsPerformance({
|
||||
isActive,
|
||||
performanceSettings,
|
||||
onReset,
|
||||
}: OwnProps & StateProps) {
|
||||
const {
|
||||
setSettingOption,
|
||||
updatePerformanceSettings,
|
||||
} = getActions();
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
onBack: onReset,
|
||||
});
|
||||
|
||||
const lang = useLang();
|
||||
const [sectionExpandedStates, setSectionExpandedStates] = useState<Record<number, boolean>>({});
|
||||
|
||||
const sectionCheckedStates = useMemo(() => {
|
||||
return PERFORMANCE_OPTIONS.reduce((acc, [, options], index) => {
|
||||
acc[index] = options.every(({ key }) => performanceSettings[key]);
|
||||
|
||||
return acc;
|
||||
}, {} as Record<number, boolean>);
|
||||
}, [performanceSettings]);
|
||||
|
||||
const animationLevelState = useMemo(() => {
|
||||
if (areDeepEqual(performanceSettings, INITIAL_PERFORMANCE_STATE_MAX)) {
|
||||
return ANIMATION_LEVEL_MAX;
|
||||
}
|
||||
if (areDeepEqual(performanceSettings, INITIAL_PERFORMANCE_STATE_MIN)) {
|
||||
return ANIMATION_LEVEL_MIN;
|
||||
}
|
||||
if (areDeepEqual(performanceSettings, INITIAL_PERFORMANCE_STATE_MID)) {
|
||||
return ANIMATION_LEVEL_MED;
|
||||
}
|
||||
|
||||
return ANIMATION_LEVEL_CUSTOM;
|
||||
}, [performanceSettings]);
|
||||
const animationLevelOptions = animationLevelState === ANIMATION_LEVEL_CUSTOM
|
||||
? ANIMATION_LEVEL_CUSTOM_OPTIONS
|
||||
: ANIMATION_LEVEL_OPTIONS;
|
||||
|
||||
const handleToggleSection = useCallback((e: React.MouseEvent, index?: string) => {
|
||||
e.preventDefault();
|
||||
const sectionIndex = Number(index);
|
||||
|
||||
setSectionExpandedStates((prev) => ({
|
||||
...prev,
|
||||
[sectionIndex]: !prev[sectionIndex],
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const handleAnimationLevelChange = useCallback((newLevel: number) => {
|
||||
const performance = newLevel === ANIMATION_LEVEL_MIN
|
||||
? INITIAL_PERFORMANCE_STATE_MIN
|
||||
: (newLevel === ANIMATION_LEVEL_MED ? INITIAL_PERFORMANCE_STATE_MID : INITIAL_PERFORMANCE_STATE_MAX);
|
||||
|
||||
setSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
updatePerformanceSettings(performance);
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handlePropertyGroupChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, checked } = e.target;
|
||||
const perfomanceSection = PERFORMANCE_OPTIONS.find(([sectionName]) => sectionName === name);
|
||||
if (!perfomanceSection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newSettings = perfomanceSection[1].reduce((acc, { key }) => {
|
||||
acc[key] = checked;
|
||||
return acc;
|
||||
}, {} as Partial<PerformanceType>);
|
||||
|
||||
updatePerformanceSettings(newSettings);
|
||||
}, []);
|
||||
|
||||
const handlePropertyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, checked } = e.target;
|
||||
|
||||
updatePerformanceSettings({ [name as PerformanceTypeKey]: checked });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="settings-content custom-scroll">
|
||||
<div className="settings-item">
|
||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
Animation Level
|
||||
</h4>
|
||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
Choose the desired animations amount.
|
||||
</p>
|
||||
|
||||
<RangeSlider
|
||||
options={animationLevelOptions}
|
||||
value={animationLevelState === ANIMATION_LEVEL_CUSTOM ? ANIMATION_LEVEL_MED : animationLevelState}
|
||||
onChange={handleAnimationLevelChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="settings-item-simple settings-item__with-shifted-dropdown">
|
||||
<h3 className="settings-item-header" dir="auto">Resource-Intensive Processes</h3>
|
||||
|
||||
{PERFORMANCE_OPTIONS.map(([sectionName, options], index) => {
|
||||
return (
|
||||
<div
|
||||
key={sectionName}
|
||||
className="settings-dropdown-section"
|
||||
>
|
||||
<div className="ListItem no-selection with-checkbox">
|
||||
<Checkbox
|
||||
name={sectionName}
|
||||
value={index.toString()}
|
||||
checked={sectionCheckedStates[index]}
|
||||
label={lang(sectionName)}
|
||||
rightIcon={sectionExpandedStates[index] ? 'up' : 'down'}
|
||||
onChange={handlePropertyGroupChange}
|
||||
onClickLabel={handleToggleSection}
|
||||
/>
|
||||
</div>
|
||||
{Boolean(sectionExpandedStates[index]) && (
|
||||
<div className="DropdownList DropdownList--open">
|
||||
{options.map(({ key, label, disabled }) => (
|
||||
<Checkbox
|
||||
key={key}
|
||||
name={key}
|
||||
checked={performanceSettings[key]}
|
||||
label={lang(label)}
|
||||
disabled={disabled}
|
||||
onChange={handlePropertyChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
return {
|
||||
performanceSettings: selectPerformanceSettings(global),
|
||||
};
|
||||
})(SettingsPerformance));
|
||||
@ -17,6 +17,7 @@ import renderText from '../../common/helpers/renderText';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -36,14 +37,14 @@ type OwnProps = {
|
||||
|
||||
type StateProps =
|
||||
Pick<ISettings, (
|
||||
'shouldSuggestStickers' |
|
||||
'shouldLoopStickers'
|
||||
'shouldSuggestStickers'
|
||||
)> & {
|
||||
addedSetIds?: string[];
|
||||
customEmojiSetIds?: string[];
|
||||
stickerSetsById: Record<string, ApiStickerSet>;
|
||||
defaultReaction?: ApiReaction;
|
||||
availableReactions?: ApiAvailableReaction[];
|
||||
canPlayAnimatedEmojis: boolean;
|
||||
};
|
||||
|
||||
const SettingsStickers: FC<OwnProps & StateProps> = ({
|
||||
@ -53,8 +54,8 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
||||
stickerSetsById,
|
||||
defaultReaction,
|
||||
shouldSuggestStickers,
|
||||
shouldLoopStickers,
|
||||
availableReactions,
|
||||
canPlayAnimatedEmojis,
|
||||
onReset,
|
||||
onScreenSelect,
|
||||
}) => {
|
||||
@ -78,10 +79,6 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
||||
setSettingOption({ shouldSuggestStickers: newValue });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleShouldLoopStickersChange = useCallback((newValue: boolean) => {
|
||||
setSettingOption({ shouldLoopStickers: newValue });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const stickerSets = useMemo(() => (
|
||||
addedSetIds && Object.values(pick(stickerSetsById, addedSetIds))
|
||||
), [addedSetIds, stickerSetsById]);
|
||||
@ -99,11 +96,6 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
||||
checked={shouldSuggestStickers}
|
||||
onCheck={handleSuggestStickersChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('LoopAnimatedStickers')}
|
||||
checked={shouldLoopStickers}
|
||||
onCheck={handleShouldLoopStickersChange}
|
||||
/>
|
||||
<ListItem
|
||||
className="mt-4"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
@ -141,6 +133,7 @@ const SettingsStickers: FC<OwnProps & StateProps> = ({
|
||||
stickerSet={stickerSet}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
onClick={handleStickerSetClick}
|
||||
noPlay={!canPlayAnimatedEmojis}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -158,13 +151,13 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
...pick(global.settings.byKey, [
|
||||
'shouldSuggestStickers',
|
||||
'shouldLoopStickers',
|
||||
]),
|
||||
addedSetIds: global.stickers.added.setIds,
|
||||
customEmojiSetIds: global.customEmojis.added.setIds,
|
||||
stickerSetsById: global.stickers.setsById,
|
||||
defaultReaction: global.config?.defaultReaction,
|
||||
availableReactions: global.availableReactions,
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
},
|
||||
)(SettingsStickers));
|
||||
|
||||
@ -5,7 +5,6 @@ import { getActions, withGlobal } from '../../global';
|
||||
import type {
|
||||
ApiContact, ApiError, ApiInviteInfo, ApiPhoto,
|
||||
} from '../../api/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
|
||||
import { selectTabState } from '../../global/selectors';
|
||||
import getReadableErrorText from '../../util/getReadableErrorText';
|
||||
@ -20,10 +19,9 @@ import Avatar from '../common/Avatar';
|
||||
|
||||
type StateProps = {
|
||||
dialogs: (ApiError | ApiInviteInfo | ApiContact)[];
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const Dialogs: FC<StateProps> = ({ dialogs, animationLevel }) => {
|
||||
const Dialogs: FC<StateProps> = ({ dialogs }) => {
|
||||
const {
|
||||
dismissDialog,
|
||||
acceptInviteConfirmation,
|
||||
@ -47,7 +45,7 @@ const Dialogs: FC<StateProps> = ({ dialogs, animationLevel }) => {
|
||||
function renderInviteHeader(title: string, photo?: ApiPhoto) {
|
||||
return (
|
||||
<div className="modal-header">
|
||||
{photo && <Avatar size="small" photo={photo} animationLevel={animationLevel} withVideo />}
|
||||
{photo && <Avatar size="small" photo={photo} withVideo />}
|
||||
<div className="modal-title">
|
||||
{renderText(title)}
|
||||
</div>
|
||||
@ -196,7 +194,6 @@ export default memo(withGlobal(
|
||||
(global): StateProps => {
|
||||
return {
|
||||
dialogs: selectTabState(global).dialogs,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(Dialogs));
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
transform: translate3d(-5rem, 0, 0);
|
||||
transition: transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -80,10 +80,6 @@
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
// @optimization
|
||||
body.is-android & {
|
||||
display: none;
|
||||
@ -159,13 +155,18 @@
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition: transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#Main.left-column-open & {
|
||||
transform: translate3d(26.5rem, 0, 0);
|
||||
}
|
||||
|
||||
body.no-right-column-animations #Main.right-column-open &,
|
||||
body.no-right-column-animations #Main.right-column-shown & {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@ -189,7 +190,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
body.is-android.animation-level-1 {
|
||||
body.is-android:not(.no-right-column-animations) {
|
||||
--layer-transition: 250ms ease-in-out;
|
||||
|
||||
#RightColumn {
|
||||
transition: transform var(--layer-transition), opacity var(--layer-transition);
|
||||
}
|
||||
}
|
||||
|
||||
body.is-android.no-page-transitions {
|
||||
--layer-transition: 250ms ease-in-out;
|
||||
|
||||
#LeftColumn, #MiddleColumn, #RightColumn {
|
||||
|
||||
@ -6,7 +6,7 @@ import { addExtraClass } from '../../lib/teact/teact-dom';
|
||||
import { requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type { AnimationLevel, LangCode } from '../../types';
|
||||
import type { LangCode } from '../../types';
|
||||
import type {
|
||||
ApiAttachBot,
|
||||
ApiChat, ApiMessage, ApiUser,
|
||||
@ -27,7 +27,10 @@ import {
|
||||
selectIsMediaViewerOpen,
|
||||
selectIsRightColumnShown,
|
||||
selectIsServiceChatReady,
|
||||
selectUser, selectIsReactionPickerOpen,
|
||||
selectUser,
|
||||
selectIsReactionPickerOpen,
|
||||
selectPerformanceSettingsValue,
|
||||
selectCanAnimateInterface,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { waitForTransitionEnd } from '../../util/cssAnimationEndListeners';
|
||||
@ -109,7 +112,6 @@ type StateProps = {
|
||||
openedCustomEmojiSetIds?: string[];
|
||||
activeGroupCallId?: string;
|
||||
isServiceChatReady?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
language?: LangCode;
|
||||
wasTimeFormatSetManually?: boolean;
|
||||
isPhoneCallActive?: boolean;
|
||||
@ -135,6 +137,8 @@ type StateProps = {
|
||||
isReceiptModalOpen?: boolean;
|
||||
isReactionPickerOpen: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
noRightColumnAnimation?: boolean;
|
||||
withInterfaceAnimations?: boolean;
|
||||
};
|
||||
|
||||
const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min
|
||||
@ -163,7 +167,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
openedStickerSetShortName,
|
||||
openedCustomEmojiSetIds,
|
||||
isServiceChatReady,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
language,
|
||||
wasTimeFormatSetManually,
|
||||
addedSetIds,
|
||||
@ -189,6 +193,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
isCurrentUserPremium,
|
||||
deleteFolderDialogId,
|
||||
isMasterTab,
|
||||
noRightColumnAnimation,
|
||||
}) => {
|
||||
const {
|
||||
initMain,
|
||||
@ -386,7 +391,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
|
||||
// Handle opening middle column
|
||||
useSyncEffect(([prevIsLeftColumnOpen]) => {
|
||||
if (prevIsLeftColumnOpen === undefined || isLeftColumnOpen === prevIsLeftColumnOpen || animationLevel === 0) {
|
||||
if (prevIsLeftColumnOpen === undefined || isLeftColumnOpen === prevIsLeftColumnOpen || !withInterfaceAnimations) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -405,7 +410,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
willAnimateLeftColumnRef.current = false;
|
||||
forceUpdate();
|
||||
});
|
||||
}, [isLeftColumnOpen, animationLevel, forceUpdate]);
|
||||
}, [isLeftColumnOpen, withInterfaceAnimations, forceUpdate]);
|
||||
|
||||
const rightColumnTransition = useShowTransition(
|
||||
isRightColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations, undefined, true,
|
||||
@ -419,7 +424,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (animationLevel === 0) {
|
||||
if (noRightColumnAnimation) {
|
||||
setIsNarrowMessageList(isRightColumnOpen);
|
||||
return;
|
||||
}
|
||||
@ -434,7 +439,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
forceUpdate();
|
||||
setIsNarrowMessageList(isRightColumnOpen);
|
||||
});
|
||||
}, [isRightColumnOpen, animationLevel, forceUpdate]);
|
||||
}, [isRightColumnOpen, noRightColumnAnimation, forceUpdate]);
|
||||
|
||||
const className = buildClassName(
|
||||
leftColumnTransition.hasShownClass && 'left-column-shown',
|
||||
@ -534,7 +539,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
settings: {
|
||||
byKey: {
|
||||
animationLevel, language, wasTimeFormatSetManually,
|
||||
language, wasTimeFormatSetManually,
|
||||
},
|
||||
},
|
||||
lastSyncTime,
|
||||
@ -574,6 +579,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const gameTitle = gameMessage?.content.game?.title;
|
||||
const currentUser = global.currentUserId ? selectUser(global, global.currentUserId) : undefined;
|
||||
const { chatId } = selectCurrentMessageList(global) || {};
|
||||
const noRightColumnAnimation = !selectPerformanceSettingsValue(global, 'rightColumnAnimations')
|
||||
|| !selectCanAnimateInterface(global);
|
||||
|
||||
return {
|
||||
lastSyncTime,
|
||||
@ -593,7 +600,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
openedCustomEmojiSetIds,
|
||||
isServiceChatReady: selectIsServiceChatReady(global),
|
||||
activeGroupCallId: isMasterTab ? global.groupCalls.activeGroupCallId : undefined,
|
||||
animationLevel,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
language,
|
||||
wasTimeFormatSetManually,
|
||||
isPhoneCallActive: isMasterTab ? Boolean(global.phoneCall) : undefined,
|
||||
@ -619,6 +626,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
deleteFolderDialogId: deleteFolderDialogModal,
|
||||
isMasterTab,
|
||||
requestedDraft,
|
||||
noRightColumnAnimation,
|
||||
};
|
||||
},
|
||||
)(Main));
|
||||
|
||||
@ -5,7 +5,6 @@ import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiPremiumGiftOption, ApiUser } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
@ -36,7 +35,6 @@ type StateProps = {
|
||||
gifts?: ApiPremiumGiftOption[];
|
||||
monthlyCurrency?: string;
|
||||
monthlyAmount?: number;
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const GiftPremiumModal: FC<OwnProps & StateProps> = ({
|
||||
@ -45,7 +43,6 @@ const GiftPremiumModal: FC<OwnProps & StateProps> = ({
|
||||
gifts,
|
||||
monthlyCurrency,
|
||||
monthlyAmount,
|
||||
animationLevel,
|
||||
}) => {
|
||||
const { openPremiumModal, closeGiftPremiumModal, openUrl } = getActions();
|
||||
|
||||
@ -131,8 +128,6 @@ const GiftPremiumModal: FC<OwnProps & StateProps> = ({
|
||||
user={renderedUser}
|
||||
size="jumbo"
|
||||
className={styles.avatar}
|
||||
animationLevel={animationLevel}
|
||||
withVideo
|
||||
/>
|
||||
<h2 className={styles.headerText}>
|
||||
{lang('GiftTelegramPremiumTitle')}
|
||||
@ -179,6 +174,5 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
gifts,
|
||||
monthlyCurrency,
|
||||
monthlyAmount: monthlyAmount ? Number(monthlyAmount) : undefined,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
})(GiftPremiumModal));
|
||||
|
||||
@ -219,7 +219,10 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className={styles.footerText} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{renderTextWithEntities(promo.statusText, promo.statusEntities)}
|
||||
{renderTextWithEntities({
|
||||
text: promo.statusText,
|
||||
entities: promo.statusEntities,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
body.animation-level-2 & {
|
||||
body:not(.no-media-viewer-animations) & {
|
||||
transition-duration: 0.3s !important;
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ import React, {
|
||||
import type {
|
||||
ApiChat, ApiMessage, ApiPhoto, ApiUser,
|
||||
} from '../../api/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
@ -23,6 +22,7 @@ import {
|
||||
selectUser,
|
||||
selectOutlyingListByMessageId,
|
||||
selectUserFullInfo,
|
||||
selectPerformanceSettingsValue,
|
||||
} from '../../global/selectors';
|
||||
import { stopCurrentAudio } from '../../util/audioPlayer';
|
||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||
@ -68,7 +68,7 @@ type StateProps = {
|
||||
chatMessages?: Record<number, ApiMessage>;
|
||||
collectionIds?: number[];
|
||||
isHidden?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
withAnimation?: boolean;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
};
|
||||
|
||||
@ -87,7 +87,7 @@ const MediaViewer: FC<StateProps> = ({
|
||||
message,
|
||||
chatMessages,
|
||||
collectionIds,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
isHidden,
|
||||
shouldSkipHistoryAnimations,
|
||||
}) => {
|
||||
@ -105,8 +105,8 @@ const MediaViewer: FC<StateProps> = ({
|
||||
/* Animation */
|
||||
const animationKey = useRef<number>();
|
||||
const prevSenderId = usePrevious<string | undefined>(senderId);
|
||||
const headerAnimation = animationLevel === 2 ? 'slideFade' : 'none';
|
||||
const isGhostAnimation = animationLevel === 2 && !shouldSkipHistoryAnimations;
|
||||
const headerAnimation = withAnimation ? 'slideFade' : 'none';
|
||||
const isGhostAnimation = Boolean(withAnimation && !shouldSkipHistoryAnimations);
|
||||
|
||||
/* Controls */
|
||||
const [isReportModalOpen, openReportModal, closeReportModal] = useFlag();
|
||||
@ -373,7 +373,7 @@ const MediaViewer: FC<StateProps> = ({
|
||||
isOpen={isOpen}
|
||||
hasFooter={hasFooter}
|
||||
isVideo={isVideo}
|
||||
animationLevel={animationLevel}
|
||||
withAnimation={withAnimation}
|
||||
onClose={handleClose}
|
||||
selectMedia={selectMedia}
|
||||
isHidden={isHidden}
|
||||
@ -394,21 +394,19 @@ export default memo(withGlobal(
|
||||
origin,
|
||||
isHidden,
|
||||
} = mediaViewer;
|
||||
const {
|
||||
animationLevel,
|
||||
} = global.settings.byKey;
|
||||
const withAnimation = selectPerformanceSettingsValue(global, 'mediaViewerAnimations');
|
||||
|
||||
const { currentUserId } = global;
|
||||
let isChatWithSelf = !!chatId && selectIsChatWithSelf(global, chatId);
|
||||
|
||||
if (origin === MediaViewerOrigin.SearchResult) {
|
||||
if (!(chatId && mediaId)) {
|
||||
return { animationLevel, shouldSkipHistoryAnimations };
|
||||
return { withAnimation, shouldSkipHistoryAnimations };
|
||||
}
|
||||
|
||||
const message = selectChatMessage(global, chatId, mediaId);
|
||||
if (!message) {
|
||||
return { animationLevel, shouldSkipHistoryAnimations };
|
||||
return { withAnimation, shouldSkipHistoryAnimations };
|
||||
}
|
||||
|
||||
return {
|
||||
@ -418,7 +416,7 @@ export default memo(withGlobal(
|
||||
isChatWithSelf,
|
||||
origin,
|
||||
message,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
isHidden,
|
||||
shouldSkipHistoryAnimations,
|
||||
};
|
||||
@ -443,7 +441,7 @@ export default memo(withGlobal(
|
||||
avatarOwnerFallbackPhoto: user ? selectUserFullInfo(global, avatarOwnerId)?.fallbackPhoto : undefined,
|
||||
isChatWithSelf,
|
||||
canUpdateMedia,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
origin,
|
||||
shouldSkipHistoryAnimations,
|
||||
isHidden,
|
||||
@ -451,7 +449,7 @@ export default memo(withGlobal(
|
||||
}
|
||||
|
||||
if (!(chatId && threadId && mediaId)) {
|
||||
return { animationLevel, shouldSkipHistoryAnimations };
|
||||
return { withAnimation, shouldSkipHistoryAnimations };
|
||||
}
|
||||
|
||||
let message: ApiMessage | undefined;
|
||||
@ -462,7 +460,7 @@ export default memo(withGlobal(
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
return { animationLevel, shouldSkipHistoryAnimations };
|
||||
return { withAnimation, shouldSkipHistoryAnimations };
|
||||
}
|
||||
|
||||
let chatMessages: Record<number, ApiMessage> | undefined;
|
||||
@ -494,7 +492,7 @@ export default memo(withGlobal(
|
||||
message,
|
||||
chatMessages,
|
||||
collectionIds,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
isHidden,
|
||||
shouldSkipHistoryAnimations,
|
||||
};
|
||||
|
||||
@ -5,7 +5,6 @@ import { withGlobal } from '../../global';
|
||||
import type {
|
||||
ApiChat, ApiDimensions, ApiMessage, ApiUser,
|
||||
} from '../../api/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import {
|
||||
@ -36,7 +35,7 @@ type OwnProps = {
|
||||
avatarOwnerId?: string;
|
||||
origin?: MediaViewerOrigin;
|
||||
isActive?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
withAnimation?: boolean;
|
||||
onClose: () => void;
|
||||
onFooterClick: () => void;
|
||||
isMoving?: boolean;
|
||||
@ -69,7 +68,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
chatId,
|
||||
message,
|
||||
origin,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
isProtected,
|
||||
volume,
|
||||
playbackRate,
|
||||
@ -82,8 +81,6 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
const isGhostAnimation = animationLevel === 2;
|
||||
|
||||
const {
|
||||
isVideo,
|
||||
isPhoto,
|
||||
@ -97,7 +94,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
videoSize,
|
||||
loadProgress,
|
||||
} = useMediaProps({
|
||||
message, avatarOwner, mediaId, origin, delay: isGhostAnimation && ANIMATION_DURATION,
|
||||
message, avatarOwner, mediaId, origin, delay: withAnimation ? ANIMATION_DURATION : false,
|
||||
});
|
||||
|
||||
const [, toggleControls] = useControlsSignal();
|
||||
|
||||
@ -3,7 +3,7 @@ import React, {
|
||||
memo, useCallback, useEffect, useLayoutEffect, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import type { AnimationLevel, MediaViewerOrigin } from '../../types';
|
||||
import type { MediaViewerOrigin } from '../../types';
|
||||
import type { RealTouchEvent } from '../../util/captureEvents';
|
||||
|
||||
import { animateNumber, timingFunctions } from '../../util/animation';
|
||||
@ -43,7 +43,7 @@ type OwnProps = {
|
||||
threadId?: number;
|
||||
avatarOwnerId?: string;
|
||||
origin?: MediaViewerOrigin;
|
||||
animationLevel: AnimationLevel;
|
||||
withAnimation?: boolean;
|
||||
onClose: () => void;
|
||||
isHidden?: boolean;
|
||||
hasFooter?: boolean;
|
||||
@ -85,7 +85,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
isPhoto,
|
||||
isOpen,
|
||||
hasFooter,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
isHidden,
|
||||
...rest
|
||||
}) => {
|
||||
@ -196,7 +196,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
selectMediaDebounced(mId);
|
||||
setIsActiveDebounced(true);
|
||||
lastTransform = { x: 0, y: 0, scale: 1 };
|
||||
if (animationLevel === 0) {
|
||||
if (!withAnimation) {
|
||||
setTransform(lastTransform);
|
||||
return true;
|
||||
}
|
||||
@ -643,7 +643,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
selectMediaDebounced,
|
||||
setIsActiveDebounced,
|
||||
clearSwipeDirectionDebounced,
|
||||
animationLevel,
|
||||
withAnimation,
|
||||
setIsMouseDown,
|
||||
setIsActive,
|
||||
isHidden,
|
||||
@ -703,7 +703,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
<MediaViewerContent
|
||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||
{...rest}
|
||||
animationLevel={animationLevel}
|
||||
withAnimation={withAnimation}
|
||||
isMoving={isMoving}
|
||||
mediaId={prevMediaId}
|
||||
/>
|
||||
@ -722,7 +722,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||
{...rest}
|
||||
mediaId={activeMediaId}
|
||||
animationLevel={animationLevel}
|
||||
withAnimation={withAnimation}
|
||||
isActive={isActive}
|
||||
isMoving={isMoving}
|
||||
/>
|
||||
@ -732,7 +732,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
<MediaViewerContent
|
||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||
{...rest}
|
||||
animationLevel={animationLevel}
|
||||
withAnimation={withAnimation}
|
||||
isMoving={isMoving}
|
||||
mediaId={nextMediaId}
|
||||
/>
|
||||
|
||||
@ -3,7 +3,6 @@ import React, { useCallback } from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiChat, ApiMessage, ApiUser } from '../../api/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
|
||||
import { getSenderTitle, isUserId } from '../../global/helpers';
|
||||
import { formatMediaDateTime } from '../../util/dateFormat';
|
||||
@ -31,7 +30,6 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
sender?: ApiUser | ApiChat;
|
||||
message?: ApiMessage;
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const ANIMATION_DURATION = 350;
|
||||
@ -43,7 +41,6 @@ const SenderInfo: FC<OwnProps & StateProps> = ({
|
||||
isFallbackAvatar,
|
||||
isAvatar,
|
||||
message,
|
||||
animationLevel,
|
||||
}) => {
|
||||
const {
|
||||
closeMediaViewer,
|
||||
@ -79,9 +76,9 @@ const SenderInfo: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<div className="SenderInfo" onClick={handleFocusMessage}>
|
||||
{isUserId(sender.id) ? (
|
||||
<Avatar key={sender.id} size="medium" user={sender as ApiUser} animationLevel={animationLevel} withVideo />
|
||||
<Avatar key={sender.id} size="medium" user={sender as ApiUser} />
|
||||
) : (
|
||||
<Avatar key={sender.id} size="medium" chat={sender as ApiChat} animationLevel={animationLevel} withVideo />
|
||||
<Avatar key={sender.id} size="medium" chat={sender as ApiChat} />
|
||||
)}
|
||||
<div className="meta">
|
||||
<div className="title" dir="auto">
|
||||
@ -99,16 +96,14 @@ const SenderInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default withGlobal<OwnProps>(
|
||||
(global, { chatId, messageId, isAvatar }): StateProps => {
|
||||
const { animationLevel } = global.settings.byKey;
|
||||
if (isAvatar && chatId) {
|
||||
return {
|
||||
sender: isUserId(chatId) ? selectUser(global, chatId) : selectChat(global, chatId),
|
||||
animationLevel,
|
||||
};
|
||||
}
|
||||
|
||||
if (!messageId || !chatId) {
|
||||
return { animationLevel };
|
||||
return {};
|
||||
}
|
||||
|
||||
const message = selectChatMessage(global, chatId, messageId);
|
||||
@ -116,7 +111,6 @@ export default withGlobal<OwnProps>(
|
||||
return {
|
||||
message,
|
||||
sender: message && selectSender(global, message),
|
||||
animationLevel,
|
||||
};
|
||||
},
|
||||
)(SenderInfo);
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
height: 3.25rem;
|
||||
background-color: rgba(0, 0, 0, 0.5) !important;
|
||||
z-index: 3;
|
||||
body:not(.animation-level-0) & {
|
||||
body:not(.no-page-transitions) & {
|
||||
transition: opacity 0.3s ease !important;
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useMemo, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type {
|
||||
ApiUser, ApiMessage, ApiChat, ApiSticker, ApiTopic,
|
||||
@ -18,6 +18,7 @@ import {
|
||||
selectChat,
|
||||
selectTopicFromMessage,
|
||||
selectTabState,
|
||||
selectCanPlayAnimatedEmojis,
|
||||
} from '../../global/selectors';
|
||||
import { getMessageHtmlId, isChatChannel } from '../../global/helpers';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -53,7 +54,6 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
usersById: Record<string, ApiUser>;
|
||||
senderUser?: ApiUser;
|
||||
senderChat?: ApiChat;
|
||||
targetUserIds?: string[];
|
||||
@ -64,6 +64,7 @@ type StateProps = {
|
||||
focusDirection?: FocusDirection;
|
||||
noFocusHighlight?: boolean;
|
||||
premiumGiftSticker?: ApiSticker;
|
||||
canPlayAnimatedEmojis?: boolean;
|
||||
};
|
||||
|
||||
const APPEARANCE_DELAY = 10;
|
||||
@ -74,7 +75,6 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
||||
appearanceOrder = 0,
|
||||
isJustAdded,
|
||||
isLastInList,
|
||||
usersById,
|
||||
senderUser,
|
||||
senderChat,
|
||||
targetUserIds,
|
||||
@ -87,6 +87,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
||||
isInsideTopic,
|
||||
topic,
|
||||
memoFirstUnreadIdRef,
|
||||
canPlayAnimatedEmojis,
|
||||
observeIntersectionForReading,
|
||||
observeIntersectionForLoading,
|
||||
observeIntersectionForPlaying,
|
||||
@ -140,6 +141,8 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const { transitionClassNames } = useShowTransition(isShown, undefined, noAppearanceAnimation, false);
|
||||
|
||||
// No need for expensive global updates on users and chats, so we avoid them
|
||||
const usersById = getGlobal().users.byId;
|
||||
const targetUsers = useMemo(() => {
|
||||
return targetUserIds
|
||||
? targetUserIds.map((userId) => usersById?.[userId]).filter(Boolean)
|
||||
@ -196,7 +199,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
||||
<AnimatedIconFromSticker
|
||||
key={message.id}
|
||||
sticker={premiumGiftSticker}
|
||||
play
|
||||
play={canPlayAnimatedEmojis}
|
||||
noLoop
|
||||
nonInteractive
|
||||
/>
|
||||
@ -256,7 +259,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
chatId, senderId, replyToMessageId, content,
|
||||
} = message;
|
||||
|
||||
const { byId: usersById } = global.users;
|
||||
const userId = senderId;
|
||||
const { targetUserIds, targetChatId } = content.action || {};
|
||||
const targetMessageId = replyToMessageId;
|
||||
@ -278,7 +280,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const topic = selectTopicFromMessage(global, message);
|
||||
|
||||
return {
|
||||
usersById,
|
||||
senderUser,
|
||||
senderChat,
|
||||
targetChatId,
|
||||
@ -287,6 +288,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isFocused,
|
||||
premiumGiftSticker,
|
||||
topic,
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
...(isFocused && {
|
||||
focusDirection,
|
||||
noFocusHighlight,
|
||||
|
||||
@ -98,7 +98,6 @@ const ActionMessageSuggestedAvatar: FC<OwnProps> = ({
|
||||
<span className="action-message-suggested-avatar" tabIndex={0} role="button" onClick={handleViewSuggestedAvatar}>
|
||||
<Avatar
|
||||
photo={message.content.action!.photo}
|
||||
forceVideo
|
||||
loopIndefinitely
|
||||
withVideo={isVideo}
|
||||
size="jumbo"
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
margin-top: -0.25rem;
|
||||
margin-bottom: -0.25rem;
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition: opacity 0.15s ease, transform var(--layer-transition);
|
||||
|
||||
body.animation-level-1 & {
|
||||
body.no-page-transitions & {
|
||||
.ripple-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:global(body.animation-level-0) & {
|
||||
:global(body.no-page-transitions) & {
|
||||
transform: none !important;
|
||||
|
||||
transition: opacity 0.15s;
|
||||
|
||||
@ -13,12 +13,12 @@ import { MAIN_THREAD_ID } from '../../api/types';
|
||||
import type { IAnchorPosition } from '../../types';
|
||||
import { ManagementScreens } from '../../types';
|
||||
|
||||
import { ANIMATION_LEVEL_MIN } from '../../config';
|
||||
import { ARE_CALLS_SUPPORTED, IS_PWA } from '../../util/windowEnvironment';
|
||||
import {
|
||||
isChatBasicGroup, isChatChannel, isChatSuperGroup, isUserId,
|
||||
} from '../../global/helpers';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectChat,
|
||||
selectChatBot,
|
||||
selectChatFullInfo,
|
||||
@ -351,7 +351,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const pendingJoinRequests = isMainThread ? chatFullInfo?.requestsPending : undefined;
|
||||
const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend);
|
||||
const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest);
|
||||
const noAnimation = global.settings.byKey.animationLevel === ANIMATION_LEVEL_MIN;
|
||||
const noAnimation = !selectCanAnimateInterface(global);
|
||||
|
||||
return {
|
||||
noMenu: false,
|
||||
|
||||
@ -18,13 +18,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
:global(body.animation-level-1) & {
|
||||
:global(body.no-page-transitions) & {
|
||||
:global(.ripple-container) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:global(body.animation-level-0) & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@ -32,6 +30,10 @@
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition: opacity 0.15s ease, transform var(--layer-transition);
|
||||
|
||||
:global(body.no-right-column-animations) & {
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
:global(#Main.right-column-open) & {
|
||||
transform: translate3d(calc(var(--right-column-width) * -1), 0, 0);
|
||||
}
|
||||
@ -264,7 +266,7 @@
|
||||
:global(.tools-stacked.animated) .root {
|
||||
animation: fade-in var(--layer-transition) forwards;
|
||||
|
||||
:global(body.animation-level-0) & {
|
||||
:global(body.no-page-transitions) & {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
transition: transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
body.keyboard-visible.animation-level-0 & {
|
||||
body.keyboard-visible.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
@ -125,10 +125,9 @@
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-message-sending-animations & {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
display: flex !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@ -155,7 +154,7 @@
|
||||
opacity: 0;
|
||||
transition: opacity var(--select-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
@ -362,7 +361,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -434,7 +433,7 @@
|
||||
width: calc(100% - var(--right-column-width));
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-right-column-animations & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,6 @@ import type {
|
||||
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
import type { MessageListType } from '../../global/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import type { Signal } from '../../util/signals';
|
||||
import type { PinnedIntersectionChangedCallback } from './hooks/usePinnedMessage';
|
||||
import { LoadMoreDirection } from '../../types';
|
||||
@ -41,7 +40,9 @@ import {
|
||||
selectLastScrollOffset,
|
||||
selectThreadInfo,
|
||||
selectTabState,
|
||||
selectUserFullInfo, selectChatFullInfo,
|
||||
selectUserFullInfo,
|
||||
selectChatFullInfo,
|
||||
selectPerformanceSettingsValue,
|
||||
} from '../../global/selectors';
|
||||
import {
|
||||
isChatChannel,
|
||||
@ -117,7 +118,6 @@ type StateProps = {
|
||||
restrictionReason?: ApiRestrictionReason;
|
||||
focusingId?: number;
|
||||
isSelectModeActive?: boolean;
|
||||
animationLevel?: AnimationLevel;
|
||||
lastMessage?: ApiMessage;
|
||||
isLoadingBotInfo?: boolean;
|
||||
botInfo?: ApiBotInfo;
|
||||
@ -126,6 +126,7 @@ type StateProps = {
|
||||
hasLinkedChat?: boolean;
|
||||
lastSyncTime?: number;
|
||||
topic?: ApiTopic;
|
||||
noMessageSendingAnimation?: boolean;
|
||||
};
|
||||
|
||||
const MESSAGE_REACTIONS_POLLING_INTERVAL = 15 * 1000;
|
||||
@ -177,6 +178,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
withBottomShift,
|
||||
withDefaultBg,
|
||||
topic,
|
||||
noMessageSendingAnimation,
|
||||
onPinnedIntersectionChange,
|
||||
getForceNextPinnedInHeader,
|
||||
}) => {
|
||||
@ -464,6 +466,9 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
lastItemElement!,
|
||||
'end',
|
||||
BOTTOM_FOCUS_MARGIN,
|
||||
undefined,
|
||||
undefined,
|
||||
noMessageSendingAnimation ? 0 : undefined,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -516,7 +521,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
};
|
||||
});
|
||||
// This should match deps for `useSyncEffect` above
|
||||
}, [messageIds, isViewportNewest, hasTools, getContainerHeight, prevContainerHeightRef]);
|
||||
}, [messageIds, isViewportNewest, hasTools, getContainerHeight, prevContainerHeightRef, noMessageSendingAnimation]);
|
||||
|
||||
useEffectWithPrevDeps(([prevIsSelectModeActive]) => {
|
||||
if (prevIsSelectModeActive !== undefined) {
|
||||
@ -730,6 +735,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
hasLinkedChat: Boolean(chatFullInfo?.linkedChatId),
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
topic,
|
||||
noMessageSendingAnimation: !selectPerformanceSettingsValue(global, 'messageSendingAnimations'),
|
||||
...(withLastMessageWhenPreloading && { lastMessage }),
|
||||
};
|
||||
},
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
top: auto;
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
:global(body:not(.animation-level-0)) &.withTransition {
|
||||
:global(body:not(.no-page-transitions)) &.withTransition {
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&.customBgImage::before {
|
||||
@ -46,14 +46,14 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1276px) {
|
||||
:global(body.animation-level-2) &:not(.customBgImage)::before {
|
||||
:global(body:not(.no-page-transitions)) &:not(.customBgImage)::before {
|
||||
overflow: hidden;
|
||||
transform: scale(1);
|
||||
transform-origin: left center;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-light body.animation-level-2) &:not(.customBgImage).withRightColumn::before {
|
||||
:global(html.theme-light body:not(.no-page-transitions)) &:not(.customBgImage).withRightColumn::before {
|
||||
@media screen and (min-width: 1276px) {
|
||||
transform: scaleX(0.73) !important;
|
||||
}
|
||||
@ -65,7 +65,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-light body.animation-level-2) &:not(.customBgImage).withRightColumn.withTransition::before {
|
||||
:global(html.theme-light body:not(.no-page-transitions)) &:not(.customBgImage).withRightColumn.withTransition::before {
|
||||
transition: transform var(--layer-transition);
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
transition: transform var(--select-transition);
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-message-composer-animations & {
|
||||
&,
|
||||
&::before {
|
||||
transition: none !important;
|
||||
@ -57,7 +57,7 @@
|
||||
opacity: 1;
|
||||
transition: opacity var(--select-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-message-composer-animations & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@
|
||||
transition: opacity var(--select-transition), transform var(--select-transition), background-color 0.15s,
|
||||
color 0.15s;
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-message-composer-animations & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
@ -120,12 +120,12 @@
|
||||
opacity: 1;
|
||||
transition: opacity var(--select-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@ -162,10 +162,15 @@
|
||||
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||
transition: top 200ms, transform var(--layer-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
body.no-right-column-animations & {
|
||||
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||
transition: top 200ms !important;
|
||||
}
|
||||
|
||||
@media (min-width: 1276px) {
|
||||
width: calc(100% - var(--right-column-width));
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import type {
|
||||
MessageListType,
|
||||
ActiveEmojiInteraction,
|
||||
} from '../../global/types';
|
||||
import type { AnimationLevel, ThemeKey } from '../../types';
|
||||
import type { ThemeKey } from '../../types';
|
||||
|
||||
import {
|
||||
MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN,
|
||||
@ -19,11 +19,9 @@ import {
|
||||
MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN,
|
||||
SAFE_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN,
|
||||
SAFE_SCREEN_WIDTH_FOR_CHAT_INFO,
|
||||
ANIMATION_LEVEL_MAX,
|
||||
ANIMATION_END_DELAY,
|
||||
DARK_THEME_BG_COLOR,
|
||||
LIGHT_THEME_BG_COLOR,
|
||||
ANIMATION_LEVEL_MIN,
|
||||
SUPPORTED_IMAGE_CONTENT_TYPES,
|
||||
GENERAL_TOPIC_ID,
|
||||
TMP_CHAT_ID,
|
||||
@ -31,6 +29,7 @@ import {
|
||||
import { IS_ANDROID, IS_IOS, MASK_IMAGE_DISABLED } from '../../util/windowEnvironment';
|
||||
import { DropAreaState } from './composer/DropArea';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectChat,
|
||||
selectChatBot,
|
||||
selectChatFullInfo,
|
||||
@ -121,7 +120,7 @@ type StateProps = {
|
||||
isReactorListModalOpen: boolean;
|
||||
isGiftPremiumModalOpen?: boolean;
|
||||
isMessageLanguageModalOpen?: boolean;
|
||||
animationLevel: AnimationLevel;
|
||||
withInterfaceAnimations?: boolean;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
currentTransitionKey: number;
|
||||
isChannel?: boolean;
|
||||
@ -171,7 +170,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
|
||||
isReactorListModalOpen,
|
||||
isGiftPremiumModalOpen,
|
||||
isMessageLanguageModalOpen,
|
||||
animationLevel,
|
||||
withInterfaceAnimations,
|
||||
shouldSkipHistoryAnimations,
|
||||
currentTransitionKey,
|
||||
isChannel,
|
||||
@ -257,7 +256,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
|
||||
);
|
||||
|
||||
const { isReady, handleCssTransitionEnd, handleSlideTransitionStop } = useIsReady(
|
||||
!shouldSkipHistoryAnimations && animationLevel !== ANIMATION_LEVEL_MIN,
|
||||
!shouldSkipHistoryAnimations && withInterfaceAnimations,
|
||||
currentTransitionKey,
|
||||
prevTransitionKey,
|
||||
chatId,
|
||||
@ -473,7 +472,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
|
||||
onFocusPinnedMessage={onFocusPinnedMessage}
|
||||
/>
|
||||
<Transition
|
||||
name={shouldSkipHistoryAnimations ? 'none' : animationLevel === ANIMATION_LEVEL_MAX ? 'slide' : 'fade'}
|
||||
name={shouldSkipHistoryAnimations ? 'none' : withInterfaceAnimations ? 'slide' : 'fade'}
|
||||
activeKey={currentTransitionKey}
|
||||
shouldCleanup
|
||||
cleanupExceptionKey={cleanupExceptionKey}
|
||||
@ -655,7 +654,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isReactorListModalOpen: Boolean(reactorModal),
|
||||
isGiftPremiumModalOpen: giftPremiumModal?.isOpen,
|
||||
isMessageLanguageModalOpen: Boolean(messageLanguageModal),
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
currentTransitionKey: Math.max(0, messageLists.length - 1),
|
||||
activeEmojiInteractions,
|
||||
lastSyncTime,
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
&,
|
||||
.AudioPlayer,
|
||||
.HeaderActions {
|
||||
@ -120,6 +120,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
body.no-right-column-animations & {
|
||||
&,
|
||||
.HeaderActions {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1276px) and (max-width: 1439px) {
|
||||
.HeaderActions {
|
||||
transform: translate3d(0, 0, 0);
|
||||
@ -169,7 +176,7 @@
|
||||
&.tools-stacked.animated .AudioPlayer {
|
||||
animation: fade-in var(--layer-transition) forwards;
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,7 +361,6 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
withFullInfo
|
||||
withMediaViewer
|
||||
withUpdatingStatus
|
||||
withVideoAvatar={isReady}
|
||||
emojiStatusSize={EMOJI_STATUS_SIZE}
|
||||
noRtl
|
||||
/>
|
||||
@ -376,7 +375,6 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
withMediaViewer={threadId === MAIN_THREAD_ID}
|
||||
withFullInfo={threadId === MAIN_THREAD_ID}
|
||||
withUpdatingStatus
|
||||
withVideoAvatar={isReady}
|
||||
noRtl
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -56,7 +56,11 @@ function renderTopic(lang: LangFn, topic: ApiTopic) {
|
||||
return (
|
||||
<div className="NoMessages">
|
||||
<div className="wrapper">
|
||||
<TopicIcon topic={topic} size={ICON_SIZE} className="no-messages-icon topic-icon" />
|
||||
<TopicIcon
|
||||
topic={topic}
|
||||
size={ICON_SIZE}
|
||||
className="no-messages-icon topic-icon"
|
||||
/>
|
||||
<h3 className="title">{lang('Chat.EmptyTopicPlaceholder.Title')}</h3>
|
||||
<p className="description topic-description">{renderText(lang('Chat.EmptyTopicPlaceholder.Text'), ['br'])}</p>
|
||||
</div>
|
||||
|
||||
@ -5,10 +5,12 @@ import React, {
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiAvailableReaction, ApiMessage, ApiReaction } from '../../api/types';
|
||||
import type { AnimationLevel } from '../../types';
|
||||
import { LoadMoreDirection } from '../../types';
|
||||
|
||||
import { selectChatMessage, selectTabState } from '../../global/selectors';
|
||||
import {
|
||||
selectChatMessage,
|
||||
selectTabState,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { formatIntegerCompact } from '../../util/textFormat';
|
||||
import { unique } from '../../util/iteratees';
|
||||
@ -39,7 +41,6 @@ export type StateProps = Pick<ApiMessage, 'reactors' | 'reactions' | 'seenByUser
|
||||
chatId?: string;
|
||||
messageId?: number;
|
||||
availableReactions?: ApiAvailableReaction[];
|
||||
animationLevel?: AnimationLevel;
|
||||
};
|
||||
|
||||
const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
@ -50,7 +51,6 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
messageId,
|
||||
seenByUserIds,
|
||||
availableReactions,
|
||||
animationLevel,
|
||||
}) => {
|
||||
const {
|
||||
loadReactors,
|
||||
@ -194,7 +194,7 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleClick(userId)}
|
||||
>
|
||||
<Avatar user={user} size="small" withVideo animationLevel={animationLevel} />
|
||||
<Avatar user={user} size="small" />
|
||||
<FullNameTitle peer={user} withEmojiStatus />
|
||||
{r.reaction && (
|
||||
<ReactionStaticEmoji
|
||||
@ -235,7 +235,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
reactors: message?.reactors,
|
||||
seenByUserIds: message?.seenByUserIds,
|
||||
availableReactions: global.availableReactions,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(ReactorListModal));
|
||||
|
||||
@ -21,10 +21,15 @@
|
||||
|
||||
transition: transform var(--layer-transition), opacity 0.2s ease;
|
||||
|
||||
:global(body.animation-level-0) & {
|
||||
:global(body.no-page-transitions) &,
|
||||
:global(body.no-right-column-animations) & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
:global(body:not(.no-right-column-animations) #Main.right-column-open) & {
|
||||
transition: transform var(--layer-transition), opacity 0.2s ease;
|
||||
}
|
||||
|
||||
:global(#Main.right-column-open) & {
|
||||
transform: translateX(calc(-1 * var(--right-column-width)));
|
||||
}
|
||||
|
||||
@ -58,6 +58,11 @@
|
||||
overflow: hidden;
|
||||
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
:global(body.no-menu-blur) & {
|
||||
background-color: #707579;
|
||||
backdrop-filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
.action-item {
|
||||
|
||||
@ -156,8 +156,7 @@
|
||||
animation-duration: 0ms !important;
|
||||
}
|
||||
|
||||
body.animation-level-0 &,
|
||||
body.animation-level-1 & {
|
||||
body.no-message-composer-animations & {
|
||||
.icon-send,
|
||||
.icon-microphone-alt,
|
||||
.icon-check,
|
||||
@ -171,7 +170,7 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
body:not(.animation-level-0) & .send-as-button.appear-animation {
|
||||
body:not(.no-message-composer-animations) & .send-as-button.appear-animation {
|
||||
animation: 0.25s ease-in-out forwards show-send-as-button;
|
||||
transform-origin: right;
|
||||
}
|
||||
@ -447,7 +446,7 @@
|
||||
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||
transition: height 100ms ease;
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-message-composer-animations & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-message-composer-animations & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
selectIsChatWithSelf,
|
||||
selectIsCurrentUserPremium,
|
||||
selectTabState,
|
||||
selectCanAnimateInterface,
|
||||
} from '../../../global/selectors';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -295,7 +296,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const editingId = messageListType === 'scheduled'
|
||||
? selectEditingScheduledId(global, chatId)
|
||||
: selectEditingId(global, chatId, threadId);
|
||||
const shouldAnimate = global.settings.byKey.animationLevel >= 1;
|
||||
const shouldAnimate = selectCanAnimateInterface(global);
|
||||
const isForwarding = toChatId === chatId;
|
||||
const forwardedMessages = forwardMessageIds?.map((id) => selectChatMessage(global, fromChatId!, id)!);
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ export type OwnProps = {
|
||||
addRecentCustomEmoji: GlobalActions['addRecentCustomEmoji'];
|
||||
onCustomEmojiSelect: (customEmoji: ApiSticker) => void;
|
||||
onClose: NoneToVoidFunction;
|
||||
noPlay?: boolean;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
@ -46,6 +47,7 @@ const CustomEmojiTooltip: FC<OwnProps & StateProps> = ({
|
||||
customEmoji,
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium,
|
||||
noPlay,
|
||||
}) => {
|
||||
const { clearCustomEmojiForEmoji } = getActions();
|
||||
|
||||
@ -97,6 +99,7 @@ const CustomEmojiTooltip: FC<OwnProps & StateProps> = ({
|
||||
isSavedMessages={isSavedMessages}
|
||||
canViewSet
|
||||
isCurrentUserPremium={isCurrentUserPremium}
|
||||
noPlay={noPlay}
|
||||
/>
|
||||
))
|
||||
) : shouldRender ? (
|
||||
|
||||
@ -17,8 +17,11 @@
|
||||
@include overflow-y-overlay();
|
||||
|
||||
.Loading, .picker-disabled {
|
||||
grid-column: 1 / -1;
|
||||
height: var(--menu-height);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.SymbolMenu.mobile-menu & {
|
||||
|
||||
@ -13,7 +13,7 @@ import { EDITABLE_INPUT_ID } from '../../../config';
|
||||
import {
|
||||
IS_ANDROID, IS_EMOJI_SUPPORTED, IS_IOS, IS_TOUCH_ENV,
|
||||
} from '../../../util/windowEnvironment';
|
||||
import { selectIsInSelectMode, selectReplyingToId } from '../../../global/selectors';
|
||||
import { selectCanPlayAnimatedEmojis, selectIsInSelectMode, selectReplyingToId } from '../../../global/selectors';
|
||||
import { debounce } from '../../../util/schedulers';
|
||||
import focusEditableElement from '../../../util/focusEditableElement';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -67,6 +67,7 @@ type StateProps = {
|
||||
replyingToId?: number;
|
||||
isSelectModeActive?: boolean;
|
||||
messageSendKeyCombo?: ISettings['messageSendKeyCombo'];
|
||||
canPlayAnimatedEmojis: boolean;
|
||||
};
|
||||
|
||||
const MAX_ATTACHMENT_MODAL_INPUT_HEIGHT = 160;
|
||||
@ -111,6 +112,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
shouldSuppressTextFormatter,
|
||||
replyingToId,
|
||||
isSelectModeActive,
|
||||
canPlayAnimatedEmojis,
|
||||
messageSendKeyCombo,
|
||||
onUpdate,
|
||||
onSuppressedFocus,
|
||||
@ -157,6 +159,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
sharedCanvasHqRef,
|
||||
absoluteContainerRef,
|
||||
isAttachmentModalInput ? 'attachment' : 'composer',
|
||||
canPlayAnimatedEmojis,
|
||||
isActive,
|
||||
);
|
||||
|
||||
@ -590,6 +593,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
messageSendKeyCombo,
|
||||
replyingToId: chatId && threadId ? selectReplyingToId(global, chatId, threadId) : undefined,
|
||||
isSelectModeActive: selectIsInSelectMode(global),
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
},
|
||||
)(MessageInput));
|
||||
|
||||
@ -24,7 +24,7 @@ import buildClassName from '../../../util/buildClassName';
|
||||
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
|
||||
import { pickTruthy, uniqueByField } from '../../../util/iteratees';
|
||||
import {
|
||||
selectChat, selectChatFullInfo, selectIsChatWithSelf, selectIsCurrentUserPremium,
|
||||
selectChat, selectChatFullInfo, selectIsChatWithSelf, selectIsCurrentUserPremium, selectShouldLoopStickers,
|
||||
} from '../../../global/selectors';
|
||||
|
||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||
@ -285,7 +285,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
) : (
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
noPlay={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
sharedCanvasRef={withSharedCanvas ? sharedCanvasRef : undefined}
|
||||
/>
|
||||
@ -300,7 +300,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
size={STICKER_SIZE_PICKER_HEADER}
|
||||
title={stickerSet.title}
|
||||
className={buttonClassName}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
noPlay={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
noContextMenu
|
||||
isCurrentUserPremium
|
||||
@ -395,7 +395,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
premiumStickers: premiumSet.stickers,
|
||||
stickerSetsById: setsById,
|
||||
addedSetIds: added.setIds,
|
||||
canAnimate: global.settings.byKey.shouldLoopStickers,
|
||||
canAnimate: selectShouldLoopStickers(global),
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
chatStickerSetId,
|
||||
|
||||
@ -25,7 +25,7 @@ import styles from './StickerSetCover.module.scss';
|
||||
type OwnProps = {
|
||||
stickerSet: ApiStickerSet;
|
||||
size?: number;
|
||||
noAnimate?: boolean;
|
||||
noPlay?: boolean;
|
||||
observeIntersection: ObserveFn;
|
||||
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>;
|
||||
};
|
||||
@ -33,7 +33,7 @@ type OwnProps = {
|
||||
const StickerSetCover: FC<OwnProps> = ({
|
||||
stickerSet,
|
||||
size = STICKER_SIZE_PICKER_HEADER,
|
||||
noAnimate,
|
||||
noPlay,
|
||||
observeIntersection,
|
||||
sharedCanvasRef,
|
||||
}) => {
|
||||
@ -44,6 +44,7 @@ const StickerSetCover: FC<OwnProps> = ({
|
||||
const { hasThumbnail, isLottie, isVideos: isVideo } = stickerSet;
|
||||
|
||||
const isIntersecting = useIsIntersecting(containerRef, observeIntersection);
|
||||
const shouldPlay = isIntersecting && !noPlay;
|
||||
|
||||
const shouldFallbackToStatic = stickerSet.stickers && isVideo && !IS_WEBM_SUPPORTED;
|
||||
const staticHash = shouldFallbackToStatic && getStickerPreviewHash(stickerSet.stickers![0].id);
|
||||
@ -75,7 +76,7 @@ const StickerSetCover: FC<OwnProps> = ({
|
||||
className={transitionClassNames}
|
||||
tgsUrl={mediaData}
|
||||
size={size}
|
||||
play={isIntersecting && !noAnimate}
|
||||
play={shouldPlay}
|
||||
isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSet)}
|
||||
sharedCanvas={sharedCanvasRef?.current || undefined}
|
||||
sharedCanvasCoords={coords}
|
||||
@ -84,7 +85,7 @@ const StickerSetCover: FC<OwnProps> = ({
|
||||
<OptimizedVideo
|
||||
className={buildClassName(styles.video, transitionClassNames)}
|
||||
src={mediaData}
|
||||
canPlay={isIntersecting && !noAnimate}
|
||||
canPlay={shouldPlay}
|
||||
loop
|
||||
disablePictureInPicture
|
||||
/>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
transform: translate3d(0, calc(var(--symbol-menu-height)), 0);
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -140,13 +140,16 @@
|
||||
.bubble {
|
||||
--offset-y: 4rem;
|
||||
|
||||
background: var(--color-background-compact-menu);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 1.25rem;
|
||||
width: calc(var(--symbol-menu-width) + 0.25rem); // Reserve width for scrollbar
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
body:not(.no-menu-blur) & {
|
||||
background: var(--color-background-compact-menu);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
&:not(.open) {
|
||||
transform: scale(0.85) !important;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import type { GlobalActions } from '../../../global';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { selectTabState, selectIsCurrentUserPremium } from '../../../global/selectors';
|
||||
import { selectTabState, selectIsCurrentUserPremium, selectIsContextMenuTranslucent } from '../../../global/selectors';
|
||||
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import useMouseInside from '../../../hooks/useMouseInside';
|
||||
@ -68,6 +68,7 @@ type StateProps = {
|
||||
isLeftColumnShown: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
lastSyncTime?: number;
|
||||
isBackgroundTranslucent?: boolean;
|
||||
};
|
||||
|
||||
let isActivated = false;
|
||||
@ -99,6 +100,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
transformOriginX,
|
||||
transformOriginY,
|
||||
style,
|
||||
isBackgroundTranslucent,
|
||||
}) => {
|
||||
const { loadPremiumSetStickers } = getActions();
|
||||
const [activeTab, setActiveTab] = useState<number>(0);
|
||||
@ -219,7 +221,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
isHidden={!isOpen || !isActive}
|
||||
loadAndPlay={isOpen && (isActive || isFrom)}
|
||||
chatId={chatId}
|
||||
isTranslucent={!isMobile}
|
||||
isTranslucent={!isMobile && isBackgroundTranslucent}
|
||||
onCustomEmojiSelect={handleCustomEmojiSelect}
|
||||
/>
|
||||
);
|
||||
@ -232,7 +234,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
canSendStickers={canSendStickers}
|
||||
chatId={chatId}
|
||||
threadId={threadId}
|
||||
isTranslucent={!isMobile}
|
||||
isTranslucent={!isMobile && isBackgroundTranslucent}
|
||||
onStickerSelect={handleStickerSelect}
|
||||
/>
|
||||
);
|
||||
@ -348,6 +350,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isLeftColumnShown: selectTabState(global).isLeftColumnShown,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
isBackgroundTranslucent: selectIsContextMenuTranslucent(global),
|
||||
};
|
||||
},
|
||||
)(SymbolMenu));
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||
transition: height 150ms ease-out, opacity 150ms ease-out;
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none !important;
|
||||
body.no-page-transitions & {
|
||||
transition: opacity 150ms ease-out;
|
||||
}
|
||||
|
||||
.select-mode-active + .middle-column-footer & {
|
||||
@ -27,6 +27,10 @@
|
||||
|
||||
.ComposerEmbeddedMessage + & {
|
||||
margin-top: 0.75rem;
|
||||
|
||||
body.no-message-composer-animations & {
|
||||
transition: opacity 150ms ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
& &-left-icon {
|
||||
|
||||
@ -41,6 +41,7 @@ export default function useInputCustomEmojis(
|
||||
sharedCanvasHqRef: React.RefObject<HTMLCanvasElement>,
|
||||
absoluteContainerRef: React.RefObject<HTMLElement>,
|
||||
prefixId: string,
|
||||
canPlayAnimatedEmojis: boolean,
|
||||
isActive?: boolean,
|
||||
) {
|
||||
const { rgbColor: textColor } = useDynamicColorListener(inputRef);
|
||||
@ -108,13 +109,18 @@ export default function useInputCustomEmojis(
|
||||
position: { x, y },
|
||||
textColor,
|
||||
});
|
||||
animation.play();
|
||||
if (canPlayAnimatedEmojis) {
|
||||
animation.play();
|
||||
}
|
||||
|
||||
playersById.current.set(playerId, animation);
|
||||
});
|
||||
|
||||
clearPlayers(Array.from(playerIdsToClear));
|
||||
}, [absoluteContainerRef, textColor, inputRef, prefixId, clearPlayers, sharedCanvasHqRef, sharedCanvasRef]);
|
||||
}, [
|
||||
inputRef, sharedCanvasRef, sharedCanvasHqRef, clearPlayers, prefixId, textColor, absoluteContainerRef,
|
||||
canPlayAnimatedEmojis,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
addCustomEmojiInputRenderCallback(synchronizeElements);
|
||||
@ -157,10 +163,14 @@ export default function useInputCustomEmojis(
|
||||
}, []);
|
||||
|
||||
const unfreezeAnimation = useCallback(() => {
|
||||
if (!canPlayAnimatedEmojis) {
|
||||
return;
|
||||
}
|
||||
|
||||
playersById.current?.forEach((player) => {
|
||||
player.play();
|
||||
});
|
||||
}, []);
|
||||
}, [canPlayAnimatedEmojis]);
|
||||
|
||||
const unfreezeAnimationOnRaf = useCallback(() => {
|
||||
requestMeasure(unfreezeAnimation);
|
||||
|
||||
@ -10,6 +10,7 @@ import { LIKE_STICKER_ID } from '../../common/helpers/mediaDimensions';
|
||||
import {
|
||||
selectAnimatedEmojiEffect,
|
||||
selectAnimatedEmojiSound,
|
||||
selectCanPlayAnimatedEmojis,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { getCustomEmojiSize } from '../composer/helpers/customEmoji';
|
||||
@ -21,7 +22,7 @@ import './AnimatedEmoji.scss';
|
||||
|
||||
type OwnProps = {
|
||||
customEmojiId: string;
|
||||
withEffects: boolean;
|
||||
withEffects?: boolean;
|
||||
isOwn?: boolean;
|
||||
lastSyncTime?: number;
|
||||
forceLoadPreview?: boolean;
|
||||
@ -35,6 +36,7 @@ interface StateProps {
|
||||
sticker?: ApiSticker;
|
||||
effect?: ApiSticker;
|
||||
soundId?: string;
|
||||
noPlay?: boolean;
|
||||
}
|
||||
|
||||
const AnimatedCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
@ -46,6 +48,7 @@ const AnimatedCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
sticker,
|
||||
effect,
|
||||
soundId,
|
||||
noPlay,
|
||||
observeIntersection,
|
||||
}) => {
|
||||
const {
|
||||
@ -65,6 +68,7 @@ const AnimatedCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
style={style}
|
||||
size={size}
|
||||
isBig
|
||||
noPlay={noPlay}
|
||||
withSharedAnimation
|
||||
forceOnHeavyAnimation
|
||||
observeIntersectionForLoading={observeIntersection}
|
||||
@ -75,9 +79,11 @@ const AnimatedCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>((global, { customEmojiId, withEffects }) => {
|
||||
const sticker = global.customEmojis.byId[customEmojiId];
|
||||
|
||||
return {
|
||||
sticker,
|
||||
effect: sticker?.emoji && withEffects ? selectAnimatedEmojiEffect(global, sticker.emoji) : undefined,
|
||||
soundId: sticker?.emoji && selectAnimatedEmojiSound(global, sticker.emoji),
|
||||
noPlay: !selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
})(AnimatedCustomEmoji));
|
||||
|
||||
@ -22,7 +22,7 @@ import './AnimatedEmoji.scss';
|
||||
|
||||
type OwnProps = {
|
||||
emoji: string;
|
||||
withEffects: boolean;
|
||||
withEffects?: boolean;
|
||||
isOwn?: boolean;
|
||||
observeIntersection?: ObserveFn;
|
||||
lastSyncTime?: number;
|
||||
|
||||
@ -20,10 +20,6 @@
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
user-select: none;
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.Message .has-appendix &::before {
|
||||
content: "";
|
||||
display: block;
|
||||
@ -40,10 +36,6 @@
|
||||
.theme-dark #root & {
|
||||
filter: invert(0.83);
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-shape & {
|
||||
|
||||
@ -3,7 +3,6 @@ import React, { useCallback } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiUser, ApiContact, ApiCountryCode } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
import { selectUser } from '../../../global/selectors';
|
||||
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
||||
@ -20,13 +19,12 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
user?: ApiUser;
|
||||
phoneCodeList: ApiCountryCode[];
|
||||
animationLevel: AnimationLevel;
|
||||
};
|
||||
|
||||
const UNREGISTERED_CONTACT_ID = '0';
|
||||
|
||||
const Contact: FC<OwnProps & StateProps> = ({
|
||||
contact, user, phoneCodeList, animationLevel,
|
||||
contact, user, phoneCodeList,
|
||||
}) => {
|
||||
const { openChat } = getActions();
|
||||
|
||||
@ -51,8 +49,6 @@ const Contact: FC<OwnProps & StateProps> = ({
|
||||
size="large"
|
||||
user={user}
|
||||
text={firstName || lastName}
|
||||
animationLevel={animationLevel}
|
||||
withVideo
|
||||
/>
|
||||
<div className="contact-info">
|
||||
<div className="contact-name">{firstName} {lastName}</div>
|
||||
@ -70,7 +66,6 @@ export default withGlobal<OwnProps>(
|
||||
return {
|
||||
user,
|
||||
phoneCodeList,
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
};
|
||||
},
|
||||
)(Contact);
|
||||
|
||||
@ -13,6 +13,7 @@ import type { IAlbum, IAnchorPosition } from '../../../types';
|
||||
import {
|
||||
selectActiveDownloadIds,
|
||||
selectAllowedMessageActions,
|
||||
selectCanPlayAnimatedEmojis,
|
||||
selectCanScheduleUntilOnline,
|
||||
selectChat,
|
||||
selectChatFullInfo,
|
||||
@ -106,6 +107,7 @@ type StateProps = {
|
||||
canScheduleUntilOnline?: boolean;
|
||||
maxUniqueReactions?: number;
|
||||
threadId?: number;
|
||||
canPlayAnimatedEmojis?: boolean;
|
||||
};
|
||||
|
||||
const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
||||
@ -147,6 +149,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
||||
canSaveGif,
|
||||
canRevote,
|
||||
canClosePoll,
|
||||
canPlayAnimatedEmojis,
|
||||
activeDownloads,
|
||||
noReplies,
|
||||
canShowSeenBy,
|
||||
@ -495,6 +498,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
||||
canTranslate={canTranslate}
|
||||
canShowOriginal={canShowOriginal}
|
||||
canSelectLanguage={canSelectLanguage}
|
||||
canPlayAnimatedEmojis={canPlayAnimatedEmojis}
|
||||
hasCustomEmoji={hasCustomEmoji}
|
||||
customEmojiSets={customEmojiSets}
|
||||
isDownloading={isDownloading}
|
||||
@ -665,6 +669,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
canTranslate,
|
||||
canShowOriginal: hasTranslation,
|
||||
canSelectLanguage: hasTranslation,
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
},
|
||||
)(ContextMenuContainer));
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
transform: scale(1) translateX(0);
|
||||
transition: transform var(--select-transition);
|
||||
|
||||
body.animation-level-0 & {
|
||||
body.no-page-transitions & {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user