Media Viewer: Chat media loading (#4606)
This commit is contained in:
parent
7803e4dbe2
commit
bd2f99efaa
@ -16,7 +16,9 @@ import {
|
|||||||
selectChatMessage,
|
selectChatMessage,
|
||||||
selectChatMessages,
|
selectChatMessages,
|
||||||
selectChatScheduledMessages,
|
selectChatScheduledMessages,
|
||||||
selectCurrentMediaSearch, selectIsChatWithSelf,
|
selectCurrentChatMediaSearch,
|
||||||
|
selectCurrentSharedMediaSearch,
|
||||||
|
selectIsChatWithSelf,
|
||||||
selectListedIds,
|
selectListedIds,
|
||||||
selectOutlyingListByMessageId,
|
selectOutlyingListByMessageId,
|
||||||
selectPerformanceSettingsValue,
|
selectPerformanceSettingsValue,
|
||||||
@ -71,6 +73,9 @@ type StateProps = {
|
|||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
withAnimation?: boolean;
|
withAnimation?: boolean;
|
||||||
shouldSkipHistoryAnimations?: boolean;
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
|
withDynamicLoading?: boolean;
|
||||||
|
isLoadingMoreMedia?: boolean;
|
||||||
|
isSynced?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ANIMATION_DURATION = 250;
|
const ANIMATION_DURATION = 250;
|
||||||
@ -91,6 +96,9 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
withAnimation,
|
withAnimation,
|
||||||
isHidden,
|
isHidden,
|
||||||
shouldSkipHistoryAnimations,
|
shouldSkipHistoryAnimations,
|
||||||
|
withDynamicLoading,
|
||||||
|
isLoadingMoreMedia,
|
||||||
|
isSynced,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
openMediaViewer,
|
openMediaViewer,
|
||||||
@ -98,6 +106,7 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
openForwardMenu,
|
openForwardMenu,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
toggleChatInfo,
|
toggleChatInfo,
|
||||||
|
searchChatMediaMessages,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const isOpen = Boolean(avatarOwner || mediaId);
|
const isOpen = Boolean(avatarOwner || mediaId);
|
||||||
@ -133,17 +142,14 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
const isVisible = !isHidden && isOpen;
|
const isVisible = !isHidden && isOpen;
|
||||||
|
|
||||||
/* Navigation */
|
/* Navigation */
|
||||||
const singleMediaId = webPagePhoto || webPageVideo || actionPhoto ? mediaId : undefined;
|
const singleMediaId = webPagePhoto || webPageVideo || actionPhoto || isGif ? mediaId : undefined;
|
||||||
|
|
||||||
const mediaIds = useMemo(() => {
|
const mediaIds = useMemo(() => {
|
||||||
if (singleMediaId) {
|
if (singleMediaId) return [singleMediaId];
|
||||||
return [singleMediaId];
|
if (avatarOwner) return avatarOwner.photos?.map((p, i) => i) || [];
|
||||||
} else if (avatarOwner) {
|
if (withDynamicLoading) return collectionIds || [];
|
||||||
return avatarOwner.photos?.map((p, i) => i) || [];
|
return getChatMediaMessageIds(chatMessages || {}, collectionIds || [], isFromSharedMedia);
|
||||||
} else {
|
}, [singleMediaId, avatarOwner, chatMessages, collectionIds, isFromSharedMedia, withDynamicLoading]);
|
||||||
return getChatMediaMessageIds(chatMessages || {}, collectionIds || [], isFromSharedMedia);
|
|
||||||
}
|
|
||||||
}, [singleMediaId, avatarOwner, chatMessages, collectionIds, isFromSharedMedia]);
|
|
||||||
|
|
||||||
const selectedMediaIndex = mediaId ? mediaIds.indexOf(mediaId) : -1;
|
const selectedMediaIndex = mediaId ? mediaIds.indexOf(mediaId) : -1;
|
||||||
|
|
||||||
@ -252,6 +258,7 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
mediaId: id,
|
mediaId: id,
|
||||||
avatarOwnerId: avatarOwner?.id,
|
avatarOwnerId: avatarOwner?.id,
|
||||||
origin: origin!,
|
origin: origin!,
|
||||||
|
withDynamicLoading,
|
||||||
}, {
|
}, {
|
||||||
forceOnHeavyAnimation: true,
|
forceOnHeavyAnimation: true,
|
||||||
});
|
});
|
||||||
@ -269,6 +276,11 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
|
|
||||||
const mediaIdsRef = useStateRef(mediaIds);
|
const mediaIdsRef = useStateRef(mediaIds);
|
||||||
|
|
||||||
|
const loadMoreMediaIfNeeded = useLastCallback((activeMediaId?: number) => {
|
||||||
|
if (!activeMediaId || !withDynamicLoading || isLoadingMoreMedia) return;
|
||||||
|
searchChatMediaMessages({ chatId, threadId, currentMediaMessageId: activeMediaId });
|
||||||
|
});
|
||||||
|
|
||||||
const getMediaId = useLastCallback((fromId?: number, direction?: number): number | undefined => {
|
const getMediaId = useLastCallback((fromId?: number, direction?: number): number | undefined => {
|
||||||
if (fromId === undefined) return undefined;
|
if (fromId === undefined) return undefined;
|
||||||
const mIds = mediaIdsRef.current;
|
const mIds = mediaIdsRef.current;
|
||||||
@ -276,6 +288,8 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
if ((direction === -1 && index > 0) || (direction === 1 && index < mIds.length - 1)) {
|
if ((direction === -1 && index > 0) || (direction === 1 && index < mIds.length - 1)) {
|
||||||
return mIds[index + direction];
|
return mIds[index + direction];
|
||||||
}
|
}
|
||||||
|
// Fallback
|
||||||
|
if (isVisible) loadMoreMediaIfNeeded(fromId);
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -359,6 +373,9 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<MediaViewerSlides
|
<MediaViewerSlides
|
||||||
mediaId={mediaId}
|
mediaId={mediaId}
|
||||||
|
loadMoreMediaIfNeeded={loadMoreMediaIfNeeded}
|
||||||
|
isLoadingMoreMedia={isLoadingMoreMedia}
|
||||||
|
isSynced={isSynced}
|
||||||
getMediaId={getMediaId}
|
getMediaId={getMediaId}
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
isPhoto={isPhoto}
|
isPhoto={isPhoto}
|
||||||
@ -389,10 +406,11 @@ export default memo(withGlobal(
|
|||||||
avatarOwnerId,
|
avatarOwnerId,
|
||||||
origin,
|
origin,
|
||||||
isHidden,
|
isHidden,
|
||||||
|
withDynamicLoading,
|
||||||
} = mediaViewer;
|
} = mediaViewer;
|
||||||
const withAnimation = selectPerformanceSettingsValue(global, 'mediaViewerAnimations');
|
const withAnimation = selectPerformanceSettingsValue(global, 'mediaViewerAnimations');
|
||||||
|
|
||||||
const { currentUserId } = global;
|
const { currentUserId, isSynced } = global;
|
||||||
let isChatWithSelf = !!chatId && selectIsChatWithSelf(global, chatId);
|
let isChatWithSelf = !!chatId && selectIsChatWithSelf(global, chatId);
|
||||||
|
|
||||||
if (origin === MediaViewerOrigin.SearchResult) {
|
if (origin === MediaViewerOrigin.SearchResult) {
|
||||||
@ -466,16 +484,24 @@ export default memo(withGlobal(
|
|||||||
} else {
|
} else {
|
||||||
chatMessages = selectChatMessages(global, chatId);
|
chatMessages = selectChatMessages(global, chatId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isLoadingMoreMedia = false;
|
||||||
|
const isOriginInline = origin === MediaViewerOrigin.Inline;
|
||||||
|
const isOriginAlbum = origin === MediaViewerOrigin.Album;
|
||||||
let collectionIds: number[] | undefined;
|
let collectionIds: number[] | undefined;
|
||||||
|
|
||||||
if (origin === MediaViewerOrigin.Inline
|
if (withDynamicLoading && (isOriginInline || isOriginAlbum)) {
|
||||||
|| origin === MediaViewerOrigin.Album) {
|
const currentSearch = selectCurrentChatMediaSearch(global);
|
||||||
collectionIds = selectOutlyingListByMessageId(global, chatId, threadId, message.id)
|
isLoadingMoreMedia = Boolean(currentSearch?.isLoading);
|
||||||
|| selectListedIds(global, chatId, threadId);
|
const { foundIds } = (currentSearch?.currentSegment) || {};
|
||||||
|
collectionIds = foundIds;
|
||||||
} else if (origin === MediaViewerOrigin.SharedMedia) {
|
} else if (origin === MediaViewerOrigin.SharedMedia) {
|
||||||
const currentSearch = selectCurrentMediaSearch(global);
|
const currentSearch = selectCurrentSharedMediaSearch(global);
|
||||||
const { foundIds } = (currentSearch && currentSearch.resultsByType && currentSearch.resultsByType.media) || {};
|
const { foundIds } = (currentSearch && currentSearch.resultsByType && currentSearch.resultsByType.media) || {};
|
||||||
collectionIds = foundIds;
|
collectionIds = foundIds;
|
||||||
|
} else if (isOriginInline || isOriginAlbum) {
|
||||||
|
const outlyingList = selectOutlyingListByMessageId(global, chatId, threadId, message.id);
|
||||||
|
collectionIds = outlyingList || selectListedIds(global, chatId, threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -491,6 +517,9 @@ export default memo(withGlobal(
|
|||||||
withAnimation,
|
withAnimation,
|
||||||
isHidden,
|
isHidden,
|
||||||
shouldSkipHistoryAnimations,
|
shouldSkipHistoryAnimations,
|
||||||
|
withDynamicLoading,
|
||||||
|
isLoadingMoreMedia,
|
||||||
|
isSynced,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(MediaViewer));
|
)(MediaViewer));
|
||||||
|
|||||||
@ -39,6 +39,9 @@ const { easeOutCubic, easeOutQuart } = timingFunctions;
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
mediaId?: number;
|
mediaId?: number;
|
||||||
|
loadMoreMediaIfNeeded: (activeMediaId?: number) => void;
|
||||||
|
isLoadingMoreMedia?: boolean;
|
||||||
|
isSynced?: boolean;
|
||||||
getMediaId: (fromId?: number, direction?: number) => number | undefined;
|
getMediaId: (fromId?: number, direction?: number) => number | undefined;
|
||||||
isVideo?: boolean;
|
isVideo?: boolean;
|
||||||
isGif?: boolean;
|
isGif?: boolean;
|
||||||
@ -84,6 +87,7 @@ enum SwipeDirection {
|
|||||||
|
|
||||||
const MediaViewerSlides: FC<OwnProps> = ({
|
const MediaViewerSlides: FC<OwnProps> = ({
|
||||||
mediaId,
|
mediaId,
|
||||||
|
loadMoreMediaIfNeeded,
|
||||||
getMediaId,
|
getMediaId,
|
||||||
selectMedia,
|
selectMedia,
|
||||||
isVideo,
|
isVideo,
|
||||||
@ -91,6 +95,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
withAnimation,
|
withAnimation,
|
||||||
isHidden,
|
isHidden,
|
||||||
|
isLoadingMoreMedia,
|
||||||
|
isSynced,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
@ -156,6 +162,11 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}, [mediaId, setActiveMediaId, transformRef]);
|
}, [mediaId, setActiveMediaId, transformRef]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isSynced || isLoadingMoreMedia) return;
|
||||||
|
loadMoreMediaIfNeeded(activeMediaId);
|
||||||
|
}, [activeMediaId, loadMoreMediaIfNeeded, isSynced, isLoadingMoreMedia]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const { x, y, scale } = getTransform();
|
const { x, y, scale } = getTransform();
|
||||||
lockControls(scale !== 1);
|
lockControls(scale !== 1);
|
||||||
@ -643,6 +654,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
[
|
[
|
||||||
onClose,
|
onClose,
|
||||||
setTransform,
|
setTransform,
|
||||||
|
loadMoreMediaIfNeeded,
|
||||||
getMediaId,
|
getMediaId,
|
||||||
windowWidth,
|
windowWidth,
|
||||||
windowHeight,
|
windowHeight,
|
||||||
|
|||||||
@ -581,6 +581,8 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
handleMediaClick,
|
handleMediaClick,
|
||||||
handleAudioPlay,
|
handleAudioPlay,
|
||||||
handleAlbumMediaClick,
|
handleAlbumMediaClick,
|
||||||
|
handlePhotoMediaClick,
|
||||||
|
handleVideoMediaClick,
|
||||||
handleMetaClick,
|
handleMetaClick,
|
||||||
handleTranslationClick,
|
handleTranslationClick,
|
||||||
handleOpenThread,
|
handleOpenThread,
|
||||||
@ -1105,7 +1107,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
asForwarded={asForwarded}
|
asForwarded={asForwarded}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
forcedWidth={contentWidth}
|
forcedWidth={contentWidth}
|
||||||
onClick={handleMediaClick}
|
onClick={handlePhotoMediaClick}
|
||||||
onCancelUpload={handleCancelUpload}
|
onCancelUpload={handleCancelUpload}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -1131,7 +1133,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
isDownloading={isDownloading}
|
isDownloading={isDownloading}
|
||||||
isProtected={isProtected}
|
isProtected={isProtected}
|
||||||
asForwarded={asForwarded}
|
asForwarded={asForwarded}
|
||||||
onClick={handleMediaClick}
|
onClick={handleVideoMediaClick}
|
||||||
onCancelUpload={handleCancelUpload}
|
onCancelUpload={handleCancelUpload}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export type OwnProps = {
|
|||||||
asForwarded?: boolean;
|
asForwarded?: boolean;
|
||||||
isDownloading?: boolean;
|
isDownloading?: boolean;
|
||||||
isProtected?: boolean;
|
isProtected?: boolean;
|
||||||
onClick?: (id: number) => void;
|
onClick?: (id: number, isGif?: boolean) => void;
|
||||||
onCancelUpload?: (message: ApiMessage) => void;
|
onCancelUpload?: (message: ApiMessage) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ const Video: FC<OwnProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick?.(message.id);
|
onClick?.(message.id, video?.isGif);
|
||||||
});
|
});
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default function useInnerHandlers(
|
|||||||
const {
|
const {
|
||||||
openChat, showNotification, focusMessage, openMediaViewer, openAudioPlayer,
|
openChat, showNotification, focusMessage, openMediaViewer, openAudioPlayer,
|
||||||
markMessagesRead, cancelUploadMedia, sendPollVote, openForwardMenu,
|
markMessagesRead, cancelUploadMedia, sendPollVote, openForwardMenu,
|
||||||
openChatLanguageModal, openThread, openStoryViewer,
|
openChatLanguageModal, openThread, openStoryViewer, searchChatMediaMessages,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -102,17 +102,39 @@ export default function useInnerHandlers(
|
|||||||
origin: isScheduled ? MediaViewerOrigin.ScheduledInline : MediaViewerOrigin.Inline,
|
origin: isScheduled ? MediaViewerOrigin.ScheduledInline : MediaViewerOrigin.Inline,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
const openMediaViewerWithPhotoOrVideo = useLastCallback((withDynamicLoading: boolean): void => {
|
||||||
|
if (withDynamicLoading) {
|
||||||
|
searchChatMediaMessages({ chatId, threadId, currentMediaMessageId: messageId });
|
||||||
|
}
|
||||||
|
openMediaViewer({
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
mediaId: messageId,
|
||||||
|
origin: isScheduled ? MediaViewerOrigin.ScheduledInline : MediaViewerOrigin.Inline,
|
||||||
|
withDynamicLoading,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const handlePhotoMediaClick = useLastCallback((): void => {
|
||||||
|
const withDynamicLoading = !isScheduled;
|
||||||
|
openMediaViewerWithPhotoOrVideo(withDynamicLoading);
|
||||||
|
});
|
||||||
|
const handleVideoMediaClick = useLastCallback((id: number, isGif?: boolean): void => {
|
||||||
|
const withDynamicLoading = !isGif && !isScheduled;
|
||||||
|
openMediaViewerWithPhotoOrVideo(withDynamicLoading);
|
||||||
|
});
|
||||||
|
|
||||||
const handleAudioPlay = useLastCallback((): void => {
|
const handleAudioPlay = useLastCallback((): void => {
|
||||||
openAudioPlayer({ chatId, messageId });
|
openAudioPlayer({ chatId, messageId });
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleAlbumMediaClick = useLastCallback((albumMessageId: number): void => {
|
const handleAlbumMediaClick = useLastCallback((albumMessageId: number): void => {
|
||||||
|
searchChatMediaMessages({ chatId, threadId, currentMediaMessageId: messageId });
|
||||||
openMediaViewer({
|
openMediaViewer({
|
||||||
chatId,
|
chatId,
|
||||||
threadId,
|
threadId,
|
||||||
mediaId: albumMessageId,
|
mediaId: albumMessageId,
|
||||||
origin: isScheduled ? MediaViewerOrigin.ScheduledAlbum : MediaViewerOrigin.Album,
|
origin: isScheduled ? MediaViewerOrigin.ScheduledAlbum : MediaViewerOrigin.Album,
|
||||||
|
withDynamicLoading: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -213,6 +235,8 @@ export default function useInnerHandlers(
|
|||||||
handleMediaClick,
|
handleMediaClick,
|
||||||
handleAudioPlay,
|
handleAudioPlay,
|
||||||
handleAlbumMediaClick,
|
handleAlbumMediaClick,
|
||||||
|
handlePhotoMediaClick,
|
||||||
|
handleVideoMediaClick,
|
||||||
handleMetaClick: selectWithGroupedId,
|
handleMetaClick: selectWithGroupedId,
|
||||||
handleTranslationClick,
|
handleTranslationClick,
|
||||||
handleOpenThread,
|
handleOpenThread,
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import {
|
|||||||
selectChat,
|
selectChat,
|
||||||
selectChatFullInfo,
|
selectChatFullInfo,
|
||||||
selectChatMessages,
|
selectChatMessages,
|
||||||
selectCurrentMediaSearch,
|
selectCurrentSharedMediaSearch,
|
||||||
selectIsCurrentUserPremium,
|
selectIsCurrentUserPremium,
|
||||||
selectIsRightColumnShown,
|
selectIsRightColumnShown,
|
||||||
selectPeerFullInfo,
|
selectPeerFullInfo,
|
||||||
@ -186,11 +186,11 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
forceScrollProfileTab,
|
forceScrollProfileTab,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
setLocalMediaSearchType,
|
setSharedMediaSearchType,
|
||||||
loadMoreMembers,
|
loadMoreMembers,
|
||||||
loadCommonChats,
|
loadCommonChats,
|
||||||
openChat,
|
openChat,
|
||||||
searchMediaMessagesLocal,
|
searchSharedMediaMessages,
|
||||||
openMediaViewer,
|
openMediaViewer,
|
||||||
openAudioPlayer,
|
openAudioPlayer,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
@ -275,7 +275,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds(
|
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds(
|
||||||
loadMoreMembers,
|
loadMoreMembers,
|
||||||
loadCommonChats,
|
loadCommonChats,
|
||||||
searchMediaMessagesLocal,
|
searchSharedMediaMessages,
|
||||||
handleLoadPeerStories,
|
handleLoadPeerStories,
|
||||||
handleLoadStoriesArchive,
|
handleLoadStoriesArchive,
|
||||||
tabType,
|
tabType,
|
||||||
@ -329,8 +329,8 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
// Update search type when switching tabs or forum topics
|
// Update search type when switching tabs or forum topics
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalMediaSearchType({ mediaType: tabType as SharedMediaType });
|
setSharedMediaSearchType({ mediaType: tabType as SharedMediaType });
|
||||||
}, [setLocalMediaSearchType, tabType, threadId]);
|
}, [setSharedMediaSearchType, tabType, threadId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadProfilePhotos({ profileId });
|
loadProfilePhotos({ profileId });
|
||||||
@ -683,7 +683,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
const chatFullInfo = selectChatFullInfo(global, chatId);
|
const chatFullInfo = selectChatFullInfo(global, chatId);
|
||||||
const messagesById = selectChatMessages(global, chatId);
|
const messagesById = selectChatMessages(global, chatId);
|
||||||
const { currentType: mediaSearchType, resultsByType } = selectCurrentMediaSearch(global) || {};
|
const { currentType: mediaSearchType, resultsByType } = selectCurrentSharedMediaSearch(global) || {};
|
||||||
const { foundIds } = (resultsByType && mediaSearchType && resultsByType[mediaSearchType]) || {};
|
const { foundIds } = (resultsByType && mediaSearchType && resultsByType[mediaSearchType]) || {};
|
||||||
|
|
||||||
const isTopicInfo = Boolean(chat?.isForum && threadId && threadId !== MAIN_THREAD_ID);
|
const isTopicInfo = Boolean(chat?.isForum && threadId && threadId !== MAIN_THREAD_ID);
|
||||||
|
|||||||
@ -72,6 +72,7 @@ export const TOPIC_HEIGHT_PX = 65;
|
|||||||
export const CHAT_LIST_SLICE = isBigScreen ? 30 : 25;
|
export const CHAT_LIST_SLICE = isBigScreen ? 30 : 25;
|
||||||
export const CHAT_LIST_LOAD_SLICE = 100;
|
export const CHAT_LIST_LOAD_SLICE = 100;
|
||||||
export const SHARED_MEDIA_SLICE = 42;
|
export const SHARED_MEDIA_SLICE = 42;
|
||||||
|
export const CHAT_MEDIA_SLICE = 42;
|
||||||
export const MESSAGE_SEARCH_SLICE = 42;
|
export const MESSAGE_SEARCH_SLICE = 42;
|
||||||
export const GLOBAL_SEARCH_SLICE = 20;
|
export const GLOBAL_SEARCH_SLICE = 20;
|
||||||
export const GLOBAL_TOPIC_SEARCH_SLICE = 5;
|
export const GLOBAL_TOPIC_SEARCH_SLICE = 5;
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
import type { ApiChat } from '../../../api/types';
|
import type { ApiChat } from '../../../api/types';
|
||||||
import type { SharedMediaType, ThreadId } from '../../../types';
|
import type {
|
||||||
|
ChatMediaSearchParams, ChatMediaSearchSegment, LoadingState, SharedMediaType, ThreadId,
|
||||||
|
} from '../../../types';
|
||||||
import type { ActionReturnType, GlobalState, TabArgs } from '../../types';
|
import type { ActionReturnType, GlobalState, TabArgs } from '../../types';
|
||||||
|
import { LoadMoreDirection } from '../../../types';
|
||||||
|
|
||||||
import { MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
|
import {
|
||||||
|
CHAT_MEDIA_SLICE, MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE,
|
||||||
|
} from '../../../config';
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey, isInsideSortedArrayRange } from '../../../util/iteratees';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import { getIsSavedDialog, isSameReaction } from '../../helpers';
|
import { getChatMediaMessageIds, getIsSavedDialog, isSameReaction } from '../../helpers';
|
||||||
import {
|
import {
|
||||||
addActionHandler, getGlobal, setGlobal,
|
addActionHandler, getGlobal, setGlobal,
|
||||||
} from '../../index';
|
} from '../../index';
|
||||||
@ -14,16 +19,23 @@ import {
|
|||||||
addChatMessagesById,
|
addChatMessagesById,
|
||||||
addChats,
|
addChats,
|
||||||
addUsers,
|
addUsers,
|
||||||
updateLocalMediaSearchResults,
|
initializeChatMediaSearchResults,
|
||||||
|
mergeWithChatMediaSearchSegment,
|
||||||
|
setChatMediaSearchLoading,
|
||||||
|
updateChatMediaSearchResults,
|
||||||
updateLocalTextSearchResults,
|
updateLocalTextSearchResults,
|
||||||
|
updateSharedMediaSearchResults,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import {
|
import {
|
||||||
selectChat,
|
selectChat,
|
||||||
selectCurrentMediaSearch,
|
selectCurrentChatMediaSearch,
|
||||||
selectCurrentMessageList,
|
selectCurrentMessageList,
|
||||||
|
selectCurrentSharedMediaSearch,
|
||||||
selectCurrentTextSearch,
|
selectCurrentTextSearch,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
|
|
||||||
|
const MEDIA_PRELOAD_OFFSET = 9;
|
||||||
|
|
||||||
addActionHandler('searchTextMessagesLocal', async (global, actions, payload): Promise<void> => {
|
addActionHandler('searchTextMessagesLocal', async (global, actions, payload): Promise<void> => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
||||||
@ -86,7 +98,7 @@ addActionHandler('searchTextMessagesLocal', async (global, actions, payload): Pr
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('searchMediaMessagesLocal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('searchSharedMediaMessages', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
||||||
if (!chatId || !threadId) {
|
if (!chatId || !threadId) {
|
||||||
@ -97,7 +109,7 @@ addActionHandler('searchMediaMessagesLocal', (global, actions, payload): ActionR
|
|||||||
const realChatId = isSavedDialog ? String(threadId) : chatId;
|
const realChatId = isSavedDialog ? String(threadId) : chatId;
|
||||||
|
|
||||||
const chat = selectChat(global, realChatId);
|
const chat = selectChat(global, realChatId);
|
||||||
const currentSearch = selectCurrentMediaSearch(global, tabId);
|
const currentSearch = selectCurrentSharedMediaSearch(global, tabId);
|
||||||
|
|
||||||
if (!chat || !currentSearch) {
|
if (!chat || !currentSearch) {
|
||||||
return;
|
return;
|
||||||
@ -113,6 +125,42 @@ addActionHandler('searchMediaMessagesLocal', (global, actions, payload): ActionR
|
|||||||
|
|
||||||
void searchSharedMedia(global, chat, threadId, type, offsetId, undefined, isSavedDialog, tabId);
|
void searchSharedMedia(global, chat, threadId, type, offsetId, undefined, isSavedDialog, tabId);
|
||||||
});
|
});
|
||||||
|
addActionHandler('searchChatMediaMessages', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
chatId, threadId, currentMediaMessageId, limit, direction, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
if (!chatId || !threadId || !currentMediaMessageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||||
|
const realChatId = isSavedDialog ? String(threadId) : chatId;
|
||||||
|
|
||||||
|
const chat = selectChat(global, realChatId);
|
||||||
|
if (!chat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let currentSearch = selectCurrentChatMediaSearch(global, tabId);
|
||||||
|
|
||||||
|
if (!currentSearch) {
|
||||||
|
global = initializeChatMediaSearchResults(global, chatId, threadId, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
currentSearch = selectCurrentChatMediaSearch(global, tabId);
|
||||||
|
if (!currentSearch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void searchChatMedia(global,
|
||||||
|
chat,
|
||||||
|
threadId,
|
||||||
|
currentMediaMessageId,
|
||||||
|
currentSearch,
|
||||||
|
direction,
|
||||||
|
isSavedDialog,
|
||||||
|
limit,
|
||||||
|
tabId);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('searchMessagesByDate', async (global, actions, payload): Promise<void> => {
|
addActionHandler('searchMessagesByDate', async (global, actions, payload): Promise<void> => {
|
||||||
const { timestamp, tabId = getCurrentTabId() } = payload;
|
const { timestamp, tabId = getCurrentTabId() } = payload;
|
||||||
@ -177,7 +225,7 @@ async function searchSharedMedia<T extends GlobalState>(
|
|||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
|
|
||||||
const currentSearch = selectCurrentMediaSearch(global, tabId);
|
const currentSearch = selectCurrentSharedMediaSearch(global, tabId);
|
||||||
if (!currentSearch) {
|
if (!currentSearch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -185,7 +233,7 @@ async function searchSharedMedia<T extends GlobalState>(
|
|||||||
global = addChats(global, buildCollectionByKey(chats, 'id'));
|
global = addChats(global, buildCollectionByKey(chats, 'id'));
|
||||||
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
||||||
global = addChatMessagesById(global, resultChatId, byId);
|
global = addChatMessagesById(global, resultChatId, byId);
|
||||||
global = updateLocalMediaSearchResults(
|
global = updateSharedMediaSearchResults(
|
||||||
global, resultChatId, threadId, type, newFoundIds, totalCount, nextOffsetId, tabId,
|
global, resultChatId, threadId, type, newFoundIds, totalCount, nextOffsetId, tabId,
|
||||||
);
|
);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
@ -194,3 +242,172 @@ async function searchSharedMedia<T extends GlobalState>(
|
|||||||
void searchSharedMedia(global, chat, threadId, type, nextOffsetId, true, isSavedDialog, tabId);
|
void searchSharedMedia(global, chat, threadId, type, nextOffsetId, true, isSavedDialog, tabId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectCurrentChatMediaSearchSegment(
|
||||||
|
params: ChatMediaSearchParams,
|
||||||
|
currentMediaMessageId: number,
|
||||||
|
): ChatMediaSearchSegment | undefined {
|
||||||
|
if (isInsideSortedArrayRange(currentMediaMessageId, params.currentSegment.foundIds)) {
|
||||||
|
return params.currentSegment;
|
||||||
|
}
|
||||||
|
const index = params.segments.findIndex(
|
||||||
|
(segment) => isInsideSortedArrayRange(currentMediaMessageId, segment.foundIds),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
if (params.currentSegment && params.currentSegment.foundIds.length) {
|
||||||
|
params.segments.push(params.currentSegment);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const result = params.segments.splice(index, 1)[0];
|
||||||
|
params.segments.push(params.currentSegment);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcChatMediaSearchAddOffset(
|
||||||
|
direction: LoadMoreDirection,
|
||||||
|
limit: number,
|
||||||
|
): number {
|
||||||
|
if (direction === LoadMoreDirection.Backwards) return 0;
|
||||||
|
if (direction === LoadMoreDirection.Forwards) return -(limit + 1);
|
||||||
|
return -(Math.round(limit / 2) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcChatMediaSearchOffsetId(
|
||||||
|
direction: LoadMoreDirection,
|
||||||
|
currentMessageId: number,
|
||||||
|
segment?: ChatMediaSearchSegment,
|
||||||
|
) : number {
|
||||||
|
if (!segment) return currentMessageId;
|
||||||
|
if (direction === LoadMoreDirection.Backwards) return segment.foundIds[0];
|
||||||
|
if (direction === LoadMoreDirection.Forwards) return segment.foundIds[segment.foundIds.length - 1];
|
||||||
|
return currentMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcLoadMoreDirection(currentMessageId: number, currentSegment?: ChatMediaSearchSegment) {
|
||||||
|
if (!currentSegment) return LoadMoreDirection.Around;
|
||||||
|
const currentSegmentFoundIdsCount = currentSegment.foundIds.length;
|
||||||
|
|
||||||
|
const idIndexInSegment = currentSegment.foundIds.indexOf(currentMessageId);
|
||||||
|
if (idIndexInSegment === -1) return LoadMoreDirection.Around;
|
||||||
|
|
||||||
|
if (currentSegment.loadingState.areAllItemsLoadedBackwards
|
||||||
|
&& currentSegment.loadingState.areAllItemsLoadedForwards) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const halfMediaCount = Math.floor(currentSegmentFoundIdsCount / 2);
|
||||||
|
|
||||||
|
const preloadOffset = MEDIA_PRELOAD_OFFSET > halfMediaCount ? 0 : MEDIA_PRELOAD_OFFSET;
|
||||||
|
const lastMediaIndex = currentSegmentFoundIdsCount - 1;
|
||||||
|
|
||||||
|
if (idIndexInSegment <= preloadOffset) {
|
||||||
|
if (currentSegment.loadingState.areAllItemsLoadedBackwards) return undefined;
|
||||||
|
return LoadMoreDirection.Backwards;
|
||||||
|
}
|
||||||
|
if (idIndexInSegment >= lastMediaIndex - preloadOffset) {
|
||||||
|
if (currentSegment.loadingState.areAllItemsLoadedForwards) return undefined;
|
||||||
|
return LoadMoreDirection.Forwards;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcLoadingState(
|
||||||
|
direction : LoadMoreDirection,
|
||||||
|
limit : number, newFoundIdsCount : number,
|
||||||
|
currentSegment?: ChatMediaSearchSegment,
|
||||||
|
) : LoadingState {
|
||||||
|
let areAllItemsLoadedForwards = Boolean(currentSegment?.loadingState.areAllItemsLoadedForwards);
|
||||||
|
let areAllItemsLoadedBackwards = Boolean(currentSegment?.loadingState.areAllItemsLoadedBackwards);
|
||||||
|
|
||||||
|
if (newFoundIdsCount < limit) {
|
||||||
|
if (direction === LoadMoreDirection.Forwards) {
|
||||||
|
areAllItemsLoadedForwards = true;
|
||||||
|
} else if (direction === LoadMoreDirection.Backwards) {
|
||||||
|
areAllItemsLoadedBackwards = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
areAllItemsLoadedForwards,
|
||||||
|
areAllItemsLoadedBackwards,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchChatMedia<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chat: ApiChat,
|
||||||
|
threadId: ThreadId,
|
||||||
|
currentMediaMessageId: number,
|
||||||
|
chatMediaSearchParams: ChatMediaSearchParams,
|
||||||
|
direction?: LoadMoreDirection,
|
||||||
|
isSavedDialog?: boolean,
|
||||||
|
limit = CHAT_MEDIA_SLICE,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
) {
|
||||||
|
const { isSynced } = global;
|
||||||
|
if (!isSynced || chatMediaSearchParams.isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let currentSegment = selectCurrentChatMediaSearchSegment(chatMediaSearchParams, currentMediaMessageId);
|
||||||
|
|
||||||
|
if (direction === undefined) {
|
||||||
|
direction = calcLoadMoreDirection(currentMediaMessageId, currentSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsetId = calcChatMediaSearchOffsetId(direction, currentMediaMessageId, currentSegment);
|
||||||
|
const addOffset = calcChatMediaSearchAddOffset(direction, limit);
|
||||||
|
|
||||||
|
const resultChatId = isSavedDialog ? global.currentUserId! : chat.id;
|
||||||
|
|
||||||
|
global = setChatMediaSearchLoading(global, resultChatId, threadId, true, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
const result = await callApi('searchMessagesLocal', {
|
||||||
|
chat,
|
||||||
|
type: 'media',
|
||||||
|
limit,
|
||||||
|
threadId,
|
||||||
|
offsetId,
|
||||||
|
isSavedDialog,
|
||||||
|
addOffset,
|
||||||
|
});
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
global = setChatMediaSearchLoading(global, resultChatId, threadId, false, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
chats, users, messages,
|
||||||
|
} = result;
|
||||||
|
|
||||||
|
const byId = buildCollectionByKey(messages, 'id');
|
||||||
|
const newFoundIds = Object.keys(byId).map(Number);
|
||||||
|
|
||||||
|
global = addChats(global, buildCollectionByKey(chats, 'id'));
|
||||||
|
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
||||||
|
global = addChatMessagesById(global, resultChatId, byId);
|
||||||
|
|
||||||
|
const loadingState = calcLoadingState(direction, limit, newFoundIds.length, currentSegment);
|
||||||
|
|
||||||
|
const filteredIds = getChatMediaMessageIds(byId, newFoundIds, false);
|
||||||
|
currentSegment = mergeWithChatMediaSearchSegment(
|
||||||
|
filteredIds,
|
||||||
|
loadingState,
|
||||||
|
currentSegment,
|
||||||
|
);
|
||||||
|
|
||||||
|
global = updateChatMediaSearchResults(
|
||||||
|
global, resultChatId, threadId, currentSegment, chatMediaSearchParams, tabId,
|
||||||
|
);
|
||||||
|
global = setChatMediaSearchLoading(global, resultChatId, threadId, false, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import {
|
|||||||
replaceThreadParam,
|
replaceThreadParam,
|
||||||
updateChat,
|
updateChat,
|
||||||
updateChatLastMessageId,
|
updateChatLastMessageId,
|
||||||
|
updateChatMediaLoadingState,
|
||||||
updateChatMessage,
|
updateChatMessage,
|
||||||
updateListedIds,
|
updateListedIds,
|
||||||
updateMessageTranslations,
|
updateMessageTranslations,
|
||||||
@ -109,6 +110,9 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
global = updateChatLastMessage(global, chatId, newMessage);
|
global = updateChatLastMessage(global, chatId, newMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const threadId = selectThreadIdFromMessage(global, newMessage);
|
||||||
|
global = updateChatMediaLoadingState(global, newMessage, chatId, threadId, tabId);
|
||||||
|
|
||||||
if (selectIsMessageInCurrentMessageList(global, chatId, message as ApiMessage, tabId)) {
|
if (selectIsMessageInCurrentMessageList(global, chatId, message as ApiMessage, tabId)) {
|
||||||
if (isLocal && message.isOutgoing && !(message.content?.action) && !storyReplyInfo?.storyId
|
if (isLocal && message.isOutgoing && !(message.content?.action) && !storyReplyInfo?.storyId
|
||||||
&& !message.content?.storyData) {
|
&& !message.content?.storyData) {
|
||||||
|
|||||||
@ -6,9 +6,9 @@ import { buildChatThreadKey, isSameReaction } from '../../helpers';
|
|||||||
import { addActionHandler } from '../../index';
|
import { addActionHandler } from '../../index';
|
||||||
import {
|
import {
|
||||||
replaceLocalTextSearchResults,
|
replaceLocalTextSearchResults,
|
||||||
updateLocalMediaSearchType,
|
|
||||||
updateLocalTextSearch,
|
updateLocalTextSearch,
|
||||||
updateLocalTextSearchTag,
|
updateLocalTextSearchTag,
|
||||||
|
updateSharedMediaSearchType,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import { selectCurrentMessageList, selectTabState } from '../../selectors';
|
import { selectCurrentMessageList, selectTabState } from '../../selectors';
|
||||||
|
|
||||||
@ -67,14 +67,14 @@ addActionHandler('setLocalTextSearchTag', (global, actions, payload): ActionRetu
|
|||||||
return global;
|
return global;
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('setLocalMediaSearchType', (global, actions, payload): ActionReturnType => {
|
addActionHandler('setSharedMediaSearchType', (global, actions, payload): ActionReturnType => {
|
||||||
const { mediaType, tabId = getCurrentTabId() } = payload;
|
const { mediaType, tabId = getCurrentTabId() } = payload;
|
||||||
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
||||||
if (!chatId || !threadId) {
|
if (!chatId || !threadId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateLocalMediaSearchType(global, chatId, threadId, mediaType, tabId);
|
return updateSharedMediaSearchType(global, chatId, threadId, mediaType, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
export function closeLocalTextSearch<T extends GlobalState>(
|
export function closeLocalTextSearch<T extends GlobalState>(
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { selectTabState } from '../../selectors';
|
|||||||
addActionHandler('openMediaViewer', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openMediaViewer', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, threadId, mediaId, avatarOwnerId, profilePhotoIndex, origin, volume, playbackRate, isMuted,
|
chatId, threadId, mediaId, avatarOwnerId, profilePhotoIndex, origin, volume, playbackRate, isMuted,
|
||||||
tabId = getCurrentTabId(),
|
withDynamicLoading, tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
const tabState = selectTabState(global, tabId);
|
const tabState = selectTabState(global, tabId);
|
||||||
@ -32,6 +32,7 @@ addActionHandler('openMediaViewer', (global, actions, payload): ActionReturnType
|
|||||||
|| DEFAULT_PLAYBACK_RATE
|
|| DEFAULT_PLAYBACK_RATE
|
||||||
),
|
),
|
||||||
isMuted: isMuted || tabState.mediaViewer.isMuted,
|
isMuted: isMuted || tabState.mediaViewer.isMuted,
|
||||||
|
withDynamicLoading,
|
||||||
},
|
},
|
||||||
forwardMessages: {},
|
forwardMessages: {},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
|
|||||||
@ -545,3 +545,10 @@ export function canReplaceMessageMedia(message: ApiMessage, attachment: ApiAttac
|
|||||||
|| (isFile && (fileType === 'audio' || fileType === 'file'))
|
|| (isFile && (fileType === 'audio' || fileType === 'file'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isMediaLoadableInViewer(newMessage: ApiMessage) {
|
||||||
|
if (!newMessage.content) return false;
|
||||||
|
if (newMessage.content.photo) return true;
|
||||||
|
if (newMessage.content.video && !newMessage.content.video.isRound && !newMessage.content.video.isGif) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { parseLocationHash } from '../util/routing';
|
|||||||
import { clearStoredSession } from '../util/sessions';
|
import { clearStoredSession } from '../util/sessions';
|
||||||
import { updatePeerColors } from '../util/theme';
|
import { updatePeerColors } from '../util/theme';
|
||||||
import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment';
|
import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment';
|
||||||
|
import { initializeChatMediaSearchResults } from './reducers/localSearch';
|
||||||
import { updateTabState } from './reducers/tabs';
|
import { updateTabState } from './reducers/tabs';
|
||||||
import { initCache, loadCache } from './cache';
|
import { initCache, loadCache } from './cache';
|
||||||
import {
|
import {
|
||||||
@ -79,6 +80,7 @@ addActionHandler('init', (global, actions, payload): ActionReturnType => {
|
|||||||
global = replaceThreadParam(global, chatId, threadId, 'lastViewportIds', undefined);
|
global = replaceThreadParam(global, chatId, threadId, 'lastViewportIds', undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
global = initializeChatMediaSearchResults(global, chatId, threadId, tabId);
|
||||||
global = replaceTabThreadParam(
|
global = replaceTabThreadParam(
|
||||||
global,
|
global,
|
||||||
chatId,
|
chatId,
|
||||||
|
|||||||
@ -313,7 +313,11 @@ export const INITIAL_TAB_STATE: TabState = {
|
|||||||
byChatThreadKey: {},
|
byChatThreadKey: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
localMediaSearch: {
|
sharedMediaSearch: {
|
||||||
|
byChatThreadKey: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
chatMediaSearch: {
|
||||||
byChatThreadKey: {},
|
byChatThreadKey: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
import type { ApiMessageSearchType, ApiReaction } from '../../api/types';
|
import type { ApiMessage, ApiMessageSearchType, ApiReaction } from '../../api/types';
|
||||||
import type { SharedMediaType, ThreadId } from '../../types';
|
import type {
|
||||||
|
ChatMediaSearchParams, ChatMediaSearchSegment, LoadingState,
|
||||||
|
SharedMediaType, ThreadId,
|
||||||
|
} from '../../types';
|
||||||
import type { GlobalState, TabArgs } from '../types';
|
import type { GlobalState, TabArgs } from '../types';
|
||||||
|
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
import { areSortedArraysEqual, unique } from '../../util/iteratees';
|
import { areSortedArraysEqual, areSortedArraysIntersecting, unique } from '../../util/iteratees';
|
||||||
import { buildChatThreadKey } from '../helpers';
|
import { buildChatThreadKey, isMediaLoadableInViewer } from '../helpers';
|
||||||
import { selectTabState } from '../selectors';
|
import { selectTabState } from '../selectors';
|
||||||
|
import { selectChatMediaSearch } from '../selectors/localSearch';
|
||||||
import { updateTabState } from './tabs';
|
import { updateTabState } from './tabs';
|
||||||
|
|
||||||
interface TextSearchParams {
|
interface TextSearchParams {
|
||||||
@ -18,7 +22,7 @@ interface TextSearchParams {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MediaSearchParams {
|
interface SharedMediaSearchParams {
|
||||||
currentType?: SharedMediaType;
|
currentType?: SharedMediaType;
|
||||||
resultsByType?: Partial<Record<SharedMediaType, {
|
resultsByType?: Partial<Record<SharedMediaType, {
|
||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
@ -110,32 +114,32 @@ export function updateLocalTextSearchResults<T extends GlobalState>(
|
|||||||
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
const { results } = selectTabState(global, tabId).localTextSearch.byChatThreadKey[chatThreadKey] || {};
|
const { results } = selectTabState(global, tabId).localTextSearch.byChatThreadKey[chatThreadKey] || {};
|
||||||
const prevFoundIds = (results?.foundIds) || [];
|
const prevFoundIds = (results?.foundIds) || [];
|
||||||
const foundIds = orderFoundIds(unique(Array.prototype.concat(prevFoundIds, newFoundIds)));
|
const foundIds = orderFoundIdsByDescending(unique(Array.prototype.concat(prevFoundIds, newFoundIds)));
|
||||||
const foundOrPrevFoundIds = areSortedArraysEqual(prevFoundIds, foundIds) ? prevFoundIds : foundIds;
|
const foundOrPrevFoundIds = areSortedArraysEqual(prevFoundIds, foundIds) ? prevFoundIds : foundIds;
|
||||||
|
|
||||||
return replaceLocalTextSearchResults(global, chatId, threadId, foundOrPrevFoundIds, totalCount, nextOffsetId, tabId);
|
return replaceLocalTextSearchResults(global, chatId, threadId, foundOrPrevFoundIds, totalCount, nextOffsetId, tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceLocalMediaSearch<T extends GlobalState>(
|
function replaceSharedMediaSearch<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
searchParams: MediaSearchParams,
|
searchParams: SharedMediaSearchParams,
|
||||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
): T {
|
): T {
|
||||||
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
return updateTabState(global, {
|
return updateTabState(global, {
|
||||||
localMediaSearch: {
|
sharedMediaSearch: {
|
||||||
byChatThreadKey: {
|
byChatThreadKey: {
|
||||||
...selectTabState(global, tabId).localMediaSearch.byChatThreadKey,
|
...selectTabState(global, tabId).sharedMediaSearch.byChatThreadKey,
|
||||||
[chatThreadKey]: searchParams,
|
[chatThreadKey]: searchParams,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateLocalMediaSearchType<T extends GlobalState>(
|
export function updateSharedMediaSearchType<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
@ -144,13 +148,13 @@ export function updateLocalMediaSearchType<T extends GlobalState>(
|
|||||||
): T {
|
): T {
|
||||||
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
return replaceLocalMediaSearch(global, chatId, threadId, {
|
return replaceSharedMediaSearch(global, chatId, threadId, {
|
||||||
...selectTabState(global, tabId).localMediaSearch.byChatThreadKey[chatThreadKey],
|
...selectTabState(global, tabId).sharedMediaSearch.byChatThreadKey[chatThreadKey],
|
||||||
currentType,
|
currentType,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceLocalMediaSearchResults<T extends GlobalState>(
|
export function replaceSharedMediaSearchResults<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
@ -162,10 +166,10 @@ export function replaceLocalMediaSearchResults<T extends GlobalState>(
|
|||||||
): T {
|
): T {
|
||||||
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
return replaceLocalMediaSearch(global, chatId, threadId, {
|
return replaceSharedMediaSearch(global, chatId, threadId, {
|
||||||
...selectTabState(global, tabId).localMediaSearch.byChatThreadKey[chatThreadKey],
|
...selectTabState(global, tabId).sharedMediaSearch.byChatThreadKey[chatThreadKey],
|
||||||
resultsByType: {
|
resultsByType: {
|
||||||
...(selectTabState(global, tabId).localMediaSearch.byChatThreadKey[chatThreadKey] || {}).resultsByType,
|
...(selectTabState(global, tabId).sharedMediaSearch.byChatThreadKey[chatThreadKey] || {}).resultsByType,
|
||||||
[type]: {
|
[type]: {
|
||||||
foundIds,
|
foundIds,
|
||||||
totalCount,
|
totalCount,
|
||||||
@ -175,7 +179,7 @@ export function replaceLocalMediaSearchResults<T extends GlobalState>(
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateLocalMediaSearchResults<T extends GlobalState>(
|
export function updateSharedMediaSearchResults<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
threadId: ThreadId,
|
threadId: ThreadId,
|
||||||
@ -187,12 +191,12 @@ export function updateLocalMediaSearchResults<T extends GlobalState>(
|
|||||||
): T {
|
): T {
|
||||||
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
const { resultsByType } = selectTabState(global, tabId).localMediaSearch.byChatThreadKey[chatThreadKey] || {};
|
const { resultsByType } = selectTabState(global, tabId).sharedMediaSearch.byChatThreadKey[chatThreadKey] || {};
|
||||||
const prevFoundIds = resultsByType?.[type] ? resultsByType[type]!.foundIds : [];
|
const prevFoundIds = resultsByType?.[type] ? resultsByType[type]!.foundIds : [];
|
||||||
const foundIds = orderFoundIds(unique(Array.prototype.concat(prevFoundIds, newFoundIds)));
|
const foundIds = orderFoundIdsByDescending(unique(Array.prototype.concat(prevFoundIds, newFoundIds)));
|
||||||
const foundOrPrevFoundIds = areSortedArraysEqual(prevFoundIds, foundIds) ? prevFoundIds : foundIds;
|
const foundOrPrevFoundIds = areSortedArraysEqual(prevFoundIds, foundIds) ? prevFoundIds : foundIds;
|
||||||
|
|
||||||
return replaceLocalMediaSearchResults(
|
return replaceSharedMediaSearchResults(
|
||||||
global,
|
global,
|
||||||
chatId,
|
chatId,
|
||||||
threadId,
|
threadId,
|
||||||
@ -204,6 +208,229 @@ export function updateLocalMediaSearchResults<T extends GlobalState>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function orderFoundIds(listedIds: number[]) {
|
function orderFoundIdsByDescending(listedIds: number[]) {
|
||||||
return listedIds.sort((a, b) => b - a);
|
return listedIds.sort((a, b) => b - a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function orderFoundIdsByAscending(array: number[]) {
|
||||||
|
return array.sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mergeWithChatMediaSearchSegment(
|
||||||
|
foundIds: number[],
|
||||||
|
loadingState: LoadingState,
|
||||||
|
segment?: ChatMediaSearchSegment,
|
||||||
|
)
|
||||||
|
: ChatMediaSearchSegment {
|
||||||
|
if (!segment) {
|
||||||
|
return {
|
||||||
|
foundIds,
|
||||||
|
loadingState,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const mergedFoundIds = orderFoundIdsByAscending(unique(Array.prototype.concat(segment.foundIds, foundIds)));
|
||||||
|
if (!areSortedArraysEqual(segment.foundIds, foundIds)) {
|
||||||
|
segment.foundIds = mergedFoundIds;
|
||||||
|
}
|
||||||
|
const mergedLoadingState : LoadingState = {
|
||||||
|
areAllItemsLoadedForwards: loadingState.areAllItemsLoadedForwards
|
||||||
|
|| segment.loadingState.areAllItemsLoadedForwards,
|
||||||
|
areAllItemsLoadedBackwards: loadingState.areAllItemsLoadedBackwards
|
||||||
|
|| segment.loadingState.areAllItemsLoadedBackwards,
|
||||||
|
};
|
||||||
|
segment.loadingState = mergedLoadingState;
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeChatMediaSearchSegments(currentSegment: ChatMediaSearchSegment, segments: ChatMediaSearchSegment[]) {
|
||||||
|
return segments.reduce((acc, segment) => {
|
||||||
|
const hasIntersection = areSortedArraysIntersecting(segment.foundIds, currentSegment.foundIds);
|
||||||
|
if (hasIntersection) {
|
||||||
|
currentSegment = mergeWithChatMediaSearchSegment(
|
||||||
|
currentSegment.foundIds,
|
||||||
|
currentSegment.loadingState,
|
||||||
|
segment,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
acc.push(segment);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, [] as ChatMediaSearchSegment[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateChatMediaSearchResults<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
currentSegment: ChatMediaSearchSegment,
|
||||||
|
searchParams: ChatMediaSearchParams,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
const segments = mergeChatMediaSearchSegments(currentSegment, searchParams.segments);
|
||||||
|
|
||||||
|
return replaceChatMediaSearchResults(
|
||||||
|
global,
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
currentSegment,
|
||||||
|
segments,
|
||||||
|
tabId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeIdFromSegment(id: number, segment: ChatMediaSearchSegment): ChatMediaSearchSegment {
|
||||||
|
const foundIds = segment.foundIds.filter((foundId) => foundId !== id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...segment,
|
||||||
|
foundIds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeIdsFromChatMediaSearchParams(
|
||||||
|
id: number,
|
||||||
|
searchParams: ChatMediaSearchParams,
|
||||||
|
): ChatMediaSearchParams {
|
||||||
|
const currentSegment = removeIdFromSegment(id, searchParams.currentSegment);
|
||||||
|
const segments = searchParams.segments.map((segment) => removeIdFromSegment(id, segment));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...searchParams,
|
||||||
|
currentSegment,
|
||||||
|
segments,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeIdFromSearchResults<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
id: number,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
const searchParams = selectChatMediaSearch(global, chatId, threadId, tabId);
|
||||||
|
if (!searchParams) return global;
|
||||||
|
|
||||||
|
const updatedSearchParams = removeIdsFromChatMediaSearchParams(id, searchParams);
|
||||||
|
|
||||||
|
return replaceChatMediaSearch(
|
||||||
|
global,
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
updatedSearchParams,
|
||||||
|
tabId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForwardsLoadingStateInParams(
|
||||||
|
searchParams: ChatMediaSearchParams,
|
||||||
|
) {
|
||||||
|
searchParams.currentSegment.loadingState.areAllItemsLoadedForwards = false;
|
||||||
|
searchParams.segments.forEach((segment) => {
|
||||||
|
segment.loadingState.areAllItemsLoadedForwards = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateChatMediaLoadingState<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
newMessage: ApiMessage,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
if (!isMediaLoadableInViewer(newMessage)) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
const searchParams = selectChatMediaSearch(global, chatId, threadId, tabId);
|
||||||
|
if (!searchParams) return global;
|
||||||
|
resetForwardsLoadingStateInParams(searchParams);
|
||||||
|
|
||||||
|
return replaceChatMediaSearch(
|
||||||
|
global,
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
searchParams,
|
||||||
|
tabId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initializeChatMediaSearchResults<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
const loadingState: LoadingState = {
|
||||||
|
areAllItemsLoadedForwards: false,
|
||||||
|
areAllItemsLoadedBackwards: false,
|
||||||
|
};
|
||||||
|
const currentSegment: ChatMediaSearchSegment = {
|
||||||
|
foundIds: [],
|
||||||
|
loadingState,
|
||||||
|
};
|
||||||
|
const segments: ChatMediaSearchSegment[] = [];
|
||||||
|
|
||||||
|
const isLoading = false;
|
||||||
|
|
||||||
|
return replaceChatMediaSearch(global, chatId, threadId, {
|
||||||
|
currentSegment,
|
||||||
|
segments,
|
||||||
|
isLoading,
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setChatMediaSearchLoading<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
isLoading: boolean,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
const searchParams = selectTabState(global, tabId).chatMediaSearch.byChatThreadKey[chatThreadKey];
|
||||||
|
|
||||||
|
if (!searchParams) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
return replaceChatMediaSearch(global, chatId, threadId, {
|
||||||
|
...searchParams,
|
||||||
|
isLoading,
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceChatMediaSearchResults<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
currentSegment: ChatMediaSearchSegment,
|
||||||
|
segments: ChatMediaSearchSegment[],
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
|
return replaceChatMediaSearch(global, chatId, threadId, {
|
||||||
|
...selectTabState(global, tabId).chatMediaSearch.byChatThreadKey[chatThreadKey],
|
||||||
|
currentSegment,
|
||||||
|
segments,
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceChatMediaSearch<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
chatId: string,
|
||||||
|
threadId: ThreadId,
|
||||||
|
searchParams: ChatMediaSearchParams,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
): T {
|
||||||
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
chatMediaSearch: {
|
||||||
|
byChatThreadKey: {
|
||||||
|
...selectTabState(global, tabId).chatMediaSearch.byChatThreadKey,
|
||||||
|
[chatThreadKey]: searchParams,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
|
|||||||
@ -16,7 +16,8 @@ import {
|
|||||||
} from '../../util/iteratees';
|
} from '../../util/iteratees';
|
||||||
import { isLocalMessageId, type MessageKey } from '../../util/messageKey';
|
import { isLocalMessageId, type MessageKey } from '../../util/messageKey';
|
||||||
import {
|
import {
|
||||||
hasMessageTtl, mergeIdRanges, orderHistoryIds, orderPinnedIds,
|
hasMessageTtl, isMediaLoadableInViewer,
|
||||||
|
mergeIdRanges, orderHistoryIds, orderPinnedIds,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import {
|
import {
|
||||||
selectChat,
|
selectChat,
|
||||||
@ -37,6 +38,7 @@ import {
|
|||||||
selectThreadInfo,
|
selectThreadInfo,
|
||||||
selectViewportIds,
|
selectViewportIds,
|
||||||
} from '../selectors';
|
} from '../selectors';
|
||||||
|
import { removeIdFromSearchResults } from './localSearch';
|
||||||
import { updateTabState } from './tabs';
|
import { updateTabState } from './tabs';
|
||||||
import { clearMessageTranslation } from './translations';
|
import { clearMessageTranslation } from './translations';
|
||||||
|
|
||||||
@ -296,9 +298,13 @@ export function deleteChatMessages<T extends GlobalState>(
|
|||||||
const updatedThreads = new Map<ThreadId, number[]>();
|
const updatedThreads = new Map<ThreadId, number[]>();
|
||||||
updatedThreads.set(MAIN_THREAD_ID, messageIds);
|
updatedThreads.set(MAIN_THREAD_ID, messageIds);
|
||||||
|
|
||||||
|
const mediaIdsToRemove: number[] = [];
|
||||||
messageIds.forEach((messageId) => {
|
messageIds.forEach((messageId) => {
|
||||||
const message = byId[messageId];
|
const message = byId[messageId];
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
|
if (isMediaLoadableInViewer(message)) {
|
||||||
|
mediaIdsToRemove.push(messageId);
|
||||||
|
}
|
||||||
const threadId = selectThreadIdFromMessage(global, message);
|
const threadId = selectThreadIdFromMessage(global, message);
|
||||||
if (!threadId || threadId === MAIN_THREAD_ID) {
|
if (!threadId || threadId === MAIN_THREAD_ID) {
|
||||||
return;
|
return;
|
||||||
@ -339,6 +345,10 @@ export function deleteChatMessages<T extends GlobalState>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||||
|
mediaIdsToRemove.forEach((mediaId) => {
|
||||||
|
global = removeIdFromSearchResults(global, chatId, threadId, mediaId, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
const viewportIds = selectViewportIds(global, chatId, threadId, tabId);
|
const viewportIds = selectViewportIds(global, chatId, threadId, tabId);
|
||||||
if (!viewportIds) return;
|
if (!viewportIds) return;
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { ThreadId } from '../../types';
|
||||||
import type { GlobalState, TabArgs } from '../types';
|
import type { GlobalState, TabArgs } from '../types';
|
||||||
|
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
@ -23,7 +24,7 @@ export function selectCurrentTextSearch<T extends GlobalState>(
|
|||||||
return currentSearch;
|
return currentSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectCurrentMediaSearch<T extends GlobalState>(
|
export function selectCurrentSharedMediaSearch<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
) {
|
) {
|
||||||
@ -34,5 +35,32 @@ export function selectCurrentMediaSearch<T extends GlobalState>(
|
|||||||
|
|
||||||
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
return selectTabState(global, tabId).localMediaSearch.byChatThreadKey[chatThreadKey];
|
return selectTabState(global, tabId).sharedMediaSearch.byChatThreadKey[chatThreadKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectCurrentChatMediaSearch<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
) {
|
||||||
|
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
||||||
|
if (!chatId || !threadId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
|
return selectTabState(global, tabId).chatMediaSearch.byChatThreadKey[chatThreadKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectChatMediaSearch<T extends GlobalState>(
|
||||||
|
global: T, chatId?: string, threadId?: ThreadId,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
) {
|
||||||
|
if (!chatId || !threadId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatThreadKey = buildChatThreadKey(chatId, threadId);
|
||||||
|
|
||||||
|
return selectTabState(global, tabId).chatMediaSearch.byChatThreadKey[chatThreadKey];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,6 +92,7 @@ import type {
|
|||||||
ApiPrivacySettings,
|
ApiPrivacySettings,
|
||||||
AudioOrigin,
|
AudioOrigin,
|
||||||
ChatCreationProgress,
|
ChatCreationProgress,
|
||||||
|
ChatMediaSearchParams,
|
||||||
EmojiKeywords,
|
EmojiKeywords,
|
||||||
FocusDirection,
|
FocusDirection,
|
||||||
GlobalSearchContent,
|
GlobalSearchContent,
|
||||||
@ -375,7 +376,7 @@ export type TabState = {
|
|||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
localMediaSearch: {
|
sharedMediaSearch: {
|
||||||
byChatThreadKey: Record<string, {
|
byChatThreadKey: Record<string, {
|
||||||
currentType?: SharedMediaType;
|
currentType?: SharedMediaType;
|
||||||
resultsByType?: Partial<Record<SharedMediaType, {
|
resultsByType?: Partial<Record<SharedMediaType, {
|
||||||
@ -386,6 +387,10 @@ export type TabState = {
|
|||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
chatMediaSearch: {
|
||||||
|
byChatThreadKey: Record<string, ChatMediaSearchParams>;
|
||||||
|
};
|
||||||
|
|
||||||
management: {
|
management: {
|
||||||
progress?: ManagementProgress;
|
progress?: ManagementProgress;
|
||||||
byChatId: Record<string, ManagementState>;
|
byChatId: Record<string, ManagementState>;
|
||||||
@ -431,6 +436,7 @@ export type TabState = {
|
|||||||
playbackRate: number;
|
playbackRate: number;
|
||||||
isMuted: boolean;
|
isMuted: boolean;
|
||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
|
withDynamicLoading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
audioPlayer: {
|
audioPlayer: {
|
||||||
@ -1280,11 +1286,18 @@ export interface ActionPayloads {
|
|||||||
setLocalTextSearchTag: {
|
setLocalTextSearchTag: {
|
||||||
tag: ApiReaction | undefined;
|
tag: ApiReaction | undefined;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
setLocalMediaSearchType: {
|
setSharedMediaSearchType: {
|
||||||
mediaType: SharedMediaType;
|
mediaType: SharedMediaType;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
searchTextMessagesLocal: WithTabId | undefined;
|
searchTextMessagesLocal: WithTabId | undefined;
|
||||||
searchMediaMessagesLocal: WithTabId | undefined;
|
searchSharedMediaMessages: WithTabId | undefined;
|
||||||
|
searchChatMediaMessages: {
|
||||||
|
currentMediaMessageId: number;
|
||||||
|
direction?: LoadMoreDirection;
|
||||||
|
chatId?: string;
|
||||||
|
threadId? : ThreadId;
|
||||||
|
limit?: number;
|
||||||
|
} & WithTabId;
|
||||||
searchMessagesByDate: {
|
searchMessagesByDate: {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
@ -2414,6 +2427,7 @@ export interface ActionPayloads {
|
|||||||
volume?: number;
|
volume?: number;
|
||||||
playbackRate?: number;
|
playbackRate?: number;
|
||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
|
withDynamicLoading?: boolean;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeMediaViewer: WithTabId | undefined;
|
closeMediaViewer: WithTabId | undefined;
|
||||||
setMediaViewerVolume: {
|
setMediaViewerVolume: {
|
||||||
|
|||||||
@ -388,6 +388,22 @@ export type ApiPrivacyKey = 'phoneNumber' | 'addByPhone' | 'lastSeen' | 'profile
|
|||||||
'forwards' | 'chatInvite' | 'phoneCall' | 'phoneP2P' | 'bio' | 'birthday';
|
'forwards' | 'chatInvite' | 'phoneCall' | 'phoneP2P' | 'bio' | 'birthday';
|
||||||
export type PrivacyVisibility = 'everybody' | 'contacts' | 'closeFriends' | 'nonContacts' | 'nobody';
|
export type PrivacyVisibility = 'everybody' | 'contacts' | 'closeFriends' | 'nonContacts' | 'nobody';
|
||||||
|
|
||||||
|
export interface LoadingState {
|
||||||
|
areAllItemsLoadedForwards: boolean;
|
||||||
|
areAllItemsLoadedBackwards: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatMediaSearchSegment {
|
||||||
|
foundIds: number[];
|
||||||
|
loadingState: LoadingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatMediaSearchParams {
|
||||||
|
currentSegment: ChatMediaSearchSegment;
|
||||||
|
segments: ChatMediaSearchSegment[];
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export enum ProfileState {
|
export enum ProfileState {
|
||||||
Profile,
|
Profile,
|
||||||
SharedMedia,
|
SharedMedia,
|
||||||
|
|||||||
@ -125,6 +125,9 @@ export function areSortedArraysEqual(array1: any[], array2: any[]) {
|
|||||||
export function areSortedArraysIntersecting(array1: any[], array2: any[]) {
|
export function areSortedArraysIntersecting(array1: any[], array2: any[]) {
|
||||||
return array1[0] <= array2[array2.length - 1] && array1[array1.length - 1] >= array2[0];
|
return array1[0] <= array2[array2.length - 1] && array1[array1.length - 1] >= array2[0];
|
||||||
}
|
}
|
||||||
|
export function isInsideSortedArrayRange(value:any, array: any[]) {
|
||||||
|
return array[0] <= value && value <= array[array.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
export function findIntersectionWithSet<T>(array: T[], set: Set<T>): T[] {
|
export function findIntersectionWithSet<T>(array: T[], set: Set<T>): T[] {
|
||||||
return array.filter((a) => set.has(a));
|
return array.filter((a) => set.has(a));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user