Styling adjustments to timeline notifications
This commit is contained in:
parent
4bed918cf8
commit
7f34bedd87
22 changed files with 437 additions and 360 deletions
|
@ -2060,7 +2060,6 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
||||||
|
|
||||||
.module-inline-notification-wrapper {
|
.module-inline-notification-wrapper {
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
@include keyboard-mode {
|
@include keyboard-mode {
|
||||||
|
@ -2074,11 +2073,6 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
||||||
|
|
||||||
// Module: Group Notification
|
// Module: Group Notification
|
||||||
|
|
||||||
.module-group-notification__change {
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.module-group-notification__contact {
|
.module-group-notification__contact {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,14 @@
|
||||||
&--system-message {
|
&--system-message {
|
||||||
@include rounded-corners;
|
@include rounded-corners;
|
||||||
|
|
||||||
|
&.module-Button--small {
|
||||||
|
padding: {
|
||||||
|
top: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
}
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
$color: $color-ultramarine;
|
$color: $color-ultramarine;
|
||||||
$background-color: $color-gray-02;
|
$background-color: $color-gray-02;
|
||||||
|
|
|
@ -16,245 +16,259 @@
|
||||||
@include font-body-2;
|
@include font-body-2;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
line-height: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
color: $color-black;
|
color: $color-gray-60;
|
||||||
}
|
}
|
||||||
@include dark-theme {
|
@include dark-theme {
|
||||||
color: $color-gray-25;
|
color: $color-gray-25;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--multiline {
|
&__contents {
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__line {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&__icon {
|
p {
|
||||||
height: 16px;
|
display: block;
|
||||||
flex: 0 0 16px;
|
margin: {
|
||||||
margin-right: 8px;
|
block: {
|
||||||
width: 16px;
|
start: 0;
|
||||||
vertical-align: middle;
|
end: 4px;
|
||||||
|
}
|
||||||
|
inline: {
|
||||||
|
start: 0;
|
||||||
|
end: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include light-theme {
|
&:first-child {
|
||||||
background-color: $color-black;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@include dark-theme {
|
|
||||||
background-color: $color-gray-25;
|
&:last-child {
|
||||||
|
margin-block-end: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--audio-incoming {
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
width: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
@include light-theme {
|
||||||
|
background-color: $color-gray-60;
|
||||||
|
}
|
||||||
|
@include dark-theme {
|
||||||
|
background-color: $color-gray-25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--icon-audio-incoming::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/phone-incoming-16.svg',
|
'../images/icons/v2/phone-incoming-16.svg',
|
||||||
'../images/icons/v2/phone-incoming-solid-16.svg'
|
'../images/icons/v2/phone-incoming-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--audio-missed {
|
&--icon-audio-missed::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/phone-x-16.svg',
|
'../images/icons/v2/phone-x-16.svg',
|
||||||
'../images/icons/v2/phone-x-solid-16.svg'
|
'../images/icons/v2/phone-x-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--audio-outgoing {
|
&--icon-audio-outgoing::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/phone-outgoing-16.svg',
|
'../images/icons/v2/phone-outgoing-16.svg',
|
||||||
'../images/icons/v2/phone-outgoing-solid-16.svg'
|
'../images/icons/v2/phone-outgoing-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group {
|
&--icon-group::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/group-outline-24.svg',
|
'../images/icons/v2/group-outline-24.svg',
|
||||||
'../images/icons/v2/group-solid-24.svg'
|
'../images/icons/v2/group-solid-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-access {
|
&--icon-group-access::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/megaphone-16.svg',
|
'../images/icons/v2/megaphone-16.svg',
|
||||||
'../images/icons/v2/megaphone-solid-16.svg'
|
'../images/icons/v2/megaphone-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-add {
|
&--icon-group-add::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/member-added-16.svg',
|
'../images/icons/v2/member-added-16.svg',
|
||||||
'../images/icons/v2/member-added-solid-16.svg'
|
'../images/icons/v2/member-added-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-approved {
|
&--icon-group-approved::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/member-accepted-16.svg',
|
'../images/icons/v2/member-accepted-16.svg',
|
||||||
'../images/icons/v2/member-accepted-solid-16.svg'
|
'../images/icons/v2/member-accepted-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-avatar {
|
&--icon-group-avatar::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/photo-16.svg',
|
'../images/icons/v2/photo-16.svg',
|
||||||
'../images/icons/v2/photo-solid-16.svg'
|
'../images/icons/v2/photo-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-decline {
|
&--icon-group-decline::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/member-declined-16.svg',
|
'../images/icons/v2/member-declined-16.svg',
|
||||||
'../images/icons/v2/member-declined-solid-16.svg'
|
'../images/icons/v2/member-declined-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-edit {
|
&--icon-group-edit::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/edit-16.svg',
|
'../images/icons/v2/edit-16.svg',
|
||||||
'../images/icons/v2/edit-solid-16.svg'
|
'../images/icons/v2/edit-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-leave {
|
&--icon-group-leave::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/leave-16.svg',
|
'../images/icons/v2/leave-16.svg',
|
||||||
'../images/icons/v2/leave-solid-16.svg'
|
'../images/icons/v2/leave-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group-remove {
|
&--icon-group-remove::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/member-remove-16.svg',
|
'../images/icons/v2/member-remove-16.svg',
|
||||||
'../images/icons/v2/member-remove-solid-16.svg'
|
'../images/icons/v2/member-remove-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--info {
|
&--icon-info::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/info-16.svg',
|
'../images/icons/v2/info-16.svg',
|
||||||
'../images/icons/v2/info-solid-24.svg'
|
'../images/icons/v2/info-solid-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--phone {
|
&--icon-phone::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/phone-right-outline-24.svg',
|
'../images/icons/v2/phone-right-outline-24.svg',
|
||||||
'../images/icons/v2/phone-right-solid-24.svg'
|
'../images/icons/v2/phone-right-solid-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--profile {
|
&--icon-profile::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/profile-outline-20.svg',
|
'../images/icons/v2/profile-outline-20.svg',
|
||||||
'../images/icons/v2/profile-outline-20.svg'
|
'../images/icons/v2/profile-outline-20.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--safety-number {
|
&--icon-safety-number::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/safety-number-outline-24.svg',
|
'../images/icons/v2/safety-number-outline-24.svg',
|
||||||
'../images/icons/v2/safety-number-solid-24.svg'
|
'../images/icons/v2/safety-number-solid-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--session-refresh {
|
&--icon-session-refresh::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/refresh-16.svg',
|
'../images/icons/v2/refresh-16.svg',
|
||||||
'../images/icons/v2/refresh-16.svg'
|
'../images/icons/v2/refresh-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--timer {
|
&--icon-timer::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/timer-outline-24.svg',
|
'../images/icons/v2/timer-outline-24.svg',
|
||||||
'../images/icons/v2/timer-solid-16.svg'
|
'../images/icons/v2/timer-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--timer-disabled {
|
&--icon-timer-disabled::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/timer-disabled-outline-24.svg',
|
'../images/icons/v2/timer-disabled-outline-24.svg',
|
||||||
'../images/icons/v2/timer-disabled-solid-16.svg'
|
'../images/icons/v2/timer-disabled-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--unsupported {
|
&--icon-unsupported::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/error-outline-24.svg',
|
'../images/icons/v2/error-outline-24.svg',
|
||||||
'../images/icons/v2/error-solid-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 {
|
&--icon-unsupported--can-process::before {
|
||||||
|
@include system-message-icon(
|
||||||
|
'../images/icons/v2/check-circle-outline-24.svg',
|
||||||
|
'../images/icons/v2/check-circle-solid-24.svg'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--icon-unsynced::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/info-outline-24.svg',
|
'../images/icons/v2/info-outline-24.svg',
|
||||||
'../images/icons/v2/info-solid-24.svg'
|
'../images/icons/v2/info-solid-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--verified {
|
&--icon-verified::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/check-24.svg',
|
'../images/icons/v2/check-24.svg',
|
||||||
'../images/icons/v2/check-24.svg'
|
'../images/icons/v2/check-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--verified-not {
|
&--icon-verified-not::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/safety-number-outline-24.svg',
|
'../images/icons/v2/safety-number-outline-24.svg',
|
||||||
'../images/icons/v2/safety-number-solid-24.svg'
|
'../images/icons/v2/safety-number-solid-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--video {
|
&--icon-video::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/video-outline-24.svg',
|
'../images/icons/v2/video-outline-24.svg',
|
||||||
'../images/icons/v2/video-outline-24.svg'
|
'../images/icons/v2/video-outline-24.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--video-incoming {
|
&--icon-video-incoming::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/video-incoming-16.svg',
|
'../images/icons/v2/video-incoming-16.svg',
|
||||||
'../images/icons/v2/video-incoming-solid-16.svg'
|
'../images/icons/v2/video-incoming-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--video-missed {
|
&--icon-video-missed::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/video-x-16.svg',
|
'../images/icons/v2/video-x-16.svg',
|
||||||
'../images/icons/v2/video-x-solid-16.svg'
|
'../images/icons/v2/video-x-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--video-outgoing {
|
&--icon-video-outgoing::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/video-outgoing-16.svg',
|
'../images/icons/v2/video-outgoing-16.svg',
|
||||||
'../images/icons/v2/video-outgoing-solid-16.svg'
|
'../images/icons/v2/video-outgoing-solid-16.svg'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--warning {
|
&--icon-warning::before {
|
||||||
@include system-message-icon(
|
@include system-message-icon(
|
||||||
'../images/icons/v2/error-outline-12.svg',
|
'../images/icons/v2/error-outline-12.svg',
|
||||||
'../images/icons/v2/error-outline-12.svg'
|
'../images/icons/v2/error-outline-12.svg'
|
||||||
|
@ -265,13 +279,12 @@
|
||||||
&--error {
|
&--error {
|
||||||
color: $color-accent-red;
|
color: $color-accent-red;
|
||||||
|
|
||||||
.SystemMessage__icon {
|
.SystemMessage__contents::before {
|
||||||
background: $color-accent-red;
|
background: $color-accent-red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img.emoji {
|
&__button-container {
|
||||||
// The negative bottom margin offset doesn't play well with align-items center
|
margin-top: 12px;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import Measure from 'react-measure';
|
import Measure from 'react-measure';
|
||||||
import { noop } from 'lodash';
|
import { noop } from 'lodash';
|
||||||
|
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
import { Timestamp } from './Timestamp';
|
import { Timestamp } from './Timestamp';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
|
@ -86,17 +86,12 @@ export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ measureRef }) => (
|
{({ measureRef }) => (
|
||||||
<div
|
<SystemMessage
|
||||||
className={classNames('SystemMessage', 'SystemMessage--multiline', {
|
button={
|
||||||
'SystemMessage--error': wasMissed,
|
hasButton ? <CallingNotificationButton {...props} /> : undefined
|
||||||
})}
|
}
|
||||||
ref={measureRef}
|
contents={
|
||||||
>
|
<>
|
||||||
<div className="SystemMessage__line">
|
|
||||||
<div
|
|
||||||
className={`SystemMessage__icon SystemMessage__icon--${icon}`}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
{getCallingNotificationText(props, i18n)} ·{' '}
|
{getCallingNotificationText(props, i18n)} ·{' '}
|
||||||
<Timestamp
|
<Timestamp
|
||||||
direction="outgoing"
|
direction="outgoing"
|
||||||
|
@ -107,14 +102,12 @@ export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
||||||
withSticker={false}
|
withSticker={false}
|
||||||
withTapToViewExpired={false}
|
withTapToViewExpired={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
</div>
|
}
|
||||||
{hasButton ? (
|
icon={icon}
|
||||||
<div className="SystemMessage__line">
|
isError={wasMissed}
|
||||||
<CallingNotificationButton {...props} />
|
ref={measureRef}
|
||||||
</div>
|
/>
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</Measure>
|
</Measure>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||||
|
import enMessages from '../../../_locales/en/messages.json';
|
||||||
|
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||||
|
|
||||||
|
import { ChangeNumberNotification } from './ChangeNumberNotification';
|
||||||
|
|
||||||
|
const story = storiesOf(
|
||||||
|
'Components/Conversation/ChangeNumberNotification',
|
||||||
|
module
|
||||||
|
);
|
||||||
|
|
||||||
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
story.add('Default', () => (
|
||||||
|
<ChangeNumberNotification
|
||||||
|
sender={getDefaultConversation()}
|
||||||
|
timestamp={1618894800000}
|
||||||
|
i18n={i18n}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
story.add('Long name', () => (
|
||||||
|
<ChangeNumberNotification
|
||||||
|
sender={getDefaultConversation({
|
||||||
|
firstName: '💅😇🖋'.repeat(50),
|
||||||
|
})}
|
||||||
|
timestamp={1618894800000}
|
||||||
|
i18n={i18n}
|
||||||
|
/>
|
||||||
|
));
|
|
@ -7,6 +7,7 @@ import { ConversationType } from '../../state/ducks/conversations';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
|
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { Timestamp } from './Timestamp';
|
import { Timestamp } from './Timestamp';
|
||||||
import { Emojify } from './Emojify';
|
import { Emojify } from './Emojify';
|
||||||
|
|
||||||
|
@ -25,17 +26,21 @@ export const ChangeNumberNotification: React.FC<Props> = props => {
|
||||||
const { i18n, sender, timestamp } = props;
|
const { i18n, sender, timestamp } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage">
|
<SystemMessage
|
||||||
<span className="SystemMessage__icon SystemMessage__icon--phone" />
|
contents={
|
||||||
<Intl
|
<>
|
||||||
id="ChangeNumber--notification"
|
<Intl
|
||||||
components={{
|
id="ChangeNumber--notification"
|
||||||
sender: <Emojify text={sender.firstName || sender.title} />,
|
components={{
|
||||||
}}
|
sender: <Emojify text={sender.firstName || sender.title} />,
|
||||||
i18n={i18n}
|
}}
|
||||||
/>
|
i18n={i18n}
|
||||||
·
|
/>
|
||||||
<Timestamp i18n={i18n} timestamp={timestamp} />
|
·
|
||||||
</div>
|
<Timestamp i18n={i18n} timestamp={timestamp} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
icon="phone"
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ import React, { useCallback, useState, ReactElement } from 'react';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
|
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog';
|
import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog';
|
||||||
|
|
||||||
type PropsHousekeepingType = {
|
type PropsHousekeepingType = {
|
||||||
|
@ -37,20 +38,20 @@ export function ChatSessionRefreshedNotification(
|
||||||
}, [contactSupport, setIsDialogOpen]);
|
}, [contactSupport, setIsDialogOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage SystemMessage--multiline">
|
<>
|
||||||
<div className="SystemMessage__line">
|
<SystemMessage
|
||||||
<span className="SystemMessage__icon SystemMessage__icon--session-refresh" />
|
contents={i18n('ChatRefresh--notification')}
|
||||||
{i18n('ChatRefresh--notification')}
|
button={
|
||||||
</div>
|
<Button
|
||||||
<div className="SystemMessage__line">
|
onClick={openDialog}
|
||||||
<Button
|
size={ButtonSize.Small}
|
||||||
onClick={openDialog}
|
variant={ButtonVariant.SystemMessage}
|
||||||
size={ButtonSize.Small}
|
>
|
||||||
variant={ButtonVariant.SystemMessage}
|
{i18n('ChatRefresh--learnMore')}
|
||||||
>
|
</Button>
|
||||||
{i18n('ChatRefresh--learnMore')}
|
}
|
||||||
</Button>
|
icon="session-refresh"
|
||||||
</div>
|
/>
|
||||||
{isDialogOpen ? (
|
{isDialogOpen ? (
|
||||||
<ChatSessionRefreshedDialog
|
<ChatSessionRefreshedDialog
|
||||||
onClose={closeDialog}
|
onClose={closeDialog}
|
||||||
|
@ -58,6 +59,6 @@ export function ChatSessionRefreshedNotification(
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React, { useCallback, useState, ReactElement } from 'react';
|
import React, { useCallback, useState, ReactElement } from 'react';
|
||||||
|
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { ConversationType } from '../../state/ducks/conversations';
|
import { ConversationType } from '../../state/ducks/conversations';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
|
@ -46,10 +47,9 @@ export function DeliveryIssueNotification(
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage SystemMessage--multiline">
|
<>
|
||||||
<div className="SystemMessage__line">
|
<SystemMessage
|
||||||
<span className="SystemMessage__icon SystemMessage__icon--info" />
|
contents={
|
||||||
<span>
|
|
||||||
<Intl
|
<Intl
|
||||||
id="DeliveryIssue--notification"
|
id="DeliveryIssue--notification"
|
||||||
components={{
|
components={{
|
||||||
|
@ -57,17 +57,18 @@ export function DeliveryIssueNotification(
|
||||||
}}
|
}}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
/>
|
/>
|
||||||
</span>
|
}
|
||||||
</div>
|
icon="info"
|
||||||
<div className="SystemMessage__line">
|
button={
|
||||||
<Button
|
<Button
|
||||||
onClick={openDialog}
|
onClick={openDialog}
|
||||||
size={ButtonSize.Small}
|
size={ButtonSize.Small}
|
||||||
variant={ButtonVariant.SystemMessage}
|
variant={ButtonVariant.SystemMessage}
|
||||||
>
|
>
|
||||||
{i18n('DeliveryIssue--learnMore')}
|
{i18n('DeliveryIssue--learnMore')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
{isDialogOpen ? (
|
{isDialogOpen ? (
|
||||||
<DeliveryIssueDialog
|
<DeliveryIssueDialog
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -77,6 +78,6 @@ export function DeliveryIssueNotification(
|
||||||
onClose={closeDialog}
|
onClose={closeDialog}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// Copyright 2018-2020 Signal Messenger, LLC
|
// Copyright 2018-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { compact, flatten } from 'lodash';
|
import { compact, flatten } from 'lodash';
|
||||||
|
|
||||||
import { ContactName } from './ContactName';
|
import { ContactName } from './ContactName';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
|
|
||||||
|
@ -133,12 +134,15 @@ export class GroupNotification extends React.Component<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const { changes, i18n, from } = this.props;
|
const { changes: rawChanges, i18n, from } = this.props;
|
||||||
|
|
||||||
|
// This check is just to be extra careful, and can probably be removed.
|
||||||
|
const changes: Array<Change> = Array.isArray(rawChanges) ? rawChanges : [];
|
||||||
|
|
||||||
// Leave messages are always from the person leaving, so we omit the fromLabel if
|
// Leave messages are always from the person leaving, so we omit the fromLabel if
|
||||||
// the change is a 'leave.'
|
// the change is a 'leave.'
|
||||||
const isLeftOnly =
|
const firstChange: undefined | Change = changes[0];
|
||||||
changes && changes.length === 1 && changes[0].type === 'remove';
|
const isLeftOnly = changes.length === 1 && firstChange?.type === 'remove';
|
||||||
|
|
||||||
const fromContact = (
|
const fromContact = (
|
||||||
<ContactName
|
<ContactName
|
||||||
|
@ -156,23 +160,23 @@ export class GroupNotification extends React.Component<Props> {
|
||||||
<Intl i18n={i18n} id="updatedTheGroup" components={[fromContact]} />
|
<Intl i18n={i18n} id="updatedTheGroup" components={[fromContact]} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
let contents: ReactNode;
|
||||||
<div className="SystemMessage">
|
if (isLeftOnly) {
|
||||||
<div>
|
contents = this.renderChange(firstChange, from);
|
||||||
{isLeftOnly ? null : (
|
} else {
|
||||||
<>
|
contents = (
|
||||||
{fromLabel}
|
<>
|
||||||
<br />
|
<p>{fromLabel}</p>
|
||||||
</>
|
{changes.map((change, i) => (
|
||||||
)}
|
|
||||||
{(changes || []).map((change, i) => (
|
|
||||||
// eslint-disable-next-line react/no-array-index-key
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
<div key={i} className="module-group-notification__change">
|
<p key={i} className="module-group-notification__change">
|
||||||
{this.renderChange(change, from)}
|
{this.renderChange(change, from)}
|
||||||
</div>
|
</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
return <SystemMessage contents={contents} icon="group" />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
// Copyright 2020-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import { ConversationType } from '../../state/ducks/conversations';
|
import { ConversationType } from '../../state/ducks/conversations';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
|
@ -35,40 +36,42 @@ export function GroupV1Migration(props: PropsType): React.ReactElement {
|
||||||
}, [setShowingDialog]);
|
}, [setShowingDialog]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage SystemMessage--multiline">
|
<>
|
||||||
<div className="SystemMessage__line">
|
<SystemMessage
|
||||||
<div className="SystemMessage__icon SystemMessage__icon--group" />
|
icon="group"
|
||||||
<div>
|
contents={
|
||||||
<div>{i18n('GroupV1--Migration--was-upgraded')}</div>
|
<>
|
||||||
<div>
|
<p>{i18n('GroupV1--Migration--was-upgraded')}</p>
|
||||||
{areWeInvited ? (
|
<p>
|
||||||
i18n('GroupV1--Migration--invited--you')
|
{areWeInvited ? (
|
||||||
) : (
|
i18n('GroupV1--Migration--invited--you')
|
||||||
<>
|
) : (
|
||||||
{renderUsers(
|
<>
|
||||||
invitedMembers,
|
{renderUsers(
|
||||||
i18n,
|
invitedMembers,
|
||||||
'GroupV1--Migration--invited'
|
i18n,
|
||||||
)}
|
'GroupV1--Migration--invited'
|
||||||
{renderUsers(
|
)}
|
||||||
droppedMembers,
|
{renderUsers(
|
||||||
i18n,
|
droppedMembers,
|
||||||
'GroupV1--Migration--removed'
|
i18n,
|
||||||
)}
|
'GroupV1--Migration--removed'
|
||||||
</>
|
)}
|
||||||
)}
|
</>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</p>
|
||||||
</div>
|
</>
|
||||||
<div className="SystemMessage__line">
|
}
|
||||||
<Button
|
button={
|
||||||
onClick={showDialog}
|
<Button
|
||||||
size={ButtonSize.Small}
|
onClick={showDialog}
|
||||||
variant={ButtonVariant.SystemMessage}
|
size={ButtonSize.Small}
|
||||||
>
|
variant={ButtonVariant.SystemMessage}
|
||||||
{i18n('GroupV1--Migration--learn-more')}
|
>
|
||||||
</Button>
|
{i18n('GroupV1--Migration--learn-more')}
|
||||||
</div>
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
{showingDialog ? (
|
{showingDialog ? (
|
||||||
<GroupV1MigrationDialog
|
<GroupV1MigrationDialog
|
||||||
areWeInvited={areWeInvited}
|
areWeInvited={areWeInvited}
|
||||||
|
@ -82,7 +85,7 @@ export function GroupV1Migration(props: PropsType): React.ReactElement {
|
||||||
onClose={dismissDialog}
|
onClose={dismissDialog}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,23 +98,17 @@ function renderUsers(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const className = 'module-group-v1-migration--text';
|
|
||||||
|
|
||||||
if (members.length === 1) {
|
if (members.length === 1) {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<p>
|
||||||
<Intl
|
<Intl
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
id={`${keyPrefix}--one`}
|
id={`${keyPrefix}--one`}
|
||||||
components={[<ContactName title={members[0].title} i18n={i18n} />]}
|
components={[<ContactName title={members[0].title} i18n={i18n} />]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <p>{i18n(`${keyPrefix}--many`, [members.length.toString()])}</p>;
|
||||||
<div className={className}>
|
|
||||||
{i18n(`${keyPrefix}--many`, [members.length.toString()])}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { ReactElement, useState } from 'react';
|
import React, { ReactElement, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
|
||||||
import { ReplacementValuesType } from '../../types/I18N';
|
import { ReplacementValuesType } from '../../types/I18N';
|
||||||
|
@ -10,6 +9,7 @@ import { FullJSXType, Intl } from '../Intl';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import { GroupDescriptionText } from '../GroupDescriptionText';
|
import { GroupDescriptionText } from '../GroupDescriptionText';
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
|
|
||||||
import { GroupV2ChangeType, GroupV2ChangeDetailType } from '../../groups';
|
import { GroupV2ChangeType, GroupV2ChangeDetailType } from '../../groups';
|
||||||
|
|
||||||
|
@ -110,17 +110,11 @@ function GroupV2Detail({
|
||||||
detail.type === 'description' && get(detail, 'description');
|
detail.type === 'description' && get(detail, 'description');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<SystemMessage
|
||||||
className={classNames('SystemMessage', {
|
icon={icon}
|
||||||
'SystemMessage--multiline': Boolean(newGroupDescription),
|
contents={text}
|
||||||
})}
|
button={
|
||||||
>
|
newGroupDescription ? (
|
||||||
<div className="SystemMessage__line">
|
|
||||||
<div className={`SystemMessage__icon SystemMessage__icon--${icon}`} />
|
|
||||||
<div className="SystemMessage__text">{text}</div>
|
|
||||||
</div>
|
|
||||||
{newGroupDescription ? (
|
|
||||||
<div className="SystemMessage__line">
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onButtonClick(newGroupDescription)}
|
onClick={() => onButtonClick(newGroupDescription)}
|
||||||
size={ButtonSize.Small}
|
size={ButtonSize.Small}
|
||||||
|
@ -128,9 +122,9 @@ function GroupV2Detail({
|
||||||
>
|
>
|
||||||
{i18n('view')}
|
{i18n('view')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
) : undefined
|
||||||
) : null}
|
}
|
||||||
</div>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
ts/components/conversation/LinkNotification.stories.tsx
Normal file
16
ts/components/conversation/LinkNotification.stories.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||||
|
import enMessages from '../../../_locales/en/messages.json';
|
||||||
|
|
||||||
|
import { LinkNotification } from './LinkNotification';
|
||||||
|
|
||||||
|
const story = storiesOf('Components/Conversation/LinkNotification', module);
|
||||||
|
|
||||||
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
story.add('Default', () => <LinkNotification i18n={i18n} />);
|
13
ts/components/conversation/LinkNotification.tsx
Normal file
13
ts/components/conversation/LinkNotification.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
|
import type { LocalizerType } from '../../types/Util';
|
||||||
|
|
||||||
|
export const LinkNotification = ({
|
||||||
|
i18n,
|
||||||
|
}: Readonly<{ i18n: LocalizerType }>): JSX.Element => (
|
||||||
|
<SystemMessage icon="unsynced" contents={i18n('messageHistoryUnsynced')} />
|
||||||
|
);
|
|
@ -1,10 +1,11 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
// Copyright 2020-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import { ConversationType } from '../../state/ducks/conversations';
|
import { ConversationType } from '../../state/ducks/conversations';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { Emojify } from './Emojify';
|
import { Emojify } from './Emojify';
|
||||||
import {
|
import {
|
||||||
getStringForProfileChange,
|
getStringForProfileChange,
|
||||||
|
@ -21,12 +22,5 @@ export function ProfileChangeNotification(props: PropsType): JSX.Element {
|
||||||
const { change, changedContact, i18n } = props;
|
const { change, changedContact, i18n } = props;
|
||||||
const message = getStringForProfileChange(change, changedContact, i18n);
|
const message = getStringForProfileChange(change, changedContact, i18n);
|
||||||
|
|
||||||
return (
|
return <SystemMessage icon="profile" contents={<Emojify text={message} />} />;
|
||||||
<div className="SystemMessage">
|
|
||||||
<div className="SystemMessage__icon SystemMessage__icon--profile" />
|
|
||||||
<span>
|
|
||||||
<Emojify text={message} />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
// Copyright 2018-2020 Signal Messenger, LLC
|
// Copyright 2018-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ResetSessionNotification = ({ i18n }: Props): JSX.Element => (
|
export const ResetSessionNotification = ({ i18n }: Props): JSX.Element => (
|
||||||
<div className="SystemMessage">{i18n('sessionEnded')}</div>
|
<SystemMessage contents={i18n('sessionEnded')} icon="session-refresh" />
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
// Copyright 2018-2020 Signal Messenger, LLC
|
// Copyright 2018-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { ContactName } from './ContactName';
|
import { ContactName } from './ContactName';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
|
@ -42,32 +43,30 @@ export const SafetyNumberNotification = ({
|
||||||
: 'safetyNumberChanged';
|
: 'safetyNumberChanged';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage SystemMessage--multiline">
|
<SystemMessage
|
||||||
<div className="SystemMessage__line">
|
icon="safety-number"
|
||||||
<div className="SystemMessage__icon SystemMessage__icon--safety-number" />
|
contents={
|
||||||
<span>
|
<Intl
|
||||||
<Intl
|
id={changeKey}
|
||||||
id={changeKey}
|
components={[
|
||||||
components={[
|
<span
|
||||||
<span
|
key="external-1"
|
||||||
key="external-1"
|
className="module-safety-number-notification__contact"
|
||||||
className="module-safety-number-notification__contact"
|
>
|
||||||
>
|
<ContactName
|
||||||
<ContactName
|
name={contact.name}
|
||||||
name={contact.name}
|
profileName={contact.profileName}
|
||||||
profileName={contact.profileName}
|
phoneNumber={contact.phoneNumber}
|
||||||
phoneNumber={contact.phoneNumber}
|
title={contact.title}
|
||||||
title={contact.title}
|
module="module-safety-number-notification__contact"
|
||||||
module="module-safety-number-notification__contact"
|
i18n={i18n}
|
||||||
i18n={i18n}
|
/>
|
||||||
/>
|
</span>,
|
||||||
</span>,
|
]}
|
||||||
]}
|
i18n={i18n}
|
||||||
i18n={i18n}
|
/>
|
||||||
/>
|
}
|
||||||
</span>
|
button={
|
||||||
</div>
|
|
||||||
<div className="SystemMessage__line">
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
showIdentity(contact.id);
|
showIdentity(contact.id);
|
||||||
|
@ -77,7 +76,7 @@ export const SafetyNumberNotification = ({
|
||||||
>
|
>
|
||||||
{i18n('verifyNewNumber')}
|
{i18n('verifyNewNumber')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
}
|
||||||
</div>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
38
ts/components/conversation/SystemMessage.tsx
Normal file
38
ts/components/conversation/SystemMessage.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React, { ReactNode, forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
icon: string;
|
||||||
|
contents: ReactNode;
|
||||||
|
button?: ReactNode;
|
||||||
|
isError?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SystemMessage = forwardRef<HTMLDivElement, PropsType>(
|
||||||
|
({ icon, contents, button, isError }, ref) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'SystemMessage',
|
||||||
|
isError && 'SystemMessage--error'
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'SystemMessage__contents',
|
||||||
|
`SystemMessage__contents--icon-${icon}`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{contents}
|
||||||
|
</div>
|
||||||
|
{button && (
|
||||||
|
<div className="SystemMessage__button-container">{button}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
|
@ -26,6 +26,7 @@ import {
|
||||||
PropsActionsType as DeliveryIssueActionProps,
|
PropsActionsType as DeliveryIssueActionProps,
|
||||||
PropsDataType as DeliveryIssueProps,
|
PropsDataType as DeliveryIssueProps,
|
||||||
} from './DeliveryIssueNotification';
|
} from './DeliveryIssueNotification';
|
||||||
|
import { LinkNotification } from './LinkNotification';
|
||||||
import {
|
import {
|
||||||
ChangeNumberNotification,
|
ChangeNumberNotification,
|
||||||
PropsData as ChangeNumberNotificationProps,
|
PropsData as ChangeNumberNotificationProps,
|
||||||
|
@ -245,12 +246,7 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
||||||
<DeliveryIssueNotification {...item.data} {...this.props} i18n={i18n} />
|
<DeliveryIssueNotification {...item.data} {...this.props} i18n={i18n} />
|
||||||
);
|
);
|
||||||
} else if (item.type === 'linkNotification') {
|
} else if (item.type === 'linkNotification') {
|
||||||
notification = (
|
notification = <LinkNotification i18n={i18n} />;
|
||||||
<div className="SystemMessage">
|
|
||||||
<div className="SystemMessage__icon SystemMessage__icon--unsynced" />
|
|
||||||
{i18n('messageHistoryUnsynced')}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (item.type === 'timerNotification') {
|
} else if (item.type === 'timerNotification') {
|
||||||
notification = (
|
notification = (
|
||||||
<TimerNotification {...this.props} {...item.data} i18n={i18n} />
|
<TimerNotification {...this.props} {...item.data} i18n={i18n} />
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { FunctionComponent, ReactNode } from 'react';
|
import React, { FunctionComponent, ReactNode } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { ContactName } from './ContactName';
|
import { ContactName } from './ContactName';
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import * as expirationTimer from '../../util/expirationTimer';
|
import * as expirationTimer from '../../util/expirationTimer';
|
||||||
|
@ -94,16 +94,7 @@ export const TimerNotification: FunctionComponent<Props> = props => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const icon = disabled ? 'timer-disabled' : 'timer';
|
||||||
<div className="SystemMessage">
|
|
||||||
<div
|
return <SystemMessage icon={icon} contents={message} />;
|
||||||
className={classNames(
|
|
||||||
'SystemMessage__icon',
|
|
||||||
'SystemMessage__icon--timer',
|
|
||||||
disabled ? 'SystemMessage__icon--timer-disabled' : null
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div>{message}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import * as expirationTimer from '../../util/expirationTimer';
|
import * as expirationTimer from '../../util/expirationTimer';
|
||||||
|
|
||||||
|
@ -21,15 +22,11 @@ export const UniversalTimerNotification: React.FC<Props> = props => {
|
||||||
const timeValue = expirationTimer.format(i18n, expireTimer);
|
const timeValue = expirationTimer.format(i18n, expireTimer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage">
|
<SystemMessage
|
||||||
<div className="SystemMessage__icon SystemMessage__icon--timer" />
|
icon="timer"
|
||||||
<div className="SystemMessage__text">
|
contents={i18n('UniversalTimerNotification__text', {
|
||||||
<div>
|
timeValue,
|
||||||
{i18n('UniversalTimerNotification__text', {
|
})}
|
||||||
timeValue,
|
/>
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright 2019-2020 Signal Messenger, LLC
|
// Copyright 2019-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||||
import { ContactName } from './ContactName';
|
import { ContactName } from './ContactName';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
|
@ -48,54 +48,47 @@ export const UnsupportedMessage = ({
|
||||||
? 'Message--from-me-unsupported-message-ask-to-resend'
|
? 'Message--from-me-unsupported-message-ask-to-resend'
|
||||||
: 'Message--from-me-unsupported-message';
|
: 'Message--from-me-unsupported-message';
|
||||||
const stringId = isMe ? meStringId : otherStringId;
|
const stringId = isMe ? meStringId : otherStringId;
|
||||||
|
const icon = canProcessNow ? 'unsupported--can-process' : 'unsupported';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage SystemMessage--multiline">
|
<SystemMessage
|
||||||
<div className="SystemMessage__line SystemMessage__text">
|
icon={icon}
|
||||||
<div
|
contents={
|
||||||
className={classNames(
|
<Intl
|
||||||
'SystemMessage__icon',
|
id={stringId}
|
||||||
'SystemMessage__icon--unsupported',
|
components={[
|
||||||
{
|
<span
|
||||||
'SystemMessage__icon--unsupported--can-process': canProcessNow,
|
key="external-1"
|
||||||
}
|
className="module-unsupported-message__contact"
|
||||||
)}
|
>
|
||||||
|
<ContactName
|
||||||
|
name={contact.name}
|
||||||
|
profileName={contact.profileName}
|
||||||
|
phoneNumber={contact.phoneNumber}
|
||||||
|
title={contact.title}
|
||||||
|
module="module-unsupported-message__contact"
|
||||||
|
i18n={i18n}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
]}
|
||||||
|
i18n={i18n}
|
||||||
/>
|
/>
|
||||||
<span>
|
}
|
||||||
<Intl
|
button={
|
||||||
id={stringId}
|
canProcessNow ? undefined : (
|
||||||
components={[
|
<div className="SystemMessage__line">
|
||||||
<span
|
<Button
|
||||||
key="external-1"
|
onClick={() => {
|
||||||
className="module-unsupported-message__contact"
|
downloadNewVersion();
|
||||||
>
|
}}
|
||||||
<ContactName
|
size={ButtonSize.Small}
|
||||||
name={contact.name}
|
variant={ButtonVariant.SystemMessage}
|
||||||
profileName={contact.profileName}
|
>
|
||||||
phoneNumber={contact.phoneNumber}
|
{i18n('Message--update-signal')}
|
||||||
title={contact.title}
|
</Button>
|
||||||
module="module-unsupported-message__contact"
|
</div>
|
||||||
i18n={i18n}
|
)
|
||||||
/>
|
}
|
||||||
</span>,
|
/>
|
||||||
]}
|
|
||||||
i18n={i18n}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{canProcessNow ? null : (
|
|
||||||
<div className="SystemMessage__line">
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
downloadNewVersion();
|
|
||||||
}}
|
|
||||||
size={ButtonSize.Small}
|
|
||||||
variant={ButtonVariant.SystemMessage}
|
|
||||||
>
|
|
||||||
{i18n('Message--update-signal')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright 2018-2020 Signal Messenger, LLC
|
// Copyright 2018-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
// import classNames from 'classnames';
|
|
||||||
|
|
||||||
|
import { SystemMessage } from './SystemMessage';
|
||||||
import { ContactName } from './ContactName';
|
import { ContactName } from './ContactName';
|
||||||
import { Intl } from '../Intl';
|
import { Intl } from '../Intl';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
|
@ -52,35 +52,28 @@ export class VerificationNotification extends React.Component<Props> {
|
||||||
const id = this.getStringId();
|
const id = this.getStringId();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SystemMessage__text">
|
<Intl
|
||||||
<Intl
|
id={id}
|
||||||
id={id}
|
components={[
|
||||||
components={[
|
<ContactName
|
||||||
<ContactName
|
key="external-1"
|
||||||
key="external-1"
|
name={contact.name}
|
||||||
name={contact.name}
|
profileName={contact.profileName}
|
||||||
profileName={contact.profileName}
|
phoneNumber={contact.phoneNumber}
|
||||||
phoneNumber={contact.phoneNumber}
|
title={contact.title}
|
||||||
title={contact.title}
|
module="module-verification-notification__contact"
|
||||||
module="module-verification-notification__contact"
|
i18n={i18n}
|
||||||
i18n={i18n}
|
/>,
|
||||||
/>,
|
]}
|
||||||
]}
|
i18n={i18n}
|
||||||
i18n={i18n}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const { type } = this.props;
|
const { type } = this.props;
|
||||||
const suffix = type === 'markVerified' ? 'verified' : 'verified-not';
|
const icon = type === 'markVerified' ? 'verified' : 'verified-not';
|
||||||
|
|
||||||
return (
|
return <SystemMessage icon={icon} contents={this.renderContents()} />;
|
||||||
<div className="SystemMessage">
|
|
||||||
<div className={`SystemMessage__icon SystemMessage__icon--${suffix}`} />
|
|
||||||
{this.renderContents()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue