Story viewer navigation improvements
This commit is contained in:
parent
0e49f7906d
commit
4882248041
5 changed files with 670 additions and 144 deletions
|
@ -88,7 +88,7 @@ export const MyStories = ({
|
|||
onClick={() =>
|
||||
viewStory({
|
||||
storyId: story.messageId,
|
||||
storyViewMode: StoryViewModeType.User,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
})
|
||||
}
|
||||
type="button"
|
||||
|
@ -151,7 +151,7 @@ export const MyStories = ({
|
|||
onClick: () => {
|
||||
viewStory({
|
||||
storyId: story.messageId,
|
||||
storyViewMode: StoryViewModeType.User,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewTarget: StoryViewTargetType.Details,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -319,6 +319,7 @@ export const StoryViewer = ({
|
|||
const canFreelyNavigateStories =
|
||||
storyViewMode === StoryViewModeType.All ||
|
||||
storyViewMode === StoryViewModeType.Hidden ||
|
||||
storyViewMode === StoryViewModeType.MyStories ||
|
||||
storyViewMode === StoryViewModeType.Unread;
|
||||
|
||||
const canNavigateLeft =
|
||||
|
|
|
@ -82,8 +82,9 @@ export type SelectedStoryDataType = {
|
|||
currentIndex: number;
|
||||
messageId: string;
|
||||
numStories: number;
|
||||
viewTarget?: StoryViewTargetType;
|
||||
storyViewMode: StoryViewModeType;
|
||||
unviewedStoryConversationIdsSorted: Array<string>;
|
||||
viewTarget?: StoryViewTargetType;
|
||||
};
|
||||
|
||||
// State
|
||||
|
@ -592,6 +593,38 @@ function verifyStoryListMembers(
|
|||
};
|
||||
}
|
||||
|
||||
const getSelectedStoryDataForDistributionListId = (
|
||||
getState: () => RootStateType,
|
||||
distributionListId: string | undefined,
|
||||
selectedStoryId?: string
|
||||
): {
|
||||
currentIndex: number;
|
||||
numStories: number;
|
||||
storiesByConversationId: Array<StoryDataType>;
|
||||
} => {
|
||||
const state = getState();
|
||||
const { stories } = state.stories;
|
||||
|
||||
const storiesByDistributionList = stories.filter(
|
||||
item =>
|
||||
item.storyDistributionListId === distributionListId &&
|
||||
!item.deletedForEveryone
|
||||
);
|
||||
|
||||
const numStories = storiesByDistributionList.length;
|
||||
const currentIndex = selectedStoryId
|
||||
? storiesByDistributionList.findIndex(
|
||||
item => item.messageId === selectedStoryId
|
||||
)
|
||||
: 0;
|
||||
|
||||
return {
|
||||
currentIndex,
|
||||
numStories,
|
||||
storiesByConversationId: [],
|
||||
};
|
||||
};
|
||||
|
||||
const getSelectedStoryDataForConversationId = (
|
||||
dispatch: ThunkDispatch<
|
||||
RootStateType,
|
||||
|
@ -615,7 +648,7 @@ const getSelectedStoryDataForConversationId = (
|
|||
);
|
||||
|
||||
// Find the index of the storyId provided, or if none provided then find the
|
||||
// oldest unread story from the user. If all stories are read then we can
|
||||
// oldest unviewed story from the user. If all stories are read then we can
|
||||
// start at the first story.
|
||||
let currentIndex: number | undefined;
|
||||
let hasUnread = false;
|
||||
|
@ -655,24 +688,23 @@ const getSelectedStoryDataForConversationId = (
|
|||
|
||||
export type ViewUserStoriesActionCreatorType = (opts: {
|
||||
conversationId: string;
|
||||
viewTarget?: StoryViewTargetType;
|
||||
storyViewMode?: StoryViewModeType;
|
||||
viewTarget?: StoryViewTargetType;
|
||||
}) => unknown;
|
||||
|
||||
const viewUserStories: ViewUserStoriesActionCreatorType = ({
|
||||
conversationId,
|
||||
viewTarget,
|
||||
storyViewMode,
|
||||
viewTarget,
|
||||
}): ThunkAction<void, RootStateType, unknown, ViewStoryActionType> => {
|
||||
return (dispatch, getState) => {
|
||||
const { currentIndex, hasUnread, numStories, storiesByConversationId } =
|
||||
getSelectedStoryDataForConversationId(dispatch, getState, conversationId);
|
||||
|
||||
const story = storiesByConversationId[currentIndex];
|
||||
const state = getState();
|
||||
|
||||
const hiddenConversationIds = new Set(
|
||||
getHideStoryConversationIds(getState())
|
||||
);
|
||||
const hiddenConversationIds = new Set(getHideStoryConversationIds(state));
|
||||
|
||||
let inferredStoryViewMode: StoryViewModeType;
|
||||
if (storyViewMode) {
|
||||
|
@ -685,14 +717,30 @@ const viewUserStories: ViewUserStoriesActionCreatorType = ({
|
|||
inferredStoryViewMode = StoryViewModeType.All;
|
||||
}
|
||||
|
||||
let unviewedStoryConversationIdsSorted: Array<string> = [];
|
||||
if (
|
||||
inferredStoryViewMode === StoryViewModeType.Unread ||
|
||||
inferredStoryViewMode === StoryViewModeType.Hidden
|
||||
) {
|
||||
const storiesSelectorState = getStories(state);
|
||||
const conversationStories =
|
||||
inferredStoryViewMode === StoryViewModeType.Hidden
|
||||
? storiesSelectorState.hiddenStories
|
||||
: storiesSelectorState.stories;
|
||||
unviewedStoryConversationIdsSorted = conversationStories
|
||||
.filter(item => item.storyView.isUnread)
|
||||
.map(item => item.conversationId);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: VIEW_STORY,
|
||||
payload: {
|
||||
currentIndex,
|
||||
messageId: story.messageId,
|
||||
numStories,
|
||||
viewTarget,
|
||||
storyViewMode: inferredStoryViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
viewTarget,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -732,7 +780,10 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
const { viewTarget, storyId, storyViewMode, viewDirection } = opts;
|
||||
|
||||
const state = getState();
|
||||
const { stories } = state.stories;
|
||||
const { selectedStoryData, stories } = state.stories;
|
||||
|
||||
const unviewedStoryConversationIdsSorted =
|
||||
selectedStoryData?.unviewedStoryConversationIdsSorted || [];
|
||||
|
||||
// Spec:
|
||||
// When opening the story viewer you should always be taken to the oldest
|
||||
|
@ -754,12 +805,18 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
}
|
||||
|
||||
const { currentIndex, numStories, storiesByConversationId } =
|
||||
getSelectedStoryDataForConversationId(
|
||||
dispatch,
|
||||
getState,
|
||||
story.conversationId,
|
||||
storyId
|
||||
);
|
||||
storyViewMode === StoryViewModeType.MyStories
|
||||
? getSelectedStoryDataForDistributionListId(
|
||||
getState,
|
||||
story.storyDistributionListId,
|
||||
storyId
|
||||
)
|
||||
: getSelectedStoryDataForConversationId(
|
||||
dispatch,
|
||||
getState,
|
||||
story.conversationId,
|
||||
storyId
|
||||
);
|
||||
|
||||
// Go directly to the storyId selected
|
||||
if (!viewDirection) {
|
||||
|
@ -769,8 +826,101 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
currentIndex,
|
||||
messageId: storyId,
|
||||
numStories,
|
||||
viewTarget,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
viewTarget,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// When paging through all sent stories
|
||||
// Note the order is reversed[1][2] here because we sort the stories by
|
||||
// recency in descending order but the story viewer plays them in
|
||||
// ascending order.
|
||||
if (storyViewMode === StoryViewModeType.MyStories) {
|
||||
const { myStories } = getStories(state);
|
||||
|
||||
let currentStoryIndex = -1;
|
||||
const currentDistributionListIndex = myStories.findIndex(item => {
|
||||
for (let i = item.stories.length - 1; i >= 0; i -= 1) {
|
||||
const myStory = item.stories[i];
|
||||
if (myStory.messageId === storyId) {
|
||||
// [1] reversed
|
||||
currentStoryIndex = item.stories.length - 1 - i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (currentDistributionListIndex < 0 || currentStoryIndex < 0) {
|
||||
log.warn('stories.viewStory: No current story found for MyStories', {
|
||||
currentDistributionListIndex,
|
||||
currentStoryIndex,
|
||||
myStories: myStories.length,
|
||||
});
|
||||
dispatch({
|
||||
type: VIEW_STORY,
|
||||
payload: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let nextSentStoryId: string | undefined;
|
||||
let nextSentStoryIndex = -1;
|
||||
let nextNumStories = numStories;
|
||||
|
||||
// [2] reversed
|
||||
const currentStories = myStories[currentDistributionListIndex].stories
|
||||
.slice()
|
||||
.reverse();
|
||||
|
||||
if (viewDirection === StoryViewDirectionType.Next) {
|
||||
if (currentStoryIndex < currentStories.length - 1) {
|
||||
nextSentStoryIndex = currentStoryIndex + 1;
|
||||
nextSentStoryId = currentStories[nextSentStoryIndex].messageId;
|
||||
} else if (currentDistributionListIndex < myStories.length - 1) {
|
||||
const nextSentStoryContainer =
|
||||
myStories[currentDistributionListIndex + 1];
|
||||
|
||||
nextNumStories = nextSentStoryContainer.stories.length;
|
||||
nextSentStoryIndex = 0;
|
||||
nextSentStoryId =
|
||||
nextSentStoryContainer.stories[nextNumStories - 1].messageId;
|
||||
}
|
||||
}
|
||||
|
||||
if (viewDirection === StoryViewDirectionType.Previous) {
|
||||
if (currentStoryIndex > 0) {
|
||||
nextSentStoryIndex = currentStoryIndex - 1;
|
||||
nextSentStoryId = currentStories[nextSentStoryIndex].messageId;
|
||||
} else if (currentDistributionListIndex > 0) {
|
||||
const nextSentStoryContainer =
|
||||
myStories[currentDistributionListIndex - 1];
|
||||
|
||||
nextNumStories = nextSentStoryContainer.stories.length;
|
||||
nextSentStoryIndex = nextNumStories - 1;
|
||||
nextSentStoryId = nextSentStoryContainer.stories[0].messageId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextSentStoryId) {
|
||||
dispatch({
|
||||
type: VIEW_STORY,
|
||||
payload: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: VIEW_STORY,
|
||||
payload: {
|
||||
currentIndex: nextSentStoryIndex,
|
||||
messageId: nextSentStoryId,
|
||||
numStories: nextNumStories,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
@ -791,6 +941,7 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
messageId: nextStory.messageId,
|
||||
numStories,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
@ -808,6 +959,7 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
messageId: nextStory.messageId,
|
||||
numStories,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
@ -832,18 +984,27 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
);
|
||||
|
||||
// Are there any unviewed stories left? If so we should play the unviewed
|
||||
// stories first. But only if we're going "next"
|
||||
if (viewDirection === StoryViewDirectionType.Next) {
|
||||
// TODO: DESKTOP-4341 only stories that succeed the current story we're on.
|
||||
const unreadStory = conversationStories.find(
|
||||
item => item.storyView.isUnread
|
||||
);
|
||||
// stories first.
|
||||
if (storyViewMode === StoryViewModeType.Unread) {
|
||||
const frozenConversationStoryIndex =
|
||||
unviewedStoryConversationIdsSorted.findIndex(
|
||||
conversationId => conversationId === story.conversationId
|
||||
);
|
||||
|
||||
if (unreadStory) {
|
||||
let nextUnreadConversationId: string | undefined;
|
||||
if (viewDirection === StoryViewDirectionType.Previous) {
|
||||
nextUnreadConversationId =
|
||||
unviewedStoryConversationIdsSorted[frozenConversationStoryIndex - 1];
|
||||
} else if (viewDirection === StoryViewDirectionType.Next) {
|
||||
nextUnreadConversationId =
|
||||
unviewedStoryConversationIdsSorted[frozenConversationStoryIndex + 1];
|
||||
}
|
||||
|
||||
if (nextUnreadConversationId) {
|
||||
const nextSelectedStoryData = getSelectedStoryDataForConversationId(
|
||||
dispatch,
|
||||
getState,
|
||||
unreadStory.conversationId
|
||||
nextUnreadConversationId
|
||||
);
|
||||
|
||||
dispatch({
|
||||
|
@ -856,20 +1017,19 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
].messageId,
|
||||
numStories: nextSelectedStoryData.numStories,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Close the viewer if we were viewing unread stories only and we did not
|
||||
// find any more unread.
|
||||
if (storyViewMode === StoryViewModeType.Unread) {
|
||||
dispatch({
|
||||
type: VIEW_STORY,
|
||||
payload: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Close the viewer if we were viewing unviewed stories only and we did
|
||||
// not find any more unviewed.
|
||||
dispatch({
|
||||
type: VIEW_STORY,
|
||||
payload: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (conversationStoryIndex < 0) {
|
||||
|
@ -913,6 +1073,7 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
messageId: nextSelectedStoryData.storiesByConversationId[0].messageId,
|
||||
numStories: nextSelectedStoryData.numStories,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
@ -947,6 +1108,7 @@ const viewStory: ViewStoryActionCreatorType = (
|
|||
messageId: nextSelectedStoryData.storiesByConversationId[0].messageId,
|
||||
numStories: nextSelectedStoryData.numStories,
|
||||
storyViewMode,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -5,7 +5,6 @@ import * as sinon from 'sinon';
|
|||
import casual from 'casual';
|
||||
import path from 'path';
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import type {
|
||||
DispatchableViewStoryType,
|
||||
|
@ -15,6 +14,7 @@ import type {
|
|||
import type { ConversationType } from '../../../state/ducks/conversations';
|
||||
import type { MessageAttributesType } from '../../../model-types.d';
|
||||
import type { StateType as RootStateType } from '../../../state/reducer';
|
||||
import type { UUIDStringType } from '../../../types/UUID';
|
||||
import { DAY } from '../../../util/durations';
|
||||
import { IMAGE_JPEG } from '../../../types/MIME';
|
||||
import { ReadStatus } from '../../../messages/MessageReadStatus';
|
||||
|
@ -22,6 +22,7 @@ import {
|
|||
StoryViewDirectionType,
|
||||
StoryViewModeType,
|
||||
} from '../../../types/Stories';
|
||||
import { UUID } from '../../../types/UUID';
|
||||
import {
|
||||
actions,
|
||||
getEmptyState,
|
||||
|
@ -31,7 +32,6 @@ import {
|
|||
import { noopAction } from '../../../state/ducks/noop';
|
||||
import { reducer as rootReducer } from '../../../state/reducer';
|
||||
import { dropNull } from '../../../util/dropNull';
|
||||
import type { UUIDStringType } from '../../../types/UUID';
|
||||
|
||||
describe('both/state/ducks/stories', () => {
|
||||
const getEmptyRootState = () => ({
|
||||
|
@ -43,7 +43,7 @@ describe('both/state/ducks/stories', () => {
|
|||
const now = Date.now();
|
||||
|
||||
return {
|
||||
conversationId: uuid(),
|
||||
conversationId: UUID.generate().toString(),
|
||||
id,
|
||||
received_at: now,
|
||||
sent_at: now,
|
||||
|
@ -56,7 +56,10 @@ describe('both/state/ducks/stories', () => {
|
|||
function getMockConversation({
|
||||
id: conversationId,
|
||||
hideStory = false,
|
||||
}: Pick<ConversationType, 'id' | 'hideStory'>): ConversationType {
|
||||
title,
|
||||
}: Pick<ConversationType, 'id' | 'hideStory'> & {
|
||||
title?: string;
|
||||
}): ConversationType {
|
||||
return {
|
||||
acceptedMessageRequest: true,
|
||||
badges: [],
|
||||
|
@ -64,14 +67,15 @@ describe('both/state/ducks/stories', () => {
|
|||
id: conversationId,
|
||||
isMe: false,
|
||||
sharedGroupNames: [],
|
||||
title: casual.username,
|
||||
title: title || casual.username,
|
||||
type: 'direct' as const,
|
||||
};
|
||||
}
|
||||
|
||||
function getStoryData(
|
||||
messageId: string,
|
||||
conversationId = uuid()
|
||||
conversationId = UUID.generate().toString(),
|
||||
timestampDelta = 0
|
||||
): StoryDataType {
|
||||
const now = Date.now();
|
||||
|
||||
|
@ -81,14 +85,15 @@ describe('both/state/ducks/stories', () => {
|
|||
expireTimer: 1 * DAY,
|
||||
messageId,
|
||||
readStatus: ReadStatus.Unread,
|
||||
timestamp: now,
|
||||
timestamp: now - timestampDelta,
|
||||
type: 'story',
|
||||
};
|
||||
}
|
||||
|
||||
function getStateFunction(
|
||||
stories: Array<StoryDataType>,
|
||||
conversationLookup: { [key: string]: ConversationType } = {}
|
||||
conversationLookup: { [key: string]: ConversationType } = {},
|
||||
unviewedStoryConversationIdsSorted: Array<string> = []
|
||||
): () => RootStateType {
|
||||
const rootState = getEmptyRootState();
|
||||
|
||||
|
@ -100,6 +105,13 @@ describe('both/state/ducks/stories', () => {
|
|||
},
|
||||
stories: {
|
||||
...rootState.stories,
|
||||
selectedStoryData: {
|
||||
currentIndex: 0,
|
||||
messageId: '',
|
||||
numStories: 0,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
stories,
|
||||
},
|
||||
});
|
||||
|
@ -121,7 +133,7 @@ describe('both/state/ducks/stories', () => {
|
|||
it('does not find a story', () => {
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: uuid(),
|
||||
storyId: UUID.generate().toString(),
|
||||
storyViewMode: StoryViewModeType.All,
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
|
||||
|
@ -132,7 +144,7 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
|
||||
it('selects a specific story', () => {
|
||||
const storyId = uuid();
|
||||
const storyId = UUID.generate().toString();
|
||||
|
||||
const getState = getStateFunction([getStoryData(storyId)]);
|
||||
|
||||
|
@ -148,18 +160,19 @@ describe('both/state/ducks/stories', () => {
|
|||
currentIndex: 0,
|
||||
messageId: storyId,
|
||||
numStories: 1,
|
||||
viewTarget: undefined,
|
||||
storyViewMode: StoryViewModeType.All,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
viewTarget: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe("navigating within a user's stories", () => {
|
||||
it('selects the next story', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const conversationId = uuid();
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const conversationId = UUID.generate().toString();
|
||||
const getState = getStateFunction([
|
||||
getStoryData(storyId1, conversationId),
|
||||
getStoryData(storyId2, conversationId),
|
||||
|
@ -180,15 +193,16 @@ describe('both/state/ducks/stories', () => {
|
|||
messageId: storyId2,
|
||||
numStories: 3,
|
||||
storyViewMode: StoryViewModeType.User,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('selects the prev story', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const conversationId = uuid();
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const conversationId = UUID.generate().toString();
|
||||
const getState = getStateFunction([
|
||||
getStoryData(storyId1, conversationId),
|
||||
getStoryData(storyId2, conversationId),
|
||||
|
@ -209,15 +223,16 @@ describe('both/state/ducks/stories', () => {
|
|||
messageId: storyId1,
|
||||
numStories: 3,
|
||||
storyViewMode: StoryViewModeType.User,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('when in StoryViewModeType.User and we have reached the end, it closes the viewer', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const conversationId = uuid();
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const conversationId = UUID.generate().toString();
|
||||
const getState = getStateFunction([
|
||||
getStoryData(storyId1, conversationId),
|
||||
getStoryData(storyId2, conversationId),
|
||||
|
@ -239,58 +254,12 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
|
||||
describe('unviewed stories', () => {
|
||||
it('finds any unviewed stories and selects them', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
|
||||
const convoId1 = uuid();
|
||||
const convoId2 = uuid();
|
||||
const convoId3 = uuid();
|
||||
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
{
|
||||
...getStoryData(storyId1, convoId1),
|
||||
readStatus: ReadStatus.Viewed,
|
||||
},
|
||||
{
|
||||
...getStoryData(storyId2, convoId2),
|
||||
readStatus: ReadStatus.Viewed,
|
||||
},
|
||||
getStoryData(storyId3, convoId3),
|
||||
],
|
||||
{
|
||||
[convoId1]: getMockConversation({ id: convoId1 }),
|
||||
[convoId2]: getMockConversation({ id: convoId2 }),
|
||||
[convoId3]: getMockConversation({ id: convoId3 }),
|
||||
}
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId1,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId3,
|
||||
numStories: 1,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not select hidden stories', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const conversationId = uuid();
|
||||
const conversationIdHide: UUIDStringType = 'test-convo-uuid-hide-story';
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const conversationId = UUID.generate().toString();
|
||||
const conversationIdHide = UUID.generate().toString();
|
||||
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
|
@ -315,7 +284,8 @@ describe('both/state/ducks/stories', () => {
|
|||
id: conversationIdHide,
|
||||
hideStory: true,
|
||||
}),
|
||||
}
|
||||
},
|
||||
[conversationId]
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
|
@ -331,21 +301,39 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO: DESKTOP-4341 - removed until implemented
|
||||
/*
|
||||
it('does not select stories that precede the currently viewed story', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const getState = getStateFunction([
|
||||
getStoryData(storyId1),
|
||||
getStoryData(storyId2),
|
||||
getStoryData(storyId3),
|
||||
]);
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const storyId4 = UUID.generate().toString();
|
||||
const conversationId1 = UUID.generate().toString();
|
||||
const conversationId2 = UUID.generate().toString();
|
||||
const conversationId3 = UUID.generate().toString();
|
||||
|
||||
// conversationId3 - storyId4
|
||||
// conversationId1 - storyId1, storyId3
|
||||
// conversationId2 - storyId2
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
getStoryData(storyId1, conversationId1, 3),
|
||||
{
|
||||
...getStoryData(storyId2, conversationId2, 2),
|
||||
readStatus: ReadStatus.Viewed,
|
||||
},
|
||||
getStoryData(storyId3, conversationId1, 1),
|
||||
getStoryData(storyId4, conversationId3),
|
||||
],
|
||||
{
|
||||
[conversationId1]: getMockConversation({ id: conversationId1 }),
|
||||
[conversationId2]: getMockConversation({ id: conversationId2 }),
|
||||
[conversationId3]: getMockConversation({ id: conversationId3 }),
|
||||
},
|
||||
[conversationId3, conversationId1, conversationId2]
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId3,
|
||||
storyId: storyId2,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
@ -355,14 +343,115 @@ describe('both/state/ducks/stories', () => {
|
|||
payload: undefined,
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
it('correctly goes to previous unviewed story', () => {
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const storyId4 = UUID.generate().toString();
|
||||
const conversationId1 = UUID.generate().toString();
|
||||
const conversationId2 = UUID.generate().toString();
|
||||
const conversationId3 = UUID.generate().toString();
|
||||
|
||||
const unviewedStoryConversationIdsSorted = [
|
||||
conversationId3,
|
||||
conversationId1,
|
||||
conversationId2,
|
||||
];
|
||||
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
getStoryData(storyId1, conversationId1, 3),
|
||||
{
|
||||
...getStoryData(storyId2, conversationId2, 2),
|
||||
readStatus: ReadStatus.Viewed,
|
||||
},
|
||||
getStoryData(storyId3, conversationId1, 1),
|
||||
getStoryData(storyId4, conversationId3),
|
||||
],
|
||||
{
|
||||
[conversationId1]: getMockConversation({ id: conversationId1 }),
|
||||
[conversationId2]: getMockConversation({ id: conversationId2 }),
|
||||
[conversationId3]: getMockConversation({ id: conversationId3 }),
|
||||
},
|
||||
unviewedStoryConversationIdsSorted
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId2,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
viewDirection: StoryViewDirectionType.Previous,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId1,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not close the viewer when playing the next story', () => {
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const storyId4 = UUID.generate().toString();
|
||||
const conversationId1 = UUID.generate().toString();
|
||||
const conversationId2 = UUID.generate().toString();
|
||||
const conversationId3 = UUID.generate().toString();
|
||||
const unviewedStoryConversationIdsSorted = [
|
||||
conversationId3,
|
||||
conversationId2,
|
||||
conversationId1,
|
||||
];
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
getStoryData(storyId1, conversationId2, 3),
|
||||
getStoryData(storyId2, conversationId1, 2),
|
||||
getStoryData(storyId3, conversationId2, 1),
|
||||
{
|
||||
...getStoryData(storyId4, conversationId3),
|
||||
readStatus: ReadStatus.Viewed,
|
||||
},
|
||||
],
|
||||
{
|
||||
[conversationId1]: getMockConversation({ id: conversationId1 }),
|
||||
[conversationId2]: getMockConversation({ id: conversationId2 }),
|
||||
[conversationId3]: getMockConversation({ id: conversationId3 }),
|
||||
},
|
||||
unviewedStoryConversationIdsSorted
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId4,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId1,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.Unread,
|
||||
unviewedStoryConversationIdsSorted,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('closes the viewer when there are no more unviewed stories', () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
|
||||
const conversationId1 = uuid();
|
||||
const conversationId2 = uuid();
|
||||
const conversationId1 = UUID.generate().toString();
|
||||
const conversationId2 = UUID.generate().toString();
|
||||
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
|
@ -378,7 +467,8 @@ describe('both/state/ducks/stories', () => {
|
|||
{
|
||||
[conversationId1]: getMockConversation({ id: conversationId1 }),
|
||||
[conversationId2]: getMockConversation({ id: conversationId2 }),
|
||||
}
|
||||
},
|
||||
[conversationId1]
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
|
@ -395,10 +485,280 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('paging through sent stories', () => {
|
||||
function getSentStoryReduxData() {
|
||||
const distributionListId1 = UUID.generate().toString();
|
||||
const distributionListId2 = UUID.generate().toString();
|
||||
const storyDistributionLists = {
|
||||
distributionLists: [
|
||||
{
|
||||
id: distributionListId1,
|
||||
name: 'List 1',
|
||||
allowsReplies: true,
|
||||
isBlockList: false,
|
||||
memberUuids: [
|
||||
UUID.generate().toString(),
|
||||
UUID.generate().toString(),
|
||||
UUID.generate().toString(),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: distributionListId2,
|
||||
name: 'List 2',
|
||||
allowsReplies: true,
|
||||
isBlockList: false,
|
||||
memberUuids: [
|
||||
UUID.generate().toString(),
|
||||
UUID.generate().toString(),
|
||||
UUID.generate().toString(),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const ourConversationId = UUID.generate().toString();
|
||||
const groupConversationId = UUID.generate().toString();
|
||||
|
||||
function getMyStoryData(
|
||||
messageId: string,
|
||||
storyDistributionListId?: string,
|
||||
timestampDelta = 0
|
||||
): StoryDataType {
|
||||
const now = Date.now();
|
||||
|
||||
return {
|
||||
conversationId: storyDistributionListId
|
||||
? ourConversationId
|
||||
: groupConversationId,
|
||||
expirationStartTimestamp: now,
|
||||
expireTimer: 1 * DAY,
|
||||
messageId,
|
||||
readStatus: ReadStatus.Unread,
|
||||
sendStateByConversationId: {},
|
||||
storyDistributionListId,
|
||||
timestamp: now - timestampDelta,
|
||||
type: 'story',
|
||||
};
|
||||
}
|
||||
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const storyId4 = UUID.generate().toString();
|
||||
const storyId5 = UUID.generate().toString();
|
||||
const myStories = [
|
||||
getMyStoryData(storyId1, distributionListId1, 5),
|
||||
getMyStoryData(storyId2, distributionListId2, 4),
|
||||
getMyStoryData(storyId3, distributionListId1, 3),
|
||||
getMyStoryData(storyId4, undefined, 2), // group story
|
||||
getMyStoryData(storyId5, distributionListId2, 1),
|
||||
];
|
||||
|
||||
const rootState = getEmptyRootState();
|
||||
|
||||
return {
|
||||
storyId1,
|
||||
storyId2,
|
||||
storyId3,
|
||||
storyId4,
|
||||
storyId5,
|
||||
|
||||
getState: () => ({
|
||||
...rootState,
|
||||
conversations: {
|
||||
...rootState.conversations,
|
||||
conversationLookup: {
|
||||
[groupConversationId]: getMockConversation({
|
||||
id: groupConversationId,
|
||||
title: 'Group',
|
||||
}),
|
||||
},
|
||||
},
|
||||
storyDistributionLists,
|
||||
stories: {
|
||||
...rootState.stories,
|
||||
stories: myStories,
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
it('closes the viewer when hitting next at the last item', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId3 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId3,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('closes the viewer when hitting prev at the first item', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId2 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId2,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Previous,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('goes to next story within a distribution list', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId1, storyId3 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId1,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 1,
|
||||
messageId: storyId3,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('goes to prev story within a distribution list', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId1, storyId3 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId3,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Previous,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId1,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('goes to the next distribution list', () => {
|
||||
const { getState, storyId4, storyId1 } = getSentStoryReduxData();
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId4,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId1,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('goes to the prev distribution list', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId4, storyId5 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId4,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Previous,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 1,
|
||||
messageId: storyId5,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('goes next to a group story', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId4, storyId5 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId5,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Next,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId4,
|
||||
numStories: 1,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('goes prev to a group story', () => {
|
||||
const { getState, ...reduxData } = getSentStoryReduxData();
|
||||
const { storyId1, storyId4 } = reduxData;
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
viewStory({
|
||||
storyId: storyId1,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
viewDirection: StoryViewDirectionType.Previous,
|
||||
})(dispatch, getState, null);
|
||||
|
||||
sinon.assert.calledWith(dispatch, {
|
||||
type: 'stories/VIEW_STORY',
|
||||
payload: {
|
||||
currentIndex: 0,
|
||||
messageId: storyId4,
|
||||
numStories: 1,
|
||||
storyViewMode: StoryViewModeType.MyStories,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('paging through collections of stories', () => {
|
||||
function getViewedStoryData(
|
||||
storyId: string,
|
||||
conversationId?: string
|
||||
conversationId?: UUIDStringType
|
||||
): StoryDataType {
|
||||
return {
|
||||
...getStoryData(storyId, conversationId),
|
||||
|
@ -407,11 +767,11 @@ describe('both/state/ducks/stories', () => {
|
|||
}
|
||||
|
||||
it("goes to the next user's stories", () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const conversationId2 = uuid();
|
||||
const conversationId1 = uuid();
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const conversationId2 = UUID.generate().toString();
|
||||
const conversationId1 = UUID.generate().toString();
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
getViewedStoryData(storyId1, conversationId1),
|
||||
|
@ -438,16 +798,17 @@ describe('both/state/ducks/stories', () => {
|
|||
messageId: storyId2,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.All,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("goes to the prev user's stories", () => {
|
||||
const storyId1 = uuid();
|
||||
const storyId2 = uuid();
|
||||
const storyId3 = uuid();
|
||||
const conversationId1 = uuid();
|
||||
const conversationId2 = uuid();
|
||||
const storyId1 = UUID.generate().toString();
|
||||
const storyId2 = UUID.generate().toString();
|
||||
const storyId3 = UUID.generate().toString();
|
||||
const conversationId1 = UUID.generate().toString();
|
||||
const conversationId2 = UUID.generate().toString();
|
||||
const getState = getStateFunction(
|
||||
[
|
||||
getViewedStoryData(storyId1, conversationId2),
|
||||
|
@ -474,6 +835,7 @@ describe('both/state/ducks/stories', () => {
|
|||
messageId: storyId1,
|
||||
numStories: 2,
|
||||
storyViewMode: StoryViewModeType.All,
|
||||
unviewedStoryConversationIdsSorted: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -484,7 +846,7 @@ describe('both/state/ducks/stories', () => {
|
|||
const { queueStoryDownload } = actions;
|
||||
|
||||
it('no attachment, no dispatch', async function test() {
|
||||
const storyId = uuid();
|
||||
const storyId = UUID.generate().toString();
|
||||
const messageAttributes = getStoryMessage(storyId);
|
||||
|
||||
window.MessageController.register(storyId, messageAttributes);
|
||||
|
@ -496,13 +858,13 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
|
||||
it('downloading, no dispatch', async function test() {
|
||||
const storyId = uuid();
|
||||
const storyId = UUID.generate().toString();
|
||||
const messageAttributes = {
|
||||
...getStoryMessage(storyId),
|
||||
attachments: [
|
||||
{
|
||||
contentType: IMAGE_JPEG,
|
||||
downloadJobId: uuid(),
|
||||
downloadJobId: UUID.generate().toString(),
|
||||
pending: true,
|
||||
size: 0,
|
||||
},
|
||||
|
@ -518,7 +880,7 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
|
||||
it('downloaded, no dispatch', async function test() {
|
||||
const storyId = uuid();
|
||||
const storyId = UUID.generate().toString();
|
||||
const messageAttributes = {
|
||||
...getStoryMessage(storyId),
|
||||
attachments: [
|
||||
|
@ -540,7 +902,7 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
|
||||
it('downloaded, but unresolved, we should resolve the path', async function test() {
|
||||
const storyId = uuid();
|
||||
const storyId = UUID.generate().toString();
|
||||
const attachment = {
|
||||
contentType: IMAGE_JPEG,
|
||||
path: 'image.jpg',
|
||||
|
@ -620,7 +982,7 @@ describe('both/state/ducks/stories', () => {
|
|||
});
|
||||
|
||||
it('not downloaded, queued for download', async function test() {
|
||||
const storyId = uuid();
|
||||
const storyId = UUID.generate().toString();
|
||||
const messageAttributes = {
|
||||
...getStoryMessage(storyId),
|
||||
attachments: [
|
||||
|
|
|
@ -127,6 +127,7 @@ export enum StoryViewTargetType {
|
|||
export enum StoryViewModeType {
|
||||
All = 'All',
|
||||
Hidden = 'Hidden',
|
||||
MyStories = 'MyStories',
|
||||
Single = 'Single',
|
||||
Unread = 'Unread',
|
||||
User = 'User',
|
||||
|
|
Loading…
Reference in a new issue