{
+ onViewersUpdated={uuids => {
if (page === Page.ChooseViewers) {
setPage(Page.NameStory);
+ } else if (page === Page.HideStoryFrom || page === Page.AddViewer) {
+ setMyStoriesPrivacyUuids(new Set(uuids));
+ setPage(Page.SetMyStoriesPrivacy);
} else {
setPage(Page.SendStory);
}
@@ -310,6 +390,15 @@ export const SendStoryModal = ({
moduleClassName="SendStoryModal__distribution-list"
name="SendStoryModal__distribution-list"
onChange={(value: boolean) => {
+ if (
+ list.id === MY_STORIES_ID &&
+ hasFirstStoryPostExperience &&
+ value
+ ) {
+ setPage(Page.SetMyStoriesPrivacy);
+ return;
+ }
+
setSelectedListIds(listIds => {
if (value) {
listIds.add(list.id);
@@ -358,7 +447,9 @@ export const SendStoryModal = ({
- {getListViewers(list, i18n, signalConnections)}
+ {hasFirstStoryPostExperience && list.id === MY_STORIES_ID
+ ? i18n('SendStoryModal__choose-who-can-view')
+ : getListViewers(list, i18n, signalConnections)}
@@ -435,11 +526,15 @@ export const SendStoryModal = ({
}
let modalTitle: string;
- if (page === Page.ChooseGroups) {
+ if (page === Page.SetMyStoriesPrivacy) {
+ modalTitle = i18n('SendStoryModal__my-stories-privacy');
+ } else if (page === Page.HideStoryFrom) {
+ modalTitle = i18n('StoriesSettings__hide-story');
+ } else if (page === Page.ChooseGroups) {
modalTitle = i18n('SendStoryModal__choose-groups');
} else if (page === Page.NameStory) {
modalTitle = i18n('StoriesSettings__name-story');
- } else if (page === Page.ChooseViewers) {
+ } else if (page === Page.ChooseViewers || page === Page.AddViewer) {
modalTitle = i18n('StoriesSettings__choose-viewers');
} else {
modalTitle = i18n('SendStoryModal__title');
@@ -457,10 +552,16 @@ export const SendStoryModal = ({
const hasBackButton = page !== Page.SendStory;
let modalFooter: JSX.Element | undefined;
- if (page === Page.SendStory || page === Page.ChooseGroups) {
+ if (
+ page === Page.SendStory ||
+ page === Page.ChooseGroups ||
+ page === Page.SetMyStoriesPrivacy
+ ) {
modalFooter = (
- {selectedNames}
+ {page !== Page.SetMyStoriesPrivacy && (
+ {selectedNames}
+ )}
{page === Page.ChooseGroups && (
)}
+ {page === Page.SetMyStoriesPrivacy && (
+ <>
+
+
+
+
+
+ >
+ )}
);
}
@@ -498,7 +636,19 @@ export const SendStoryModal = ({
onBackButtonClick={
hasBackButton
? () => {
- if (page === Page.ChooseGroups) {
+ if (page === Page.SetMyStoriesPrivacy) {
+ setSelectedContacts([]);
+ setMyStoriesPrivacyUuids(new Set());
+ setMyStoriesPrivacy(MyStoriesPrivacy.AllSignalConnections);
+ setPage(Page.SendStory);
+ } else if (
+ page === Page.HideStoryFrom ||
+ page === Page.AddViewer
+ ) {
+ setSelectedContacts([]);
+ setMyStoriesPrivacyUuids(new Set());
+ setPage(Page.SetMyStoriesPrivacy);
+ } else if (page === Page.ChooseGroups) {
setChosenGroupIds(new Set());
setPage(Page.SendStory);
} else if (page === Page.ChooseViewers) {
diff --git a/ts/components/StoriesSettingsModal.tsx b/ts/components/StoriesSettingsModal.tsx
index c22e71378..06544b5e6 100644
--- a/ts/components/StoriesSettingsModal.tsx
+++ b/ts/components/StoriesSettingsModal.tsx
@@ -187,79 +187,22 @@ export const StoriesSettingsModal = ({
{isMyStories && (
- <>
- {
- setMyStoriesToAllSignalConnections();
- }}
- />
-
- 0}
- description={i18n('StoriesSettings__mine__exclude--description', [
- listToEdit.isBlockList
- ? String(listToEdit.members.length)
- : '0',
- ])}
- isRadio
- label={i18n('StoriesSettings__mine__exclude--label')}
- moduleClassName="StoriesSettingsModal__checkbox"
- name="share"
- onChange={noop}
- onClick={() => {
- if (listToEdit.isBlockList) {
- setSelectedContacts(listToEdit.members);
- }
- setPage(Page.HideStoryFrom);
- }}
- />
-
- 0}
- description={
- !listToEdit.isBlockList && listToEdit.members.length
- ? i18n('StoriesSettings__mine__only--description--people', [
- String(listToEdit.members.length),
- ])
- : i18n('StoriesSettings__mine__only--description')
- }
- isRadio
- label={i18n('StoriesSettings__mine__only--label')}
- moduleClassName="StoriesSettingsModal__checkbox"
- name="share"
- onChange={noop}
- onClick={() => {
- if (!listToEdit.isBlockList) {
- setSelectedContacts(listToEdit.members);
- }
- setPage(Page.AddViewer);
- }}
- />
-
-
-
- {i18n('StoriesSettings__mine__disclaimer--learn-more')}
-
- ),
- }}
- i18n={i18n}
- id="StoriesSettings__mine__disclaimer"
- />
-
- >
+ {
+ setPage(Page.HideStoryFrom);
+ }}
+ onClickOnlyShareWith={() => {
+ setPage(Page.AddViewer);
+ }}
+ setSelectedContacts={setSelectedContacts}
+ setMyStoriesToAllSignalConnections={
+ setMyStoriesToAllSignalConnections
+ }
+ toggleSignalConnectionsModal={toggleSignalConnectionsModal}
+ />
)}
{!isMyStories && (
@@ -532,6 +475,111 @@ export const StoriesSettingsModal = ({
);
};
+type EditMyStoriesPrivacyPropsType = {
+ hasDisclaimerAbove?: boolean;
+ i18n: LocalizerType;
+ learnMore: string;
+ myStories: StoryDistributionListWithMembersDataType;
+ onClickExclude: () => unknown;
+ onClickOnlyShareWith: () => unknown;
+ setSelectedContacts: (contacts: Array) => unknown;
+} & Pick<
+ PropsType,
+ 'setMyStoriesToAllSignalConnections' | 'toggleSignalConnectionsModal'
+>;
+
+export const EditMyStoriesPrivacy = ({
+ hasDisclaimerAbove,
+ i18n,
+ learnMore,
+ myStories,
+ onClickExclude,
+ onClickOnlyShareWith,
+ setSelectedContacts,
+ setMyStoriesToAllSignalConnections,
+ toggleSignalConnectionsModal,
+}: EditMyStoriesPrivacyPropsType): JSX.Element => {
+ const disclaimerElement = (
+
+
+ {i18n('StoriesSettings__mine__disclaimer--learn-more')}
+
+ ),
+ }}
+ i18n={i18n}
+ id={learnMore}
+ />
+
+ );
+
+ return (
+ <>
+ {hasDisclaimerAbove && disclaimerElement}
+
+ {
+ setMyStoriesToAllSignalConnections();
+ }}
+ />
+
+ 0}
+ description={i18n('StoriesSettings__mine__exclude--description', [
+ myStories.isBlockList ? String(myStories.members.length) : '0',
+ ])}
+ isRadio
+ label={i18n('StoriesSettings__mine__exclude--label')}
+ moduleClassName="StoriesSettingsModal__checkbox"
+ name="share"
+ onChange={noop}
+ onClick={() => {
+ if (myStories.isBlockList) {
+ setSelectedContacts(myStories.members);
+ }
+ onClickExclude();
+ }}
+ />
+
+ 0}
+ description={
+ !myStories.isBlockList && myStories.members.length
+ ? i18n('StoriesSettings__mine__only--description--people', [
+ String(myStories.members.length),
+ ])
+ : i18n('StoriesSettings__mine__only--description')
+ }
+ isRadio
+ label={i18n('StoriesSettings__mine__only--label')}
+ moduleClassName="StoriesSettingsModal__checkbox"
+ name="share"
+ onChange={noop}
+ onClick={() => {
+ if (!myStories.isBlockList) {
+ setSelectedContacts(myStories.members);
+ }
+ onClickOnlyShareWith();
+ }}
+ />
+
+ {!hasDisclaimerAbove && disclaimerElement}
+ >
+ );
+};
+
type EditDistributionListPropsType = {
onDone: (name: string, viewerUuids: Array) => unknown;
onViewersUpdated: (viewerUuids: Array) => unknown;
diff --git a/ts/components/StoryCreator.stories.tsx b/ts/components/StoryCreator.stories.tsx
index 13922c599..9f4679cac 100644
--- a/ts/components/StoryCreator.stories.tsx
+++ b/ts/components/StoryCreator.stories.tsx
@@ -33,6 +33,9 @@ export default {
groupStories: {
defaultValue: Array.from(Array(4), getDefaultGroup),
},
+ hasFirstStoryPostExperience: {
+ defaultValue: false,
+ },
i18n: { defaultValue: i18n },
installedPacks: {
defaultValue: [],
@@ -45,14 +48,18 @@ export default {
},
onClose: { action: true },
onDistributionListCreated: { action: true },
+ onHideMyStoriesFrom: { action: true },
onSend: { action: true },
+ onViewersUpdated: { action: true },
processAttachment: { action: true },
recentStickers: {
defaultValue: [],
},
+ setMyStoriesToAllSignalConnections: { action: true },
signalConnections: {
defaultValue: Array.from(Array(42), getDefaultConversation),
},
+ toggleSignalConnectionsModal: { action: true },
},
} as Meta;
@@ -78,3 +85,11 @@ LinkPreview.args = {
LinkPreview.story = {
name: 'with Link Preview ready to be applied',
};
+
+export const FirstTime = Template.bind({});
+FirstTime.args = {
+ hasFirstStoryPostExperience: true,
+};
+FirstTime.story = {
+ name: 'First time posting a story',
+};
diff --git a/ts/components/StoryCreator.tsx b/ts/components/StoryCreator.tsx
index d13fc66b7..7a66384ee 100644
--- a/ts/components/StoryCreator.tsx
+++ b/ts/components/StoryCreator.tsx
@@ -8,13 +8,11 @@ import type {
AttachmentType,
InMemoryAttachmentDraftType,
} from '../types/Attachment';
-import type { ConversationType } from '../state/ducks/conversations';
import type { LinkPreviewSourceType } from '../types/LinkPreview';
import type { LinkPreviewType } from '../types/message/LinkPreviews';
import type { LocalizerType } from '../types/Util';
-import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { Props as StickerButtonProps } from './stickers/StickerButton';
-import type { StoryDistributionListDataType } from '../state/ducks/storyDistributionLists';
+import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
import type { UUIDStringType } from '../types/UUID';
import { IMAGE_JPEG, TEXT_ATTACHMENT } from '../types/MIME';
@@ -25,24 +23,14 @@ import { MediaEditor } from './MediaEditor';
import { TextStoryCreator } from './TextStoryCreator';
export type PropsType = {
- candidateConversations: Array;
debouncedMaybeGrabLinkPreview: (
message: string,
source: LinkPreviewSourceType
) => unknown;
- distributionLists: Array;
file?: File;
- getPreferredBadge: PreferredBadgeSelectorType;
- groupConversations: Array;
- groupStories: Array;
i18n: LocalizerType;
linkPreview?: LinkPreviewType;
- me: ConversationType;
onClose: () => unknown;
- onDistributionListCreated: (
- name: string,
- viewerUuids: Array
- ) => unknown;
onSelectedStoryList: (memberUuids: Array) => unknown;
onSend: (
listIds: Array,
@@ -53,9 +41,24 @@ export type PropsType = {
file: File
) => Promise;
sendStoryModalOpenStateChanged: (isOpen: boolean) => unknown;
- signalConnections: Array;
- tagGroupsAsNewGroupStory: (cids: Array) => unknown;
-} & Pick;
+} & Pick &
+ Pick<
+ SendStoryModalPropsType,
+ | 'candidateConversations'
+ | 'distributionLists'
+ | 'getPreferredBadge'
+ | 'groupConversations'
+ | 'groupStories'
+ | 'hasFirstStoryPostExperience'
+ | 'me'
+ | 'onDistributionListCreated'
+ | 'onHideMyStoriesFrom'
+ | 'onViewersUpdated'
+ | 'setMyStoriesToAllSignalConnections'
+ | 'signalConnections'
+ | 'tagGroupsAsNewGroupStory'
+ | 'toggleSignalConnectionsModal'
+ >;
export const StoryCreator = ({
candidateConversations,
@@ -65,19 +68,24 @@ export const StoryCreator = ({
getPreferredBadge,
groupConversations,
groupStories,
+ hasFirstStoryPostExperience,
i18n,
installedPacks,
linkPreview,
me,
onClose,
onDistributionListCreated,
+ onHideMyStoriesFrom,
onSelectedStoryList,
onSend,
+ onViewersUpdated,
processAttachment,
recentStickers,
sendStoryModalOpenStateChanged,
+ setMyStoriesToAllSignalConnections,
signalConnections,
tagGroupsAsNewGroupStory,
+ toggleSignalConnectionsModal,
}: PropsType): JSX.Element => {
const [draftAttachment, setDraftAttachment] = useState<
AttachmentType | undefined
@@ -129,18 +137,25 @@ export const StoryCreator = ({
getPreferredBadge={getPreferredBadge}
groupConversations={groupConversations}
groupStories={groupStories}
+ hasFirstStoryPostExperience={hasFirstStoryPostExperience}
i18n={i18n}
me={me}
onClose={() => setDraftAttachment(undefined)}
onDistributionListCreated={onDistributionListCreated}
+ onHideMyStoriesFrom={onHideMyStoriesFrom}
onSelectedStoryList={onSelectedStoryList}
onSend={(listIds, groupIds) => {
onSend(listIds, groupIds, draftAttachment);
setDraftAttachment(undefined);
onClose();
}}
+ onViewersUpdated={onViewersUpdated}
+ setMyStoriesToAllSignalConnections={
+ setMyStoriesToAllSignalConnections
+ }
signalConnections={signalConnections}
tagGroupsAsNewGroupStory={tagGroupsAsNewGroupStory}
+ toggleSignalConnectionsModal={toggleSignalConnectionsModal}
/>
)}
{attachmentUrl && (
diff --git a/ts/state/ducks/storyDistributionLists.ts b/ts/state/ducks/storyDistributionLists.ts
index 8e2c25eb4..39823a604 100644
--- a/ts/state/ducks/storyDistributionLists.ts
+++ b/ts/state/ducks/storyDistributionLists.ts
@@ -282,6 +282,8 @@ function hideMyStoriesFrom(
storageServiceUploadJob();
+ window.storage.put('hasSetMyStoriesPrivacy', true);
+
dispatch({
type: HIDE_MY_STORIES_FROM,
payload: memberUuids,
@@ -378,6 +380,8 @@ function setMyStoriesToAllSignalConnections(): ThunkAction<
storageServiceUploadJob();
}
+ window.storage.put('hasSetMyStoriesPrivacy', true);
+
dispatch({
type: RESET_MY_STORIES,
});
@@ -432,6 +436,10 @@ function updateStoryViewers(
storageServiceUploadJob();
+ if (listId === MY_STORIES_ID) {
+ window.storage.put('hasSetMyStoriesPrivacy', true);
+ }
+
dispatch({
type: VIEWERS_CHANGED,
payload: {
@@ -599,7 +607,7 @@ export function reducer(
state.distributionLists,
MY_STORIES_ID,
() => ({
- isBlockList: false,
+ isBlockList: true,
memberUuids: [],
})
);
diff --git a/ts/state/selectors/items.ts b/ts/state/selectors/items.ts
index ce4de90ab..85cbda8a1 100644
--- a/ts/state/selectors/items.ts
+++ b/ts/state/selectors/items.ts
@@ -125,3 +125,8 @@ export const getHideMenuBar = createSelector(
getItems,
(state: ItemsStateType): boolean => Boolean(state['hide-menu-bar'])
);
+
+export const getHasSetMyStoriesPrivacy = createSelector(
+ getItems,
+ (state: ItemsStateType): boolean => Boolean(state.hasSetMyStoriesPrivacy)
+);
diff --git a/ts/state/smart/StoryCreator.tsx b/ts/state/smart/StoryCreator.tsx
index 7de71cfd4..eca36c3c5 100644
--- a/ts/state/smart/StoryCreator.tsx
+++ b/ts/state/smart/StoryCreator.tsx
@@ -21,10 +21,12 @@ import {
getInstalledStickerPacks,
getRecentStickers,
} from '../selectors/stickers';
+import { getHasSetMyStoriesPrivacy } from '../selectors/items';
import { getLinkPreview } from '../selectors/linkPreviews';
import { getPreferredBadgeSelector } from '../selectors/badges';
import { processAttachment } from '../../util/processAttachment';
import { useConversationsActions } from '../ducks/conversations';
+import { useGlobalModalActions } from '../ducks/globalModals';
import { useLinkPreviewActions } from '../ducks/linkPreviews';
import { useStoriesActions } from '../ducks/stories';
import { useStoryDistributionListsActions } from '../ducks/storyDistributionLists';
@@ -45,13 +47,20 @@ export function SmartStoryCreator({
verifyStoryListMembers,
} = useStoriesActions();
const { tagGroupsAsNewGroupStory } = useConversationsActions();
- const { createDistributionList } = useStoryDistributionListsActions();
+ const {
+ createDistributionList,
+ hideMyStoriesFrom,
+ setMyStoriesToAllSignalConnections,
+ updateStoryViewers,
+ } = useStoryDistributionListsActions();
+ const { toggleSignalConnectionsModal } = useGlobalModalActions();
const candidateConversations = useSelector(getCandidateContactsForNewGroup);
const distributionLists = useSelector(getDistributionLists);
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const groupConversations = useSelector(getNonGroupStories);
const groupStories = useSelector(getGroupStories);
+ const hasSetMyStoriesPrivacy = useSelector(getHasSetMyStoriesPrivacy);
const i18n = useSelector(getIntl);
const installedPacks = useSelector(getInstalledStickerPacks);
const linkPreviewForSource = useSelector(getLinkPreview);
@@ -68,19 +77,24 @@ export function SmartStoryCreator({
getPreferredBadge={getPreferredBadge}
groupConversations={groupConversations}
groupStories={groupStories}
+ hasFirstStoryPostExperience={!hasSetMyStoriesPrivacy}
i18n={i18n}
installedPacks={installedPacks}
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
me={me}
onClose={onClose}
onDistributionListCreated={createDistributionList}
+ onHideMyStoriesFrom={hideMyStoriesFrom}
onSelectedStoryList={verifyStoryListMembers}
onSend={sendStoryMessage}
+ onViewersUpdated={updateStoryViewers}
processAttachment={processAttachment}
recentStickers={recentStickers}
sendStoryModalOpenStateChanged={sendStoryModalOpenStateChanged}
+ setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
signalConnections={signalConnections}
tagGroupsAsNewGroupStory={tagGroupsAsNewGroupStory}
+ toggleSignalConnectionsModal={toggleSignalConnectionsModal}
/>
);
}
diff --git a/ts/types/Storage.d.ts b/ts/types/Storage.d.ts
index 9822e72e0..ee071d407 100644
--- a/ts/types/Storage.d.ts
+++ b/ts/types/Storage.d.ts
@@ -65,6 +65,7 @@ export type StorageAccessType = {
customColors: CustomColorsItemType;
device_name: string;
hasRegisterSupportForUnauthenticatedDelivery: boolean;
+ hasSetMyStoriesPrivacy: boolean;
hasStoriesEnabled: boolean;
identityKeyMap: IdentityKeyMap;
lastHeartbeat: number;
diff --git a/ts/types/Stories.ts b/ts/types/Stories.ts
index 21f4269ae..d642454bf 100644
--- a/ts/types/Stories.ts
+++ b/ts/types/Stories.ts
@@ -8,6 +8,7 @@ import type { LocalizerType } from './Util';
import type { ReadStatus } from '../messages/MessageReadStatus';
import type { SendStatus } from '../messages/MessageSendState';
import type { StoryDistributionListDataType } from '../state/ducks/storyDistributionLists';
+import type { UUIDStringType } from './UUID';
export type ReplyType = {
author: Pick<
@@ -97,7 +98,8 @@ export type MyStoryType = {
stories: Array;
};
-export const MY_STORIES_ID = '00000000-0000-0000-0000-000000000000';
+export const MY_STORIES_ID: UUIDStringType =
+ '00000000-0000-0000-0000-000000000000';
export enum StoryViewDirectionType {
Next = 'Next',