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