diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 8360d28708ec..f0446ceb6ae3 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -3142,6 +3142,10 @@ "message": "No groups in common", "description": "Shown to indicate this user is not a member of any groups" }, + "no-groups-in-common-warning": { + "message": "No groups in common. Review requests carefully.", + "description": "When a user has no common groups, show this warning" + }, "acceptCall": { "message": "Answer", "description": "Shown in tooltip for the button to accept a call (audio or video)" @@ -5165,6 +5169,18 @@ "message": "Continue", "description": "aria-label for the 'next' button in the forward a message modal dialog" }, + "MessageRequestWarning__learn-more": { + "message": "Learn more", + "description": "Shown on the message request warning. Clicking this button will open a dialog with more information" + }, + "MessageRequestWarning__dialog__details": { + "message": "You have no groups in common with this person. Review requests carefully before accepting to avoid unwanted messages.", + "description": "Shown in the message request warning dialog. Gives more information about message requests" + }, + "MessageRequestWarning__dialog__learn-even-more": { + "message": "About Message Requests", + "description": "Shown in the message request warning dialog. Clicking this button will open a page on Signal's support site" + }, "ContactSpoofing__same-name": { "message": "Review requests carefully. Signal found another contact with the same name. $link$", "description": "Shown in the timeline warning when you have a message request from someone with the same name as someone else", diff --git a/stylesheets/_mixins.scss b/stylesheets/_mixins.scss index 5d7c346f1d9f..411339945aea 100644 --- a/stylesheets/_mixins.scss +++ b/stylesheets/_mixins.scss @@ -106,7 +106,12 @@ } } -// Smooth scrolling +// Utilities + +@mixin rounded-corners() { + // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) + border-radius: 9999px; +} @mixin smooth-scroll() { scroll-behavior: smooth; @@ -472,7 +477,7 @@ } @mixin button-small { - border-radius: 9999px; // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) + @include rounded-corners; padding: 7px 14px; } diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 53e66c2e9e1c..e666e5800be7 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -3934,6 +3934,46 @@ button.module-conversation-details__action-button { @include font-body-2-bold; } } + + &__message-request-warning { + @include font-body-2; + + &__message { + display: flex; + margin-bottom: 12px; + align-items: center; + justify-content: center; + user-select: none; + + @include light-theme { + color: $color-gray-60; + } + @include dark-theme { + color: $color-gray-25; + } + + &::before { + content: ''; + display: block; + height: 14px; + margin-right: 8px; + width: 14px; + + @include light-theme { + @include color-svg( + '../images/icons/v2/info-outline-24.svg', + $color-gray-60 + ); + } + @include dark-theme { + @include color-svg( + '../images/icons/v2/info-solid-24.svg', + $color-gray-25 + ); + } + } + } + } } // Module: Message Request Actions diff --git a/stylesheets/components/Button.scss b/stylesheets/components/Button.scss index 501f7453b8ed..609a899db297 100644 --- a/stylesheets/components/Button.scss +++ b/stylesheets/components/Button.scss @@ -18,8 +18,6 @@ } @include button-reset; - @include font-body-1-bold; - border-radius: 4px; padding: 8px 16px; text-align: center; @@ -37,6 +35,16 @@ cursor: not-allowed; } + &--medium { + @include font-body-1-bold; + } + + &--small { + @include font-body-2; + @include rounded-corners; + padding: 6px 12px; + } + &--primary { $color: $color-white; $background-color: $ultramarine-ui-light; diff --git a/stylesheets/components/ContactPill.scss b/stylesheets/components/ContactPill.scss index 796ab2561ff2..d2ebd440aea3 100644 --- a/stylesheets/components/ContactPill.scss +++ b/stylesheets/components/ContactPill.scss @@ -2,8 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only .module-ContactPill { + @include rounded-corners; align-items: center; - border-radius: 9999px; // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) display: inline-flex; user-select: none; overflow: hidden; diff --git a/stylesheets/components/ConversationHeader.scss b/stylesheets/components/ConversationHeader.scss index 83f3b238c021..fdf6efb2fc9d 100644 --- a/stylesheets/components/ConversationHeader.scss +++ b/stylesheets/components/ConversationHeader.scss @@ -279,9 +279,9 @@ &--join-call { @include font-body-1; + @include rounded-corners; align-items: center; background-color: $color-accent-green; - border-radius: 9999px; // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) color: $color-white; display: flex; outline: none; diff --git a/ts/components/Button.stories.tsx b/ts/components/Button.stories.tsx index a8cf6919e97e..93766e6be1c4 100644 --- a/ts/components/Button.stories.tsx +++ b/ts/components/Button.stories.tsx @@ -5,30 +5,39 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; -import { Button, ButtonVariant } from './Button'; +import { Button, ButtonSize, ButtonVariant } from './Button'; const story = storiesOf('Components/Button', module); story.add('Kitchen sink', () => ( <> - {[ - ButtonVariant.Primary, - ButtonVariant.Secondary, - ButtonVariant.SecondaryAffirmative, - ButtonVariant.SecondaryDestructive, - ButtonVariant.Destructive, - ].map(variant => ( - -

- -

-

- -

+ {[ButtonSize.Medium, ButtonSize.Small].map(size => ( + + {[ + ButtonVariant.Primary, + ButtonVariant.Secondary, + ButtonVariant.SecondaryAffirmative, + ButtonVariant.SecondaryDestructive, + ButtonVariant.Destructive, + ].map(variant => ( + +

+ +

+

+ +

+
+ ))}
))} diff --git a/ts/components/Button.tsx b/ts/components/Button.tsx index 2d062a1fc31b..c030ca0cad54 100644 --- a/ts/components/Button.tsx +++ b/ts/components/Button.tsx @@ -6,6 +6,11 @@ import classNames from 'classnames'; import { assert } from '../util/assert'; +export enum ButtonSize { + Medium, + Small, +} + export enum ButtonVariant { Primary, Secondary, @@ -17,6 +22,7 @@ export enum ButtonVariant { type PropsType = { className?: string; disabled?: boolean; + size?: ButtonSize; variant?: ButtonVariant; } & ( | { @@ -41,6 +47,11 @@ type PropsType = { } ); +const SIZE_CLASS_NAMES = new Map([ + [ButtonSize.Medium, 'module-Button--medium'], + [ButtonSize.Small, 'module-Button--small'], +]); + const VARIANT_CLASS_NAMES = new Map([ [ButtonVariant.Primary, 'module-Button--primary'], [ButtonVariant.Secondary, 'module-Button--secondary'], @@ -61,6 +72,7 @@ export const Button = React.forwardRef( children, className, disabled = false, + size = ButtonSize.Medium, variant = ButtonVariant.Primary, } = props; const ariaLabel = props['aria-label']; @@ -75,13 +87,21 @@ export const Button = React.forwardRef( ({ type } = props); } + const sizeClassName = SIZE_CLASS_NAMES.get(size); + assert(sizeClassName, ' + + ); }; export const ConversationHero = ({ @@ -81,37 +109,31 @@ export const ConversationHero = ({ unblurredAvatarPath, updateSharedGroups, }: Props): JSX.Element => { - const firstRenderRef = React.useRef(true); + const firstRenderRef = useRef(true); - // TODO: DESKTOP-686 - /* eslint-disable react-hooks/exhaustive-deps */ - React.useEffect(() => { - // If any of the depenencies for this hook change then the height of this - // component may have changed. The cleanup function notifies listeners of - // any potential height changes. - return () => { - // Kick off the expensive hydration of the current sharedGroupNames - if (updateSharedGroups) { - updateSharedGroups(); - } + const [height, setHeight] = useState(); + const [ + isShowingMessageRequestWarning, + setIsShowingMessageRequestWarning, + ] = useState(false); + const closeMessageRequestWarning = () => { + setIsShowingMessageRequestWarning(false); + }; - if (onHeightChange && !firstRenderRef.current) { - onHeightChange(); - } else { - firstRenderRef.current = false; - } - }; - }, [ - firstRenderRef, - onHeightChange, - // Avoid collisions in these dependencies by prefixing them - // These dependencies may be dynamic, and therefore may cause height changes - `mc-${membersCount}`, - `n-${name}`, - `pn-${profileName}`, - sharedGroupNames.map(g => `g-${g}`).join(' '), - ]); - /* eslint-enable react-hooks/exhaustive-deps */ + useEffect(() => { + // Kick off the expensive hydration of the current sharedGroupNames + updateSharedGroups(); + }, [updateSharedGroups]); + + useEffect(() => { + firstRenderRef.current = false; + }, []); + + useEffect(() => { + if (!firstRenderRef.current && onHeightChange) { + onHeightChange(); + } + }, [height, onHeightChange]); let avatarBlur: AvatarBlur; let avatarOnClick: undefined | (() => void); @@ -136,58 +158,92 @@ export const ConversationHero = ({ /* eslint-disable no-nested-ternary */ return ( -
- -

- {isMe ? ( - i18n('noteToSelf') - ) : ( - + <> + { + assert(bounds, 'We should be measuring the bounds'); + setHeight(bounds.height); + }} + > + {({ measureRef }) => ( +
+ +

+ {isMe ? ( + i18n('noteToSelf') + ) : ( + + )} +

+ {about && !isMe && ( +
+ +
+ )} + {!isMe ? ( +
+ {membersCount === 1 + ? i18n('ConversationHero--members-1') + : membersCount !== undefined + ? i18n('ConversationHero--members', [`${membersCount}`]) + : phoneNumberOnly + ? null + : phoneNumber} +
+ ) : null} + {renderMembershipRow({ + acceptedMessageRequest, + conversationType, + i18n, + isMe, + onClickMessageRequestWarning() { + setIsShowingMessageRequestWarning(true); + }, + phoneNumber, + sharedGroupNames, + })} +
)} -

- {about && !isMe && ( -
- -
+ + {isShowingMessageRequestWarning && ( + { + window.location.href = + 'https://support.signal.org/hc/articles/360007459591'; + closeMessageRequestWarning(); + }, + }, + ]} + > + {i18n('MessageRequestWarning__dialog__details')} + )} - {!isMe ? ( -
- {membersCount === 1 - ? i18n('ConversationHero--members-1') - : membersCount !== undefined - ? i18n('ConversationHero--members', [`${membersCount}`]) - : phoneNumberOnly - ? null - : phoneNumber} -
- ) : null} - {renderMembershipRow({ - conversationType, - i18n, - isMe, - phoneNumber, - sharedGroupNames, - })} -
+ ); /* eslint-enable no-nested-ternary */ }; diff --git a/ts/components/conversation/Timeline.stories.tsx b/ts/components/conversation/Timeline.stories.tsx index c9e9d0e768cc..7f5e8aad7313 100644 --- a/ts/components/conversation/Timeline.stories.tsx +++ b/ts/components/conversation/Timeline.stories.tsx @@ -315,6 +315,7 @@ const renderHeroRow = () => ( conversationType="direct" sharedGroupNames={['NYC Rock Climbers', 'Dinner Party']} unblurAvatar={action('unblurAvatar')} + updateSharedGroups={noop} /> ); const renderLoadingRow = () => ; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 176c02628491..83439e5af3fa 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -16509,8 +16509,8 @@ { "rule": "React-useRef", "path": "ts/components/conversation/ConversationHero.js", - "line": " const firstRenderRef = React.useRef(true);", - "lineNumber": 49, + "line": " const firstRenderRef = react_1.useRef(true);", + "lineNumber": 61, "reasonCategory": "falseMatch", "updated": "2020-10-26T19:12:24.410Z", "reasonDetail": "Doesn't refer to a DOM element." @@ -16518,8 +16518,8 @@ { "rule": "React-useRef", "path": "ts/components/conversation/ConversationHero.tsx", - "line": " const firstRenderRef = React.useRef(true);", - "lineNumber": 84, + "line": " const firstRenderRef = useRef(true);", + "lineNumber": 112, "reasonCategory": "falseMatch", "updated": "2020-10-26T19:12:24.410Z", "reasonDetail": "Doesn't refer to a DOM element."