From 2b08cbfdfe816ad93bc0bf53c6b6c20b640c23b9 Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Thu, 26 Aug 2021 16:51:55 -0400 Subject: [PATCH] Streamlined system messages --- images/icons/v2/edit-16.svg | 1 + images/icons/v2/edit-solid-16.svg | 1 + images/icons/v2/info-16.svg | 1 + images/icons/v2/info-solid-24.svg | 2 +- images/icons/v2/leave-16.svg | 1 + images/icons/v2/leave-solid-16.svg | 1 + images/icons/v2/megaphone-16.svg | 1 + images/icons/v2/megaphone-solid-16.svg | 1 + images/icons/v2/member-accepted-16.svg | 1 + images/icons/v2/member-accepted-solid-16.svg | 1 + images/icons/v2/member-added-16.svg | 1 + images/icons/v2/member-added-solid-16.svg | 1 + images/icons/v2/member-declined-16.svg | 1 + images/icons/v2/member-declined-solid-16.svg | 1 + images/icons/v2/member-remove-16.svg | 1 + images/icons/v2/member-remove-solid-16.svg | 1 + images/icons/v2/phone-incoming-16.svg | 1 + images/icons/v2/phone-incoming-solid-16.svg | 1 + images/icons/v2/phone-outgoing-16.svg | 1 + images/icons/v2/phone-outgoing-solid-16.svg | 1 + images/icons/v2/phone-x-16.svg | 1 + images/icons/v2/phone-x-solid-16.svg | 1 + images/icons/v2/photo-16.svg | 1 + images/icons/v2/photo-solid-16.svg | 1 + images/icons/v2/video-incoming-16.svg | 1 + images/icons/v2/video-incoming-solid-16.svg | 1 + images/icons/v2/video-outgoing-16.svg | 1 + images/icons/v2/video-outgoing-solid-16.svg | 1 + images/icons/v2/video-x-16.svg | 1 + images/icons/v2/video-x-solid-16.svg | 1 + preload.js | 1 + stylesheets/_modules.scss | 685 ------------------ stylesheets/components/Button.scss | 32 + stylesheets/components/SystemMessage.scss | 277 +++++++ stylesheets/manifest.scss | 1 + ts/components/Button.stories.tsx | 27 +- ts/components/Button.tsx | 14 +- .../conversation/CallingNotification.tsx | 63 +- .../conversation/ChangeNumberNotification.tsx | 6 +- .../ChatSessionRefreshedNotification.tsx | 23 +- .../DeliveryIssueNotification.tsx | 23 +- .../conversation/GroupNotification.tsx | 28 +- .../conversation/GroupV1Migration.tsx | 55 +- .../conversation/GroupV2Change.stories.tsx | 9 + ts/components/conversation/GroupV2Change.tsx | 150 +++- .../ProfileChangeNotification.tsx | 4 +- .../conversation/ResetSessionNotification.tsx | 4 +- .../conversation/SafetyNumberNotification.tsx | 67 +- .../conversation/TimelineItem.stories.tsx | 84 ++- ts/components/conversation/TimelineItem.tsx | 4 +- .../conversation/TimerNotification.tsx | 20 +- .../UniversalTimerNotification.tsx | 19 +- .../conversation/UnsupportedMessage.tsx | 78 +- .../conversation/VerificationNotification.tsx | 39 +- ts/util/callingNotification.ts | 50 ++ ts/util/scaleImageToLevel.ts | 5 +- ts/window.d.ts | 2 + 57 files changed, 864 insertions(+), 937 deletions(-) create mode 100644 images/icons/v2/edit-16.svg create mode 100644 images/icons/v2/edit-solid-16.svg create mode 100644 images/icons/v2/info-16.svg create mode 100644 images/icons/v2/leave-16.svg create mode 100644 images/icons/v2/leave-solid-16.svg create mode 100644 images/icons/v2/megaphone-16.svg create mode 100644 images/icons/v2/megaphone-solid-16.svg create mode 100644 images/icons/v2/member-accepted-16.svg create mode 100644 images/icons/v2/member-accepted-solid-16.svg create mode 100644 images/icons/v2/member-added-16.svg create mode 100644 images/icons/v2/member-added-solid-16.svg create mode 100644 images/icons/v2/member-declined-16.svg create mode 100644 images/icons/v2/member-declined-solid-16.svg create mode 100644 images/icons/v2/member-remove-16.svg create mode 100644 images/icons/v2/member-remove-solid-16.svg create mode 100644 images/icons/v2/phone-incoming-16.svg create mode 100644 images/icons/v2/phone-incoming-solid-16.svg create mode 100644 images/icons/v2/phone-outgoing-16.svg create mode 100644 images/icons/v2/phone-outgoing-solid-16.svg create mode 100644 images/icons/v2/phone-x-16.svg create mode 100644 images/icons/v2/phone-x-solid-16.svg create mode 100644 images/icons/v2/photo-16.svg create mode 100644 images/icons/v2/photo-solid-16.svg create mode 100644 images/icons/v2/video-incoming-16.svg create mode 100644 images/icons/v2/video-incoming-solid-16.svg create mode 100644 images/icons/v2/video-outgoing-16.svg create mode 100644 images/icons/v2/video-outgoing-solid-16.svg create mode 100644 images/icons/v2/video-x-16.svg create mode 100644 images/icons/v2/video-x-solid-16.svg create mode 100644 stylesheets/components/SystemMessage.scss diff --git a/images/icons/v2/edit-16.svg b/images/icons/v2/edit-16.svg new file mode 100644 index 000000000000..71ec99c5d87c --- /dev/null +++ b/images/icons/v2/edit-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/edit-solid-16.svg b/images/icons/v2/edit-solid-16.svg new file mode 100644 index 000000000000..23c1c815f440 --- /dev/null +++ b/images/icons/v2/edit-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/info-16.svg b/images/icons/v2/info-16.svg new file mode 100644 index 000000000000..3dcb70e1351d --- /dev/null +++ b/images/icons/v2/info-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/info-solid-24.svg b/images/icons/v2/info-solid-24.svg index d7be23befd69..849f9cc44711 100644 --- a/images/icons/v2/info-solid-24.svg +++ b/images/icons/v2/info-solid-24.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/icons/v2/leave-16.svg b/images/icons/v2/leave-16.svg new file mode 100644 index 000000000000..6aac3a91eeba --- /dev/null +++ b/images/icons/v2/leave-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/leave-solid-16.svg b/images/icons/v2/leave-solid-16.svg new file mode 100644 index 000000000000..c754757daf47 --- /dev/null +++ b/images/icons/v2/leave-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/megaphone-16.svg b/images/icons/v2/megaphone-16.svg new file mode 100644 index 000000000000..212dc0437f75 --- /dev/null +++ b/images/icons/v2/megaphone-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/megaphone-solid-16.svg b/images/icons/v2/megaphone-solid-16.svg new file mode 100644 index 000000000000..377a7657ea56 --- /dev/null +++ b/images/icons/v2/megaphone-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-accepted-16.svg b/images/icons/v2/member-accepted-16.svg new file mode 100644 index 000000000000..46999512d9ef --- /dev/null +++ b/images/icons/v2/member-accepted-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-accepted-solid-16.svg b/images/icons/v2/member-accepted-solid-16.svg new file mode 100644 index 000000000000..1e3b18db8e4b --- /dev/null +++ b/images/icons/v2/member-accepted-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-added-16.svg b/images/icons/v2/member-added-16.svg new file mode 100644 index 000000000000..4ab881445e63 --- /dev/null +++ b/images/icons/v2/member-added-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-added-solid-16.svg b/images/icons/v2/member-added-solid-16.svg new file mode 100644 index 000000000000..f2245117f862 --- /dev/null +++ b/images/icons/v2/member-added-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-declined-16.svg b/images/icons/v2/member-declined-16.svg new file mode 100644 index 000000000000..a2f25c85269b --- /dev/null +++ b/images/icons/v2/member-declined-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-declined-solid-16.svg b/images/icons/v2/member-declined-solid-16.svg new file mode 100644 index 000000000000..11957627dfde --- /dev/null +++ b/images/icons/v2/member-declined-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-remove-16.svg b/images/icons/v2/member-remove-16.svg new file mode 100644 index 000000000000..e96eecb26cb5 --- /dev/null +++ b/images/icons/v2/member-remove-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/member-remove-solid-16.svg b/images/icons/v2/member-remove-solid-16.svg new file mode 100644 index 000000000000..5859fe4a0da4 --- /dev/null +++ b/images/icons/v2/member-remove-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/phone-incoming-16.svg b/images/icons/v2/phone-incoming-16.svg new file mode 100644 index 000000000000..6fb9d10225ea --- /dev/null +++ b/images/icons/v2/phone-incoming-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/phone-incoming-solid-16.svg b/images/icons/v2/phone-incoming-solid-16.svg new file mode 100644 index 000000000000..a2d751fb5c5b --- /dev/null +++ b/images/icons/v2/phone-incoming-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/phone-outgoing-16.svg b/images/icons/v2/phone-outgoing-16.svg new file mode 100644 index 000000000000..f5f27b1c52c6 --- /dev/null +++ b/images/icons/v2/phone-outgoing-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/phone-outgoing-solid-16.svg b/images/icons/v2/phone-outgoing-solid-16.svg new file mode 100644 index 000000000000..85bba3542e13 --- /dev/null +++ b/images/icons/v2/phone-outgoing-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/phone-x-16.svg b/images/icons/v2/phone-x-16.svg new file mode 100644 index 000000000000..97e0d410f793 --- /dev/null +++ b/images/icons/v2/phone-x-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/phone-x-solid-16.svg b/images/icons/v2/phone-x-solid-16.svg new file mode 100644 index 000000000000..91209df87580 --- /dev/null +++ b/images/icons/v2/phone-x-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/photo-16.svg b/images/icons/v2/photo-16.svg new file mode 100644 index 000000000000..edd6799d3085 --- /dev/null +++ b/images/icons/v2/photo-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/photo-solid-16.svg b/images/icons/v2/photo-solid-16.svg new file mode 100644 index 000000000000..cee85fd5c676 --- /dev/null +++ b/images/icons/v2/photo-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/video-incoming-16.svg b/images/icons/v2/video-incoming-16.svg new file mode 100644 index 000000000000..326ae01bb6eb --- /dev/null +++ b/images/icons/v2/video-incoming-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/video-incoming-solid-16.svg b/images/icons/v2/video-incoming-solid-16.svg new file mode 100644 index 000000000000..b6fb93ee6696 --- /dev/null +++ b/images/icons/v2/video-incoming-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/video-outgoing-16.svg b/images/icons/v2/video-outgoing-16.svg new file mode 100644 index 000000000000..73e79b37985e --- /dev/null +++ b/images/icons/v2/video-outgoing-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/video-outgoing-solid-16.svg b/images/icons/v2/video-outgoing-solid-16.svg new file mode 100644 index 000000000000..c7be25d438e4 --- /dev/null +++ b/images/icons/v2/video-outgoing-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/video-x-16.svg b/images/icons/v2/video-x-16.svg new file mode 100644 index 000000000000..46e3703e0098 --- /dev/null +++ b/images/icons/v2/video-x-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/v2/video-x-solid-16.svg b/images/icons/v2/video-x-solid-16.svg new file mode 100644 index 000000000000..195d3005996d --- /dev/null +++ b/images/icons/v2/video-x-solid-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/preload.js b/preload.js index a112b7ce5299..f0a60c606eab 100644 --- a/preload.js +++ b/preload.js @@ -405,6 +405,7 @@ try { window.ReactDOM = require('react-dom'); window.moment = require('moment'); window.PQueue = require('p-queue').default; + window.sharp = require('sharp'); const Signal = require('./js/modules/signal'); const i18n = require('./js/modules/i18n'); diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 3b6f1fab9e28..2563da0e86d8 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -2074,22 +2074,6 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05', // Module: Group Notification -.module-group-notification { - margin-left: 1em; - margin-right: 1em; - margin-top: 5px; - margin-bottom: 5px; - - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} - .module-group-notification__change { margin-top: 2px; margin-bottom: 2px; @@ -2099,369 +2083,10 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05', font-weight: bold; } -// Module: Reset Session Notification - -.module-reset-session-notification { - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} - -// Module: Safety Number Notification - -.module-safety-number-notification { - text-align: center; -} - -.module-safety-number-notification__icon { - height: 24px; - width: 24px; - margin-left: auto; - margin-right: auto; - margin-bottom: 7px; - - @include light-theme { - @include color-svg( - '../images/icons/v2/safety-number-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/safety-number-solid-24.svg', - $color-gray-25 - ); - } -} - -.module-safety-number-notification__text { - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} - -.module-safety-number-notification__contact { - font-weight: bold; -} - -.module-safety-number-notification__button { - @include button-reset; - - @include font-body-2-bold; - - margin-top: 5px; - display: inline-block; - - padding: 12px; - border-radius: 4px; - - color: $color-ultramarine; - - @include light-theme { - background-color: $color-gray-02; - } - @include keyboard-mode { - &:focus { - background-color: $color-gray-15; - } - } - - @include dark-theme { - background-color: $color-gray-75; - } - @include dark-keyboard-mode { - &:focus { - background-color: $color-gray-60; - } - } -} - -.module-message-calling--notification { - .module-message__metadata__date { - @include light-theme { - color: $color-gray-90; - } - @include dark-theme { - color: $color-gray-05; - } - } - - &__button { - @include button-reset; - @include button-small; - @include button-green; - @include font-body-1-bold; - - display: block; - margin: 0.5rem auto 0 auto; - } -} - .module-safety-number__bold-name { font-weight: bold; } -.module-message-calling { - &--audio { - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } - } - - &--audio__icon { - height: 24px; - margin-bottom: 4px; - margin-left: auto; - margin-right: auto; - width: 24px; - - @include light-theme { - @include color-svg( - '../images/icons/v2/phone-right-outline-24.svg', - $color-gray-75 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/phone-right-outline-24.svg', - $color-gray-15 - ); - } - } - - &--video { - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } - } - - &--video__icon { - height: 24px; - margin-bottom: 4px; - margin-left: auto; - margin-right: auto; - width: 24px; - - @include light-theme { - @include color-svg( - '../images/icons/v2/video-outline-24.svg', - $color-gray-75 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/video-outline-24.svg', - $color-gray-15 - ); - } - } -} - -.module-message-unsynced { - padding-bottom: 24px; - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} - -.module-message-unsynced__icon { - height: 24px; - margin-bottom: 4px; - margin-left: auto; - margin-right: auto; - width: 24px; - - @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: Verification Notification - -.module-verification-notification { - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} - -.module-verification-notification__contact { - font-weight: bold; -} - -.module-verification-notification__icon--mark-verified { - height: 24px; - width: 24px; - margin-left: auto; - margin-right: auto; - margin-bottom: 4px; - - @include light-theme { - @include color-svg('../images/icons/v2/check-24.svg', $color-gray-60); - } - @include dark-theme { - @include color-svg('../images/icons/v2/check-24.svg', $color-gray-25); - } -} - -.module-verification-notification__icon--mark-not-verified { - height: 24px; - width: 24px; - margin-left: auto; - margin-right: auto; - margin-bottom: 7px; - @include light-theme { - @include color-svg( - '../images/icons/v2/safety-number-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/safety-number-solid-24.svg', - $color-gray-25 - ); - } -} - -// Module: Timer Notification -// Module: Universal Timer Notification - -.module-timer-notification, -.module-universal-timer-notification { - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } - - &__icon-container { - margin-left: auto; - margin-right: auto; - display: inline-flex; - flex-direction: row; - align-items: center; - margin-bottom: 8px; - } - - &__icon { - height: 20px; - width: 20px; - display: inline-block; - opacity: 0.6; - - @include light-theme { - @include color-svg( - '../images/icons/v2/timer-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/timer-solid-16.svg', - $color-gray-05 - ); - } - - &--disabled { - @include light-theme { - @include color-svg( - '../images/icons/v2/timer-disabled-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/timer-disabled-solid-16.svg', - $color-gray-05 - ); - } - } - } - - &__icon-label { - margin-left: 4px; - - // Didn't seem centered otherwise - margin-top: 1px; - - opacity: 0.8; - } -} - -// Module: Change Number Notification - -.module-change-number-notification { - @include font-body-2; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } - - &__icon { - height: 16px; - width: 16px; - display: inline-block; - margin-right: 8px; - - @include light-theme { - @include color-svg( - '../images/icons/v2/phone-right-outline-24.svg', - $color-gray-75 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/phone-right-solid-24.svg', - $color-gray-15 - ); - } - } -} - // Module: Error Boundary .module-error-boundary-notification { @@ -8202,85 +7827,6 @@ button.module-image__border-overlay:focus { @include emoji-size(66px); } -// Module: Unsupported Message - -.module-unsupported-message { - text-align: center; -} - -.module-unsupported-message__icon { - height: 24px; - width: 24px; - margin-left: auto; - margin-right: auto; - margin-bottom: 7px; - - @include light-theme { - @include color-svg( - '../images/icons/v2/error-outline-24.svg', - $color-gray-60 - ); - } - - @include dark-theme { - @include color-svg('../images/icons/v2/error-solid-24.svg', $color-gray-25); - } -} - -.module-unsupported-message__icon--can-process { - @include light-theme { - @include color-svg( - '../images/icons/v2/check-circle-outline-24.svg', - $color-gray-60 - ); - } - - @include dark-theme { - @include color-svg( - '../images/icons/v2/check-circle-solid-24.svg', - $color-gray-25 - ); - } -} - -.module-unsupported-message__text { - max-width: 396px; - margin-left: auto; - margin-right: auto; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} - -.module-unsupported-message__contact { - font-weight: bold; -} - -.module-unsupported-message__button { - @include button-reset; - - margin-top: 5px; - display: inline-block; - - @include font-body-1-bold; - - padding: 12px; - border-radius: 4px; - - @include light-theme { - color: $color-ultramarine; - background-color: $color-gray-02; - } - @include dark-theme { - color: $color-ultramarine-light; - background-color: $color-gray-75; - } -} - // Module: Countdown .module-countdown { @@ -8952,45 +8498,6 @@ button.module-image__border-overlay:focus { padding-right: 0px; } -// Module: Profile Change Notification - -.module-profile-change-notification { - @include font-body-1; - - margin-left: 2em; - margin-right: 2em; - - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } -} - -.module-profile-change-notification--icon { - @include light-theme { - @include color-svg( - '../images/icons/v2/profile-outline-20.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/profile-outline-20.svg', - $color-gray-05 - ); - } - - height: 20px; - width: 20px; - - margin-left: auto; - margin-right: auto; -} - /* Calling: Device Selection */ .module-calling-device-selection { @@ -9066,105 +8573,6 @@ button.module-image__border-overlay:focus { } } -// Module: GroupV2 Change - -.module-group-v2-change { - @include font-body-1; - - margin-left: 2em; - margin-right: 2em; - - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } -} - -.module-group-v2-change--icon { - @include light-theme { - @include color-svg( - '../images/icons/v2/group-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/group-outline-24.svg', - $color-gray-05 - ); - } - - height: 20px; - width: 20px; - - margin-left: auto; - margin-right: auto; -} - -.module-group-v2-change--button-container { - margin-top: 10px; -} - -// Module: GV1 Migration - -.module-group-v1-migration { - @include font-body-1; - - margin-left: 2em; - margin-right: 2em; - - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-05; - } -} - -.module-group-v1-migration--icon { - @include light-theme { - @include color-svg( - '../images/icons/v2/group-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/group-outline-24.svg', - $color-gray-05 - ); - } - - height: 20px; - width: 20px; - - margin-left: auto; - margin-right: auto; -} - -.module-group-v1-migration--text { - margin-top: 8px; - margin-bottom: 8px; -} - -.module-group-v1-migration--button { - @include button-reset; - @include font-body-2-bold; - border-radius: 4px; - - padding: 8px; - padding-left: 40px; - padding-right: 40px; - - @include button-light-blue-text; -} - // Module: GroupV1 Disabled Actions .module-group-v1-disabled-actions { @@ -9765,99 +9173,6 @@ $contact-modal-padding: 18px; } } -// Module: Chat Session Refreshed Notification - -.module-chat-session-refreshed-notification { - @include font-body-2; - display: flex; - flex-direction: column; - align-items: center; -} - -.module-chat-session-refreshed-notification__first-line { - margin-bottom: 12px; - display: flex; - flex-direction: row; - align-items: center; - - margin-left: auto; - margin-right: auto; -} -.module-chat-session-refreshed-notification__icon { - height: 16px; - width: 16px; - display: inline-block; - margin-right: 8px; - - @include light-theme { - @include color-svg('../images/icons/v2/refresh-16.svg', $color-gray-60); - } - @include dark-theme { - @include color-svg('../images/icons/v2/refresh-16.svg', $color-gray-25); - } -} -.module-chat-session-refreshed-notification__button { - @include button-reset; - @include button-light-blue-text; - @include button-small; - - @include font-body-2; - padding: 5px 12px; -} - -// Module: Delivery Issue Notification - -.module-delivery-issue-notification { - @include font-body-2; - display: flex; - flex-direction: column; - align-items: center; -} - -.module-delivery-issue-notification__first-line { - margin-bottom: 12px; - display: flex; - flex-direction: row; - align-items: center; - - margin-left: auto; - margin-right: auto; - - @include light-theme { - color: $color-gray-60; - } - @include dark-theme { - color: $color-gray-25; - } -} -.module-delivery-issue-notification__icon { - height: 14px; - width: 14px; - display: inline-block; - margin-right: 8px; - - @include light-theme { - @include color-svg( - '../images/icons/v2/error-outline-12.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/error-outline-12.svg', - $color-gray-25 - ); - } -} -.module-delivery-issue-notification__button { - @include button-reset; - @include button-light-blue-text; - @include button-small; - - @include font-body-2; - padding: 5px 12px; -} - // Module: Chat Session Refreshed Dialog .module-chat-session-refreshed-dialog { diff --git a/stylesheets/components/Button.scss b/stylesheets/components/Button.scss index f18b2d13890a..a6af7ba6f7f1 100644 --- a/stylesheets/components/Button.scss +++ b/stylesheets/components/Button.scss @@ -156,4 +156,36 @@ @include hover-and-active-states($background-color, $color-white); } } + + &--system-message { + @include rounded-corners; + + @include light-theme { + $color: $color-ultramarine; + $background-color: $color-gray-02; + + color: $color; + background: $background-color; + + &:disabled { + color: fade-out($color, 0.4); + background: fade-out($background-color, 0.6); + } + @include hover-and-active-states($background-color, $color-black); + } + + @include dark-theme { + $color: $color-white; + $background-color: $color-gray-65; + + color: $color; + background: $background-color; + + &:disabled { + color: fade-out($color, 0.4); + background: fade-out($background-color, 0.6); + } + @include hover-and-active-states($background-color, $color-white); + } + } } diff --git a/stylesheets/components/SystemMessage.scss b/stylesheets/components/SystemMessage.scss new file mode 100644 index 000000000000..1cc784e98f1e --- /dev/null +++ b/stylesheets/components/SystemMessage.scss @@ -0,0 +1,277 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +@mixin system-message-icon($light, $dark) { + @include light-theme { + -webkit-mask: url($light) no-repeat center; + -webkit-mask-size: 100%; + } + @include dark-theme { + -webkit-mask: url($dark) no-repeat center; + -webkit-mask-size: 100%; + } +} + +.SystemMessage { + @include font-body-2; + align-items: center; + display: flex; + flex-direction: row; + justify-content: center; + text-align: center; + + @include light-theme { + color: $color-black; + } + @include dark-theme { + color: $color-gray-25; + } + + &--multiline { + flex-direction: column; + } + + &__line { + align-items: center; + display: flex; + + &:not(:first-child) { + margin-top: 12px; + } + } + + &__text { + max-width: 400px; + } + + &__icon { + height: 16px; + flex: 0 0 16px; + margin-right: 8px; + width: 16px; + vertical-align: middle; + + @include light-theme { + background-color: $color-black; + } + @include dark-theme { + background-color: $color-gray-25; + } + + &--audio-incoming { + @include system-message-icon( + '../images/icons/v2/phone-incoming-16.svg', + '../images/icons/v2/phone-incoming-solid-16.svg' + ); + } + + &--audio-missed { + @include system-message-icon( + '../images/icons/v2/phone-x-16.svg', + '../images/icons/v2/phone-x-solid-16.svg' + ); + } + + &--audio-outgoing { + @include system-message-icon( + '../images/icons/v2/phone-outgoing-16.svg', + '../images/icons/v2/phone-outgoing-solid-16.svg' + ); + } + + &--group { + @include system-message-icon( + '../images/icons/v2/group-outline-24.svg', + '../images/icons/v2/group-solid-24.svg' + ); + } + + &--group-access { + @include system-message-icon( + '../images/icons/v2/megaphone-16.svg', + '../images/icons/v2/megaphone-solid-16.svg' + ); + } + + &--group-add { + @include system-message-icon( + '../images/icons/v2/member-added-16.svg', + '../images/icons/v2/member-added-solid-16.svg' + ); + } + + &--group-approved { + @include system-message-icon( + '../images/icons/v2/member-accepted-16.svg', + '../images/icons/v2/member-accepted-solid-16.svg' + ); + } + + &--group-avatar { + @include system-message-icon( + '../images/icons/v2/photo-16.svg', + '../images/icons/v2/photo-solid-16.svg' + ); + } + + &--group-decline { + @include system-message-icon( + '../images/icons/v2/member-declined-16.svg', + '../images/icons/v2/member-declined-solid-16.svg' + ); + } + + &--group-edit { + @include system-message-icon( + '../images/icons/v2/edit-16.svg', + '../images/icons/v2/edit-solid-16.svg' + ); + } + + &--group-leave { + @include system-message-icon( + '../images/icons/v2/leave-16.svg', + '../images/icons/v2/leave-solid-16.svg' + ); + } + + &--group-remove { + @include system-message-icon( + '../images/icons/v2/member-remove-16.svg', + '../images/icons/v2/member-remove-solid-16.svg' + ); + } + + &--info { + @include system-message-icon( + '../images/icons/v2/info-16.svg', + '../images/icons/v2/info-solid-24.svg' + ); + } + + &--phone { + @include system-message-icon( + '../images/icons/v2/phone-right-outline-24.svg', + '../images/icons/v2/phone-right-solid-24.svg' + ); + } + + &--profile { + @include system-message-icon( + '../images/icons/v2/profile-outline-20.svg', + '../images/icons/v2/profile-outline-20.svg' + ); + } + + &--safety-number { + @include system-message-icon( + '../images/icons/v2/safety-number-outline-24.svg', + '../images/icons/v2/safety-number-solid-24.svg' + ); + } + + &--session-refresh { + @include system-message-icon( + '../images/icons/v2/refresh-16.svg', + '../images/icons/v2/refresh-16.svg' + ); + } + + &--timer { + @include system-message-icon( + '../images/icons/v2/timer-outline-24.svg', + '../images/icons/v2/timer-solid-16.svg' + ); + } + + &--timer-disabled { + @include system-message-icon( + '../images/icons/v2/timer-disabled-outline-24.svg', + '../images/icons/v2/timer-disabled-solid-16.svg' + ); + } + + &--unsupported { + @include system-message-icon( + '../images/icons/v2/error-outline-24.svg', + '../images/icons/v2/error-solid-24.svg' + ); + + &--can-process { + @include system-message-icon( + '../images/icons/v2/check-circle-outline-24.svg', + '../images/icons/v2/check-circle-solid-24.svg' + ); + } + } + + &--unsynced { + @include system-message-icon( + '../images/icons/v2/info-outline-24.svg', + '../images/icons/v2/info-solid-24.svg' + ); + } + + &--verified { + @include system-message-icon( + '../images/icons/v2/check-24.svg', + '../images/icons/v2/check-24.svg' + ); + } + + &--verified-not { + @include system-message-icon( + '../images/icons/v2/safety-number-outline-24.svg', + '../images/icons/v2/safety-number-solid-24.svg' + ); + } + + &--video { + @include system-message-icon( + '../images/icons/v2/video-outline-24.svg', + '../images/icons/v2/video-outline-24.svg' + ); + } + + &--video-incoming { + @include system-message-icon( + '../images/icons/v2/video-incoming-16.svg', + '../images/icons/v2/video-incoming-solid-16.svg' + ); + } + + &--video-missed { + @include system-message-icon( + '../images/icons/v2/video-x-16.svg', + '../images/icons/v2/video-x-solid-16.svg' + ); + } + + &--video-outgoing { + @include system-message-icon( + '../images/icons/v2/video-outgoing-16.svg', + '../images/icons/v2/video-outgoing-solid-16.svg' + ); + } + + &--warning { + @include system-message-icon( + '../images/icons/v2/error-outline-12.svg', + '../images/icons/v2/error-outline-12.svg' + ); + } + } + + &--error { + color: $color-accent-red; + + .SystemMessage__icon { + background: $color-accent-red; + } + } + + img.emoji { + // The negative bottom margin offset doesn't play well with align-items center + margin-bottom: 0; + } +} diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index 72ef897023c8..cc0221440a82 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -76,6 +76,7 @@ @import './components/SearchResultsLoadingFakeRow.scss'; @import './components/Select.scss'; @import './components/Slider.scss'; +@import './components/SystemMessage.scss'; @import './components/Tabs.scss'; @import './components/TimelineWarning.scss'; @import './components/TimelineWarnings.scss'; diff --git a/ts/components/Button.stories.tsx b/ts/components/Button.stories.tsx index 776968c226bc..92ecd5bc3283 100644 --- a/ts/components/Button.stories.tsx +++ b/ts/components/Button.stories.tsx @@ -11,20 +11,21 @@ const story = storiesOf('Components/Button', module); story.add('Kitchen sink', () => ( <> - {[ButtonSize.Medium, ButtonSize.Small].map(size => ( - - {[ - ButtonVariant.Primary, - ButtonVariant.Secondary, - ButtonVariant.SecondaryAffirmative, - ButtonVariant.SecondaryDestructive, - ButtonVariant.Destructive, - ButtonVariant.Calling, - ].map(variant => ( - + {[ + ButtonVariant.Primary, + ButtonVariant.Secondary, + ButtonVariant.SecondaryAffirmative, + ButtonVariant.SecondaryDestructive, + ButtonVariant.Destructive, + ButtonVariant.Calling, + ButtonVariant.SystemMessage, + ].map(variant => ( + + {[ButtonSize.Medium, ButtonSize.Small].map(size => ( +

@@ -34,7 +35,7 @@ story.add('Kitchen sink', () => ( size={size} variant={variant} > - Hello world + {variant}

diff --git a/ts/components/Button.tsx b/ts/components/Button.tsx index dceaec2ca68e..f4cd8ea55f62 100644 --- a/ts/components/Button.tsx +++ b/ts/components/Button.tsx @@ -12,12 +12,13 @@ export enum ButtonSize { } export enum ButtonVariant { - Primary, - Secondary, - SecondaryAffirmative, - SecondaryDestructive, - Destructive, - Calling, + Primary = 'Primary', + Secondary = 'Secondary', + SecondaryAffirmative = 'SecondaryAffirmative', + SecondaryDestructive = 'SecondaryDestructive', + Destructive = 'Destructive', + Calling = 'Calling', + SystemMessage = 'SystemMessage', } type PropsType = { @@ -68,6 +69,7 @@ const VARIANT_CLASS_NAMES = new Map([ ], [ButtonVariant.Destructive, 'module-Button--destructive'], [ButtonVariant.Calling, 'module-Button--calling'], + [ButtonVariant.SystemMessage, 'module-Button--system-message'], ]); export const Button = React.forwardRef( diff --git a/ts/components/conversation/CallingNotification.tsx b/ts/components/conversation/CallingNotification.tsx index e735262f9942..e3a285da3151 100644 --- a/ts/components/conversation/CallingNotification.tsx +++ b/ts/components/conversation/CallingNotification.tsx @@ -2,13 +2,17 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState, useEffect } from 'react'; +import classNames from 'classnames'; import Measure from 'react-measure'; +import { noop } from 'lodash'; +import { Button, ButtonSize, ButtonVariant } from '../Button'; import { Timestamp } from './Timestamp'; import { LocalizerType } from '../../types/Util'; import { CallMode } from '../../types/Calling'; import { CallingNotificationType, + getCallingIcon, getCallingNotificationText, } from '../../util/callingNotification'; import { usePrevious } from '../../util/hooks'; @@ -48,16 +52,18 @@ export const CallingNotification: React.FC = React.memo(props => { } }, [height, previousHeight, conversationId, messageId, messageSizeChanged]); + let hasButton = false; let timestamp: number; - let callType: 'audio' | 'video'; + let wasMissed = false; switch (props.callMode) { case CallMode.Direct: timestamp = props.acceptedTime || props.endedTime; - callType = props.wasVideoCall ? 'video' : 'audio'; + wasMissed = + props.wasIncoming && !props.acceptedTime && !props.wasDeclined; break; case CallMode.Group: + hasButton = !props.ended; timestamp = props.startedTime; - callType = 'video'; break; default: window.log.error( @@ -66,6 +72,8 @@ export const CallingNotification: React.FC = React.memo(props => { return null; } + const icon = getCallingIcon(props); + return ( = React.memo(props => { > {({ measureRef }) => (
-
- {getCallingNotificationText(props, i18n)} -
- +
+
+ {getCallingNotificationText(props, i18n)} ·{' '} + +
- + {hasButton ? ( +
+ +
+ ) : null}
)} @@ -120,7 +137,7 @@ function CallingNotificationButton(props: PropsType) { let buttonText: string; let disabledTooltipText: undefined | string; - let onClick: undefined | (() => void); + let onClick: () => void; if (activeCallConversationId) { if (activeCallConversationId === conversationId) { buttonText = i18n('calling__return'); @@ -130,6 +147,7 @@ function CallingNotificationButton(props: PropsType) { disabledTooltipText = i18n( 'calling__call-notification__button__in-another-call-tooltip' ); + onClick = noop; } } else if (deviceCount >= maxDevices) { buttonText = i18n('calling__call-is-full'); @@ -137,6 +155,7 @@ function CallingNotificationButton(props: PropsType) { 'calling__call-notification__button__call-full-tooltip', [String(deviceCount)] ); + onClick = noop; } else { buttonText = i18n('calling__join'); onClick = () => { @@ -145,14 +164,14 @@ function CallingNotificationButton(props: PropsType) { } const button = ( - + ); if (disabledTooltipText) { diff --git a/ts/components/conversation/ChangeNumberNotification.tsx b/ts/components/conversation/ChangeNumberNotification.tsx index f63ce58c6ece..d022eee9edeb 100644 --- a/ts/components/conversation/ChangeNumberNotification.tsx +++ b/ts/components/conversation/ChangeNumberNotification.tsx @@ -21,14 +21,12 @@ export type PropsHousekeeping = { export type Props = PropsData & PropsHousekeeping; -const CSS_MODULE = 'module-change-number-notification'; - export const ChangeNumberNotification: React.FC = props => { const { i18n, sender, timestamp } = props; return ( -
- +
+ -
- +
+
+ {i18n('ChatRefresh--notification')}
- +
+ +
{isDialogOpen ? ( -
- +
+
+
- +
+ +
{isDialogOpen ? ( { ); return ( -
- {isLeftOnly ? null : ( - <> - {fromLabel} -
- - )} - {(changes || []).map((change, i) => ( - // eslint-disable-next-line react/no-array-index-key -
- {this.renderChange(change, from)} -
- ))} +
+
+ {isLeftOnly ? null : ( + <> + {fromLabel} +
+ + )} + {(changes || []).map((change, i) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {this.renderChange(change, from)} +
+ ))} +
); } diff --git a/ts/components/conversation/GroupV1Migration.tsx b/ts/components/conversation/GroupV1Migration.tsx index 08c0c4ddd2fa..7975312e0d3e 100644 --- a/ts/components/conversation/GroupV1Migration.tsx +++ b/ts/components/conversation/GroupV1Migration.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; +import { Button, ButtonSize, ButtonVariant } from '../Button'; import { LocalizerType } from '../../types/Util'; import { ConversationType } from '../../state/ducks/conversations'; import { Intl } from '../Intl'; @@ -34,28 +35,40 @@ export function GroupV1Migration(props: PropsType): React.ReactElement { }, [setShowingDialog]); return ( -
-
-
- {i18n('GroupV1--Migration--was-upgraded')} -
- {areWeInvited ? ( -
- {i18n('GroupV1--Migration--invited--you')} +
+
+
+
+
{i18n('GroupV1--Migration--was-upgraded')}
+
+ {areWeInvited ? ( + i18n('GroupV1--Migration--invited--you') + ) : ( + <> + {renderUsers( + invitedMembers, + i18n, + 'GroupV1--Migration--invited' + )} + {renderUsers( + droppedMembers, + i18n, + 'GroupV1--Migration--removed' + )} + + )} +
- ) : ( - <> - {renderUsers(invitedMembers, i18n, 'GroupV1--Migration--invited')} - {renderUsers(droppedMembers, i18n, 'GroupV1--Migration--removed')} - - )} - +
+
+ +
{showingDialog ? ( ; } -export function GroupV2Change(props: PropsType): ReactElement { - const { change, groupName, i18n, ourConversationId, renderContact } = props; +type GroupIconType = + | 'group' + | 'group-access' + | 'group-add' + | 'group-approved' + | 'group-avatar' + | 'group-decline' + | 'group-edit' + | 'group-leave' + | 'group-remove'; - const [ - isGroupDescriptionDialogOpen, - setIsGroupDescriptionDialogOpen, - ] = useState(false); +const changeToIconMap = new Map([ + ['access-attributes', 'group-access'], + ['access-invite-link', 'group-access'], + ['access-members', 'group-access'], + ['admin-approval-add-one', 'group-add'], + ['admin-approval-remove-one', 'group-decline'], + ['announcements-only', 'group-access'], + ['avatar', 'group-avatar'], + ['description', 'group-edit'], + ['group-link-add', 'group-access'], + ['group-link-remove', 'group-access'], + ['group-link-reset', 'group-access'], + ['member-add', 'group-add'], + ['member-add-from-admin-approval', 'group-approved'], + ['member-add-from-invite', 'group-add'], + ['member-add-from-link', 'group-add'], + ['member-privilege', 'group-access'], + ['member-remove', 'group-remove'], + ['pending-add-many', 'group-add'], + ['pending-add-one', 'group-add'], + ['pending-remove-many', 'group-decline'], + ['pending-remove-one', 'group-decline'], + ['title', 'group-edit'], +]); - const newGroupDescription = change.details.find( - (item): item is GroupV2DescriptionChangeType => - Boolean(item.type === 'description' && item.description) - )?.description; +function getIcon( + detail: GroupV2ChangeDetailType, + fromId?: string +): GroupIconType { + const changeType = detail.type; + let possibleIcon = changeToIconMap.get(changeType); + const isSameId = fromId === get(detail, 'conversationId', null); + if (isSameId) { + if (changeType === 'member-remove') { + possibleIcon = 'group-leave'; + } + if (changeType === 'member-add-from-invite') { + possibleIcon = 'group-approved'; + } + } + return possibleIcon || 'group'; +} + +function GroupV2Detail({ + detail, + i18n, + fromId, + onButtonClick, + text, +}: { + detail: GroupV2ChangeDetailType; + i18n: LocalizerType; + fromId?: string; + onButtonClick: (x: string) => unknown; + text: FullJSXType; +}): JSX.Element { + const icon = getIcon(detail, fromId); + + const newGroupDescription = + detail.type === 'description' && get(detail, 'description'); return ( -
-
- {renderChange(change, { - i18n, - ourConversationId, - renderContact, - renderString: renderStringToIntl, - }).map((item: FullJSXType, index: number) => ( - // Difficult to find a unique key for this type - // eslint-disable-next-line react/no-array-index-key -
{item}
- ))} +
+
+
+
{text}
+
{newGroupDescription ? ( -
+
) : null} - {newGroupDescription && isGroupDescriptionDialogOpen ? ( +
+ ); +} + +export function GroupV2Change(props: PropsType): ReactElement { + const { change, groupName, i18n, ourConversationId, renderContact } = props; + + const [groupDescription, setGroupDescription] = useState< + string | undefined + >(); + + return ( + <> + {renderChange(change, { + i18n, + ourConversationId, + renderContact, + renderString: renderStringToIntl, + }).map((text: FullJSXType, index: number) => ( + + setGroupDescription(nextGroupDescription) + } + text={text} + /> + ))} + {groupDescription ? ( setIsGroupDescriptionDialogOpen(false)} + onClose={() => setGroupDescription(undefined)} > - + ) : null} -
+ ); } diff --git a/ts/components/conversation/ProfileChangeNotification.tsx b/ts/components/conversation/ProfileChangeNotification.tsx index a023d577ac08..a6163a9b99ac 100644 --- a/ts/components/conversation/ProfileChangeNotification.tsx +++ b/ts/components/conversation/ProfileChangeNotification.tsx @@ -22,8 +22,8 @@ export function ProfileChangeNotification(props: PropsType): JSX.Element { const message = getStringForProfileChange(change, changedContact, i18n); return ( -
-
+
+
); diff --git a/ts/components/conversation/ResetSessionNotification.tsx b/ts/components/conversation/ResetSessionNotification.tsx index 18fad2304afc..422a27ec3bde 100644 --- a/ts/components/conversation/ResetSessionNotification.tsx +++ b/ts/components/conversation/ResetSessionNotification.tsx @@ -10,7 +10,5 @@ export type Props = { }; export const ResetSessionNotification = ({ i18n }: Props): JSX.Element => ( -
- {i18n('sessionEnded')} -
+
{i18n('sessionEnded')}
); diff --git a/ts/components/conversation/SafetyNumberNotification.tsx b/ts/components/conversation/SafetyNumberNotification.tsx index 2220bc0135fd..f7c137bfe877 100644 --- a/ts/components/conversation/SafetyNumberNotification.tsx +++ b/ts/components/conversation/SafetyNumberNotification.tsx @@ -3,6 +3,7 @@ import React from 'react'; +import { Button, ButtonSize, ButtonVariant } from '../Button'; import { ContactName } from './ContactName'; import { Intl } from '../Intl'; import { LocalizerType } from '../../types/Util'; @@ -41,38 +42,42 @@ export const SafetyNumberNotification = ({ : 'safetyNumberChanged'; return ( -
-
-
- - - , - ]} - i18n={i18n} - /> +
+
+
+ + + + , + ]} + i18n={i18n} + /> + +
+
+
-
); }; diff --git a/ts/components/conversation/TimelineItem.stories.tsx b/ts/components/conversation/TimelineItem.stories.tsx index 2588395f4a5f..d282530f5fca 100644 --- a/ts/components/conversation/TimelineItem.stories.tsx +++ b/ts/components/conversation/TimelineItem.stories.tsx @@ -118,19 +118,26 @@ storiesOf('Components/Conversation/TimelineItem', module) type: 'fromOther', }, }, + { + type: 'universalTimerNotification', + data: null, + }, { type: 'chatSessionRefreshed', }, + { + type: 'safetyNumberNotification', + data: { + isGroup: false, + contact: getDefaultConversation(), + }, + }, { type: 'deliveryIssue', data: { sender: getDefaultConversation(), }, }, - { - type: 'universalTimerNotification', - data: null, - }, { type: 'changeNumberNotification', data: { @@ -255,7 +262,7 @@ storiesOf('Components/Conversation/TimelineItem', module) { type: 'callHistory', data: { - // missed (neither accepted nor declined) outgoing audio + // unanswered (neither accepted nor declined) outgoing audio callMode: CallMode.Direct, wasDeclined: false, wasIncoming: false, @@ -266,7 +273,7 @@ storiesOf('Components/Conversation/TimelineItem', module) { type: 'callHistory', data: { - // missed (neither accepted nor declined) outgoing video + // unanswered (neither accepted nor declined) outgoing video callMode: CallMode.Direct, wasDeclined: false, wasIncoming: false, @@ -390,6 +397,71 @@ storiesOf('Components/Conversation/TimelineItem', module) startedTime: Date.now(), }, }, + { + type: 'linkNotification', + data: null, + }, + { + type: 'profileChange', + data: { + change: { + type: 'name', + oldName: 'Fred', + newName: 'John', + }, + changedContact: getDefaultConversation(), + }, + }, + { + type: 'resetSessionNotification', + data: null, + }, + { + type: 'unsupportedMessage', + data: { + canProcessNow: true, + contact: getDefaultConversation(), + }, + }, + { + type: 'unsupportedMessage', + data: { + canProcessNow: false, + contact: getDefaultConversation(), + }, + }, + { + type: 'verificationNotification', + data: { + type: 'markVerified', + isLocal: false, + contact: getDefaultConversation(), + }, + }, + { + type: 'verificationNotification', + data: { + type: 'markVerified', + isLocal: true, + contact: getDefaultConversation(), + }, + }, + { + type: 'verificationNotification', + data: { + type: 'markNotVerified', + isLocal: false, + contact: getDefaultConversation(), + }, + }, + { + type: 'verificationNotification', + data: { + type: 'markNotVerified', + isLocal: true, + contact: getDefaultConversation(), + }, + }, ]; return ( diff --git a/ts/components/conversation/TimelineItem.tsx b/ts/components/conversation/TimelineItem.tsx index 1bbbb9950c5d..30119d50824a 100644 --- a/ts/components/conversation/TimelineItem.tsx +++ b/ts/components/conversation/TimelineItem.tsx @@ -246,8 +246,8 @@ export class TimelineItem extends React.PureComponent { ); } else if (item.type === 'linkNotification') { notification = ( -
-
+
+
{i18n('messageHistoryUnsynced')}
); diff --git a/ts/components/conversation/TimerNotification.tsx b/ts/components/conversation/TimerNotification.tsx index d358f07ebc74..d946abaf7d7c 100644 --- a/ts/components/conversation/TimerNotification.tsx +++ b/ts/components/conversation/TimerNotification.tsx @@ -95,17 +95,15 @@ export const TimerNotification: FunctionComponent = props => { } return ( -
-
-
-
{timespan}
-
-
{message}
+
+
+
{message}
); }; diff --git a/ts/components/conversation/UniversalTimerNotification.tsx b/ts/components/conversation/UniversalTimerNotification.tsx index aecdd66d06f9..9475245b02d3 100644 --- a/ts/components/conversation/UniversalTimerNotification.tsx +++ b/ts/components/conversation/UniversalTimerNotification.tsx @@ -11,8 +11,6 @@ export type Props = { expireTimer: number; }; -const CSS_MODULE = 'module-universal-timer-notification'; - export const UniversalTimerNotification: React.FC = props => { const { i18n, expireTimer } = props; @@ -23,15 +21,14 @@ export const UniversalTimerNotification: React.FC = props => { const timeValue = expirationTimer.format(i18n, expireTimer); return ( -
-
-
-
{timeValue}
-
-
- {i18n('UniversalTimerNotification__text', { - timeValue, - })} +
+
+
+
+ {i18n('UniversalTimerNotification__text', { + timeValue, + })} +
); diff --git a/ts/components/conversation/UnsupportedMessage.tsx b/ts/components/conversation/UnsupportedMessage.tsx index 54355c4b60ec..4f65b9771225 100644 --- a/ts/components/conversation/UnsupportedMessage.tsx +++ b/ts/components/conversation/UnsupportedMessage.tsx @@ -4,6 +4,7 @@ import React from 'react'; import classNames from 'classnames'; +import { Button, ButtonSize, ButtonVariant } from '../Button'; import { ContactName } from './ContactName'; import { Intl } from '../Intl'; import { LocalizerType } from '../../types/Util'; @@ -49,44 +50,51 @@ export const UnsupportedMessage = ({ const stringId = isMe ? meStringId : otherStringId; return ( -
-
-
- - - , - ]} - i18n={i18n} +
+
+
+ + + + , + ]} + i18n={i18n} + /> +
{canProcessNow ? null : ( - +
+ +
)}
); diff --git a/ts/components/conversation/VerificationNotification.tsx b/ts/components/conversation/VerificationNotification.tsx index d06a08ccee6d..e69ec5b4182d 100644 --- a/ts/components/conversation/VerificationNotification.tsx +++ b/ts/components/conversation/VerificationNotification.tsx @@ -52,32 +52,33 @@ export class VerificationNotification extends React.Component { const id = this.getStringId(); return ( - , - ]} - i18n={i18n} - /> +
+ , + ]} + i18n={i18n} + /> +
); } public render(): JSX.Element { const { type } = this.props; - const suffix = - type === 'markVerified' ? 'mark-verified' : 'mark-not-verified'; + const suffix = type === 'markVerified' ? 'verified' : 'verified-not'; return ( -
-
+
+
{this.renderContents()}
); diff --git a/ts/util/callingNotification.ts b/ts/util/callingNotification.ts index 159aca17f46b..2ebd4cbf00fe 100644 --- a/ts/util/callingNotification.ts +++ b/ts/util/callingNotification.ts @@ -110,3 +110,53 @@ export function getCallingNotificationText( return ''; } } + +type CallingIconType = + | 'audio-incoming' + | 'audio-missed' + | 'audio-outgoing' + | 'phone' + | 'video' + | 'video-incoming' + | 'video-missed' + | 'video-outgoing'; + +function getDirectCallingIcon({ + wasIncoming, + wasVideoCall, + acceptedTime, +}: DirectCallNotificationType): CallingIconType { + const wasAccepted = Boolean(acceptedTime); + + // video + if (wasVideoCall) { + if (wasAccepted) { + return wasIncoming ? 'video-incoming' : 'video-outgoing'; + } + return 'video-missed'; + } + + if (wasAccepted) { + return wasIncoming ? 'audio-incoming' : 'audio-outgoing'; + } + + return 'audio-missed'; +} + +export function getCallingIcon( + notification: CallingNotificationType +): CallingIconType { + switch (notification.callMode) { + case CallMode.Direct: + return getDirectCallingIcon(notification); + case CallMode.Group: + return 'video'; + default: + window.log.error( + `getCallingNotificationText: missing case ${missingCaseError( + notification + )}` + ); + return 'phone'; + } +} diff --git a/ts/util/scaleImageToLevel.ts b/ts/util/scaleImageToLevel.ts index e1e98ffebac8..4ef2906d74eb 100644 --- a/ts/util/scaleImageToLevel.ts +++ b/ts/util/scaleImageToLevel.ts @@ -1,7 +1,6 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import sharp from 'sharp'; import loadImage from 'blueimp-load-image'; import { MIMEType, IMAGE_JPEG } from '../types/MIME'; @@ -107,7 +106,9 @@ async function stripImageFileEXIFData( type: MIMEType ): Promise { const arrayBuffer = await file.arrayBuffer(); - const xArrayBuffer = await sharp(new Uint8Array(arrayBuffer)).toBuffer(); + const xArrayBuffer = await window + .sharp(new Uint8Array(arrayBuffer)) + .toBuffer(); return new Blob([xArrayBuffer], { type }); } diff --git a/ts/window.d.ts b/ts/window.d.ts index eca2fbfbd552..73f028ca2a54 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -7,6 +7,7 @@ import { DeepPartial, Store } from 'redux'; import * as Backbone from 'backbone'; import * as Underscore from 'underscore'; import moment from 'moment'; +import sharp from 'sharp'; import PQueue from 'p-queue/dist'; import { Attributes, ComponentClass, FunctionComponent, Ref } from 'react'; import { imageToBlurHash } from './util/imageToBlurHash'; @@ -161,6 +162,7 @@ declare global { _: typeof Underscore; $: typeof jQuery; + sharp: typeof sharp; moment: typeof moment; imageToBlurHash: typeof imageToBlurHash; loadImage: any;