-
- {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}
- ))}
+
+
{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 (
-
-
-
{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 (
-
-
-
- {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;