Allow paging through My Stories
This commit is contained in:
parent
70bdbe33d5
commit
6f7094bc19
14 changed files with 166 additions and 122 deletions
|
@ -85,7 +85,7 @@ export const MyStories = ({
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
viewStory({
|
viewStory({
|
||||||
storyId: story.messageId,
|
storyId: story.messageId,
|
||||||
storyViewMode: StoryViewModeType.Single,
|
storyViewMode: StoryViewModeType.User,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -148,7 +148,7 @@ export const MyStories = ({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
viewStory({
|
viewStory({
|
||||||
storyId: story.messageId,
|
storyId: story.messageId,
|
||||||
storyViewMode: StoryViewModeType.Single,
|
storyViewMode: StoryViewModeType.User,
|
||||||
shouldShowDetailsModal: true,
|
shouldShowDetailsModal: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,10 @@ import type { LocalizerType } from '../types/Util';
|
||||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||||
import type { PropsType as SmartStoryCreatorPropsType } from '../state/smart/StoryCreator';
|
import type { PropsType as SmartStoryCreatorPropsType } from '../state/smart/StoryCreator';
|
||||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||||
import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
|
import type {
|
||||||
|
ViewUserStoriesActionCreatorType,
|
||||||
|
ViewStoryActionCreatorType,
|
||||||
|
} from '../state/ducks/stories';
|
||||||
import { MyStories } from './MyStories';
|
import { MyStories } from './MyStories';
|
||||||
import { StoriesPane } from './StoriesPane';
|
import { StoriesPane } from './StoriesPane';
|
||||||
import { Theme, themeClassName } from '../util/theme';
|
import { Theme, themeClassName } from '../util/theme';
|
||||||
|
@ -40,7 +43,7 @@ export type PropsType = {
|
||||||
stories: Array<ConversationStoryType>;
|
stories: Array<ConversationStoryType>;
|
||||||
toggleHideStories: (conversationId: string) => unknown;
|
toggleHideStories: (conversationId: string) => unknown;
|
||||||
toggleStoriesView: () => unknown;
|
toggleStoriesView: () => unknown;
|
||||||
viewUserStories: (conversationId: string) => unknown;
|
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||||
viewStory: ViewStoryActionCreatorType;
|
viewStory: ViewStoryActionCreatorType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import type {
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||||
|
import type { ViewUserStoriesActionCreatorType } from '../state/ducks/stories';
|
||||||
import { ContextMenu } from './ContextMenu';
|
import { ContextMenu } from './ContextMenu';
|
||||||
import { MyStoriesButton } from './MyStoriesButton';
|
import { MyStoriesButton } from './MyStoriesButton';
|
||||||
import { SearchInput } from './SearchInput';
|
import { SearchInput } from './SearchInput';
|
||||||
|
@ -76,7 +77,7 @@ export type PropsType = {
|
||||||
stories: Array<ConversationStoryType>;
|
stories: Array<ConversationStoryType>;
|
||||||
toggleHideStories: (conversationId: string) => unknown;
|
toggleHideStories: (conversationId: string) => unknown;
|
||||||
toggleStoriesView: () => unknown;
|
toggleStoriesView: () => unknown;
|
||||||
viewUserStories: (conversationId: string) => unknown;
|
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StoriesPane = ({
|
export const StoriesPane = ({
|
||||||
|
|
|
@ -7,6 +7,7 @@ import type { ConversationType } from '../state/ducks/conversations';
|
||||||
import type { ConversationStoryType, StoryViewType } from '../types/Stories';
|
import type { ConversationStoryType, StoryViewType } from '../types/Stories';
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||||
|
import type { ViewUserStoriesActionCreatorType } from '../state/ducks/stories';
|
||||||
import { Avatar, AvatarSize } from './Avatar';
|
import { Avatar, AvatarSize } from './Avatar';
|
||||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||||
import { ContextMenu } from './ContextMenu';
|
import { ContextMenu } from './ContextMenu';
|
||||||
|
@ -24,10 +25,7 @@ export type PropsType = Pick<ConversationStoryType, 'group' | 'isHidden'> & {
|
||||||
onHideStory: (conversationId: string) => unknown;
|
onHideStory: (conversationId: string) => unknown;
|
||||||
queueStoryDownload: (storyId: string) => unknown;
|
queueStoryDownload: (storyId: string) => unknown;
|
||||||
story: StoryViewType;
|
story: StoryViewType;
|
||||||
viewUserStories: (
|
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||||
conversationId: string,
|
|
||||||
shouldShowDetailsModal?: boolean
|
|
||||||
) => unknown;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function StoryListItemAvatar({
|
function StoryListItemAvatar({
|
||||||
|
@ -138,7 +136,8 @@ export const StoryListItem = ({
|
||||||
{
|
{
|
||||||
icon: 'StoryListItem__icon--info',
|
icon: 'StoryListItem__icon--info',
|
||||||
label: i18n('StoryListItem__info'),
|
label: i18n('StoryListItem__info'),
|
||||||
onClick: () => viewUserStories(conversationId, true),
|
onClick: () =>
|
||||||
|
viewUserStories({ conversationId, shouldShowDetailsModal: true }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'StoryListItem__icon--chat',
|
icon: 'StoryListItem__icon--chat',
|
||||||
|
@ -149,7 +148,7 @@ export const StoryListItem = ({
|
||||||
moduleClassName={classNames('StoryListItem', {
|
moduleClassName={classNames('StoryListItem', {
|
||||||
'StoryListItem--hidden': isHidden,
|
'StoryListItem--hidden': isHidden,
|
||||||
})}
|
})}
|
||||||
onClick={() => viewUserStories(conversationId)}
|
onClick={() => viewUserStories({ conversationId })}
|
||||||
popperOptions={{
|
popperOptions={{
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
strategy: 'absolute',
|
strategy: 'absolute',
|
||||||
|
|
|
@ -7,6 +7,7 @@ import React from 'react';
|
||||||
import type { PropsType } from './StoryViewer';
|
import type { PropsType } from './StoryViewer';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
import { SendStatus } from '../messages/MessageSendState';
|
import { SendStatus } from '../messages/MessageSendState';
|
||||||
|
import { StoryViewModeType } from '../types/Stories';
|
||||||
import { StoryViewer } from './StoryViewer';
|
import { StoryViewer } from './StoryViewer';
|
||||||
import { VIDEO_MP4 } from '../types/MIME';
|
import { VIDEO_MP4 } from '../types/MIME';
|
||||||
import { fakeAttachment } from '../test-both/helpers/fakeAttachment';
|
import { fakeAttachment } from '../test-both/helpers/fakeAttachment';
|
||||||
|
@ -57,6 +58,9 @@ export default {
|
||||||
story: {
|
story: {
|
||||||
defaultValue: getFakeStoryView(),
|
defaultValue: getFakeStoryView(),
|
||||||
},
|
},
|
||||||
|
storyViewMode: {
|
||||||
|
defaultValue: StoryViewModeType.All,
|
||||||
|
},
|
||||||
toggleHasAllStoriesMuted: { action: true },
|
toggleHasAllStoriesMuted: { action: true },
|
||||||
viewStory: { action: true },
|
viewStory: { action: true },
|
||||||
},
|
},
|
||||||
|
|
|
@ -83,7 +83,7 @@ export type PropsType = {
|
||||||
showToast: ShowToastActionCreatorType;
|
showToast: ShowToastActionCreatorType;
|
||||||
skinTone?: number;
|
skinTone?: number;
|
||||||
story: StoryViewType;
|
story: StoryViewType;
|
||||||
storyViewMode?: StoryViewModeType;
|
storyViewMode: StoryViewModeType;
|
||||||
toggleHasAllStoriesMuted: () => unknown;
|
toggleHasAllStoriesMuted: () => unknown;
|
||||||
viewStory: ViewStoryActionCreatorType;
|
viewStory: ViewStoryActionCreatorType;
|
||||||
};
|
};
|
||||||
|
@ -303,9 +303,22 @@ export const StoryViewer = ({
|
||||||
log.info('stories.markStoryRead', { messageId });
|
log.info('stories.markStoryRead', { messageId });
|
||||||
}, [markStoryRead, messageId]);
|
}, [markStoryRead, messageId]);
|
||||||
|
|
||||||
|
const canFreelyNavigateStories =
|
||||||
|
storyViewMode === StoryViewModeType.All ||
|
||||||
|
storyViewMode === StoryViewModeType.Unread;
|
||||||
|
|
||||||
|
const canNavigateLeft =
|
||||||
|
(storyViewMode === StoryViewModeType.User && currentIndex > 0) ||
|
||||||
|
canFreelyNavigateStories;
|
||||||
|
|
||||||
|
const canNavigateRight =
|
||||||
|
(storyViewMode === StoryViewModeType.User &&
|
||||||
|
currentIndex < numStories - 1) ||
|
||||||
|
canFreelyNavigateStories;
|
||||||
|
|
||||||
const navigateStories = useCallback(
|
const navigateStories = useCallback(
|
||||||
(ev: KeyboardEvent) => {
|
(ev: KeyboardEvent) => {
|
||||||
if (ev.key === 'ArrowRight') {
|
if (canNavigateRight && ev.key === 'ArrowRight') {
|
||||||
viewStory({
|
viewStory({
|
||||||
storyId: story.messageId,
|
storyId: story.messageId,
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
|
@ -313,7 +326,7 @@ export const StoryViewer = ({
|
||||||
});
|
});
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
} else if (ev.key === 'ArrowLeft') {
|
} else if (canNavigateLeft && ev.key === 'ArrowLeft') {
|
||||||
viewStory({
|
viewStory({
|
||||||
storyId: story.messageId,
|
storyId: story.messageId,
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
|
@ -323,7 +336,13 @@ export const StoryViewer = ({
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[story.messageId, storyViewMode, viewStory]
|
[
|
||||||
|
canNavigateLeft,
|
||||||
|
canNavigateRight,
|
||||||
|
story.messageId,
|
||||||
|
storyViewMode,
|
||||||
|
viewStory,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -383,8 +402,6 @@ export const StoryViewer = ({
|
||||||
const replyCount = replies.length;
|
const replyCount = replies.length;
|
||||||
const viewCount = views.length;
|
const viewCount = views.length;
|
||||||
|
|
||||||
const hasPrevNextArrows = storyViewMode !== StoryViewModeType.Single;
|
|
||||||
|
|
||||||
const canMuteStory = isVideoAttachment(attachment);
|
const canMuteStory = isVideoAttachment(attachment);
|
||||||
const isStoryMuted = hasAllStoriesMuted || !canMuteStory;
|
const isStoryMuted = hasAllStoriesMuted || !canMuteStory;
|
||||||
|
|
||||||
|
@ -453,7 +470,7 @@ export const StoryViewer = ({
|
||||||
style={{ background: getStoryBackground(attachment) }}
|
style={{ background: getStoryBackground(attachment) }}
|
||||||
/>
|
/>
|
||||||
<div className="StoryViewer__content">
|
<div className="StoryViewer__content">
|
||||||
{hasPrevNextArrows && (
|
{canNavigateLeft && (
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('back')}
|
aria-label={i18n('back')}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
@ -685,7 +702,7 @@ export const StoryViewer = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{hasPrevNextArrows && (
|
{canNavigateRight && (
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('forward')}
|
aria-label={i18n('forward')}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
|
|
@ -11,6 +11,8 @@ import type {
|
||||||
import type { BadgeType } from '../../badges/types';
|
import type { BadgeType } from '../../badges/types';
|
||||||
import type { HasStories } from '../../types/Stories';
|
import type { HasStories } from '../../types/Stories';
|
||||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
|
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
|
||||||
|
import { StoryViewModeType } from '../../types/Stories';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { About } from './About';
|
import { About } from './About';
|
||||||
import { Avatar } from '../Avatar';
|
import { Avatar } from '../Avatar';
|
||||||
|
@ -42,7 +44,7 @@ type PropsActionType = {
|
||||||
toggleAdmin: (conversationId: string, contactId: string) => void;
|
toggleAdmin: (conversationId: string, contactId: string) => void;
|
||||||
toggleSafetyNumberModal: (conversationId: string) => unknown;
|
toggleSafetyNumberModal: (conversationId: string) => unknown;
|
||||||
updateConversationModelSharedGroups: (conversationId: string) => void;
|
updateConversationModelSharedGroups: (conversationId: string) => void;
|
||||||
viewUserStories: (cid: string) => unknown;
|
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PropsType = PropsDataType & PropsActionType;
|
export type PropsType = PropsDataType & PropsActionType;
|
||||||
|
@ -179,7 +181,10 @@ export const ContactModal = ({
|
||||||
name={contact.name}
|
name={contact.name}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (conversation && hasStories) {
|
if (conversation && hasStories) {
|
||||||
viewUserStories(conversation.id);
|
viewUserStories({
|
||||||
|
conversationId: conversation.id,
|
||||||
|
storyViewMode: StoryViewModeType.User,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
setView(ContactModalView.ShowingAvatar);
|
setView(ContactModalView.ShowingAvatar);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
import type { ConversationType } from '../../state/ducks/conversations';
|
||||||
import type { BadgeType } from '../../badges/types';
|
import type { BadgeType } from '../../badges/types';
|
||||||
import type { HasStories } from '../../types/Stories';
|
import type { HasStories } from '../../types/Stories';
|
||||||
|
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
|
||||||
|
import { StoryViewModeType } from '../../types/Stories';
|
||||||
import { getMuteOptions } from '../../util/getMuteOptions';
|
import { getMuteOptions } from '../../util/getMuteOptions';
|
||||||
import * as expirationTimer from '../../util/expirationTimer';
|
import * as expirationTimer from '../../util/expirationTimer';
|
||||||
import { missingCaseError } from '../../util/missingCaseError';
|
import { missingCaseError } from '../../util/missingCaseError';
|
||||||
|
@ -90,7 +92,7 @@ export type PropsActionsType = {
|
||||||
onArchive: () => void;
|
onArchive: () => void;
|
||||||
onMarkUnread: () => void;
|
onMarkUnread: () => void;
|
||||||
onMoveToInbox: () => void;
|
onMoveToInbox: () => void;
|
||||||
viewUserStories: (cid: string) => unknown;
|
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PropsHousekeepingType = {
|
export type PropsHousekeepingType = {
|
||||||
|
@ -232,7 +234,10 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
||||||
onClick={
|
onClick={
|
||||||
hasStories
|
hasStories
|
||||||
? () => {
|
? () => {
|
||||||
viewUserStories(id);
|
viewUserStories({
|
||||||
|
conversationId: id,
|
||||||
|
storyViewMode: StoryViewModeType.User,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { GroupDescription } from './GroupDescription';
|
||||||
import { SharedGroupNames } from '../SharedGroupNames';
|
import { SharedGroupNames } from '../SharedGroupNames';
|
||||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
import type { HasStories } from '../../types/Stories';
|
import type { HasStories } from '../../types/Stories';
|
||||||
|
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
|
||||||
|
import { StoryViewModeType } from '../../types/Stories';
|
||||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
import { shouldBlurAvatar } from '../../util/shouldBlurAvatar';
|
import { shouldBlurAvatar } from '../../util/shouldBlurAvatar';
|
||||||
|
@ -30,7 +32,7 @@ export type Props = {
|
||||||
unblurredAvatarPath?: string;
|
unblurredAvatarPath?: string;
|
||||||
updateSharedGroups: () => unknown;
|
updateSharedGroups: () => unknown;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
viewUserStories: (cid: string) => unknown;
|
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||||
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
|
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
|
||||||
|
|
||||||
const renderMembershipRow = ({
|
const renderMembershipRow = ({
|
||||||
|
@ -146,7 +148,10 @@ export const ConversationHero = ({
|
||||||
avatarOnClick = unblurAvatar;
|
avatarOnClick = unblurAvatar;
|
||||||
} else if (hasStories) {
|
} else if (hasStories) {
|
||||||
avatarOnClick = () => {
|
avatarOnClick = () => {
|
||||||
viewUserStories(id);
|
viewUserStories({
|
||||||
|
conversationId: id,
|
||||||
|
storyViewMode: StoryViewModeType.User,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1556,6 +1556,9 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
isViewOnce={false}
|
isViewOnce={false}
|
||||||
moduleClassName="StoryReplyQuote"
|
moduleClassName="StoryReplyQuote"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (!storyReplyContext.storyId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
viewStory({
|
viewStory({
|
||||||
storyId: storyReplyContext.storyId,
|
storyId: storyReplyContext.storyId,
|
||||||
storyViewMode: StoryViewModeType.Single,
|
storyViewMode: StoryViewModeType.Single,
|
||||||
|
|
|
@ -78,6 +78,7 @@ export type SelectedStoryDataType = {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
numStories: number;
|
numStories: number;
|
||||||
shouldShowDetailsModal: boolean;
|
shouldShowDetailsModal: boolean;
|
||||||
|
storyViewMode: StoryViewModeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
@ -94,7 +95,6 @@ export type StoriesStateType = {
|
||||||
verifiedUuids: Array<string>;
|
verifiedUuids: Array<string>;
|
||||||
};
|
};
|
||||||
readonly stories: Array<StoryDataType>;
|
readonly stories: Array<StoryDataType>;
|
||||||
readonly storyViewMode?: StoryViewModeType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -172,12 +172,7 @@ type ToggleViewActionType = {
|
||||||
|
|
||||||
type ViewStoryActionType = {
|
type ViewStoryActionType = {
|
||||||
type: typeof VIEW_STORY;
|
type: typeof VIEW_STORY;
|
||||||
payload:
|
payload: SelectedStoryDataType | undefined;
|
||||||
| {
|
|
||||||
selectedStoryData: SelectedStoryDataType;
|
|
||||||
storyViewMode: StoryViewModeType;
|
|
||||||
}
|
|
||||||
| undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StoriesActionType =
|
export type StoriesActionType =
|
||||||
|
@ -787,10 +782,17 @@ const getSelectedStoryDataForConversationId = (
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function viewUserStories(
|
export type ViewUserStoriesActionCreatorType = (opts: {
|
||||||
conversationId: string,
|
conversationId: string;
|
||||||
shouldShowDetailsModal = false
|
shouldShowDetailsModal?: boolean;
|
||||||
): ThunkAction<void, RootStateType, unknown, ViewStoryActionType> {
|
storyViewMode?: StoryViewModeType;
|
||||||
|
}) => unknown;
|
||||||
|
|
||||||
|
const viewUserStories: ViewUserStoriesActionCreatorType = ({
|
||||||
|
conversationId,
|
||||||
|
shouldShowDetailsModal = false,
|
||||||
|
storyViewMode,
|
||||||
|
}): ThunkAction<void, RootStateType, unknown, ViewStoryActionType> => {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { currentIndex, hasUnread, numStories, storiesByConversationId } =
|
const { currentIndex, hasUnread, numStories, storiesByConversationId } =
|
||||||
getSelectedStoryDataForConversationId(dispatch, getState, conversationId);
|
getSelectedStoryDataForConversationId(dispatch, getState, conversationId);
|
||||||
|
@ -800,37 +802,36 @@ function viewUserStories(
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex,
|
currentIndex,
|
||||||
messageId: story.messageId,
|
messageId: story.messageId,
|
||||||
numStories,
|
numStories,
|
||||||
shouldShowDetailsModal,
|
shouldShowDetailsModal,
|
||||||
},
|
storyViewMode:
|
||||||
storyViewMode: hasUnread
|
storyViewMode ||
|
||||||
? StoryViewModeType.Unread
|
(hasUnread ? StoryViewModeType.Unread : StoryViewModeType.All),
|
||||||
: StoryViewModeType.All,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export type ViewStoryActionCreatorType = (opts: {
|
export type ViewStoryActionCreatorType = (
|
||||||
closeViewer?: boolean;
|
opts:
|
||||||
storyId?: string;
|
| {
|
||||||
storyViewMode?: StoryViewModeType;
|
closeViewer: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
storyId: string;
|
||||||
|
storyViewMode: StoryViewModeType;
|
||||||
viewDirection?: StoryViewDirectionType;
|
viewDirection?: StoryViewDirectionType;
|
||||||
shouldShowDetailsModal?: boolean;
|
shouldShowDetailsModal?: boolean;
|
||||||
}) => unknown;
|
}
|
||||||
|
) => unknown;
|
||||||
|
|
||||||
const viewStory: ViewStoryActionCreatorType = ({
|
const viewStory: ViewStoryActionCreatorType = (
|
||||||
closeViewer,
|
opts
|
||||||
shouldShowDetailsModal = false,
|
): ThunkAction<void, RootStateType, unknown, ViewStoryActionType> => {
|
||||||
storyId,
|
|
||||||
storyViewMode,
|
|
||||||
viewDirection,
|
|
||||||
}): ThunkAction<void, RootStateType, unknown, ViewStoryActionType> => {
|
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (closeViewer || !storyId || !storyViewMode) {
|
if ('closeViewer' in opts) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: undefined,
|
payload: undefined,
|
||||||
|
@ -838,6 +839,13 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
shouldShowDetailsModal = false,
|
||||||
|
storyId,
|
||||||
|
storyViewMode,
|
||||||
|
viewDirection,
|
||||||
|
} = opts;
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { stories } = state.stories;
|
const { stories } = state.stories;
|
||||||
|
|
||||||
|
@ -852,6 +860,11 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!story) {
|
if (!story) {
|
||||||
|
log.warn('stories.viewStory: No story found', storyId);
|
||||||
|
dispatch({
|
||||||
|
type: VIEW_STORY,
|
||||||
|
payload: undefined,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,12 +881,10 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex,
|
currentIndex,
|
||||||
messageId: storyId,
|
messageId: storyId,
|
||||||
numStories,
|
numStories,
|
||||||
shouldShowDetailsModal,
|
shouldShowDetailsModal,
|
||||||
},
|
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -891,12 +902,10 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex: nextIndex,
|
currentIndex: nextIndex,
|
||||||
messageId: nextStory.messageId,
|
messageId: nextStory.messageId,
|
||||||
numStories,
|
numStories,
|
||||||
shouldShowDetailsModal: false,
|
shouldShowDetailsModal: false,
|
||||||
},
|
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -911,18 +920,25 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex: nextIndex,
|
currentIndex: nextIndex,
|
||||||
messageId: nextStory.messageId,
|
messageId: nextStory.messageId,
|
||||||
numStories,
|
numStories,
|
||||||
shouldShowDetailsModal: false,
|
shouldShowDetailsModal: false,
|
||||||
},
|
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We were just viewing a single user's stories. Close the viewer.
|
||||||
|
if (storyViewMode === StoryViewModeType.User) {
|
||||||
|
dispatch({
|
||||||
|
type: VIEW_STORY,
|
||||||
|
payload: undefined,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Are there any unviewed stories left? If so we should play the unviewed
|
// Are there any unviewed stories left? If so we should play the unviewed
|
||||||
// stories first. But only if we're going "next"
|
// stories first. But only if we're going "next"
|
||||||
if (viewDirection === StoryViewDirectionType.Next) {
|
if (viewDirection === StoryViewDirectionType.Next) {
|
||||||
|
@ -940,12 +956,10 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex: nextSelectedStoryData.currentIndex,
|
currentIndex: nextSelectedStoryData.currentIndex,
|
||||||
messageId: unreadStory.messageId,
|
messageId: unreadStory.messageId,
|
||||||
numStories: nextSelectedStoryData.numStories,
|
numStories: nextSelectedStoryData.numStories,
|
||||||
shouldShowDetailsModal: false,
|
shouldShowDetailsModal: false,
|
||||||
},
|
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -959,6 +973,13 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
if (conversationStoryIndex < 0) {
|
if (conversationStoryIndex < 0) {
|
||||||
|
log.warn('stories.viewStory: No stories found for conversation', {
|
||||||
|
storiesLength: conversationStories.length,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: VIEW_STORY,
|
||||||
|
payload: undefined,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,13 +1022,10 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
messageId:
|
messageId: nextSelectedStoryData.storiesByConversationId[0].messageId,
|
||||||
nextSelectedStoryData.storiesByConversationId[0].messageId,
|
|
||||||
numStories: nextSelectedStoryData.numStories,
|
numStories: nextSelectedStoryData.numStories,
|
||||||
shouldShowDetailsModal: false,
|
shouldShowDetailsModal: false,
|
||||||
},
|
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1039,13 +1057,10 @@ const viewStory: ViewStoryActionCreatorType = ({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
selectedStoryData: {
|
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
messageId:
|
messageId: nextSelectedStoryData.storiesByConversationId[0].messageId,
|
||||||
nextSelectedStoryData.storiesByConversationId[0].messageId,
|
|
||||||
numStories: nextSelectedStoryData.numStories,
|
numStories: nextSelectedStoryData.numStories,
|
||||||
shouldShowDetailsModal: false,
|
shouldShowDetailsModal: false,
|
||||||
},
|
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1105,7 +1120,6 @@ export function reducer(
|
||||||
selectedStoryData: isShowingStoriesView
|
selectedStoryData: isShowingStoriesView
|
||||||
? undefined
|
? undefined
|
||||||
: state.selectedStoryData,
|
: state.selectedStoryData,
|
||||||
storyViewMode: isShowingStoriesView ? undefined : state.storyViewMode,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1341,12 +1355,9 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === VIEW_STORY) {
|
if (action.type === VIEW_STORY) {
|
||||||
const { selectedStoryData, storyViewMode } = action.payload || {};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
selectedStoryData,
|
selectedStoryData: action.payload,
|
||||||
storyViewMode,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,25 +263,15 @@ export const getStories = createSelector(
|
||||||
getConversationSelector,
|
getConversationSelector,
|
||||||
getDistributionListSelector,
|
getDistributionListSelector,
|
||||||
getStoriesState,
|
getStoriesState,
|
||||||
shouldShowStoriesView,
|
|
||||||
(
|
(
|
||||||
conversationSelector,
|
conversationSelector,
|
||||||
distributionListSelector,
|
distributionListSelector,
|
||||||
{ stories }: Readonly<StoriesStateType>,
|
{ stories }: Readonly<StoriesStateType>
|
||||||
isShowingStoriesView
|
|
||||||
): {
|
): {
|
||||||
hiddenStories: Array<ConversationStoryType>;
|
hiddenStories: Array<ConversationStoryType>;
|
||||||
myStories: Array<MyStoryType>;
|
myStories: Array<MyStoryType>;
|
||||||
stories: Array<ConversationStoryType>;
|
stories: Array<ConversationStoryType>;
|
||||||
} => {
|
} => {
|
||||||
if (!isShowingStoriesView) {
|
|
||||||
return {
|
|
||||||
hiddenStories: [],
|
|
||||||
myStories: [],
|
|
||||||
stories: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const hiddenStoriesById = new Map<string, ConversationStoryType>();
|
const hiddenStoriesById = new Map<string, ConversationStoryType>();
|
||||||
const myStoriesById = new Map<string, MyStoryType>();
|
const myStoriesById = new Map<string, MyStoryType>();
|
||||||
const storiesById = new Map<string, ConversationStoryType>();
|
const storiesById = new Map<string, ConversationStoryType>();
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
import type { GetConversationByIdType } from '../selectors/conversations';
|
import type { GetConversationByIdType } from '../selectors/conversations';
|
||||||
import type { LocalizerType } from '../../types/Util';
|
import type { LocalizerType } from '../../types/Util';
|
||||||
import type { StoryViewModeType } from '../../types/Stories';
|
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import type { SelectedStoryDataType } from '../ducks/stories';
|
import type { SelectedStoryDataType } from '../ducks/stories';
|
||||||
import { StoryViewer } from '../../components/StoryViewer';
|
import { StoryViewer } from '../../components/StoryViewer';
|
||||||
|
@ -69,10 +68,6 @@ export function SmartStoryViewer(): JSX.Element | null {
|
||||||
);
|
);
|
||||||
const { conversationStory, storyView } = storyInfo;
|
const { conversationStory, storyView } = storyInfo;
|
||||||
|
|
||||||
const storyViewMode = useSelector<StateType, StoryViewModeType | undefined>(
|
|
||||||
state => state.stories.storyViewMode
|
|
||||||
);
|
|
||||||
|
|
||||||
const recentEmojis = useRecentEmojis();
|
const recentEmojis = useRecentEmojis();
|
||||||
const skinTone = useSelector<StateType, number>(getEmojiSkinTone);
|
const skinTone = useSelector<StateType, number>(getEmojiSkinTone);
|
||||||
const replyState = useSelector(getStoryReplies);
|
const replyState = useSelector(getStoryReplies);
|
||||||
|
@ -120,7 +115,7 @@ export function SmartStoryViewer(): JSX.Element | null {
|
||||||
showToast={showToast}
|
showToast={showToast}
|
||||||
skinTone={skinTone}
|
skinTone={skinTone}
|
||||||
story={storyView}
|
story={storyView}
|
||||||
storyViewMode={storyViewMode}
|
storyViewMode={selectedStoryData.storyViewMode}
|
||||||
toggleHasAllStoriesMuted={toggleHasAllStoriesMuted}
|
toggleHasAllStoriesMuted={toggleHasAllStoriesMuted}
|
||||||
{...storiesActions}
|
{...storiesActions}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -104,10 +104,16 @@ export enum StoryViewDirectionType {
|
||||||
Previous = 'Previous',
|
Previous = 'Previous',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type of stories to view before closing the viewer
|
||||||
|
// All = All the stories in order
|
||||||
|
// Single = A single story. Like when clicking on a qouted story
|
||||||
|
// Unread = View only unread stories
|
||||||
|
// User = All of a user's stories
|
||||||
export enum StoryViewModeType {
|
export enum StoryViewModeType {
|
||||||
Unread = 'Unread',
|
|
||||||
All = 'All',
|
All = 'All',
|
||||||
Single = 'Single',
|
Single = 'Single',
|
||||||
|
Unread = 'Unread',
|
||||||
|
User = 'User',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StoryDistributionListWithMembersDataType = Omit<
|
export type StoryDistributionListWithMembersDataType = Omit<
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue