Message List, Shared Media, Left Search, Symbol Menu: Add transition to loading spinner
This commit is contained in:
parent
c0dbdc35a0
commit
b66048aad2
@ -1,7 +1,7 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '@teact';
|
||||||
import {
|
import {
|
||||||
memo, useEffect, useMemo, useRef,
|
memo, useEffect, useMemo, useRef,
|
||||||
} from '../../lib/teact/teact';
|
} from '@teact';
|
||||||
import { getGlobal, withGlobal } from '../../global';
|
import { getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@ -48,6 +48,7 @@ import { useStickerPickerObservers } from './hooks/useStickerPickerObservers';
|
|||||||
import StickerSetCover from '../middle/composer/StickerSetCover';
|
import StickerSetCover from '../middle/composer/StickerSetCover';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
|
import Transition from '../ui/Transition.tsx';
|
||||||
import Icon from './icons/Icon';
|
import Icon from './icons/Icon';
|
||||||
import StickerButton from './StickerButton';
|
import StickerButton from './StickerButton';
|
||||||
import StickerSet from './StickerSet';
|
import StickerSet from './StickerSet';
|
||||||
@ -397,19 +398,6 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fullClassName = buildClassName('StickerPicker', styles.root, className);
|
const fullClassName = buildClassName('StickerPicker', styles.root, className);
|
||||||
|
|
||||||
if (!shouldRenderContent) {
|
|
||||||
return (
|
|
||||||
<div className={fullClassName}>
|
|
||||||
{noPopulatedSets ? (
|
|
||||||
<div className={pickerStyles.pickerDisabled}>{oldLang('NoStickers')}</div>
|
|
||||||
) : (
|
|
||||||
<Loading />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerClassName = buildClassName(
|
const headerClassName = buildClassName(
|
||||||
pickerStyles.header,
|
pickerStyles.header,
|
||||||
'no-scrollbar',
|
'no-scrollbar',
|
||||||
@ -423,62 +411,72 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
|||||||
pickerStyles.hasHeader,
|
pickerStyles.hasHeader,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const isLoading = !shouldRenderContent && !noPopulatedSets;
|
||||||
<div className={fullClassName}>
|
|
||||||
<div
|
|
||||||
ref={headerRef}
|
|
||||||
className={headerClassName}
|
|
||||||
>
|
|
||||||
<div className="shared-canvas-container">
|
|
||||||
<canvas ref={sharedCanvasRef} className="shared-canvas" />
|
|
||||||
<canvas ref={sharedCanvasHqRef} className="shared-canvas" />
|
|
||||||
{allSets.map(renderCover)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
ref={containerRef}
|
|
||||||
onScroll={handleContentScroll}
|
|
||||||
className={listClassName}
|
|
||||||
>
|
|
||||||
{allSets.map((stickerSet, i) => {
|
|
||||||
const shouldHideHeader = stickerSet.id === TOP_SYMBOL_SET_ID
|
|
||||||
|| (stickerSet.id === RECENT_SYMBOL_SET_ID && (withDefaultTopicIcons || isStatusPicker));
|
|
||||||
const isChatEmojiSet = stickerSet.id === chatEmojiSetId;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StickerSet
|
<Transition className={fullClassName} name="fade" activeKey={isLoading ? 0 : 1} shouldCleanup>
|
||||||
key={stickerSet.id}
|
{!shouldRenderContent && !noPopulatedSets ? (
|
||||||
stickerSet={stickerSet}
|
<Loading />
|
||||||
loadAndPlay={Boolean(canAnimate && canLoadAndPlay)}
|
) : !shouldRenderContent && noPopulatedSets ? (
|
||||||
index={i}
|
<div className={pickerStyles.pickerDisabled}>{oldLang('NoStickers')}</div>
|
||||||
idPrefix={prefix}
|
) : (
|
||||||
observeIntersection={observeIntersectionForSet}
|
<>
|
||||||
observeIntersectionForPlayingItems={observeIntersectionForPlayingItems}
|
<div
|
||||||
observeIntersectionForShowingItems={observeIntersectionForShowingItems}
|
ref={headerRef}
|
||||||
isNearActive={activeSetIndex >= i - 1 && activeSetIndex <= i + 1}
|
className={headerClassName}
|
||||||
isSavedMessages={isSavedMessages}
|
>
|
||||||
isStatusPicker={isStatusPicker}
|
<div className="shared-canvas-container">
|
||||||
isReactionPicker={isReactionPicker}
|
<canvas ref={sharedCanvasRef} className="shared-canvas" />
|
||||||
shouldHideHeader={shouldHideHeader}
|
<canvas ref={sharedCanvasHqRef} className="shared-canvas" />
|
||||||
withDefaultTopicIcon={withDefaultTopicIcons && stickerSet.id === RECENT_SYMBOL_SET_ID}
|
{allSets.map(renderCover)}
|
||||||
withDefaultStatusIcon={isStatusPicker && stickerSet.id === RECENT_SYMBOL_SET_ID}
|
</div>
|
||||||
isChatEmojiSet={isChatEmojiSet}
|
</div>
|
||||||
isCurrentUserPremium={isCurrentUserPremium}
|
<div
|
||||||
selectedReactionIds={selectedReactionIds}
|
ref={containerRef}
|
||||||
availableReactions={availableReactions}
|
onScroll={handleContentScroll}
|
||||||
isTranslucent={isTranslucent}
|
className={listClassName}
|
||||||
onReactionSelect={onReactionSelect}
|
>
|
||||||
onReactionContext={onReactionContext}
|
{allSets.map((stickerSet, i) => {
|
||||||
onStickerSelect={handleEmojiSelect}
|
const shouldHideHeader = stickerSet.id === TOP_SYMBOL_SET_ID
|
||||||
onContextMenuOpen={onContextMenuOpen}
|
|| (stickerSet.id === RECENT_SYMBOL_SET_ID && (withDefaultTopicIcons || isStatusPicker));
|
||||||
onContextMenuClose={onContextMenuClose}
|
const isChatEmojiSet = stickerSet.id === chatEmojiSetId;
|
||||||
onContextMenuClick={onContextMenuClick}
|
|
||||||
forcePlayback
|
return (
|
||||||
/>
|
<StickerSet
|
||||||
);
|
key={stickerSet.id}
|
||||||
})}
|
stickerSet={stickerSet}
|
||||||
</div>
|
loadAndPlay={Boolean(canAnimate && canLoadAndPlay)}
|
||||||
</div>
|
index={i}
|
||||||
|
idPrefix={prefix}
|
||||||
|
observeIntersection={observeIntersectionForSet}
|
||||||
|
observeIntersectionForPlayingItems={observeIntersectionForPlayingItems}
|
||||||
|
observeIntersectionForShowingItems={observeIntersectionForShowingItems}
|
||||||
|
isNearActive={activeSetIndex >= i - 1 && activeSetIndex <= i + 1}
|
||||||
|
isSavedMessages={isSavedMessages}
|
||||||
|
isStatusPicker={isStatusPicker}
|
||||||
|
isReactionPicker={isReactionPicker}
|
||||||
|
shouldHideHeader={shouldHideHeader}
|
||||||
|
withDefaultTopicIcon={withDefaultTopicIcons && stickerSet.id === RECENT_SYMBOL_SET_ID}
|
||||||
|
withDefaultStatusIcon={isStatusPicker && stickerSet.id === RECENT_SYMBOL_SET_ID}
|
||||||
|
isChatEmojiSet={isChatEmojiSet}
|
||||||
|
isCurrentUserPremium={isCurrentUserPremium}
|
||||||
|
selectedReactionIds={selectedReactionIds}
|
||||||
|
availableReactions={availableReactions}
|
||||||
|
isTranslucent={isTranslucent}
|
||||||
|
onReactionSelect={onReactionSelect}
|
||||||
|
onReactionContext={onReactionContext}
|
||||||
|
onStickerSelect={handleEmojiSelect}
|
||||||
|
onContextMenuOpen={onContextMenuOpen}
|
||||||
|
onContextMenuClose={onContextMenuClose}
|
||||||
|
onContextMenuClick={onContextMenuClick}
|
||||||
|
forcePlayback
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import Audio from '../../common/Audio';
|
|||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isVoice?: boolean;
|
isVoice?: boolean;
|
||||||
@ -126,7 +127,12 @@ const AudioResults: FC<OwnProps & StateProps> = ({
|
|||||||
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="LeftSearch--content">
|
<Transition
|
||||||
|
slideClassName="LeftSearch--content"
|
||||||
|
name="fade"
|
||||||
|
activeKey={canRenderContents ? 1 : 0}
|
||||||
|
shouldCleanup
|
||||||
|
>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className="search-content documents-list custom-scroll"
|
className="search-content documents-list custom-scroll"
|
||||||
items={canRenderContents ? foundMessages : undefined}
|
items={canRenderContents ? foundMessages : undefined}
|
||||||
@ -143,7 +149,7 @@ const AudioResults: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import NothingFound from '../../common/NothingFound';
|
|||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Link from '../../ui/Link';
|
import Link from '../../ui/Link';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
import LeftSearchResultChat from './LeftSearchResultChat';
|
import LeftSearchResultChat from './LeftSearchResultChat';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
@ -79,7 +80,13 @@ const BotAppResults: FC<OwnProps & StateProps> = ({
|
|||||||
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="LeftSearch--content">
|
<Transition
|
||||||
|
ref={containerRef}
|
||||||
|
slideClassName="LeftSearch--content"
|
||||||
|
name="fade"
|
||||||
|
activeKey={canRenderContents ? 1 : 0}
|
||||||
|
shouldCleanup
|
||||||
|
>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className="search-content custom-scroll"
|
className="search-content custom-scroll"
|
||||||
items={canRenderContents ? filteredFoundIds : undefined}
|
items={canRenderContents ? filteredFoundIds : undefined}
|
||||||
@ -134,7 +141,7 @@ const BotAppResults: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import Document from '../../common/Document';
|
|||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
@ -129,7 +130,13 @@ const FileResults: FC<OwnProps & StateProps> = ({
|
|||||||
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="LeftSearch--content">
|
<Transition
|
||||||
|
ref={containerRef}
|
||||||
|
slideClassName="LeftSearch--content"
|
||||||
|
name="fade"
|
||||||
|
activeKey={canRenderContents ? 1 : 0}
|
||||||
|
shouldCleanup
|
||||||
|
>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className="search-content documents-list custom-scroll"
|
className="search-content documents-list custom-scroll"
|
||||||
items={canRenderContents ? foundMessages : undefined}
|
items={canRenderContents ? foundMessages : undefined}
|
||||||
@ -146,7 +153,7 @@ const FileResults: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import NothingFound from '../../common/NothingFound';
|
|||||||
import WebLink from '../../common/WebLink';
|
import WebLink from '../../common/WebLink';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
@ -122,7 +123,13 @@ const LinkResults: FC<OwnProps & StateProps> = ({
|
|||||||
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="LeftSearch--content">
|
<Transition
|
||||||
|
ref={containerRef}
|
||||||
|
slideClassName="LeftSearch--content"
|
||||||
|
name="fade"
|
||||||
|
activeKey={canRenderContents ? 1 : 0}
|
||||||
|
shouldCleanup
|
||||||
|
>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className="search-content documents-list custom-scroll"
|
className="search-content documents-list custom-scroll"
|
||||||
items={canRenderContents ? foundMessages : undefined}
|
items={canRenderContents ? foundMessages : undefined}
|
||||||
@ -139,7 +146,7 @@ const LinkResults: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import Media from '../../common/Media';
|
|||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
import ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
@ -122,7 +123,13 @@ const MediaResults: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="LeftSearch--content LeftSearch--media">
|
<Transition
|
||||||
|
ref={containerRef}
|
||||||
|
slideClassName="LeftSearch--content LeftSearch--media"
|
||||||
|
name="fade"
|
||||||
|
activeKey={canRenderContents ? 1 : 0}
|
||||||
|
shouldCleanup
|
||||||
|
>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className={classNames}
|
className={classNames}
|
||||||
items={canRenderContents ? foundMessages : undefined}
|
items={canRenderContents ? foundMessages : undefined}
|
||||||
@ -141,7 +148,7 @@ const MediaResults: FC<OwnProps & StateProps> = ({
|
|||||||
{isMediaGrid && renderGallery()}
|
{isMediaGrid && renderGallery()}
|
||||||
{isMessageList && renderSearchResult()}
|
{isMessageList && renderSearchResult()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,9 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '@teact';
|
||||||
import {
|
import { beginHeavyAnimation, memo, useEffect, useMemo, useRef } from '@teact';
|
||||||
beginHeavyAnimation, memo, useEffect, useMemo, useRef,
|
import { addExtraClass, removeExtraClass } from '@teact/teact-dom.ts';
|
||||||
} from '../../lib/teact/teact';
|
|
||||||
import { addExtraClass, removeExtraClass } from '../../lib/teact/teact-dom';
|
|
||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type {
|
import type { ApiChatFullInfo, ApiMessage, ApiRestrictionReason, ApiTopic } from '../../api/types';
|
||||||
ApiChatFullInfo,
|
|
||||||
ApiMessage,
|
|
||||||
ApiRestrictionReason,
|
|
||||||
ApiTopic,
|
|
||||||
} from '../../api/types';
|
|
||||||
import type { OnIntersectPinnedMessage } from './hooks/usePinnedMessage';
|
import type { OnIntersectPinnedMessage } from './hooks/usePinnedMessage';
|
||||||
import { MAIN_THREAD_ID } from '../../api/types';
|
import { MAIN_THREAD_ID } from '../../api/types';
|
||||||
import { LoadMoreDirection, type MessageListType, type ThreadId } from '../../types';
|
import { LoadMoreDirection, type MessageListType, type ThreadId } from '../../types';
|
||||||
@ -83,6 +76,7 @@ import useContainerHeight from './hooks/useContainerHeight';
|
|||||||
import useStickyDates from './hooks/useStickyDates';
|
import useStickyDates from './hooks/useStickyDates';
|
||||||
|
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
|
import Transition from '../ui/Transition.tsx';
|
||||||
import ContactGreeting from './ContactGreeting';
|
import ContactGreeting from './ContactGreeting';
|
||||||
import MessageListAccountInfo from './MessageListAccountInfo';
|
import MessageListAccountInfo from './MessageListAccountInfo';
|
||||||
import MessageListContent from './MessageListContent';
|
import MessageListContent from './MessageListContent';
|
||||||
@ -150,6 +144,17 @@ type StateProps = {
|
|||||||
shouldAutoTranslate?: boolean;
|
shouldAutoTranslate?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Content {
|
||||||
|
Loading,
|
||||||
|
Restricted,
|
||||||
|
StarsRequired,
|
||||||
|
PremiumRequired,
|
||||||
|
AccountInfo,
|
||||||
|
ContactGreeting,
|
||||||
|
NoMessages,
|
||||||
|
MessageList,
|
||||||
|
}
|
||||||
|
|
||||||
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
|
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
|
||||||
const MESSAGE_COMMENTS_POLLING_INTERVAL = 20 * 1000;
|
const MESSAGE_COMMENTS_POLLING_INTERVAL = 20 * 1000;
|
||||||
const MESSAGE_FACT_CHECK_UPDATE_INTERVAL = 5 * 1000;
|
const MESSAGE_FACT_CHECK_UPDATE_INTERVAL = 5 * 1000;
|
||||||
@ -711,73 +716,98 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
onScrollDownToggle(false);
|
onScrollDownToggle(false);
|
||||||
}, [hasMessages, onScrollDownToggle]);
|
}, [hasMessages, onScrollDownToggle]);
|
||||||
|
|
||||||
|
const activeKey = isRestricted ? (
|
||||||
|
Content.Restricted
|
||||||
|
) : paidMessagesStars && !hasMessages && !hasCustomGreeting ? (
|
||||||
|
Content.StarsRequired
|
||||||
|
) : isContactRequirePremium && !hasMessages ? (
|
||||||
|
Content.PremiumRequired
|
||||||
|
) : (isBot || isNonContact) && !hasMessages ? (
|
||||||
|
Content.AccountInfo
|
||||||
|
) : shouldRenderGreeting ? (
|
||||||
|
Content.ContactGreeting
|
||||||
|
) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? (
|
||||||
|
Content.NoMessages
|
||||||
|
) : hasMessages ? (
|
||||||
|
Content.MessageList
|
||||||
|
) : (
|
||||||
|
Content.Loading
|
||||||
|
);
|
||||||
|
|
||||||
|
function renderContent() {
|
||||||
|
return activeKey === Content.Restricted ? (
|
||||||
|
<div className="empty">
|
||||||
|
<span>
|
||||||
|
{restrictionReasons?.[0]?.text || `This is a private ${isChannelChat ? 'channel' : 'chat'}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : activeKey === Content.StarsRequired ? (
|
||||||
|
<RequirementToContactMessage paidMessagesStars={paidMessagesStars} peerId={monoforumChannelId || chatId} />
|
||||||
|
) : activeKey === Content.PremiumRequired ? (
|
||||||
|
<RequirementToContactMessage peerId={chatId} />
|
||||||
|
) : activeKey === Content.AccountInfo ? (
|
||||||
|
<MessageListAccountInfo chatId={chatId} hasMessages={hasMessages} />
|
||||||
|
) : activeKey === Content.ContactGreeting ? (
|
||||||
|
<ContactGreeting key={chatId} userId={chatId} />
|
||||||
|
) : activeKey === Content.NoMessages ? (
|
||||||
|
<NoMessages
|
||||||
|
chatId={chatId}
|
||||||
|
topic={topic}
|
||||||
|
type={type}
|
||||||
|
isChatWithSelf={isChatWithSelf}
|
||||||
|
isGroupChatJustCreated={isGroupChatJustCreated}
|
||||||
|
/>
|
||||||
|
) : activeKey === Content.MessageList ? (
|
||||||
|
<MessageListContent
|
||||||
|
canShowAds={areAdsEnabled && isChannelChat}
|
||||||
|
chatId={chatId}
|
||||||
|
isComments={isComments}
|
||||||
|
isChannelChat={isChannelChat}
|
||||||
|
isChatMonoforum={isChatMonoforum}
|
||||||
|
isSavedDialog={isSavedDialog}
|
||||||
|
messageIds={messageIds || [lastMessage!.id]}
|
||||||
|
messageGroups={messageGroups || groupMessages([lastMessage!])}
|
||||||
|
getContainerHeight={getContainerHeight}
|
||||||
|
isViewportNewest={Boolean(isViewportNewest)}
|
||||||
|
isUnread={Boolean(firstUnreadId)}
|
||||||
|
isEmptyThread={isEmptyThread}
|
||||||
|
withUsers={withUsers}
|
||||||
|
noAvatars={noAvatars}
|
||||||
|
containerRef={containerRef}
|
||||||
|
anchorIdRef={anchorIdRef}
|
||||||
|
memoUnreadDividerBeforeIdRef={memoUnreadDividerBeforeIdRef}
|
||||||
|
memoFirstUnreadIdRef={memoFirstUnreadIdRef}
|
||||||
|
threadId={threadId}
|
||||||
|
type={type}
|
||||||
|
isReady={isReady}
|
||||||
|
hasLinkedChat={hasLinkedChat}
|
||||||
|
isSchedule={messageGroups ? type === 'scheduled' : false}
|
||||||
|
shouldRenderAccountInfo={isBot || isNonContact}
|
||||||
|
nameChangeDate={nameChangeDate}
|
||||||
|
photoChangeDate={photoChangeDate}
|
||||||
|
noAppearanceAnimation={!messageGroups || !shouldAnimateAppearanceRef.current}
|
||||||
|
onScrollDownToggle={onScrollDownToggle}
|
||||||
|
onNotchToggle={onNotchToggle}
|
||||||
|
onIntersectPinnedMessage={onIntersectPinnedMessage}
|
||||||
|
canPost={canPost}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Loading color="white" backgroundColor="dark" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Transition
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className={className}
|
className={className}
|
||||||
|
name="fade"
|
||||||
|
activeKey={activeKey}
|
||||||
|
shouldCleanup
|
||||||
onScroll={handleScroll}
|
onScroll={handleScroll}
|
||||||
onMouseDown={preventMessageInputBlur}
|
onMouseDown={preventMessageInputBlur}
|
||||||
>
|
>
|
||||||
{isRestricted ? (
|
{renderContent()}
|
||||||
<div className="empty">
|
</Transition>
|
||||||
<span>
|
|
||||||
{restrictionReasons?.[0]?.text || `This is a private ${isChannelChat ? 'channel' : 'chat'}`}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : paidMessagesStars && !hasMessages && !hasCustomGreeting ? (
|
|
||||||
<RequirementToContactMessage paidMessagesStars={paidMessagesStars} peerId={monoforumChannelId || chatId} />
|
|
||||||
) : isContactRequirePremium && !hasMessages ? (
|
|
||||||
<RequirementToContactMessage peerId={chatId} />
|
|
||||||
) : (isBot || isNonContact) && !hasMessages ? (
|
|
||||||
<MessageListAccountInfo chatId={chatId} hasMessages={hasMessages} />
|
|
||||||
) : shouldRenderGreeting ? (
|
|
||||||
<ContactGreeting key={chatId} userId={chatId} />
|
|
||||||
) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? (
|
|
||||||
<NoMessages
|
|
||||||
chatId={chatId}
|
|
||||||
topic={topic}
|
|
||||||
type={type}
|
|
||||||
isChatWithSelf={isChatWithSelf}
|
|
||||||
isGroupChatJustCreated={isGroupChatJustCreated}
|
|
||||||
/>
|
|
||||||
) : hasMessages ? (
|
|
||||||
<MessageListContent
|
|
||||||
canShowAds={areAdsEnabled && isChannelChat}
|
|
||||||
chatId={chatId}
|
|
||||||
isComments={isComments}
|
|
||||||
isChannelChat={isChannelChat}
|
|
||||||
isChatMonoforum={isChatMonoforum}
|
|
||||||
isSavedDialog={isSavedDialog}
|
|
||||||
messageIds={messageIds || [lastMessage!.id]}
|
|
||||||
messageGroups={messageGroups || groupMessages([lastMessage!])}
|
|
||||||
getContainerHeight={getContainerHeight}
|
|
||||||
isViewportNewest={Boolean(isViewportNewest)}
|
|
||||||
isUnread={Boolean(firstUnreadId)}
|
|
||||||
isEmptyThread={isEmptyThread}
|
|
||||||
withUsers={withUsers}
|
|
||||||
noAvatars={noAvatars}
|
|
||||||
containerRef={containerRef}
|
|
||||||
anchorIdRef={anchorIdRef}
|
|
||||||
memoUnreadDividerBeforeIdRef={memoUnreadDividerBeforeIdRef}
|
|
||||||
memoFirstUnreadIdRef={memoFirstUnreadIdRef}
|
|
||||||
threadId={threadId}
|
|
||||||
type={type}
|
|
||||||
isReady={isReady}
|
|
||||||
hasLinkedChat={hasLinkedChat}
|
|
||||||
isSchedule={messageGroups ? type === 'scheduled' : false}
|
|
||||||
shouldRenderAccountInfo={isBot || isNonContact}
|
|
||||||
nameChangeDate={nameChangeDate}
|
|
||||||
photoChangeDate={photoChangeDate}
|
|
||||||
noAppearanceAnimation={!messageGroups || !shouldAnimateAppearanceRef.current}
|
|
||||||
onScrollDownToggle={onScrollDownToggle}
|
|
||||||
onNotchToggle={onNotchToggle}
|
|
||||||
onIntersectPinnedMessage={onIntersectPinnedMessage}
|
|
||||||
canPost={canPost}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Loading color="white" backgroundColor="dark" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,10 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
import {
|
import { memo, useEffect, useMemo, useRef, useState } from '../../../lib/teact/teact';
|
||||||
memo, useEffect, useMemo,
|
|
||||||
useRef, useState,
|
|
||||||
} from '../../../lib/teact/teact';
|
|
||||||
import { withGlobal } from '../../../global';
|
import { withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { GlobalState } from '../../../global/types';
|
import type { GlobalState } from '../../../global/types';
|
||||||
import type { IconName } from '../../../types/icons';
|
import type { IconName } from '../../../types/icons';
|
||||||
import type {
|
import type { EmojiData, EmojiModule, EmojiRawData } from '../../../util/emoji/emoji';
|
||||||
EmojiData,
|
|
||||||
EmojiModule,
|
|
||||||
EmojiRawData,
|
|
||||||
} from '../../../util/emoji/emoji';
|
|
||||||
|
|
||||||
import { MENU_TRANSITION_DURATION, RECENT_SYMBOL_SET_ID } from '../../../config';
|
import { MENU_TRANSITION_DURATION, RECENT_SYMBOL_SET_ID } from '../../../config';
|
||||||
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
|
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
|
||||||
@ -34,6 +27,7 @@ import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
|||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
import EmojiCategory from './EmojiCategory';
|
import EmojiCategory from './EmojiCategory';
|
||||||
|
|
||||||
import './EmojiPicker.scss';
|
import './EmojiPicker.scss';
|
||||||
@ -206,46 +200,43 @@ const EmojiPicker: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const containerClassName = buildClassName('EmojiPicker', className);
|
const containerClassName = buildClassName('EmojiPicker', className);
|
||||||
|
|
||||||
if (!shouldRenderContent) {
|
|
||||||
return (
|
|
||||||
<div className={containerClassName}>
|
|
||||||
<Loading />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerClassName = buildClassName(
|
const headerClassName = buildClassName(
|
||||||
'EmojiPicker-header',
|
'EmojiPicker-header',
|
||||||
!shouldHideTopBorder && 'with-top-border',
|
!shouldHideTopBorder && 'with-top-border',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClassName}>
|
<Transition className={containerClassName} activeKey={shouldRenderContent ? 1 : 0} name="fade" shouldCleanup>
|
||||||
<div
|
{!shouldRenderContent ? (
|
||||||
ref={headerRef}
|
<Loading />
|
||||||
className={headerClassName}
|
) : (
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
<>
|
||||||
>
|
<div
|
||||||
{allCategories.map(renderCategoryButton)}
|
ref={headerRef}
|
||||||
</div>
|
className={headerClassName}
|
||||||
<div
|
dir={lang.isRtl ? 'rtl' : undefined}
|
||||||
ref={containerRef}
|
>
|
||||||
onScroll={handleContentScroll}
|
{allCategories.map(renderCategoryButton)}
|
||||||
className={buildClassName('EmojiPicker-main', IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll')}
|
</div>
|
||||||
>
|
<div
|
||||||
{allCategories.map((category, i) => (
|
ref={containerRef}
|
||||||
<EmojiCategory
|
onScroll={handleContentScroll}
|
||||||
category={category}
|
className={buildClassName('EmojiPicker-main', IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll')}
|
||||||
index={i}
|
>
|
||||||
allEmojis={emojis}
|
{allCategories.map((category, i) => (
|
||||||
observeIntersection={observeIntersection}
|
<EmojiCategory
|
||||||
shouldRender={activeCategoryIndex >= i - 1 && activeCategoryIndex <= i + 1}
|
category={category}
|
||||||
onEmojiSelect={handleEmojiSelect}
|
index={i}
|
||||||
/>
|
allEmojis={emojis}
|
||||||
))}
|
observeIntersection={observeIntersection}
|
||||||
</div>
|
shouldRender={activeCategoryIndex >= i - 1 && activeCategoryIndex <= i + 1}
|
||||||
</div>
|
onEmojiSelect={handleEmojiSelect}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,6 @@
|
|||||||
top: 0.1875rem;
|
top: 0.1875rem;
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: grid;
|
|
||||||
grid-auto-flow: dense;
|
|
||||||
grid-auto-rows: 6.25rem;
|
|
||||||
grid-gap: 0.125rem;
|
|
||||||
grid-template-columns: repeat(6, 1fr);
|
|
||||||
|
|
||||||
height: calc(100% - 0.1875rem);
|
height: calc(100% - 0.1875rem);
|
||||||
margin: 0 0.1875rem;
|
margin: 0 0.1875rem;
|
||||||
@ -30,3 +25,11 @@
|
|||||||
border-radius: var(--border-radius-default) !important;
|
border-radius: var(--border-radius-default) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.GifPickerGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: dense;
|
||||||
|
grid-auto-rows: 6.25rem;
|
||||||
|
grid-gap: 0.125rem;
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
import {
|
import { memo, useEffect, useRef } from '../../../lib/teact/teact';
|
||||||
memo, useEffect, useRef,
|
|
||||||
} from '../../../lib/teact/teact';
|
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiVideo } from '../../../api/types';
|
import type { ApiVideo } from '../../../api/types';
|
||||||
@ -17,6 +15,7 @@ import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
|||||||
|
|
||||||
import GifButton from '../../common/GifButton';
|
import GifButton from '../../common/GifButton';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
|
|
||||||
import './GifPicker.scss';
|
import './GifPicker.scss';
|
||||||
|
|
||||||
@ -61,34 +60,37 @@ const GifPicker: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const canRenderContents = useAsyncRendering([], SLIDE_TRANSITION_DURATION);
|
const canRenderContents = useAsyncRendering([], SLIDE_TRANSITION_DURATION);
|
||||||
|
const isLoading = canSendGifs && (!canRenderContents || !savedGifs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Transition
|
||||||
<div
|
ref={containerRef}
|
||||||
ref={containerRef}
|
className={buildClassName('GifPicker', className, IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll')}
|
||||||
className={buildClassName('GifPicker', className, IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll')}
|
slideClassName="GifPickerGrid"
|
||||||
>
|
activeKey={isLoading ? 0 : 1}
|
||||||
{!canSendGifs ? (
|
name="fade"
|
||||||
<div className="picker-disabled">Sending GIFs is not allowed in this chat.</div>
|
shouldCleanup
|
||||||
) : canRenderContents && savedGifs && savedGifs.length ? (
|
>
|
||||||
savedGifs.map((gif) => (
|
{!canSendGifs ? (
|
||||||
<GifButton
|
<div className="picker-disabled">Sending GIFs is not allowed in this chat.</div>
|
||||||
key={gif.id}
|
) : canRenderContents && savedGifs && savedGifs.length ? (
|
||||||
gif={gif}
|
savedGifs.map((gif) => (
|
||||||
observeIntersection={observeIntersection}
|
<GifButton
|
||||||
isDisabled={!loadAndPlay}
|
key={gif.id}
|
||||||
onClick={canSendGifs ? onGifSelect : undefined}
|
gif={gif}
|
||||||
onUnsaveClick={handleUnsaveClick}
|
observeIntersection={observeIntersection}
|
||||||
isSavedMessages={isSavedMessages}
|
isDisabled={!loadAndPlay}
|
||||||
/>
|
onClick={canSendGifs ? onGifSelect : undefined}
|
||||||
))
|
onUnsaveClick={handleUnsaveClick}
|
||||||
) : canRenderContents && savedGifs ? (
|
isSavedMessages={isSavedMessages}
|
||||||
<div className="picker-disabled">No saved GIFs.</div>
|
/>
|
||||||
) : (
|
))
|
||||||
<Loading />
|
) : canRenderContents && savedGifs ? (
|
||||||
)}
|
<div className="picker-disabled">No saved GIFs.</div>
|
||||||
</div>
|
) : (
|
||||||
</div>
|
<Loading color="yellow" />
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
import {
|
import { memo, useEffect, useMemo, useRef } from '../../../lib/teact/teact';
|
||||||
memo, useEffect, useMemo,
|
|
||||||
useRef,
|
|
||||||
} from '../../../lib/teact/teact';
|
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiChat, ApiSticker, ApiStickerSet } from '../../../api/types';
|
import type { ApiChat, ApiSticker, ApiStickerSet } from '../../../api/types';
|
||||||
@ -19,7 +16,11 @@ import {
|
|||||||
STICKER_SIZE_PICKER_HEADER,
|
STICKER_SIZE_PICKER_HEADER,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import {
|
import {
|
||||||
selectChat, selectChatFullInfo, selectIsChatWithSelf, selectIsCurrentUserPremium, selectShouldLoopStickers,
|
selectChat,
|
||||||
|
selectChatFullInfo,
|
||||||
|
selectIsChatWithSelf,
|
||||||
|
selectIsCurrentUserPremium,
|
||||||
|
selectShouldLoopStickers,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
|
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
|
||||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||||
@ -43,6 +44,7 @@ import StickerButton from '../../common/StickerButton';
|
|||||||
import StickerSet from '../../common/StickerSet';
|
import StickerSet from '../../common/StickerSet';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
import Transition from '../../ui/Transition.tsx';
|
||||||
import StickerSetCover from './StickerSetCover';
|
import StickerSetCover from './StickerSetCover';
|
||||||
|
|
||||||
import styles from './StickerPicker.module.scss';
|
import styles from './StickerPicker.module.scss';
|
||||||
@ -329,76 +331,75 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fullClassName = buildClassName(styles.root, className);
|
const fullClassName = buildClassName(styles.root, className);
|
||||||
|
|
||||||
if (!shouldRenderContents) {
|
|
||||||
return (
|
|
||||||
<div className={fullClassName}>
|
|
||||||
{!canSendStickers && !isForEffects ? (
|
|
||||||
<div className={styles.pickerDisabled}>{lang('ErrorSendRestrictedStickersAll')}</div>
|
|
||||||
) : noPopulatedSets ? (
|
|
||||||
<div className={styles.pickerDisabled}>{lang('NoStickers')}</div>
|
|
||||||
) : (
|
|
||||||
<Loading />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerClassName = buildClassName(
|
const headerClassName = buildClassName(
|
||||||
styles.header,
|
styles.header,
|
||||||
'no-scrollbar',
|
'no-scrollbar',
|
||||||
!shouldHideTopBorder && styles.headerWithBorder,
|
!shouldHideTopBorder && styles.headerWithBorder,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isLoading = !shouldRenderContents && (canSendStickers || isForEffects) && !noPopulatedSets;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={fullClassName}>
|
<Transition className={fullClassName} activeKey={isLoading ? 0 : 1} name="fade" shouldCleanup>
|
||||||
{!isForEffects && (
|
{!shouldRenderContents ? (
|
||||||
<div ref={headerRef} className={headerClassName}>
|
!canSendStickers && !isForEffects ? (
|
||||||
<div className="shared-canvas-container">
|
<div className={styles.pickerDisabled}>{lang('ErrorSendRestrictedStickersAll')}</div>
|
||||||
<canvas ref={sharedCanvasRef} className="shared-canvas" />
|
) : noPopulatedSets ? (
|
||||||
{allSets.map(renderCover)}
|
<div className={styles.pickerDisabled}>{lang('NoStickers')}</div>
|
||||||
|
) : (
|
||||||
|
<Loading />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{!isForEffects && (
|
||||||
|
<div ref={headerRef} className={headerClassName}>
|
||||||
|
<div className="shared-canvas-container">
|
||||||
|
<canvas ref={sharedCanvasRef} className="shared-canvas" />
|
||||||
|
{allSets.map(renderCover)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
onScroll={handleContentScroll}
|
||||||
|
className={
|
||||||
|
buildClassName(
|
||||||
|
styles.main,
|
||||||
|
IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll',
|
||||||
|
!isForEffects && styles.hasHeader,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{allSets.map((stickerSet, i) => (
|
||||||
|
<StickerSet
|
||||||
|
key={stickerSet.id}
|
||||||
|
stickerSet={stickerSet}
|
||||||
|
loadAndPlay={Boolean(canAnimate && loadAndPlay)}
|
||||||
|
noContextMenus={noContextMenus}
|
||||||
|
index={i}
|
||||||
|
idPrefix={prefix}
|
||||||
|
observeIntersection={observeIntersectionForSet}
|
||||||
|
observeIntersectionForPlayingItems={observeIntersectionForPlayingItems}
|
||||||
|
observeIntersectionForShowingItems={observeIntersectionForShowingItems}
|
||||||
|
isNearActive={activeSetIndex >= i - 1 && activeSetIndex <= i + 1}
|
||||||
|
favoriteStickers={favoriteStickers}
|
||||||
|
isSavedMessages={isSavedMessages}
|
||||||
|
isCurrentUserPremium={isCurrentUserPremium}
|
||||||
|
isTranslucent={isTranslucent}
|
||||||
|
isChatStickerSet={stickerSet.id === chatStickerSetId}
|
||||||
|
onStickerSelect={handleStickerSelect}
|
||||||
|
onStickerUnfave={handleStickerUnfave}
|
||||||
|
onStickerFave={handleStickerFave}
|
||||||
|
onStickerRemoveRecent={handleRemoveRecentSticker}
|
||||||
|
forcePlayback
|
||||||
|
shouldHideHeader={stickerSet.id === EFFECT_EMOJIS_SET_ID}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<div
|
</Transition>
|
||||||
ref={containerRef}
|
|
||||||
onMouseMove={handleMouseMove}
|
|
||||||
onScroll={handleContentScroll}
|
|
||||||
className={
|
|
||||||
buildClassName(
|
|
||||||
styles.main,
|
|
||||||
IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll',
|
|
||||||
!isForEffects && styles.hasHeader,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{allSets.map((stickerSet, i) => (
|
|
||||||
<StickerSet
|
|
||||||
key={stickerSet.id}
|
|
||||||
stickerSet={stickerSet}
|
|
||||||
loadAndPlay={Boolean(canAnimate && loadAndPlay)}
|
|
||||||
noContextMenus={noContextMenus}
|
|
||||||
index={i}
|
|
||||||
idPrefix={prefix}
|
|
||||||
observeIntersection={observeIntersectionForSet}
|
|
||||||
observeIntersectionForPlayingItems={observeIntersectionForPlayingItems}
|
|
||||||
observeIntersectionForShowingItems={observeIntersectionForShowingItems}
|
|
||||||
isNearActive={activeSetIndex >= i - 1 && activeSetIndex <= i + 1}
|
|
||||||
favoriteStickers={favoriteStickers}
|
|
||||||
isSavedMessages={isSavedMessages}
|
|
||||||
isCurrentUserPremium={isCurrentUserPremium}
|
|
||||||
isTranslucent={isTranslucent}
|
|
||||||
isChatStickerSet={stickerSet.id === chatStickerSetId}
|
|
||||||
onStickerSelect={handleStickerSelect}
|
|
||||||
onStickerUnfave={handleStickerUnfave}
|
|
||||||
onStickerFave={handleStickerFave}
|
|
||||||
onStickerRemoveRecent={handleRemoveRecentSticker}
|
|
||||||
forcePlayback
|
|
||||||
shouldHideHeader={stickerSet.id === EFFECT_EMOJIS_SET_ID}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -331,7 +331,6 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
|||||||
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
|
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
|
||||||
noCloseOnBackdrop={!IS_TOUCH_ENV}
|
noCloseOnBackdrop={!IS_TOUCH_ENV}
|
||||||
noCompact
|
noCompact
|
||||||
|
|
||||||
{...(isAttachmentModal ? menuPositionOptions : {
|
{...(isAttachmentModal ? menuPositionOptions : {
|
||||||
positionX: 'left',
|
positionX: 'left',
|
||||||
positionY: 'bottom',
|
positionY: 'bottom',
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '@teact';
|
||||||
import {
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from '@teact';
|
||||||
memo, useCallback,
|
|
||||||
useEffect, useMemo, useRef, useState,
|
|
||||||
} from '../../lib/teact/teact';
|
|
||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@ -16,20 +13,12 @@ import type {
|
|||||||
ApiUserStatus,
|
ApiUserStatus,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { TabState } from '../../global/types';
|
import type { TabState } from '../../global/types';
|
||||||
import type {
|
import type { AnimationLevel, ProfileState, ProfileTabType, SharedMediaType, ThemeKey, ThreadId } from '../../types';
|
||||||
AnimationLevel,
|
|
||||||
ProfileState, ProfileTabType, SharedMediaType, ThemeKey, ThreadId,
|
|
||||||
} from '../../types';
|
|
||||||
import type { RegularLangKey } from '../../types/language';
|
import type { RegularLangKey } from '../../types/language';
|
||||||
import { MAIN_THREAD_ID } from '../../api/types';
|
import { MAIN_THREAD_ID } from '../../api/types';
|
||||||
import { AudioOrigin, MediaViewerOrigin, NewChatMembersProgress } from '../../types';
|
import { AudioOrigin, MediaViewerOrigin, NewChatMembersProgress } from '../../types';
|
||||||
|
|
||||||
import {
|
import { MEMBERS_SLICE, PROFILE_SENSITIVE_AREA, SHARED_MEDIA_SLICE, SLIDE_TRANSITION_DURATION } from '../../config';
|
||||||
MEMBERS_SLICE,
|
|
||||||
PROFILE_SENSITIVE_AREA,
|
|
||||||
SHARED_MEDIA_SLICE,
|
|
||||||
SLIDE_TRANSITION_DURATION,
|
|
||||||
} from '../../config';
|
|
||||||
import {
|
import {
|
||||||
getHasAdminRight,
|
getHasAdminRight,
|
||||||
getIsDownloading,
|
getIsDownloading,
|
||||||
@ -592,8 +581,19 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!viewportIds && !botPreviewMedia) || !canRenderContent || !messagesById) {
|
const noContent = (!viewportIds && !botPreviewMedia) || !canRenderContent || !messagesById;
|
||||||
const noSpinner = isFirstTab && !canRenderContent;
|
const noSpinner = isFirstTab && !canRenderContent;
|
||||||
|
const isSpinner = noContent && !noSpinner;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition activeKey={isSpinner ? 0 : 1} name="fade">
|
||||||
|
{renderSpinnerOrContent(noContent, noSpinner)}
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSpinnerOrContent(noContent: boolean, noSpinner: boolean) {
|
||||||
|
if (noContent) {
|
||||||
const forceRenderHiddenMembers = Boolean(resultType === 'members' && areMembersHidden);
|
const forceRenderHiddenMembers = Boolean(resultType === 'members' && areMembersHidden);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -651,6 +651,11 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!messagesById) {
|
||||||
|
// A TypeScript assertion, should never be really reached
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`content ${resultType}-list`}
|
className={`content ${resultType}-list`}
|
||||||
|
|||||||
@ -57,7 +57,6 @@ const ResponsiveHoverButton: FC<OwnProps> = ({ onActivate, ...buttonProps }) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
|
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
|
||||||
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
|
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
|
||||||
|
|||||||
@ -241,12 +241,12 @@
|
|||||||
&-fadeBackwards {
|
&-fadeBackwards {
|
||||||
> .Transition_slide-from {
|
> .Transition_slide-from {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
animation: fade-out-opacity 0.15s ease;
|
animation: fade-out-opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .Transition_slide-to {
|
> .Transition_slide-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: fade-in-opacity 0.15s ease;
|
animation: fade-in-opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -49,6 +49,8 @@ export type TransitionProps = {
|
|||||||
isBlockingAnimation?: boolean;
|
isBlockingAnimation?: boolean;
|
||||||
onStart?: NoneToVoidFunction;
|
onStart?: NoneToVoidFunction;
|
||||||
onStop?: NoneToVoidFunction;
|
onStop?: NoneToVoidFunction;
|
||||||
|
onScroll?: NoneToVoidFunction;
|
||||||
|
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
children: React.ReactNode | ChildrenFn;
|
children: React.ReactNode | ChildrenFn;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -89,6 +91,8 @@ function Transition({
|
|||||||
isBlockingAnimation,
|
isBlockingAnimation,
|
||||||
onStart,
|
onStart,
|
||||||
onStop,
|
onStop,
|
||||||
|
onScroll,
|
||||||
|
onMouseDown,
|
||||||
children,
|
children,
|
||||||
}: TransitionProps) {
|
}: TransitionProps) {
|
||||||
const currentKeyRef = useRef<number>();
|
const currentKeyRef = useRef<number>();
|
||||||
@ -379,6 +383,8 @@ function Transition({
|
|||||||
id={id}
|
id={id}
|
||||||
className={buildClassName('Transition', className)}
|
className={buildClassName('Transition', className)}
|
||||||
teactFastList={asFastList}
|
teactFastList={asFastList}
|
||||||
|
onScroll={onScroll}
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
>
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user