Right Column: Show your profile (#6279)
This commit is contained in:
parent
a8099e2f34
commit
37a033ca83
@ -981,6 +981,7 @@
|
||||
"EventLogFilterEditedMessages" = "Edited messages";
|
||||
"EventLogFilterLeavingMembers" = "Members leaving";
|
||||
"ChannelManagementTitle" = "Admins";
|
||||
"MyProfileHeader" = "My Profile";
|
||||
"EventLogAllAdmins" = "All admins";
|
||||
"UserRestrictionsCanDo" = "What can this user do?";
|
||||
"UserRestrictionsBlock" = "Ban and remove from group";
|
||||
|
||||
@ -68,6 +68,7 @@ import styles from './ChatExtra.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
chatOrUserId: string;
|
||||
isOwnProfile?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
isInSettings?: boolean;
|
||||
className?: string;
|
||||
@ -106,7 +107,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
user,
|
||||
chat,
|
||||
userFullInfo,
|
||||
isInSettings,
|
||||
isOwnProfile,
|
||||
canInviteUsers,
|
||||
isMuted,
|
||||
phoneCodeList,
|
||||
@ -122,6 +123,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
botVerification,
|
||||
className,
|
||||
style,
|
||||
isInSettings,
|
||||
}) => {
|
||||
const {
|
||||
showNotification,
|
||||
@ -283,7 +285,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
}, { withNodes: true });
|
||||
|
||||
const isRestricted = chatId ? selectIsChatRestricted(getGlobal(), chatId) : false;
|
||||
if (isRestricted || (isSelf && !isInSettings)) {
|
||||
if (isRestricted || (isSelf && !isOwnProfile && !isInSettings)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -422,7 +424,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
</ListItem>
|
||||
)}
|
||||
{!isInSettings && (
|
||||
{!isOwnProfile && !isInSettings && (
|
||||
<ListItem icon={isMuted ? 'mute' : 'unmute'} narrow ripple onClick={handleToggleNotifications}>
|
||||
<span>{lang('Notifications')}</span>
|
||||
<Switcher
|
||||
@ -449,7 +451,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
<span className="subtitle">{oldLang('BusinessProfileLocation')}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
{hasSavedMessages && !isInSettings && (
|
||||
{hasSavedMessages && !isOwnProfile && !isInSettings && (
|
||||
<ListItem icon="saved-messages" narrow ripple onClick={handleOpenSavedDialog}>
|
||||
<span>{oldLang('SavedMessagesTab')}</span>
|
||||
</ListItem>
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
}
|
||||
|
||||
.plain.minimized {
|
||||
padding-block: 1.5rem 0;
|
||||
color: var(--color-text);
|
||||
|
||||
.userRatingNegativeWrapper,
|
||||
@ -34,6 +35,14 @@
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.status {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.getStatus, .userStatus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:global(.VerifiedIcon),
|
||||
:global(.StarIcon) {
|
||||
--color-fill: var(--color-primary);
|
||||
|
||||
@ -93,7 +93,7 @@ const LeftSideMenuItems = ({
|
||||
const bots = useMemo(() => Object.values(attachBots).filter((bot) => bot.isForSideMenu), [attachBots]);
|
||||
|
||||
const handleSelectMyProfile = useLastCallback(() => {
|
||||
openChatWithInfo({ id: currentUserId, shouldReplaceHistory: true, profileTab: 'stories' });
|
||||
openChatWithInfo({ id: currentUserId, shouldReplaceHistory: true, isOwnProfile: true });
|
||||
});
|
||||
|
||||
const handleSelectSaved = useLastCallback(() => {
|
||||
|
||||
@ -874,6 +874,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||
const savedDialog = isSavedDialog ? selectChat(global, String(threadId)) : undefined;
|
||||
const isAccountFrozen = selectIsCurrentUserFrozen(global);
|
||||
const chatInfo = selectTabState(global).chatInfo;
|
||||
|
||||
return {
|
||||
chat,
|
||||
@ -889,8 +890,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
hasLinkedChat: Boolean(chatFullInfo?.linkedChatId),
|
||||
botCommands: chatBot ? userFullInfo?.botInfo?.commands : undefined,
|
||||
botPrivacyPolicyUrl: chatBot ? userFullInfo?.botInfo?.privacyPolicyUrl : undefined,
|
||||
isChatInfoShown: selectTabState(global).isChatInfoShown
|
||||
&& currentChatId === chatId && currentThreadId === threadId,
|
||||
isChatInfoShown: chatInfo.isOpen && currentChatId === chatId && currentThreadId === threadId,
|
||||
canCreateTopic,
|
||||
canEditTopic,
|
||||
canManage,
|
||||
|
||||
@ -147,7 +147,8 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
const handleOpenChat = useLastCallback((event: React.MouseEvent | React.TouchEvent) => {
|
||||
if ((event.target as Element).closest('.title > .custom-emoji')) return;
|
||||
|
||||
openThreadWithInfo({ chatId, threadId });
|
||||
// Force close My Profile if clicked on Saved Messages header
|
||||
openThreadWithInfo({ chatId, threadId, isOwnProfile: false });
|
||||
});
|
||||
|
||||
const {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { FC } from '@teact';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from '@teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
@ -167,7 +166,7 @@ type StateProps = {
|
||||
isRestricted?: boolean;
|
||||
activeDownloads: TabState['activeDownloads'];
|
||||
isChatProtected?: boolean;
|
||||
nextProfileTab?: ProfileTabType;
|
||||
chatInfo: TabState['chatInfo'];
|
||||
animationLevel: AnimationLevel;
|
||||
shouldWarnAboutFiles?: boolean;
|
||||
similarChannels?: string[];
|
||||
@ -177,7 +176,7 @@ type StateProps = {
|
||||
limitSimilarPeers: number;
|
||||
isTopicInfo?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
forceScrollProfileTab?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
isSynced?: boolean;
|
||||
hasAvatar?: boolean;
|
||||
};
|
||||
@ -197,10 +196,13 @@ const TABS: TabProps[] = [
|
||||
const HIDDEN_RENDER_DELAY = 1000;
|
||||
const INTERSECTION_THROTTLE = 500;
|
||||
|
||||
const Profile: FC<OwnProps & StateProps> = ({
|
||||
const SHARED_MEDIA_TYPES = new Set<string>(['media', 'documents', 'links', 'audio', 'voice']);
|
||||
|
||||
const Profile = ({
|
||||
chatId,
|
||||
isActive,
|
||||
threadId,
|
||||
chatInfo,
|
||||
profileState,
|
||||
theme,
|
||||
monoforumChannel,
|
||||
@ -239,7 +241,6 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
isRestricted,
|
||||
activeDownloads,
|
||||
isChatProtected,
|
||||
nextProfileTab,
|
||||
animationLevel,
|
||||
shouldWarnAboutFiles,
|
||||
similarChannels,
|
||||
@ -248,11 +249,11 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
limitSimilarPeers,
|
||||
isTopicInfo,
|
||||
isSavedDialog,
|
||||
forceScrollProfileTab,
|
||||
isSavedMessages,
|
||||
isSynced,
|
||||
hasAvatar,
|
||||
onProfileStateChange,
|
||||
}) => {
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
setSharedMediaSearchType,
|
||||
loadMoreMembers,
|
||||
@ -274,6 +275,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
loadStarGiftCollections,
|
||||
loadStoryAlbums,
|
||||
resetSelectedStoryAlbum,
|
||||
changeProfileTab,
|
||||
} = getActions();
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
@ -285,15 +287,18 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
const [deletingUserId, setDeletingUserId] = useState<string | undefined>();
|
||||
const [isGiftTransitionEnabled, enableGiftTransition, disableGiftTransition] = useFlag();
|
||||
|
||||
const isClosed = !chatInfo.isOpen;
|
||||
const { profileTab, forceScrollProfileTab, isOwnProfile } = chatInfo;
|
||||
|
||||
const profileId = isSavedDialog ? String(threadId) : chatId;
|
||||
const isSavedMessages = profileId === currentUserId && !isSavedDialog;
|
||||
const isGeneralSavedMessages = isSavedMessages && !isSavedDialog;
|
||||
const [isProfileExpanded, expandProfile, collapseProfile] = useFlag();
|
||||
|
||||
const [restoreContentHeightKey, setRestoreContentHeightKey] = useState(0);
|
||||
|
||||
const tabs = useMemo(() => {
|
||||
const arr: TabProps[] = [];
|
||||
if (isSavedMessages && !isSavedDialog) {
|
||||
if (isGeneralSavedMessages) {
|
||||
arr.push({ type: 'dialogs', key: 'ProfileTabSavedDialogs' });
|
||||
}
|
||||
|
||||
@ -301,7 +306,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
arr.push({ type: 'stories', key: 'ProfileTabStories' });
|
||||
}
|
||||
|
||||
if (hasStoriesTab && isSavedMessages) {
|
||||
if (hasStoriesTab && isOwnProfile) {
|
||||
arr.push({ type: 'storiesArchive', key: 'ProfileTabStoriesArchive' });
|
||||
}
|
||||
|
||||
@ -309,67 +314,73 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
arr.push({ type: 'gifts', key: 'ProfileTabGifts' });
|
||||
}
|
||||
|
||||
if (hasMembersTab) {
|
||||
if (hasMembersTab && !isOwnProfile) {
|
||||
arr.push({ type: 'members', key: isChannel ? 'ProfileTabSubscribers' : 'ProfileTabMembers' });
|
||||
}
|
||||
|
||||
if (hasPreviewMediaTab) {
|
||||
if (hasPreviewMediaTab && !isOwnProfile) {
|
||||
arr.push({ type: 'previewMedia', key: 'ProfileTabBotPreview' });
|
||||
}
|
||||
|
||||
arr.push(...TABS);
|
||||
if (!isOwnProfile) {
|
||||
arr.push(...TABS);
|
||||
}
|
||||
|
||||
// Voice messages filter currently does not work in forum topics. Return it when it's fixed on the server side.
|
||||
if (!isTopicInfo) {
|
||||
if (!isTopicInfo && !isOwnProfile) {
|
||||
arr.push({ type: 'voice', key: 'ProfileTabVoice' });
|
||||
}
|
||||
|
||||
if (hasCommonChatsTab) {
|
||||
if (hasCommonChatsTab && !isOwnProfile) {
|
||||
arr.push({ type: 'commonChats', key: 'ProfileTabSharedGroups' });
|
||||
}
|
||||
|
||||
if (isChannel && similarChannels?.length) {
|
||||
if (isChannel && similarChannels?.length && !isOwnProfile) {
|
||||
arr.push({ type: 'similarChannels', key: 'ProfileTabSimilarChannels' });
|
||||
}
|
||||
|
||||
if (isBot && similarBots?.length) {
|
||||
if (isBot && similarBots?.length && !isOwnProfile) {
|
||||
arr.push({ type: 'similarBots', key: 'ProfileTabSimilarBots' });
|
||||
}
|
||||
|
||||
// Fallback to prevent errors in edge cases
|
||||
// TODO: Handle no tabs case, skip shared media block
|
||||
if (!arr.length) {
|
||||
arr.push(TABS[0]);
|
||||
}
|
||||
|
||||
return arr.map((tab) => ({
|
||||
type: tab.type,
|
||||
title: lang(tab.key),
|
||||
}));
|
||||
}, [
|
||||
isSavedMessages, isSavedDialog, hasStoriesTab, hasGiftsTab, hasMembersTab, hasPreviewMediaTab, isTopicInfo,
|
||||
hasCommonChatsTab, isChannel, isBot, similarChannels?.length, similarBots?.length, lang,
|
||||
isGeneralSavedMessages, hasStoriesTab, hasGiftsTab, hasMembersTab, hasPreviewMediaTab, isTopicInfo,
|
||||
hasCommonChatsTab, isChannel, isBot, similarChannels?.length, similarBots?.length, lang, isOwnProfile,
|
||||
]);
|
||||
|
||||
const initialTab = useMemo(() => {
|
||||
if (!nextProfileTab) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const index = tabs.findIndex(({ type }) => type === nextProfileTab);
|
||||
return index === -1 ? 0 : index;
|
||||
}, [nextProfileTab, tabs]);
|
||||
|
||||
const [allowAutoScrollToTabs, startAutoScrollToTabsIfNeeded, stopAutoScrollToTabs] = useFlag(false);
|
||||
|
||||
const [activeTab, setActiveTab] = useState(initialTab);
|
||||
const setActiveTab = useLastCallback((type: ProfileTabType) => {
|
||||
if (isClosed) return;
|
||||
changeProfileTab({ profileTab: type });
|
||||
setSharedMediaSearchType({ mediaType: SHARED_MEDIA_TYPES.has(type) ? type as SharedMediaType : undefined });
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!nextProfileTab) return;
|
||||
const index = tabs.findIndex(({ type }) => type === nextProfileTab);
|
||||
if (isClosed) return;
|
||||
if (profileTab) {
|
||||
// Force reset scroll marker
|
||||
changeProfileTab({ profileTab, shouldScrollTo: undefined });
|
||||
return;
|
||||
};
|
||||
|
||||
if (index === -1) return;
|
||||
setActiveTab(index);
|
||||
}, [nextProfileTab, tabs]);
|
||||
setActiveTab(tabs[0].type); // Set default tab
|
||||
}, [isClosed, profileTab, tabs]);
|
||||
|
||||
const handleSwitchTab = useCallback((index: number) => {
|
||||
startAutoScrollToTabsIfNeeded();
|
||||
setActiveTab(index);
|
||||
}, []);
|
||||
setActiveTab(tabs[index].type);
|
||||
}, [tabs]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasPreviewMediaTab && !botPreviewMedia) {
|
||||
@ -414,8 +425,12 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const giftIds = useMemo(() => renderingGifts?.map((gift) => getSavedGiftKey(gift)), [renderingGifts]);
|
||||
|
||||
const renderingActiveTab = activeTab > tabs.length - 1 ? tabs.length - 1 : activeTab;
|
||||
const tabType = tabs[renderingActiveTab].type;
|
||||
const activeTabIndex = useMemo(() => {
|
||||
const index = tabs.findIndex(({ type }) => type === profileTab);
|
||||
return index === -1 ? 0 : index;
|
||||
}, [profileTab, tabs]);
|
||||
|
||||
const tabType = tabs[activeTabIndex].type;
|
||||
const handleLoadCommonChats = useCallback(() => {
|
||||
loadCommonChats({ userId: chatId });
|
||||
}, [chatId]);
|
||||
@ -481,7 +496,9 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
similarBots,
|
||||
});
|
||||
|
||||
const isFirstTab = (isSavedMessages && resultType === 'dialogs')
|
||||
const shouldRenderProfileInfo = !noProfileInfo && !isSavedMessages;
|
||||
|
||||
const isFirstTab = (isGeneralSavedMessages && resultType === 'dialogs')
|
||||
|| (hasStoriesTab && resultType === 'stories')
|
||||
|| resultType === 'members'
|
||||
|| (!hasMembersTab && resultType === 'media');
|
||||
@ -562,11 +579,6 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.InProgress });
|
||||
});
|
||||
|
||||
// Update search type when switching tabs or forum topics
|
||||
useEffect(() => {
|
||||
setSharedMediaSearchType({ mediaType: tabType as SharedMediaType });
|
||||
}, [setSharedMediaSearchType, tabType, threadId]);
|
||||
|
||||
const handleSelectMedia = useLastCallback((messageId: number) => {
|
||||
openMediaViewer({
|
||||
chatId: profileId,
|
||||
@ -602,21 +614,21 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
useEffectWithPrevDeps(([prevHasMemberTabs]) => {
|
||||
if (prevHasMemberTabs === undefined || activeTab === 0 || prevHasMemberTabs === hasMembersTab) {
|
||||
if (prevHasMemberTabs === undefined || activeTabIndex === 0 || prevHasMemberTabs === hasMembersTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newActiveTab = activeTab + (hasMembersTab ? 1 : -1);
|
||||
const newActiveTab = Math.min(activeTabIndex + (hasMembersTab ? 1 : -1), tabs.length - 1);
|
||||
|
||||
setActiveTab(Math.min(newActiveTab, tabs.length - 1));
|
||||
}, [hasMembersTab, activeTab, tabs]);
|
||||
setActiveTab(tabs[newActiveTab].type);
|
||||
}, [hasMembersTab, activeTabIndex, tabs]);
|
||||
|
||||
const handleResetGiftsFilter = useLastCallback(() => {
|
||||
resetGiftProfileFilter({ peerId: chatId });
|
||||
});
|
||||
|
||||
useTopOverscroll(
|
||||
containerRef, handleExpandProfile, handleCollapseProfile, !hasAvatar,
|
||||
containerRef, handleExpandProfile, handleCollapseProfile, !hasAvatar || !shouldRenderProfileInfo,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -628,17 +640,19 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
selectorToPreventScroll: '.Profile',
|
||||
onSwipe: (e, direction) => {
|
||||
if (direction === SwipeDirection.Left) {
|
||||
setActiveTab(Math.min(renderingActiveTab + 1, tabs.length - 1));
|
||||
const nextIndex = Math.min(activeTabIndex + 1, tabs.length - 1);
|
||||
setActiveTab(tabs[nextIndex].type);
|
||||
return true;
|
||||
} else if (direction === SwipeDirection.Right) {
|
||||
setActiveTab(Math.max(0, renderingActiveTab - 1));
|
||||
const nextIndex = Math.max(0, activeTabIndex - 1);
|
||||
setActiveTab(tabs[nextIndex].type);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
}, [renderingActiveTab, tabs.length]);
|
||||
}, [activeTabIndex, tabs]);
|
||||
|
||||
let renderingDelay;
|
||||
// @optimization Used to unparallelize rendering of message list and profile media
|
||||
@ -650,7 +664,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
const canRenderContent = useAsyncRendering([chatId, threadId, resultType,
|
||||
renderingActiveTab, activeCollectionId, selectedStoryAlbumId], renderingDelay);
|
||||
activeTabIndex, activeCollectionId, selectedStoryAlbumId], renderingDelay);
|
||||
|
||||
function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined {
|
||||
return memberId === currentUserId || !canDeleteMembers ? undefined : [{
|
||||
@ -1045,6 +1059,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
<ChatExtra
|
||||
chatOrUserId={profileId}
|
||||
isSavedDialog={isSavedDialog}
|
||||
isOwnProfile={isOwnProfile}
|
||||
style={createVtnStyle('chatExtra')}
|
||||
/>
|
||||
</div>
|
||||
@ -1095,10 +1110,11 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
const activeListSelector = `.shared-media-transition > .Transition_slide-active`;
|
||||
// eslint-disable-next-line @stylistic/max-len
|
||||
const nestedSelector = `${activeListSelector} > .Transition > .Transition_slide-active > .Transition > .Transition_slide-active`;
|
||||
const itemSelector = !shouldUseTransitionForContent
|
||||
? `${activeListSelector} .${resultType}-list > .scroll-item`
|
||||
/* eslint-disable @stylistic/max-len */
|
||||
: `${activeListSelector} > .Transition > .Transition_slide-active > .Transition > .Transition_slide-active > .gifts-list > .scroll-item`;
|
||||
: `${nestedSelector} > .${resultType}-list > .scroll-item`;
|
||||
|
||||
return (
|
||||
<InfiniteScroll
|
||||
@ -1142,7 +1158,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
{renderContent()}
|
||||
</Transition>
|
||||
<TabList activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={handleSwitchTab} />
|
||||
<TabList activeTab={activeTabIndex} tabs={tabs} onSwitchTab={handleSwitchTab} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1177,6 +1193,10 @@ export default memo(withGlobal<OwnProps>(
|
||||
const userFullInfo = selectUserFullInfo(global, chatId);
|
||||
const messagesById = selectChatMessages(global, chatId);
|
||||
|
||||
const tabState = selectTabState(global);
|
||||
const { chatInfo, savedGifts } = tabState;
|
||||
const { isOwnProfile } = chatInfo;
|
||||
|
||||
const { animationLevel, shouldWarnAboutFiles } = selectSharedSettings(global);
|
||||
|
||||
const { currentType: mediaSearchType, resultsByType } = selectCurrentSharedMediaSearch(global) || {};
|
||||
@ -1187,7 +1207,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { byId: usersById, statusesById: userStatusesById } = global.users;
|
||||
const { byId: chatsById } = global.chats;
|
||||
|
||||
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||
const isSavedMessages = chatId === global.currentUserId && !isOwnProfile;
|
||||
const isSavedDialog = !isOwnProfile ? getIsSavedDialog(chatId, threadId, global.currentUserId) : undefined;
|
||||
|
||||
const isGroup = chat && isChatGroup(chat);
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
@ -1210,7 +1231,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const peer = user || chat;
|
||||
const peerFullInfo = userFullInfo || chatFullInfo;
|
||||
|
||||
const hasCommonChatsTab = user && !user.isSelf && !isUserBot(user) && !isSavedDialog
|
||||
const hasCommonChatsTab = user && !user.isSelf && !isUserBot(user) && !isSavedMessages
|
||||
&& Boolean(userFullInfo?.commonChatsCount);
|
||||
const commonChats = selectUserCommonChats(global, chatId);
|
||||
|
||||
@ -1218,10 +1239,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const botPreviewMedia = global.users.previewMediaByBotId[chatId];
|
||||
|
||||
const hasStoriesTab = peer && (user?.isSelf || (!peer.areStoriesHidden && peerFullInfo?.hasPinnedStories))
|
||||
&& !isSavedDialog;
|
||||
&& !isSavedMessages;
|
||||
const peerStories = hasStoriesTab ? selectPeerStories(global, peer.id) : undefined;
|
||||
const tabState = selectTabState(global);
|
||||
const { nextProfileTab, forceScrollProfileTab, savedGifts } = tabState;
|
||||
const selectedStoryAlbumId = selectActiveStoriesCollectionId(global);
|
||||
const storyIds = selectedStoryAlbumId !== 'all'
|
||||
? peerStories?.idsByAlbumId?.[selectedStoryAlbumId]?.ids
|
||||
@ -1230,7 +1249,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const storyByIds = peerStories?.byId;
|
||||
const archiveStoryIds = peerStories?.archiveIds;
|
||||
|
||||
const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog;
|
||||
const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedMessages;
|
||||
const activeCollectionId = selectActiveGiftsCollectionId(global, chatId);
|
||||
const peerGifts = savedGifts.collectionsByPeerId[chatId]?.[activeCollectionId];
|
||||
|
||||
@ -1274,8 +1293,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
activeCollectionId,
|
||||
giftsFilter: savedGifts.filter,
|
||||
isChatProtected: chat?.isProtected,
|
||||
nextProfileTab,
|
||||
forceScrollProfileTab,
|
||||
chatInfo,
|
||||
animationLevel,
|
||||
shouldWarnAboutFiles,
|
||||
similarChannels: similarChannelIds,
|
||||
@ -1284,6 +1302,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isCurrentUserPremium,
|
||||
isTopicInfo,
|
||||
isSavedDialog,
|
||||
isSavedMessages,
|
||||
isSynced: global.isSynced,
|
||||
limitSimilarPeers: selectPremiumLimit(global, 'recommendedChannels'),
|
||||
members: hasMembersTab ? members : undefined,
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC } from '@teact';
|
||||
import { memo, useEffect, useRef, useState } from '@teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { AnimationLevel, ProfileTabType, ThreadId } from '../../types';
|
||||
import type { AnimationLevel, ThreadId } from '../../types';
|
||||
import { ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent } from '../../types';
|
||||
|
||||
import { ANIMATION_END_DELAY, MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config';
|
||||
@ -55,10 +55,10 @@ type StateProps = {
|
||||
animationLevel: AnimationLevel;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
nextManagementScreen?: ManagementScreens;
|
||||
nextProfileTab?: ProfileTabType;
|
||||
shouldCloseRightColumn?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
isOwnProfile?: boolean;
|
||||
};
|
||||
|
||||
const ANIMATION_DURATION = 450 + ANIMATION_END_DELAY;
|
||||
@ -81,10 +81,10 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
animationLevel,
|
||||
shouldSkipHistoryAnimations,
|
||||
nextManagementScreen,
|
||||
nextProfileTab,
|
||||
shouldCloseRightColumn,
|
||||
isSavedMessages,
|
||||
isSavedDialog,
|
||||
isOwnProfile,
|
||||
}) => {
|
||||
const {
|
||||
toggleChatInfo,
|
||||
@ -100,7 +100,6 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
toggleStoryStatistics,
|
||||
setOpenedInviteInfo,
|
||||
requestNextManagementScreen,
|
||||
resetNextProfileTab,
|
||||
closeCreateTopicPanel,
|
||||
closeEditTopicPanel,
|
||||
closeBoostStatistics,
|
||||
@ -260,12 +259,6 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [nextManagementScreen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!nextProfileTab) return;
|
||||
|
||||
resetNextProfileTab();
|
||||
}, [nextProfileTab]);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldCloseRightColumn) {
|
||||
close();
|
||||
@ -320,7 +313,7 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
case RightColumnContent.ChatInfo:
|
||||
return (
|
||||
<Profile
|
||||
key={`profile_${chatId!}_${threadId}`}
|
||||
key={`profile_${chatId!}_${threadId}_${Boolean(isOwnProfile)}`}
|
||||
chatId={chatId!}
|
||||
threadId={threadId}
|
||||
profileState={profileState}
|
||||
@ -426,11 +419,12 @@ export default memo(withGlobal<OwnProps>(
|
||||
const areActiveChatsLoaded = selectAreActiveChatsLoaded(global);
|
||||
const { animationLevel } = selectSharedSettings(global);
|
||||
const {
|
||||
management, shouldSkipHistoryAnimations, nextProfileTab, shouldCloseRightColumn,
|
||||
management, shouldSkipHistoryAnimations, shouldCloseRightColumn, chatInfo,
|
||||
} = selectTabState(global);
|
||||
const nextManagementScreen = chatId ? management.byChatId[chatId]?.nextScreen : undefined;
|
||||
|
||||
const isSavedMessages = chatId ? selectIsChatWithSelf(global, chatId) : undefined;
|
||||
const isOwnProfile = chatInfo?.isOwnProfile;
|
||||
const isSavedMessages = chatId && !isOwnProfile ? selectIsChatWithSelf(global, chatId) : undefined;
|
||||
const isSavedDialog = chatId ? getIsSavedDialog(chatId, threadId, global.currentUserId) : undefined;
|
||||
|
||||
return {
|
||||
@ -441,10 +435,10 @@ export default memo(withGlobal<OwnProps>(
|
||||
animationLevel,
|
||||
shouldSkipHistoryAnimations,
|
||||
nextManagementScreen,
|
||||
nextProfileTab,
|
||||
shouldCloseRightColumn,
|
||||
isSavedMessages,
|
||||
isSavedDialog,
|
||||
isOwnProfile,
|
||||
};
|
||||
},
|
||||
)(RightColumn));
|
||||
|
||||
@ -7,7 +7,7 @@ import { getActions, withGlobal } from '../../global';
|
||||
import type { ApiExportedInvite } from '../../api/types';
|
||||
import type { GiftProfileFilterOptions, ThreadId } from '../../types';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
import { ManagementScreens, ProfileState } from '../../types';
|
||||
import { ManagementScreens, ProfileState, SettingsScreens } from '../../types';
|
||||
|
||||
import { ANIMATION_END_DELAY, SAVED_FOLDER_ID } from '../../config';
|
||||
import {
|
||||
@ -94,6 +94,7 @@ type StateProps = {
|
||||
isInsideTopic?: boolean;
|
||||
canEditTopic?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
isOwnProfile?: boolean;
|
||||
};
|
||||
|
||||
const COLUMN_ANIMATION_DURATION = 450 + ANIMATION_END_DELAY;
|
||||
@ -179,6 +180,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
giftProfileFilter,
|
||||
canUseGiftFilter,
|
||||
canUseGiftAdminFilter,
|
||||
isOwnProfile,
|
||||
onClose,
|
||||
onScreenSelect,
|
||||
}) => {
|
||||
@ -192,6 +194,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
deleteExportedChatInvite,
|
||||
openEditTopicPanel,
|
||||
updateGiftProfileFilter,
|
||||
openSettingsScreen,
|
||||
} = getActions();
|
||||
|
||||
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useFlag();
|
||||
@ -246,6 +249,10 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
toggleStatistics();
|
||||
});
|
||||
|
||||
const handleEditProfile = useLastCallback(() => {
|
||||
openSettingsScreen({ screen: SettingsScreens.EditProfile });
|
||||
});
|
||||
|
||||
const handleClose = useLastCallback(() => {
|
||||
onClose(!isSavedMessages);
|
||||
});
|
||||
@ -345,6 +352,10 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
const renderingContentKey = useCurrentOrPrev(contentKey, true) ?? -1;
|
||||
|
||||
function getHeaderTitle() {
|
||||
if (isOwnProfile) {
|
||||
return lang('MyProfileHeader');
|
||||
}
|
||||
|
||||
if (isSavedMessages) {
|
||||
return oldLang('SavedMessages');
|
||||
}
|
||||
@ -673,6 +684,17 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
<Icon name="stats" />
|
||||
</Button>
|
||||
)}
|
||||
{isOwnProfile && (
|
||||
<Button
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Edit')}
|
||||
onClick={handleEditProfile}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
</Button>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
@ -738,7 +760,8 @@ export default withGlobal<OwnProps>(
|
||||
const topic = isInsideTopic ? selectTopic(global, chatId!, threadId!) : undefined;
|
||||
const canEditTopic = isInsideTopic && topic && getCanManageTopic(chat, topic);
|
||||
const isBot = user && isUserBot(user);
|
||||
const isSavedMessages = chatId ? selectIsChatWithSelf(global, chatId) : undefined;
|
||||
const isOwnProfile = tabState.chatInfo?.isOwnProfile;
|
||||
const isSavedMessages = chatId && !isOwnProfile ? selectIsChatWithSelf(global, chatId) : undefined;
|
||||
const canEditBot = isBot && user?.canEditBot;
|
||||
|
||||
const canAddContact = user && getCanAddContact(user);
|
||||
@ -775,6 +798,7 @@ export default withGlobal<OwnProps>(
|
||||
giftProfileFilter,
|
||||
canUseGiftFilter,
|
||||
canUseGiftAdminFilter,
|
||||
isOwnProfile,
|
||||
};
|
||||
},
|
||||
)(RightHeader);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import type { ActionReturnType } from '../../types';
|
||||
import type { ProfileTabType } from '../../../types';
|
||||
import type { ActionReturnType, GlobalState } from '../../types';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { createMessageHashUrl } from '../../../util/routing';
|
||||
import { addActionHandler, setGlobal } from '../../index';
|
||||
import { addActionHandler, execAfterActions, getGlobal, setGlobal } from '../../index';
|
||||
import {
|
||||
closeMiddleSearch,
|
||||
exitMessageSelectMode, replaceTabThreadParam, updateCurrentMessageList, updateRequestedChatTranslation,
|
||||
@ -69,6 +70,10 @@ addActionHandler('processOpenChatOrThread', (global, actions, payload): ActionRe
|
||||
forwardMessages: {},
|
||||
isShareMessageModalShown: false,
|
||||
}),
|
||||
// Reset chat info state for new chat
|
||||
chatInfo: {
|
||||
isOpen: tabState.chatInfo.isOpen,
|
||||
},
|
||||
}, tabId);
|
||||
}
|
||||
|
||||
@ -102,33 +107,69 @@ addActionHandler('openPreviousChat', (global, actions, payload): ActionReturnTyp
|
||||
});
|
||||
|
||||
addActionHandler('openChatWithInfo', (global, actions, payload): ActionReturnType => {
|
||||
const { profileTab, forceScrollProfileTab = false, tabId = getCurrentTabId() } = payload;
|
||||
const { profileTab, forceScrollProfileTab, isOwnProfile, tabId = getCurrentTabId(), ...rest } = payload;
|
||||
|
||||
global = updateTabState(global, {
|
||||
...selectTabState(global, tabId),
|
||||
isChatInfoShown: true,
|
||||
nextProfileTab: profileTab,
|
||||
forceScrollProfileTab,
|
||||
}, tabId);
|
||||
global = { ...global, lastIsChatInfoShown: true };
|
||||
setGlobal(global);
|
||||
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||
const isSameMessageList = currentMessageList?.chatId === rest.id
|
||||
&& currentMessageList?.threadId === MAIN_THREAD_ID
|
||||
&& currentMessageList?.type === (rest.type || 'thread');
|
||||
|
||||
actions.openChat({ ...payload, tabId });
|
||||
processChatInfoState({ global, isSameMessageList, profileTab, forceScrollProfileTab, isOwnProfile, tabId });
|
||||
|
||||
actions.openChat({ ...rest, tabId });
|
||||
});
|
||||
|
||||
addActionHandler('openThreadWithInfo', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload;
|
||||
const { profileTab, forceScrollProfileTab, isOwnProfile, tabId = getCurrentTabId(), ...rest } = payload;
|
||||
|
||||
global = updateTabState(global, {
|
||||
...selectTabState(global, tabId),
|
||||
isChatInfoShown: true,
|
||||
}, tabId);
|
||||
global = { ...global, lastIsChatInfoShown: true };
|
||||
setGlobal(global);
|
||||
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||
const isSameMessageList = currentMessageList?.chatId === rest.chatId
|
||||
&& currentMessageList?.threadId === rest.threadId
|
||||
&& currentMessageList?.type === (rest.type || 'thread');
|
||||
|
||||
actions.openThread({ ...payload, tabId });
|
||||
processChatInfoState({ global, isSameMessageList, profileTab, forceScrollProfileTab, isOwnProfile, tabId });
|
||||
|
||||
actions.openThread({ ...rest, tabId });
|
||||
});
|
||||
|
||||
function processChatInfoState<T extends GlobalState>({
|
||||
global,
|
||||
isSameMessageList,
|
||||
profileTab,
|
||||
forceScrollProfileTab,
|
||||
isOwnProfile,
|
||||
tabId,
|
||||
}: {
|
||||
global: T;
|
||||
isSameMessageList: boolean;
|
||||
profileTab?: ProfileTabType;
|
||||
forceScrollProfileTab?: boolean;
|
||||
isOwnProfile?: boolean;
|
||||
tabId: number;
|
||||
}) {
|
||||
const currentChatInfo = selectTabState(global, tabId).chatInfo;
|
||||
|
||||
const newProfileTab = profileTab ?? (isSameMessageList ? currentChatInfo.profileTab : undefined);
|
||||
const newForceScrollProfileTab = forceScrollProfileTab
|
||||
?? (isSameMessageList ? currentChatInfo.forceScrollProfileTab : undefined);
|
||||
const newIsOwnProfile = isOwnProfile ?? (isSameMessageList ? currentChatInfo.isOwnProfile : undefined);
|
||||
|
||||
execAfterActions(() => {
|
||||
global = getGlobal();
|
||||
global = updateTabState(global, {
|
||||
...selectTabState(global, tabId),
|
||||
chatInfo: {
|
||||
isOpen: true,
|
||||
profileTab: newProfileTab,
|
||||
forceScrollProfileTab: newForceScrollProfileTab,
|
||||
isOwnProfile: newIsOwnProfile,
|
||||
},
|
||||
}, tabId);
|
||||
global = { ...global, lastIsChatInfoShown: true };
|
||||
setGlobal(global);
|
||||
});
|
||||
}
|
||||
|
||||
addActionHandler('openChatWithDraft', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
chatId, text, threadId = MAIN_THREAD_ID, files, filter, tabId = getCurrentTabId(),
|
||||
|
||||
@ -51,10 +51,18 @@ const MAX_STORED_EMOJIS = 8 * 4; // Represents four rows of recent emojis
|
||||
|
||||
addActionHandler('toggleChatInfo', (global, actions, payload): ActionReturnType => {
|
||||
const { force, tabId = getCurrentTabId() } = payload || {};
|
||||
const isChatInfoShown = force !== undefined ? force : !selectTabState(global, tabId).isChatInfoShown;
|
||||
const chatInfo = selectTabState(global, tabId).chatInfo;
|
||||
const willChatInfoBeShown = force !== undefined ? force : !chatInfo.isOpen;
|
||||
|
||||
global = updateTabState(global, { isChatInfoShown }, tabId);
|
||||
global = { ...global, lastIsChatInfoShown: isChatInfoShown };
|
||||
if (willChatInfoBeShown !== chatInfo.isOpen) {
|
||||
global = updateTabState(global, {
|
||||
chatInfo: {
|
||||
...chatInfo,
|
||||
isOpen: willChatInfoBeShown,
|
||||
},
|
||||
}, tabId);
|
||||
}
|
||||
global = { ...global, lastIsChatInfoShown: willChatInfoBeShown };
|
||||
|
||||
return global;
|
||||
});
|
||||
@ -156,15 +164,24 @@ addActionHandler('processOpenChatOrThread', (global, actions, payload): ActionRe
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('resetNextProfileTab', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
addActionHandler('changeProfileTab', (global, actions, payload): ActionReturnType => {
|
||||
const { profileTab, shouldScrollTo, tabId = getCurrentTabId() } = payload;
|
||||
const { chatId } = selectCurrentMessageList(global, tabId) || {};
|
||||
|
||||
if (!chatId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return updateTabState(global, { nextProfileTab: undefined, forceScrollProfileTab: false }, tabId);
|
||||
const chatInfo = selectTabState(global, tabId).chatInfo;
|
||||
|
||||
return updateTabState(global, {
|
||||
chatInfo: {
|
||||
...chatInfo,
|
||||
isOpen: true,
|
||||
profileTab,
|
||||
forceScrollProfileTab: shouldScrollTo,
|
||||
},
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('toggleStatistics', (global, actions, payload): ActionReturnType => {
|
||||
|
||||
@ -52,5 +52,6 @@ export const addActionHandler = typed.addActionHandler as <ActionName extends Pr
|
||||
name: ActionName,
|
||||
handler: ActionHandlers[ActionName],
|
||||
) => void;
|
||||
export const execAfterActions = typed.execAfterActions;
|
||||
export const withGlobal = typed.withGlobal;
|
||||
export type GlobalActions = ReturnType<typeof getActions>;
|
||||
|
||||
@ -34,10 +34,14 @@ addActionHandler('init', (global, actions, payload): ActionReturnType => {
|
||||
|
||||
const initialTabState = cloneDeep(INITIAL_TAB_STATE);
|
||||
initialTabState.id = tabId;
|
||||
initialTabState.isChatInfoShown = Boolean(global.lastIsChatInfoShown);
|
||||
initialTabState.audioPlayer.playbackRate = global.audioPlayer.lastPlaybackRate;
|
||||
initialTabState.audioPlayer.isPlaybackRateActive = global.audioPlayer.isLastPlaybackRateActive;
|
||||
initialTabState.mediaViewer.playbackRate = global.mediaViewer.lastPlaybackRate;
|
||||
if (global.lastIsChatInfoShown) {
|
||||
initialTabState.chatInfo = {
|
||||
isOpen: true,
|
||||
};
|
||||
}
|
||||
|
||||
global = {
|
||||
...global,
|
||||
|
||||
@ -339,7 +339,6 @@ export const INITIAL_TAB_STATE: TabState = {
|
||||
id: 0,
|
||||
isMasterTab: false,
|
||||
isLeftColumnShown: true,
|
||||
isChatInfoShown: false,
|
||||
newChatMembersProgress: NewChatMembersProgress.Closed,
|
||||
uiReadyState: 0,
|
||||
shouldInit: true,
|
||||
@ -390,6 +389,10 @@ export const INITIAL_TAB_STATE: TabState = {
|
||||
byChatId: {},
|
||||
},
|
||||
|
||||
chatInfo: {
|
||||
isOpen: false,
|
||||
},
|
||||
|
||||
savedGifts: {
|
||||
filter: {
|
||||
...DEFAULT_GIFT_PROFILE_FILTER_OPTIONS,
|
||||
|
||||
@ -59,7 +59,7 @@ export function selectRightColumnContentKey<T extends GlobalState>(
|
||||
RightColumnContent.GifSearch
|
||||
) : tabState.newChatMembersProgress !== NewChatMembersProgress.Closed ? (
|
||||
RightColumnContent.AddingMembers
|
||||
) : tabState.isChatInfoShown && tabState.messageLists.length ? (
|
||||
) : tabState.chatInfo.isOpen && tabState.messageLists.length ? (
|
||||
RightColumnContent.ChatInfo
|
||||
) : undefined;
|
||||
}
|
||||
|
||||
@ -311,7 +311,7 @@ export interface ActionPayloads {
|
||||
hashtag: string;
|
||||
} & WithTabId;
|
||||
setSharedMediaSearchType: {
|
||||
mediaType: SharedMediaType;
|
||||
mediaType?: SharedMediaType;
|
||||
} & WithTabId;
|
||||
searchSharedMediaMessages: WithTabId | undefined;
|
||||
searchChatMediaMessages: {
|
||||
@ -360,8 +360,13 @@ export interface ActionPayloads {
|
||||
openChatWithInfo: ActionPayloads['openChat'] & {
|
||||
profileTab?: ProfileTabType;
|
||||
forceScrollProfileTab?: boolean;
|
||||
isOwnProfile?: boolean;
|
||||
} & WithTabId;
|
||||
openThreadWithInfo: ActionPayloads['openThread'] & {
|
||||
profileTab?: ProfileTabType;
|
||||
forceScrollProfileTab?: boolean;
|
||||
isOwnProfile?: boolean;
|
||||
} & WithTabId;
|
||||
openThreadWithInfo: ActionPayloads['openThread'] & WithTabId;
|
||||
openLinkedChat: { id: string } & WithTabId;
|
||||
loadMoreMembers: {
|
||||
chatId: string;
|
||||
@ -1186,6 +1191,7 @@ export interface ActionPayloads {
|
||||
chatId?: string;
|
||||
originMessageId: number;
|
||||
originChannelId: string;
|
||||
threadId?: never;
|
||||
} | {
|
||||
isComments?: false;
|
||||
chatId: string;
|
||||
@ -1241,7 +1247,10 @@ export interface ActionPayloads {
|
||||
chatId: string;
|
||||
isEnabled: boolean;
|
||||
};
|
||||
resetNextProfileTab: WithTabId | undefined;
|
||||
changeProfileTab: {
|
||||
profileTab: ProfileTabType | undefined;
|
||||
shouldScrollTo?: boolean;
|
||||
} & WithTabId;
|
||||
|
||||
openForumPanel: {
|
||||
chatId: string;
|
||||
|
||||
@ -105,7 +105,6 @@ export type TabState = {
|
||||
shouldPreventComposerAnimation?: boolean;
|
||||
inviteHash?: string;
|
||||
canInstall?: boolean;
|
||||
isChatInfoShown: boolean;
|
||||
isStatisticsShown?: boolean;
|
||||
isLeftColumnShown: boolean;
|
||||
newChatMembersProgress?: NewChatMembersProgress;
|
||||
@ -126,8 +125,12 @@ export type TabState = {
|
||||
};
|
||||
|
||||
shouldCloseRightColumn?: boolean;
|
||||
nextProfileTab?: ProfileTabType;
|
||||
forceScrollProfileTab?: boolean;
|
||||
chatInfo: {
|
||||
isOpen: boolean;
|
||||
profileTab?: ProfileTabType;
|
||||
forceScrollProfileTab?: boolean;
|
||||
isOwnProfile?: boolean;
|
||||
};
|
||||
nextFoldersAction?: ReducerAction<FoldersActions>;
|
||||
shareFolderScreen?: {
|
||||
folderId: number;
|
||||
|
||||
@ -132,43 +132,59 @@ export function forceOnHeavyAnimationOnce() {
|
||||
}
|
||||
|
||||
let actionQueue: NoneToVoidFunction[] = [];
|
||||
let afterActionQueue: NoneToVoidFunction[] = [];
|
||||
|
||||
function handleAction(name: string, payload?: ActionPayload, options?: ActionOptions): Promise<void> {
|
||||
const deferred = new Deferred<void>();
|
||||
actionQueue.push(() => {
|
||||
actionHandlers[name]?.forEach((handler) => {
|
||||
const response = handler(DEBUG ? getUntypedGlobal() : currentGlobal, actions, payload);
|
||||
if (!response) {
|
||||
const result = handler(DEBUG ? getUntypedGlobal() : currentGlobal, actions, payload);
|
||||
if (!result) {
|
||||
deferred.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof response.then === 'function') {
|
||||
response.then(() => {
|
||||
if (typeof result.then === 'function') {
|
||||
result.then(() => {
|
||||
deferred.resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setUntypedGlobal(response as GlobalState, options);
|
||||
setUntypedGlobal(result as GlobalState, options);
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// Important: Keep 1 as start requirement to avoid immediate nested action calls
|
||||
// Do not remove element from array before it is executed for the same reason
|
||||
if (actionQueue.length === 1) {
|
||||
try {
|
||||
while (actionQueue.length) {
|
||||
actionQueue[0]();
|
||||
actionQueue.shift();
|
||||
}
|
||||
while (afterActionQueue.length) {
|
||||
afterActionQueue[0]();
|
||||
afterActionQueue.shift();
|
||||
}
|
||||
} finally {
|
||||
actionQueue = [];
|
||||
afterActionQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function after all actions in stack are executed
|
||||
* Call only from action handlers
|
||||
*/
|
||||
export function execAfterActions(fn: NoneToVoidFunction) {
|
||||
afterActionQueue.push(fn);
|
||||
}
|
||||
|
||||
function updateContainers() {
|
||||
let DEBUG_startAt: number | undefined;
|
||||
if (DEBUG) {
|
||||
@ -357,6 +373,7 @@ export function typify<
|
||||
handler: ActionHandlers[ActionName],
|
||||
) => void,
|
||||
withGlobal: withUntypedGlobal as WithGlobalFn,
|
||||
execAfterActions,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
1
src/types/language.d.ts
vendored
1
src/types/language.d.ts
vendored
@ -856,6 +856,7 @@ export interface LangPair {
|
||||
'EventLogFilterEditedMessages': undefined;
|
||||
'EventLogFilterLeavingMembers': undefined;
|
||||
'ChannelManagementTitle': undefined;
|
||||
'MyProfileHeader': undefined;
|
||||
'EventLogAllAdmins': undefined;
|
||||
'UserRestrictionsCanDo': undefined;
|
||||
'UserRestrictionsBlock': undefined;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user