From 5ccfabeb0c2eb751870bbb705156e6eb0cfcfe60 Mon Sep 17 00:00:00 2001
From: Jamie Kyle <113370520+jamiebuilds-signal@users.noreply.github.com>
Date: Wed, 26 Oct 2022 16:17:39 -0700
Subject: [PATCH] Story settings modal copy and design changes
---
_locales/en/messages.json | 14 +-
stylesheets/_mixins.scss | 2 +-
.../components/StoriesSettingsModal.scss | 75 ++++-
ts/components/Checkbox.tsx | 3 +-
ts/components/SendStoryModal.tsx | 2 +-
.../StoriesSettingsModal.stories.tsx | 12 +-
ts/components/StoriesSettingsModal.tsx | 264 ++++++++++++------
ts/state/ducks/stories.ts | 9 +
ts/state/smart/StoriesSettingsModal.tsx | 7 +
9 files changed, 295 insertions(+), 93 deletions(-)
diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 476c2ba6db..6af86609c4 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -5388,7 +5388,7 @@
"description": "Label of the button on the bottom of the media editor that trigger the add-caption dialog"
},
"MyStories__title": {
- "message": "My Stories",
+ "message": "My Story",
"description": "Title for the my stories list"
},
"MyStories__story": {
@@ -5460,7 +5460,7 @@
"description": "Title for the stories list"
},
"Stories__mine": {
- "message": "My Stories",
+ "message": "My Story",
"description": "Label for your stories"
},
"Stories__add": {
@@ -5515,6 +5515,10 @@
"message": "Story settings",
"description": "Title for the story settings modal"
},
+ "icu:StoriesSettings__description": {
+ "messageformat": "Stories automatically disapper after 24 hours. Choose who can view your story or create new stories with specific viewers or groups.",
+ "description": "Description for story settings modal"
+ },
"StoriesSettings__new-list": {
"message": "New custom story",
"description": "Label to create a new custom distribution list"
@@ -5600,7 +5604,7 @@
"description": "Description of button StoriesSettings__mine__all--label"
},
"StoriesSettings__mine__exclude--label": {
- "message": "All Signal connections except...",
+ "message": "All except...",
"description": "Input label to create a block list"
},
"StoriesSettings__mine__exclude--description": {
@@ -5704,8 +5708,8 @@
"description": "Modal title when choosing groups"
},
"SendStoryModal__my-stories-privacy": {
- "message": "My stories privacy",
- "description": "Modal title for setting privacy for My Stories"
+ "message": "My Story privacy",
+ "description": "Modal title for setting privacy for My Story"
},
"SendStoryModal__privacy-disclaimer": {
"message": "Choose which Signal connections can view your story. You can always change this in privacy settings. $learnMore$",
diff --git a/stylesheets/_mixins.scss b/stylesheets/_mixins.scss
index 7dc1dd579e..262830cc2f 100644
--- a/stylesheets/_mixins.scss
+++ b/stylesheets/_mixins.scss
@@ -74,7 +74,7 @@
@mixin font-subtitle {
@include font-family;
- font-size: 11px;
+ font-size: 12px;
line-height: 16px;
letter-spacing: 0;
}
diff --git a/stylesheets/components/StoriesSettingsModal.scss b/stylesheets/components/StoriesSettingsModal.scss
index 351707592f..080bd58af1 100644
--- a/stylesheets/components/StoriesSettingsModal.scss
+++ b/stylesheets/components/StoriesSettingsModal.scss
@@ -74,9 +74,9 @@
@include font-body-1;
align-items: center;
display: flex;
- height: 52px;
justify-content: space-between;
width: 100%;
+ padding: 8px 0;
&--no-pointer {
cursor: inherit;
@@ -144,9 +144,9 @@
}
&__divider {
- border-color: $color-gray-65;
- border-style: solid;
width: 100%;
+ border: 0 solid $color-gray-65;
+ border-top-width: 1px;
}
&__input__container {
@@ -165,12 +165,51 @@
margin-top: 32px;
}
+ &__description {
+ @include font-subtitle;
+ color: $color-gray-25;
+ margin-top: 0px;
+ margin-bottom: 16px;
+ }
+
+ &__listHeader {
+ display: flex;
+ align-items: center;
+ }
+
+ &__listHeader__title {
+ flex: 1;
+ @include font-body-1-bold;
+ margin-right: 8px;
+ }
+
+ &__listHeader__button {
+ @include button-reset;
+ @include button-secondary;
+ @include rounded-corners;
+ padding: 4px 10px;
+ @include font-family;
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+
+ &::before {
+ content: '';
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ margin-right: 6px;
+ @include color-svg('../images/icons/v2/plus-24.svg', $color-white);
+ }
+ }
+
&__delete-list {
@include button-reset;
align-items: center;
color: $color-accent-red;
display: flex;
- height: 52px;
+ padding: 8px 0;
width: 100%;
&::before {
@@ -186,7 +225,22 @@
}
&__checkbox {
- margin: 18px 0;
+ margin: 14px 0;
+ }
+
+ &__checkbox-container {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ }
+
+ &__checkbox-label {
+ flex: 1;
+ margin-right: 8px;
+ }
+
+ &__checkbox-description {
+ color: $color-gray-25;
}
&__conversation-list {
@@ -204,4 +258,15 @@
color: $color-gray-05;
}
}
+
+ &__stories-off-container {
+ display: flex;
+ gap: 16;
+ align-items: center;
+ }
+
+ &__stories-off-text {
+ flex: 1;
+ font-size: 12px;
+ }
}
diff --git a/ts/components/Checkbox.tsx b/ts/components/Checkbox.tsx
index 9059d3fa68..ccfa26fc1f 100644
--- a/ts/components/Checkbox.tsx
+++ b/ts/components/Checkbox.tsx
@@ -12,6 +12,7 @@ export type PropsType = {
id: string;
checkboxNode: JSX.Element;
labelNode: JSX.Element;
+ checked?: boolean;
}) => JSX.Element;
description?: string;
disabled?: boolean;
@@ -65,7 +66,7 @@ export const Checkbox = ({
{children ? (
- children({ id, checkboxNode, labelNode })
+ children({ id, checkboxNode, labelNode, checked })
) : (
<>
{checkboxNode}
diff --git a/ts/components/SendStoryModal.tsx b/ts/components/SendStoryModal.tsx
index ea3b3600b6..e8d6ead3c1 100644
--- a/ts/components/SendStoryModal.tsx
+++ b/ts/components/SendStoryModal.tsx
@@ -113,7 +113,7 @@ function getKeyForMyStoryType(list: StoryDistributionListWithMembersDataType) {
return 'StoriesSettings__mine__all--label';
}
-function getListViewers(
+export function getListViewers(
list: StoryDistributionListWithMembersDataType,
i18n: LocalizerType,
signalConnections: Array
diff --git a/ts/components/StoriesSettingsModal.stories.tsx b/ts/components/StoriesSettingsModal.stories.tsx
index 516336892c..8afd1eb1ba 100644
--- a/ts/components/StoriesSettingsModal.stories.tsx
+++ b/ts/components/StoriesSettingsModal.stories.tsx
@@ -7,7 +7,10 @@ import React from 'react';
import type { PropsType } from './StoriesSettingsModal';
import enMessages from '../../_locales/en/messages.json';
import { StoriesSettingsModal } from './StoriesSettingsModal';
-import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
+import {
+ getDefaultConversation,
+ getDefaultGroup,
+} from '../test-both/helpers/getDefaultConversation';
import { setupI18n } from '../util/setupI18n';
import {
getMyStories,
@@ -23,9 +26,15 @@ export default {
candidateConversations: {
defaultValue: Array.from(Array(100), () => getDefaultConversation()),
},
+ signalConnections: {
+ defaultValue: Array.from(Array(42), getDefaultConversation),
+ },
distributionLists: {
defaultValue: [],
},
+ groupStories: {
+ defaultValue: Array.from(Array(2), getDefaultGroup),
+ },
getPreferredBadge: { action: true },
hideStoriesSettings: { action: true },
i18n: {
@@ -43,6 +52,7 @@ export default {
onViewersUpdated: { action: true },
setMyStoriesToAllSignalConnections: { action: true },
toggleSignalConnectionsModal: { action: true },
+ setStoriesDisabled: { action: true },
},
} as Meta;
diff --git a/ts/components/StoriesSettingsModal.tsx b/ts/components/StoriesSettingsModal.tsx
index 806934413a..7a22f8d69d 100644
--- a/ts/components/StoriesSettingsModal.tsx
+++ b/ts/components/StoriesSettingsModal.tsx
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { MeasuredComponentProps } from 'react-measure';
+import type { ReactNode } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import Measure from 'react-measure';
import { noop } from 'lodash';
@@ -36,10 +37,12 @@ import {
asyncShouldNeverBeCalled,
} from '../util/shouldNeverBeCalled';
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
+import { getListViewers } from './SendStoryModal';
export type PropsType = {
candidateConversations: Array;
distributionLists: Array;
+ signalConnections: Array;
getPreferredBadge: PreferredBadgeSelectorType;
hideStoriesSettings: () => unknown;
i18n: LocalizerType;
@@ -62,6 +65,8 @@ export type PropsType = {
setMyStoriesToAllSignalConnections: () => unknown;
storyViewReceiptsEnabled: boolean;
toggleSignalConnectionsModal: () => unknown;
+ toggleStoriesView: () => void;
+ setStoriesDisabled: (value: boolean) => void;
};
export enum Page {
@@ -89,9 +94,65 @@ const modalCommonProps: Pick =
moduleClassName: 'StoriesSettingsModal__modal',
};
+type DistributionListItemProps = {
+ i18n: LocalizerType;
+ distributionList: StoryDistributionListWithMembersDataType;
+ me: ConversationType;
+ signalConnections: Array;
+ onSelectItemToEdit(id: UUIDStringType): void;
+};
+
+function DistributionListItem({
+ i18n,
+ distributionList,
+ me,
+ signalConnections,
+ onSelectItemToEdit,
+}: DistributionListItemProps) {
+ return (
+
+ );
+}
+
export const StoriesSettingsModal = ({
candidateConversations,
distributionLists,
+ signalConnections,
getPreferredBadge,
hideStoriesSettings,
i18n,
@@ -105,6 +166,8 @@ export const StoriesSettingsModal = ({
setMyStoriesToAllSignalConnections,
storyViewReceiptsEnabled,
toggleSignalConnectionsModal,
+ toggleStoriesView,
+ setStoriesDisabled,
}: PropsType): JSX.Element => {
const [confirmDiscardModal, confirmDiscardIf] = useConfirmDiscard(i18n);
@@ -200,10 +263,6 @@ export const StoriesSettingsModal = ({
/>
);
} else {
- const privateStories = distributionLists.filter(
- list => list.id !== MY_STORIES_ID
- );
-
modal = onClose => (
-
-
-
- {privateStories.map(list => (
+
+
+ {i18n('Stories__mine')}
+
- ))}
+
+
+ {distributionLists.map(distributionList => {
+ return (
+
+ );
+ })}
@@ -290,6 +313,22 @@ export const StoriesSettingsModal = ({
name="view-receipts"
onChange={noop}
/>
+
+
+
+ {i18n('Preferences__turn-stories-off--body')}
+
+
+
);
}
@@ -555,6 +594,30 @@ export const DistributionListSettingsModal = ({
);
};
+type CheckboxRenderProps = {
+ checkboxNode: ReactNode;
+ labelNode: ReactNode;
+ descriptionNode: ReactNode;
+};
+
+function CheckboxRender({
+ checkboxNode,
+ labelNode,
+ descriptionNode,
+}: CheckboxRenderProps) {
+ return (
+ <>
+ {checkboxNode}
+
+
{labelNode}
+
+ {descriptionNode}
+
+
+ >
+ );
+}
+
type EditMyStoriesPrivacyPropsType = {
hasDisclaimerAbove?: boolean;
i18n: LocalizerType;
@@ -605,7 +668,6 @@ export const EditMyStoriesPrivacy = ({
{
setMyStoriesToAllSignalConnections();
}}
- />
+ >
+ {({ checkboxNode, labelNode, checked }) => {
+ return (
+
+ {i18n('icu:StoriesSettings__viewers', {
+ count: myStories.members.length,
+ })}
+ >
+ )
+ }
+ />
+ );
+ }}
+
0}
- description={i18n('StoriesSettings__mine__exclude--description', [
- myStories.isBlockList ? String(myStories.members.length) : '0',
- ])}
isRadio
label={i18n('StoriesSettings__mine__exclude--label')}
moduleClassName="StoriesSettingsModal__checkbox"
@@ -631,17 +708,28 @@ export const EditMyStoriesPrivacy = ({
}
onClickExclude();
}}
- />
+ >
+ {({ checkboxNode, labelNode, checked }) => {
+ return (
+
+ {i18n('icu:StoriesSettings__viewers', {
+ count: myStories.members.length,
+ })}
+ >
+ )
+ }
+ />
+ );
+ }}
+
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"
@@ -653,7 +741,25 @@ export const EditMyStoriesPrivacy = ({
}
onClickOnlyShareWith();
}}
- />
+ >
+ {({ checkboxNode, labelNode, checked }) => {
+ return (
+
+ {i18n('icu:StoriesSettings__viewers', {
+ count: myStories.members.length,
+ })}
+ >
+ )
+ }
+ />
+ );
+ }}
+
{!hasDisclaimerAbove && disclaimerElement}
>
diff --git a/ts/state/ducks/stories.ts b/ts/state/ducks/stories.ts
index 85df7f9645..492a505629 100644
--- a/ts/state/ducks/stories.ts
+++ b/ts/state/ducks/stories.ts
@@ -1130,6 +1130,14 @@ const viewStory: ViewStoryActionCreatorType = (
};
};
+function setStoriesDisabled(
+ value: boolean
+): ThunkAction {
+ return async () => {
+ await window.Events.setHasStoriesDisabled(value);
+ };
+}
+
export const actions = {
deleteStoryForEveryone,
loadStoryReplies,
@@ -1145,6 +1153,7 @@ export const actions = {
verifyStoryListMembers,
viewUserStories,
viewStory,
+ setStoriesDisabled,
};
export const useStoriesActions = (): typeof actions => useBoundActions(actions);
diff --git a/ts/state/smart/StoriesSettingsModal.tsx b/ts/state/smart/StoriesSettingsModal.tsx
index 8a34a6678b..506f1e5c4c 100644
--- a/ts/state/smart/StoriesSettingsModal.tsx
+++ b/ts/state/smart/StoriesSettingsModal.tsx
@@ -8,6 +8,7 @@ import type { LocalizerType } from '../../types/Util';
import type { StateType } from '../reducer';
import { StoriesSettingsModal } from '../../components/StoriesSettingsModal';
import {
+ getAllSignalConnections,
getCandidateContactsForNewGroup,
getMe,
} from '../selectors/conversations';
@@ -17,8 +18,10 @@ import { getPreferredBadgeSelector } from '../selectors/badges';
import { getHasStoryViewReceiptSetting } from '../selectors/items';
import { useGlobalModalActions } from '../ducks/globalModals';
import { useStoryDistributionListsActions } from '../ducks/storyDistributionLists';
+import { useStoriesActions } from '../ducks/stories';
export function SmartStoriesSettingsModal(): JSX.Element | null {
+ const { toggleStoriesView, setStoriesDisabled } = useStoriesActions();
const { hideStoriesSettings, toggleSignalConnectionsModal } =
useGlobalModalActions();
const {
@@ -30,6 +33,7 @@ export function SmartStoriesSettingsModal(): JSX.Element | null {
setMyStoriesToAllSignalConnections,
updateStoryViewers,
} = useStoryDistributionListsActions();
+ const signalConnections = useSelector(getAllSignalConnections);
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const storyViewReceiptsEnabled = useSelector(getHasStoryViewReceiptSetting);
@@ -43,6 +47,7 @@ export function SmartStoriesSettingsModal(): JSX.Element | null {
);
}