Stories: Fix flickering while switching stories (#4149)
This commit is contained in:
parent
5b821bd350
commit
b94999c204
@ -15,11 +15,13 @@ import { MAIN_THREAD_ID } from '../../api/types';
|
|||||||
import { EDITABLE_STORY_INPUT_CSS_SELECTOR, EDITABLE_STORY_INPUT_ID } from '../../config';
|
import { EDITABLE_STORY_INPUT_CSS_SELECTOR, EDITABLE_STORY_INPUT_ID } from '../../config';
|
||||||
import { getSenderTitle, isUserId } from '../../global/helpers';
|
import { getSenderTitle, isUserId } from '../../global/helpers';
|
||||||
import {
|
import {
|
||||||
selectChat, selectIsCurrentUserPremium,
|
selectChat,
|
||||||
|
selectIsCurrentUserPremium,
|
||||||
selectPeer,
|
selectPeer,
|
||||||
selectPeerStories, selectPeerStory,
|
selectPeerStory,
|
||||||
selectPerformanceSettingsValue,
|
selectPerformanceSettingsValue,
|
||||||
selectTabState, selectUser,
|
selectTabState,
|
||||||
|
selectUser,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
||||||
@ -864,8 +866,6 @@ function Story({
|
|||||||
export default memo(withGlobal<OwnProps>((global, {
|
export default memo(withGlobal<OwnProps>((global, {
|
||||||
peerId,
|
peerId,
|
||||||
storyId,
|
storyId,
|
||||||
isPrivateStories,
|
|
||||||
isArchivedStories,
|
|
||||||
isReportModalOpen,
|
isReportModalOpen,
|
||||||
isDeleteModalOpen,
|
isDeleteModalOpen,
|
||||||
}): StateProps => {
|
}): StateProps => {
|
||||||
@ -879,6 +879,7 @@ export default memo(withGlobal<OwnProps>((global, {
|
|||||||
viewModal,
|
viewModal,
|
||||||
isPrivacyModalOpen,
|
isPrivacyModalOpen,
|
||||||
isStealthModalOpen,
|
isStealthModalOpen,
|
||||||
|
storyList,
|
||||||
},
|
},
|
||||||
forwardMessages: { storyId: forwardedStoryId },
|
forwardMessages: { storyId: forwardedStoryId },
|
||||||
premiumModal,
|
premiumModal,
|
||||||
@ -886,7 +887,6 @@ export default memo(withGlobal<OwnProps>((global, {
|
|||||||
mapModal,
|
mapModal,
|
||||||
} = tabState;
|
} = tabState;
|
||||||
const { isOpen: isPremiumModalOpen } = premiumModal || {};
|
const { isOpen: isPremiumModalOpen } = premiumModal || {};
|
||||||
const { orderedIds, pinnedIds, archiveIds } = selectPeerStories(global, peerId) || {};
|
|
||||||
const story = selectPeerStory(global, peerId, storyId);
|
const story = selectPeerStory(global, peerId, storyId);
|
||||||
const shouldForcePause = Boolean(
|
const shouldForcePause = Boolean(
|
||||||
viewModal || forwardedStoryId || tabState.reactionPicker?.storyId || isReportModalOpen || isPrivacyModalOpen
|
viewModal || forwardedStoryId || tabState.reactionPicker?.storyId || isReportModalOpen || isPrivacyModalOpen
|
||||||
@ -904,7 +904,7 @@ export default memo(withGlobal<OwnProps>((global, {
|
|||||||
peer: (user || chat)!,
|
peer: (user || chat)!,
|
||||||
forwardSender,
|
forwardSender,
|
||||||
story,
|
story,
|
||||||
orderedIds: isArchivedStories ? archiveIds : (isPrivateStories ? pinnedIds : orderedIds),
|
orderedIds: storyList?.storyIdsByPeerId[peerId],
|
||||||
isMuted,
|
isMuted,
|
||||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||||
shouldForcePause,
|
shouldForcePause,
|
||||||
|
|||||||
@ -26,10 +26,11 @@ interface OwnProps {
|
|||||||
interface StateProps {
|
interface StateProps {
|
||||||
lastViewedId?: number;
|
lastViewedId?: number;
|
||||||
origin?: StoryViewerOrigin;
|
origin?: StoryViewerOrigin;
|
||||||
|
storyIdsForViewer?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function StoryPreview({
|
function StoryPreview({
|
||||||
peer, peerStories, lastViewedId, origin,
|
peer, peerStories, lastViewedId, storyIdsForViewer, origin,
|
||||||
}: OwnProps & StateProps) {
|
}: OwnProps & StateProps) {
|
||||||
const { openStoryViewer, loadPeerSkippedStories } = getActions();
|
const { openStoryViewer, loadPeerSkippedStories } = getActions();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -43,11 +44,13 @@ function StoryPreview({
|
|||||||
orderedIds, lastReadId, byId,
|
orderedIds, lastReadId, byId,
|
||||||
} = peerStories;
|
} = peerStories;
|
||||||
const hasUnreadStories = orderedIds[orderedIds.length - 1] !== lastReadId;
|
const hasUnreadStories = orderedIds[orderedIds.length - 1] !== lastReadId;
|
||||||
const previewIndexId = lastViewedId ?? (hasUnreadStories ? (lastReadId ?? -1) : -1);
|
const previewIndexId = lastViewedId && storyIdsForViewer?.includes(lastViewedId)
|
||||||
const resultId = byId[previewIndexId]?.id || orderedIds[0];
|
? lastViewedId ?? (hasUnreadStories ? (lastReadId ?? -1) : -1)
|
||||||
|
: -1;
|
||||||
|
const resultId = byId[previewIndexId]?.id || storyIdsForViewer?.[0] || orderedIds[0];
|
||||||
|
|
||||||
return byId[resultId];
|
return byId[resultId];
|
||||||
}, [lastViewedId, peerStories]);
|
}, [lastViewedId, peerStories, storyIdsForViewer]);
|
||||||
|
|
||||||
const isLoaded = story && 'content' in story;
|
const isLoaded = story && 'content' in story;
|
||||||
|
|
||||||
@ -95,11 +98,13 @@ export default memo(withGlobal<OwnProps>((global, { peer }): StateProps => {
|
|||||||
storyViewer: {
|
storyViewer: {
|
||||||
lastViewedByPeerIds,
|
lastViewedByPeerIds,
|
||||||
origin,
|
origin,
|
||||||
|
storyList,
|
||||||
},
|
},
|
||||||
} = selectTabState(global);
|
} = selectTabState(global);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lastViewedId: peer?.id ? lastViewedByPeerIds?.[peer.id] : undefined,
|
lastViewedId: peer?.id ? lastViewedByPeerIds?.[peer.id] : undefined,
|
||||||
origin,
|
origin,
|
||||||
|
storyIdsForViewer: peer?.id ? storyList?.storyIdsByPeerId[peer.id] : undefined,
|
||||||
};
|
};
|
||||||
})(StoryPreview));
|
})(StoryPreview));
|
||||||
|
|||||||
@ -158,45 +158,20 @@ function StorySlides({
|
|||||||
return renderingPeerIds.indexOf(currentPeerId);
|
return renderingPeerIds.indexOf(currentPeerId);
|
||||||
}, [currentPeerId, renderingPeerIds]);
|
}, [currentPeerId, renderingPeerIds]);
|
||||||
|
|
||||||
|
// Handling the flipping of stories from a current user
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timeoutId = window.setTimeout(() => {
|
if (renderingPeerId === currentPeerId && currentStoryId !== renderingStoryId) {
|
||||||
setRenderingPeerId(currentPeerId);
|
|
||||||
}, animationDuration);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.clearTimeout(timeoutId);
|
|
||||||
};
|
|
||||||
}, [animationDuration, currentPeerId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let timeOutId: number | undefined;
|
|
||||||
|
|
||||||
if (renderingPeerId !== currentPeerId) {
|
|
||||||
timeOutId = window.setTimeout(() => {
|
|
||||||
setRenderingStoryId(currentStoryId);
|
|
||||||
}, animationDuration);
|
|
||||||
} else if (currentStoryId !== renderingStoryId) {
|
|
||||||
setRenderingStoryId(currentStoryId);
|
setRenderingStoryId(currentStoryId);
|
||||||
}
|
}
|
||||||
|
}, [currentPeerId, currentStoryId, renderingPeerId, renderingStoryId]);
|
||||||
return () => {
|
|
||||||
window.clearTimeout(timeOutId);
|
|
||||||
};
|
|
||||||
}, [renderingPeerId, currentStoryId, currentPeerId, renderingStoryId, animationDuration]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timeOutId: number | undefined;
|
|
||||||
|
|
||||||
if (prevPeerId && prevPeerId !== currentPeerId) {
|
if (prevPeerId && prevPeerId !== currentPeerId) {
|
||||||
setIsAnimating(true);
|
setIsAnimating(true);
|
||||||
timeOutId = window.setTimeout(() => {
|
|
||||||
setIsAnimating(false);
|
|
||||||
}, animationDuration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setIsAnimating(false);
|
setIsAnimating(false);
|
||||||
window.clearTimeout(timeOutId);
|
|
||||||
};
|
};
|
||||||
}, [prevPeerId, currentPeerId, setIsAnimating, animationDuration]);
|
}, [prevPeerId, currentPeerId, setIsAnimating, animationDuration]);
|
||||||
|
|
||||||
@ -373,6 +348,21 @@ function StorySlides({
|
|||||||
});
|
});
|
||||||
}, [currentPeerId, getIsAnimating, renderingPeerId, slideSizes, isMobile]);
|
}, [currentPeerId, getIsAnimating, renderingPeerId, slideSizes, isMobile]);
|
||||||
|
|
||||||
|
const handleTransitionEnd = useLastCallback((event: React.TransitionEvent<HTMLDivElement>) => {
|
||||||
|
// It is `target` that is needed here, not `currentTarget`
|
||||||
|
const target = event.target as HTMLDivElement | null;
|
||||||
|
|
||||||
|
if (!target || !target.classList.contains(styles.activeSlide)) return;
|
||||||
|
|
||||||
|
if (renderingPeerId !== currentPeerId) {
|
||||||
|
setRenderingPeerId(currentPeerId);
|
||||||
|
setRenderingStoryId(currentStoryId);
|
||||||
|
} else if (currentStoryId !== renderingStoryId) {
|
||||||
|
setRenderingStoryId(currentStoryId);
|
||||||
|
}
|
||||||
|
setIsAnimating(false);
|
||||||
|
});
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper} ref={containerRef}>
|
<div className={styles.wrapper} ref={containerRef}>
|
||||||
@ -461,6 +451,7 @@ function StorySlides({
|
|||||||
className={styles.wrapper}
|
className={styles.wrapper}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={`--story-viewer-scale: ${slideSizes.scale}`}
|
style={`--story-viewer-scale: ${slideSizes.scale}`}
|
||||||
|
onTransitionEnd={handleTransitionEnd}
|
||||||
>
|
>
|
||||||
<div className={styles.fullSize} onClick={onClose} />
|
<div className={styles.fullSize} onClick={onClose} />
|
||||||
{renderingPeerIds.length > 1 && (
|
{renderingPeerIds.length > 1 && (
|
||||||
@ -480,15 +471,14 @@ function StorySlides({
|
|||||||
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||||
const {
|
const {
|
||||||
storyViewer: {
|
storyViewer: {
|
||||||
peerId: currentPeerId, storyId: currentStoryId, isSinglePeer, isSingleStory, isPrivate, isArchive,
|
peerId: currentPeerId, storyId: currentStoryId, isSinglePeer, isSingleStory, isPrivate, isArchive, storyList,
|
||||||
},
|
},
|
||||||
} = selectTabState(global);
|
} = selectTabState(global);
|
||||||
const { byPeerId, orderedPeerIds: { archived, active } } = global.stories;
|
const { byPeerId, orderedPeerIds: { active } } = global.stories;
|
||||||
const peer = currentPeerId ? selectPeer(global, currentPeerId) : undefined;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
byPeerId,
|
byPeerId,
|
||||||
peerIds: peer?.areStoriesHidden ? archived : active,
|
peerIds: storyList?.peerIds ?? active,
|
||||||
currentPeerId,
|
currentPeerId,
|
||||||
currentStoryId,
|
currentStoryId,
|
||||||
isSinglePeer,
|
isSinglePeer,
|
||||||
|
|||||||
@ -89,6 +89,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.slideAnimation {
|
.slideAnimation {
|
||||||
|
/* Slide switching is made using the `onTransitionEnd` event, so don't remove the animation */
|
||||||
transition: transform 350ms ease-in-out !important;
|
transition: transform 350ms ease-in-out !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +133,14 @@
|
|||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contentInner {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
width: calc(100% * var(--slide-translate-scale));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide {
|
.slide {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
selectPeerFirstStoryId,
|
selectPeerFirstStoryId,
|
||||||
selectPeerFirstUnreadStoryId,
|
selectPeerFirstUnreadStoryId,
|
||||||
selectPeerStories,
|
selectPeerStories,
|
||||||
|
selectStoryListForViewer,
|
||||||
selectTabState,
|
selectTabState,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { fetchChatByUsername } from '../api/chats';
|
import { fetchChatByUsername } from '../api/chats';
|
||||||
@ -43,6 +44,9 @@ addActionHandler('openStoryViewer', async (global, actions, payload): Promise<vo
|
|||||||
global = addStoriesForPeer(global, peerId, result.stories);
|
global = addStoriesForPeer(global, peerId, result.stories);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const storyList = tabState.storyViewer.storyList
|
||||||
|
|| selectStoryListForViewer(global, peerId, storyId, isSingleStory, isSinglePeer, isPrivate, isArchive);
|
||||||
|
|
||||||
global = updateTabState(global, {
|
global = updateTabState(global, {
|
||||||
storyViewer: {
|
storyViewer: {
|
||||||
...tabState.storyViewer,
|
...tabState.storyViewer,
|
||||||
@ -54,6 +58,7 @@ addActionHandler('openStoryViewer', async (global, actions, payload): Promise<vo
|
|||||||
isSingleStory,
|
isSingleStory,
|
||||||
viewModal: undefined,
|
viewModal: undefined,
|
||||||
origin,
|
origin,
|
||||||
|
storyList,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
@ -94,6 +99,7 @@ addActionHandler('closeStoryViewer', (global, actions, payload): ActionReturnTyp
|
|||||||
isRibbonShown,
|
isRibbonShown,
|
||||||
isArchivedRibbonShown,
|
isArchivedRibbonShown,
|
||||||
lastViewedByPeerIds: undefined,
|
lastViewedByPeerIds: undefined,
|
||||||
|
storyList: undefined,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
|
|
||||||
@ -134,15 +140,14 @@ addActionHandler('openPreviousStory', (global, actions, payload): ActionReturnTy
|
|||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
const tabState = selectTabState(global, tabId);
|
const tabState = selectTabState(global, tabId);
|
||||||
const {
|
const {
|
||||||
peerId, storyId, isSinglePeer, isSingleStory, isPrivate, isArchive,
|
peerId, storyId, isSinglePeer, isSingleStory, storyList,
|
||||||
} = tabState.storyViewer;
|
} = tabState.storyViewer;
|
||||||
|
|
||||||
if (isSingleStory) {
|
if (isSingleStory || !storyList) {
|
||||||
actions.closeStoryViewer({ tabId });
|
actions.closeStoryViewer({ tabId });
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { orderedPeerIds: { active, archived } } = global.stories;
|
|
||||||
if (!peerId || !storyId) {
|
if (!peerId || !storyId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -153,9 +158,8 @@ addActionHandler('openPreviousStory', (global, actions, payload): ActionReturnTy
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderedPeerIds = (peer.areStoriesHidden ? archived : active) ?? [];
|
const { peerIds: orderedPeerIds, storyIdsByPeerId } = storyList;
|
||||||
const storySourceProp = isArchive ? 'archiveIds' : isPrivate ? 'pinnedIds' : 'orderedIds';
|
const peerStoryIds = storyIdsByPeerId[peerId] ?? [];
|
||||||
const peerStoryIds = peerStories[storySourceProp] ?? [];
|
|
||||||
const currentStoryIndex = peerStoryIds.indexOf(storyId);
|
const currentStoryIndex = peerStoryIds.indexOf(storyId);
|
||||||
let previousStoryIndex: number;
|
let previousStoryIndex: number;
|
||||||
let previousPeerId: string;
|
let previousPeerId: string;
|
||||||
@ -170,10 +174,10 @@ addActionHandler('openPreviousStory', (global, actions, payload): ActionReturnTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
previousPeerId = orderedPeerIds[previousPeerIdIndex];
|
previousPeerId = orderedPeerIds[previousPeerIdIndex];
|
||||||
previousStoryIndex = (selectPeerStories(global, previousPeerId)?.orderedIds.length || 1) - 1;
|
previousStoryIndex = (storyIdsByPeerId?.[previousPeerId]?.length || 1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousStoryId = selectPeerStories(global, previousPeerId)?.[storySourceProp]?.[previousStoryIndex];
|
const previousStoryId = storyIdsByPeerId?.[previousPeerId]?.[previousStoryIndex];
|
||||||
if (!previousStoryId) {
|
if (!previousStoryId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -191,14 +195,13 @@ addActionHandler('openNextStory', (global, actions, payload): ActionReturnType =
|
|||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
const tabState = selectTabState(global, tabId);
|
const tabState = selectTabState(global, tabId);
|
||||||
const {
|
const {
|
||||||
peerId, storyId, isSinglePeer, isSingleStory, isPrivate, isArchive,
|
peerId, storyId, isSinglePeer, isSingleStory, storyList,
|
||||||
} = tabState.storyViewer;
|
} = tabState.storyViewer;
|
||||||
if (isSingleStory) {
|
if (isSingleStory || !storyList) {
|
||||||
actions.closeStoryViewer({ tabId });
|
actions.closeStoryViewer({ tabId });
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { orderedPeerIds: { active, archived } } = global.stories;
|
|
||||||
if (!peerId || !storyId) {
|
if (!peerId || !storyId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -209,9 +212,8 @@ addActionHandler('openNextStory', (global, actions, payload): ActionReturnType =
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderedPeerIds = (peer.areStoriesHidden ? archived : active) ?? [];
|
const { peerIds: orderedPeerIds, storyIdsByPeerId } = storyList;
|
||||||
const storySourceProp = isArchive ? 'archiveIds' : isPrivate ? 'pinnedIds' : 'orderedIds';
|
const peerStoryIds = storyIdsByPeerId[peerId] ?? [];
|
||||||
const peerStoryIds = peerStories[storySourceProp] ?? [];
|
|
||||||
const currentStoryIndex = peerStoryIds.indexOf(storyId);
|
const currentStoryIndex = peerStoryIds.indexOf(storyId);
|
||||||
let nextStoryIndex: number;
|
let nextStoryIndex: number;
|
||||||
let nextPeerId: string;
|
let nextPeerId: string;
|
||||||
@ -230,7 +232,7 @@ addActionHandler('openNextStory', (global, actions, payload): ActionReturnType =
|
|||||||
nextStoryIndex = 0;
|
nextStoryIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextStoryId = selectPeerStories(global, nextPeerId)?.[storySourceProp]?.[nextStoryIndex];
|
const nextStoryId = storyIdsByPeerId?.[nextPeerId]?.[nextStoryIndex];
|
||||||
if (!nextStoryId) {
|
if (!nextStoryId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { ApiPeerStories, ApiTypeStory } from '../../api/types';
|
|||||||
import type { GlobalState, TabArgs } from '../types';
|
import type { GlobalState, TabArgs } from '../types';
|
||||||
|
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
|
import { selectPeer } from './chats';
|
||||||
import { selectTabState } from './tabs';
|
import { selectTabState } from './tabs';
|
||||||
|
|
||||||
export function selectCurrentViewedStory<T extends GlobalState>(
|
export function selectCurrentViewedStory<T extends GlobalState>(
|
||||||
@ -56,3 +57,86 @@ export function selectPeerFirstStoryId<T extends GlobalState>(
|
|||||||
) {
|
) {
|
||||||
return selectPeerStories(global, peerId)?.orderedIds?.[0];
|
return selectPeerStories(global, peerId)?.orderedIds?.[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectStoryListForViewer<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
peerId: string,
|
||||||
|
storyId?: number,
|
||||||
|
isSingleStory?: boolean,
|
||||||
|
isSinglePeer?: boolean,
|
||||||
|
isPrivate?: boolean,
|
||||||
|
isArchive?: boolean,
|
||||||
|
): {
|
||||||
|
peerIds: string[];
|
||||||
|
storyIdsByPeerId: Record<string, number[]>;
|
||||||
|
} | undefined {
|
||||||
|
const currentStoryId = storyId
|
||||||
|
|| selectPeerFirstUnreadStoryId(global, peerId)
|
||||||
|
|| selectPeerFirstStoryId(global, peerId);
|
||||||
|
if (!currentStoryId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSingleStory) {
|
||||||
|
return {
|
||||||
|
peerIds: [peerId],
|
||||||
|
storyIdsByPeerId: { [peerId]: [currentStoryId] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const peer = selectPeer(global, peerId);
|
||||||
|
const story = selectPeerStory(global, peerId, currentStoryId);
|
||||||
|
if (!peer || !story) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUnread = (global.stories.byPeerId[peerId].lastReadId || 0) < story.id;
|
||||||
|
|
||||||
|
if (isSinglePeer) {
|
||||||
|
const storyIds = getPeerStoryIdsForViewer(global, peerId, isUnread, isArchive, isPrivate);
|
||||||
|
|
||||||
|
return storyIds?.length
|
||||||
|
? { peerIds: [peerId], storyIdsByPeerId: { [peerId]: storyIds } }
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { orderedPeerIds: { active, archived } } = global.stories;
|
||||||
|
const orderedPeerIds = (peer.areStoriesHidden ? archived : active) ?? [];
|
||||||
|
const peerIds: string[] = [];
|
||||||
|
const storyIdsByPeerId: Record<string, number[]> = {};
|
||||||
|
|
||||||
|
for (const currentPeerId of orderedPeerIds) {
|
||||||
|
const storyIds = getPeerStoryIdsForViewer(global, currentPeerId, isUnread, isArchive, isPrivate);
|
||||||
|
if (storyIds?.length) {
|
||||||
|
peerIds.push(currentPeerId);
|
||||||
|
storyIdsByPeerId[currentPeerId] = storyIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return peerIds.length ? { peerIds, storyIdsByPeerId } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPeerStoryIdsForViewer<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
peerId: string,
|
||||||
|
isUnread?: boolean,
|
||||||
|
isArchive?: boolean,
|
||||||
|
isPrivate?: boolean,
|
||||||
|
): number[] | undefined {
|
||||||
|
const peerStories = selectPeerStories(global, peerId);
|
||||||
|
const storySourceProp = isArchive ? 'archiveIds' : isPrivate ? 'pinnedIds' : 'orderedIds';
|
||||||
|
const storyIds = peerStories?.[storySourceProp];
|
||||||
|
|
||||||
|
if (!peerStories || !storyIds?.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!peerStories.lastReadId || !isUnread) {
|
||||||
|
return storyIds.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastReadIndex = storyIds.indexOf(peerStories.lastReadId);
|
||||||
|
return (storyIds.length > lastReadIndex + 1)
|
||||||
|
? storyIds.slice(lastReadIndex + 1)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|||||||
@ -383,6 +383,11 @@ export type TabState = {
|
|||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
};
|
};
|
||||||
origin?: StoryViewerOrigin;
|
origin?: StoryViewerOrigin;
|
||||||
|
// Copy of story list for current view session
|
||||||
|
storyList?: {
|
||||||
|
peerIds: string[];
|
||||||
|
storyIdsByPeerId: Record<string, number[]>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mediaViewer: {
|
mediaViewer: {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user