From 86f2e1a6ac38163a26853781fd67717355b2ea25 Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:15:17 -0600 Subject: [PATCH] A few CSS tweaks and storybook fixes Co-authored-by: Scott Nonnenberg --- stylesheets/_modules.scss | 5 + stylesheets/components/ContactName.scss | 512 +++++++++--------- .../AboutContactModal.dom.stories.tsx | 1 + ts/components/conversation/Message.dom.tsx | 12 +- .../TimelineMessage.dom.stories.tsx | 368 +++++++------ 5 files changed, 469 insertions(+), 429 deletions(-) diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 24c297bf39..81ae9fff06 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -559,6 +559,7 @@ $message-padding-horizontal: 12px; margin-block-start: 0; border-top-left-radius: 0; border-top-right-radius: 0; + margin-top: -7px; } .module-message__attachment-too-big--content-below { border-bottom-left-radius: 0; @@ -1080,6 +1081,10 @@ $message-padding-horizontal: 12px; user-select: none; } +.module-message__author--with-quote { + margin-bottom: 4px; +} + .module-message__author_with_sticker { @include mixins.font-body-2-bold; diff --git a/stylesheets/components/ContactName.scss b/stylesheets/components/ContactName.scss index 09f00cebd8..eeb40008b6 100644 --- a/stylesheets/components/ContactName.scss +++ b/stylesheets/components/ContactName.scss @@ -1,6 +1,8 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +@use 'sass:map'; + @use '../mixins'; button.module-contact-name { @@ -19,256 +21,268 @@ button.module-contact-name { } } +$contact-000: ( + name: '000', + light: #d00b0b, + dark: #ff7070, +); + +$contact-010: ( + name: '010', + light: #c13215, + dark: #ff6f52, +); + +$contact-020: ( + name: '020', + light: #b34209, + dark: #f57a3d, +); + +$contact-030: ( + name: '030', + light: #9c5711, + dark: #d5920b, +); + +$contact-040: ( + name: '040', + light: #866118, + dark: #d68f00, +); + +$contact-050: ( + name: '050', + light: #76681e, + dark: #b89b0a, +); + +$contact-060: ( + name: '060', + light: #6b6b24, + dark: #a4a437, +); + +$contact-070: ( + name: '070', + light: #5e6e0c, + dark: #8faa09, +); + +$contact-080: ( + name: '080', + light: #4b7000, + dark: #74ad00, +); + +$contact-090: ( + name: '090', + light: #3d7406, + dark: #5eb309, +); + +$contact-100: ( + name: '100', + light: #2d7906, + dark: #42b309, +); + +$contact-110: ( + name: '110', + light: #2d761e, + dark: #43b42d, +); + +$contact-120: ( + name: '120', + light: #067906, + dark: #0ab80a, +); + +$contact-130: ( + name: '130', + light: #32763e, + dark: #4baf5c, +); + +$contact-140: ( + name: '140', + light: #06792d, + dark: #0ab844, +); + +$contact-150: ( + name: '150', + light: #007a3d, + dark: #00b85c, +); + +$contact-160: ( + name: '160', + light: #067953, + dark: #00b87a, +); + +$contact-170: ( + name: '170', + light: #067462, + dark: #09b397, +); + +$contact-180: ( + name: '180', + light: #007575, + dark: #00b2b2, +); + +$contact-190: ( + name: '190', + light: #077288, + dark: #00aed1, +); + +$contact-200: ( + name: '200', + light: #006da3, + dark: #00a7fa, +); + +$contact-210: ( + name: '210', + light: #5b6976, + dark: #8ba1b6, +); + +$contact-220: ( + name: '220', + light: #2662d9, + dark: #7da1e8, +); + +$contact-230: ( + name: '230', + light: #2e51ff, + dark: #8599ff, +); + +$contact-240: ( + name: '240', + light: #5151f6, + dark: #9494ff, +); + +$contact-250: ( + name: '250', + light: #6447f5, + dark: #a18ff9, +); + +$contact-260: ( + name: '260', + light: #7a3df5, + dark: #af8af9, +); + +$contact-270: ( + name: '270', + light: #8f2af4, + dark: #bf80ff, +); + +$contact-280: ( + name: '280', + light: #a20ced, + dark: #cf7cf8, +); + +$contact-290: ( + name: '290', + light: #af0bd0, + dark: #e06ef7, +); + +$contact-300: ( + name: '300', + light: #b814b8, + dark: #f65af6, +); + +$contact-310: ( + name: '310', + light: #c20aa3, + dark: #f75fdd, +); + +$contact-320: ( + name: '320', + light: #c70a88, + dark: #f76ec9, +); + +$contact-330: ( + name: '330', + light: #cc0066, + dark: #f76eb2, +); + +$contact-340: ( + name: '340', + light: #d00b4d, + dark: #ff6b9c, +); + +$contact-350: ( + name: '350', + light: #d00b2c, + dark: #f77389, +); + +$contact-colors: ( + C000: $contact-000, + C010: $contact-010, + C020: $contact-020, + C030: $contact-030, + C040: $contact-040, + C050: $contact-050, + C060: $contact-060, + C070: $contact-070, + C080: $contact-080, + C090: $contact-090, + C100: $contact-100, + C110: $contact-110, + C120: $contact-120, + C130: $contact-130, + C140: $contact-140, + C150: $contact-150, + C160: $contact-160, + C170: $contact-170, + C180: $contact-180, + C190: $contact-190, + C200: $contact-200, + C210: $contact-210, + C220: $contact-220, + C230: $contact-230, + C240: $contact-240, + C250: $contact-250, + C260: $contact-260, + C270: $contact-270, + C280: $contact-280, + C290: $contact-290, + C300: $contact-300, + C310: $contact-310, + C320: $contact-320, + C330: $contact-330, + C340: $contact-340, + C350: $contact-350, +); + .module-contact-name { - &--000 { - color: #d00b0b; - @include mixins.dark-theme { - color: #ff7070; - } - } - - &--010 { - color: #c13215; - @include mixins.dark-theme { - color: #ff6f52; - } - } - - &--020 { - color: #b34209; - @include mixins.dark-theme { - color: #f57a3d; - } - } - - &--030 { - color: #9c5711; - @include mixins.dark-theme { - color: #d5920b; - } - } - - &--040 { - color: #866118; - @include mixins.dark-theme { - color: #d68f00; - } - } - - &--050 { - color: #76681e; - @include mixins.dark-theme { - color: #b89b0a; - } - } - - &--060 { - color: #6b6b24; - @include mixins.dark-theme { - color: #a4a437; - } - } - - &--070 { - color: #5e6e0c; - @include mixins.dark-theme { - color: #8faa09; - } - } - - &--080 { - color: #4b7000; - @include mixins.dark-theme { - color: #74ad00; - } - } - - &--090 { - color: #3d7406; - @include mixins.dark-theme { - color: #5eb309; - } - } - - &--100 { - color: #2d7906; - @include mixins.dark-theme { - color: #42b309; - } - } - - &--110 { - color: #2d761e; - @include mixins.dark-theme { - color: #43b42d; - } - } - - &--120 { - color: #067906; - @include mixins.dark-theme { - color: #0ab80a; - } - } - - &--130 { - color: #067919; - @include mixins.dark-theme { - color: #0ab827; - } - } - - &--140 { - color: #06792d; - @include mixins.dark-theme { - color: #0ab844; - } - } - - &--150 { - color: #007a3d; - @include mixins.dark-theme { - color: #00b85c; - } - } - - &--160 { - color: #067953; - @include mixins.dark-theme { - color: #00b87a; - } - } - - &--170 { - color: #067462; - @include mixins.dark-theme { - color: #09b397; - } - } - - &--180 { - color: #007575; - @include mixins.dark-theme { - color: #00b2b2; - } - } - - &--190 { - color: #077288; - @include mixins.dark-theme { - color: #00aed1; - } - } - - &--200 { - color: #006da3; - @include mixins.dark-theme { - color: #00a7fa; - } - } - - &--210 { - color: #5b6976; - @include mixins.dark-theme { - color: #8ba1b6; - } - } - - &--220 { - color: #2662d9; - @include mixins.dark-theme { - color: #7da1e8; - } - } - - &--230 { - color: #2e51ff; - @include mixins.dark-theme { - color: #8599ff; - } - } - - &--240 { - color: #5151f6; - @include mixins.dark-theme { - color: #9494ff; - } - } - - &--250 { - color: #6447f5; - @include mixins.dark-theme { - color: #a18ff9; - } - } - - &--260 { - color: #7a3df5; - @include mixins.dark-theme { - color: #af8af9; - } - } - - &--270 { - color: #8f2af4; - @include mixins.dark-theme { - color: #bf80ff; - } - } - - &--280 { - color: #a20ced; - @include mixins.dark-theme { - color: #cf7cf8; - } - } - - &--290 { - color: #af0bd0; - @include mixins.dark-theme { - color: #e06ef7; - } - } - - &--300 { - color: #b814b8; - @include mixins.dark-theme { - color: #f65af6; - } - } - - &--310 { - color: #c20aa3; - @include mixins.dark-theme { - color: #f75fdd; - } - } - - &--320 { - color: #c70a88; - @include mixins.dark-theme { - color: #f76ec9; - } - } - - &--330 { - color: #cc0066; - @include mixins.dark-theme { - color: #f76eb2; - } - } - - &--340 { - color: #d00b4d; - @include mixins.dark-theme { - color: #ff6b9c; - } - } - - &--350 { - color: #d00b2c; - @include mixins.dark-theme { - color: #f77389; + @each $name, $value in $contact-colors { + &--#{map.get($value, 'name')} { + color: map.get($value, 'light'); + @include mixins.dark-theme() { + color: map.get($value, 'dark'); + } } } } diff --git a/ts/components/conversation/AboutContactModal.dom.stories.tsx b/ts/components/conversation/AboutContactModal.dom.stories.tsx index 26997caa75..73d303ab92 100644 --- a/ts/components/conversation/AboutContactModal.dom.stories.tsx +++ b/ts/components/conversation/AboutContactModal.dom.stories.tsx @@ -52,6 +52,7 @@ const me = getDefaultConversation({ isMe: true, acceptedMessageRequest: true, hasMessages: true, + phoneNumber: '(111) 231-2132', }); export default { diff --git a/ts/components/conversation/Message.dom.tsx b/ts/components/conversation/Message.dom.tsx index 6b4bb2b0b0..3315ad55dd 100644 --- a/ts/components/conversation/Message.dom.tsx +++ b/ts/components/conversation/Message.dom.tsx @@ -1136,7 +1136,7 @@ export class Message extends React.PureComponent { } #renderAuthor(): ReactNode { - const { author, contactNameColor, i18n, isSticker } = this.props; + const { author, contactNameColor, i18n, isSticker, quote } = this.props; if (!this.#shouldRenderAuthor()) { return null; @@ -1146,7 +1146,12 @@ export class Message extends React.PureComponent { const moduleName = `module-message__author${stickerSuffix}`; return ( -
+
{ // For attachments which aren't full-frame const withContentBelow = Boolean(text || attachmentDroppedDueToSize); const withContentAbove = Boolean(quote) || this.#shouldRenderAuthor(); - const displayImage = - canDisplayImage(attachments) && !attachmentDroppedDueToSize; + const displayImage = canDisplayImage(attachments); // attachmentDroppedDueToSize is handled in renderAttachmentTooBig const isAttachmentNotAvailable = diff --git a/ts/components/conversation/TimelineMessage.dom.stories.tsx b/ts/components/conversation/TimelineMessage.dom.stories.tsx index fafb468df7..29348a5562 100644 --- a/ts/components/conversation/TimelineMessage.dom.stories.tsx +++ b/ts/components/conversation/TimelineMessage.dom.stories.tsx @@ -257,6 +257,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ conversationType: overrideProps.conversationType || 'direct', contact: overrideProps.contact, // disableMenu: overrideProps.disableMenu, + deletedForEveryone: overrideProps.deletedForEveryone, disableScroll: overrideProps.disableScroll, direction: overrideProps.direction || 'incoming', showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'), @@ -400,6 +401,20 @@ const renderBothDirections = (props: Props) => ( ); +const renderOneInBothDirections = (props: Props) => ( + <> + + + +); + export const PlainMessage = Template.bind({}); PlainMessage.args = { text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.', @@ -581,156 +596,162 @@ Older.args = { timestamp: Date.now() - 180 * 24 * 60 * 60 * 1000, }; -export const ReactionsWiderMessage = Template.bind({}); -ReactionsWiderMessage.args = { - text: 'Hello there from a pal!', - timestamp: Date.now() - 180 * 24 * 60 * 60 * 1000, - reactions: [ - { - emoji: '👍', - from: getDefaultConversation({ - isMe: true, - id: '+14155552672', - phoneNumber: '+14155552672', - name: 'Me', - title: 'Me', - }), - timestamp: Date.now() - 10, - }, - { - emoji: '👍', - from: getDefaultConversation({ - id: '+14155552672', - phoneNumber: '+14155552672', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now() - 10, - }, - { - emoji: '👍', - from: getDefaultConversation({ - id: '+14155552673', - phoneNumber: '+14155552673', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now() - 10, - }, - { - emoji: '😂', - from: getDefaultConversation({ - id: '+14155552674', - phoneNumber: '+14155552674', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now() - 10, - }, - { - emoji: '😡', - from: getDefaultConversation({ - id: '+14155552677', - phoneNumber: '+14155552677', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now() - 10, - }, - { - emoji: '👎', - from: getDefaultConversation({ - id: '+14155552678', - phoneNumber: '+14155552678', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now() - 10, - }, - { - emoji: '❤️', - from: getDefaultConversation({ - id: '+14155552679', - phoneNumber: '+14155552679', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now() - 10, - }, - ], -}; +// Render only one message, because reactions break up clusters of messages +export function ReactionsWiderMessage(): JSX.Element { + const props = createProps({ + text: 'Hello there from a pal!', + timestamp: Date.now() - 180 * 24 * 60 * 60 * 1000, + reactions: [ + { + emoji: '👍', + from: getDefaultConversation({ + isMe: true, + id: '+14155552672', + phoneNumber: '+14155552672', + name: 'Me', + title: 'Me', + }), + timestamp: Date.now() - 10, + }, + { + emoji: '👍', + from: getDefaultConversation({ + id: '+14155552672', + phoneNumber: '+14155552672', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now() - 10, + }, + { + emoji: '👍', + from: getDefaultConversation({ + id: '+14155552673', + phoneNumber: '+14155552673', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now() - 10, + }, + { + emoji: '😂', + from: getDefaultConversation({ + id: '+14155552674', + phoneNumber: '+14155552674', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now() - 10, + }, + { + emoji: '😡', + from: getDefaultConversation({ + id: '+14155552677', + phoneNumber: '+14155552677', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now() - 10, + }, + { + emoji: '👎', + from: getDefaultConversation({ + id: '+14155552678', + phoneNumber: '+14155552678', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now() - 10, + }, + { + emoji: '❤️', + from: getDefaultConversation({ + id: '+14155552679', + phoneNumber: '+14155552679', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now() - 10, + }, + ], + }); + return renderOneInBothDirections(props); +} const joyReactions = Array.from({ length: 52 }, () => getJoyReaction()); -export const ReactionsShortMessage = Template.bind({}); -ReactionsShortMessage.args = { - text: 'h', - timestamp: Date.now(), - reactions: [ - ...joyReactions, - { - emoji: '👍', - from: getDefaultConversation({ - isMe: true, - id: '+14155552672', - phoneNumber: '+14155552672', - name: 'Me', - title: 'Me', - }), - timestamp: Date.now(), - }, - { - emoji: '👍', - from: getDefaultConversation({ - id: '+14155552672', - phoneNumber: '+14155552672', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now(), - }, - { - emoji: '👍', - from: getDefaultConversation({ - id: '+14155552673', - phoneNumber: '+14155552673', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now(), - }, - { - emoji: '😡', - from: getDefaultConversation({ - id: '+14155552677', - phoneNumber: '+14155552677', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now(), - }, - { - emoji: '👎', - from: getDefaultConversation({ - id: '+14155552678', - phoneNumber: '+14155552678', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now(), - }, - { - emoji: '❤️', - from: getDefaultConversation({ - id: '+14155552679', - phoneNumber: '+14155552679', - name: 'Amelia Briggs', - title: 'Amelia', - }), - timestamp: Date.now(), - }, - ], -}; +// Render only one message, because reactions break up clusters of messages +export function ReactionsShortMessage(): JSX.Element { + const props = createProps({ + text: 'h', + timestamp: Date.now(), + reactions: [ + ...joyReactions, + { + emoji: '👍', + from: getDefaultConversation({ + isMe: true, + id: '+14155552672', + phoneNumber: '+14155552672', + name: 'Me', + title: 'Me', + }), + timestamp: Date.now(), + }, + { + emoji: '👍', + from: getDefaultConversation({ + id: '+14155552672', + phoneNumber: '+14155552672', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now(), + }, + { + emoji: '👍', + from: getDefaultConversation({ + id: '+14155552673', + phoneNumber: '+14155552673', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now(), + }, + { + emoji: '😡', + from: getDefaultConversation({ + id: '+14155552677', + phoneNumber: '+14155552677', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now(), + }, + { + emoji: '👎', + from: getDefaultConversation({ + id: '+14155552678', + phoneNumber: '+14155552678', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now(), + }, + { + emoji: '❤️', + from: getDefaultConversation({ + id: '+14155552679', + phoneNumber: '+14155552679', + name: 'Amelia Briggs', + title: 'Amelia', + }), + timestamp: Date.now(), + }, + ], + }); + return renderOneInBothDirections(props); +} export const AvatarInGroup = Template.bind({}); AvatarInGroup.args = { @@ -855,65 +876,60 @@ CanDeleteForEveryone.args = { direction: 'outgoing', }; -const bigAttachment = { - contentType: stringToMIMEType('text/plain'), - fileName: 'why-i-love-birds.txt', - size: 100000000000, - width: undefined, - height: undefined, - path: undefined, - key: undefined, - id: undefined, - error: true, - wasTooBig: true, - isPermanentlyUndownloadable: true, -}; - +// Too-large attachments don't get to the component export function AttachmentTooBig(): React.JSX.Element { const propsSent = createProps({ conversationType: 'direct', attachmentDroppedDueToSize: true, - attachments: [bigAttachment], }); return <>{renderBothDirections(propsSent)}; } +// Too-large attachments don't get to the component export function AttachmentTooBigWithText(): React.JSX.Element { const propsSent = createProps({ conversationType: 'direct', attachmentDroppedDueToSize: true, - attachments: [bigAttachment], text: 'Check out this file!', }); return <>{renderBothDirections(propsSent)}; } -const bigImageAttachment = { - ...bigAttachment, - contentType: IMAGE_JPEG, - fileName: 'bird.jpg', - blurHash: 'LDA,FDBnm+I=p{tkIUI;~UkpELV]', - width: 1000, - height: 1000, -}; - -export function AttachmentTooBigImage(): React.JSX.Element { +// Too-large attachments don't get to the component +export function AttachmentTooBigWithImage(): React.JSX.Element { const propsSent = createProps({ conversationType: 'direct', attachmentDroppedDueToSize: true, - attachments: [bigImageAttachment], + attachments: [ + fakeAttachment({ + contentType: IMAGE_PNG, + fileName: 'the-sax.png', + height: 240, + url: pngUrl, + width: 320, + }), + ], }); return <>{renderBothDirections(propsSent)}; } -export function AttachmentTooBigImageWithText(): React.JSX.Element { +// Too-large attachments don't get to the component +export function AttachmentTooBigWithImageAndText(): React.JSX.Element { const propsSent = createProps({ conversationType: 'direct', attachmentDroppedDueToSize: true, - attachments: [bigImageAttachment], + attachments: [ + fakeAttachment({ + contentType: IMAGE_PNG, + fileName: 'the-sax.png', + height: 240, + url: pngUrl, + width: 320, + }), + ], text: 'Check out this file!', });