diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 086a0ee571c1..6d16920b164e 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -50,5 +50,9 @@ hasCustomTitleBar: () => false, }, }; + + window.ConversationController = window.ConversationController || {}; + window.ConversationController.isSignalConversation = () => false; + window.ConversationController.onConvoMessageMount = noop; window.getPreferredSystemLocales = () => ['en']; diff --git a/ts/components/EditHistoryMessagesModal.tsx b/ts/components/EditHistoryMessagesModal.tsx index 9a57b8b50ea2..42745a550789 100644 --- a/ts/components/EditHistoryMessagesModal.tsx +++ b/ts/components/EditHistoryMessagesModal.tsx @@ -19,6 +19,7 @@ export type PropsType = { editHistoryMessages: Array; getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; + platform: string; kickOffAttachmentDownload: (options: { attachment: AttachmentType; messageId: string; @@ -70,6 +71,7 @@ export function EditHistoryMessagesModal({ getPreferredBadge, editHistoryMessages, i18n, + platform, kickOffAttachmentDownload, showLightbox, }: PropsType): JSX.Element { @@ -100,6 +102,7 @@ export function EditHistoryMessagesModal({ containerElementRef={containerElementRef} getPreferredBadge={getPreferredBadge} i18n={i18n} + platform={platform} key={messageAttributes.timestamp} kickOffAttachmentDownload={kickOffAttachmentDownload} showLightbox={closeAndShowLightbox} diff --git a/ts/components/GroupV1MigrationDialog.tsx b/ts/components/GroupV1MigrationDialog.tsx index a30a9aa4ee04..72aafb47d2dd 100644 --- a/ts/components/GroupV1MigrationDialog.tsx +++ b/ts/components/GroupV1MigrationDialog.tsx @@ -7,7 +7,7 @@ import type { ConversationType } from '../state/ducks/conversations'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import { GroupDialog } from './GroupDialog'; import { sortByTitle } from '../util/sortByTitle'; -import { missingCaseError } from '../util'; +import { missingCaseError } from '../util/missingCaseError'; export type DataPropsType = { conversationId: string; diff --git a/ts/components/StoryViewer.stories.tsx b/ts/components/StoryViewer.stories.tsx index aeecb86f6f41..254d3dad947a 100644 --- a/ts/components/StoryViewer.stories.tsx +++ b/ts/components/StoryViewer.stories.tsx @@ -39,6 +39,9 @@ export default { i18n: { defaultValue: i18n, }, + platform: { + defaultValue: 'darwin', + }, loadStoryReplies: { action: true }, markStoryRead: { action: true }, numStories: { diff --git a/ts/components/StoryViewer.tsx b/ts/components/StoryViewer.tsx index c569ff379c53..7f909b919d3d 100644 --- a/ts/components/StoryViewer.tsx +++ b/ts/components/StoryViewer.tsx @@ -101,6 +101,7 @@ export type PropsType = { ) => unknown; onUseEmoji: (_: EmojiPickDataType) => unknown; onMediaPlaybackStart: () => void; + platform: string; preferredReactionEmoji: ReadonlyArray; queueStoryDownload: (storyId: string) => unknown; recentEmojis?: ReadonlyArray; @@ -155,6 +156,7 @@ export function StoryViewer({ onTextTooLong, onUseEmoji, onMediaPlaybackStart, + platform, preferredReactionEmoji, queueStoryDownload, recentEmojis, @@ -914,6 +916,7 @@ export function StoryViewer({ hasViewReceiptSetting={hasViewReceiptSetting} hasViewsCapability={isSent} i18n={i18n} + platform={platform} isInternalUser={isInternalUser} group={group} onClose={() => setCurrentViewTarget(null)} diff --git a/ts/components/StoryViewsNRepliesModal.stories.tsx b/ts/components/StoryViewsNRepliesModal.stories.tsx index c6c20db36810..88e495398481 100644 --- a/ts/components/StoryViewsNRepliesModal.stories.tsx +++ b/ts/components/StoryViewsNRepliesModal.stories.tsx @@ -39,6 +39,9 @@ export default { i18n: { defaultValue: i18n, }, + platform: { + defaultValue: 'darwin', + }, onClose: { action: true }, onSetSkinTone: { action: true }, onReact: { action: true }, diff --git a/ts/components/StoryViewsNRepliesModal.tsx b/ts/components/StoryViewsNRepliesModal.tsx index 9d34b4b713fc..8c427407c700 100644 --- a/ts/components/StoryViewsNRepliesModal.tsx +++ b/ts/components/StoryViewsNRepliesModal.tsx @@ -87,6 +87,7 @@ export type PropsType = { hasViewReceiptSetting: boolean; hasViewsCapability: boolean; i18n: LocalizerType; + platform: string; isInternalUser?: boolean; onChangeViewTarget: (target: StoryViewTargetType) => unknown; onClose: () => unknown; @@ -120,6 +121,7 @@ export function StoryViewsNRepliesModal({ hasViewReceiptSetting, hasViewsCapability, i18n, + platform, isInternalUser, onChangeViewTarget, onClose, @@ -286,6 +288,7 @@ export function StoryViewsNRepliesModal({ } getPreferredBadge={getPreferredBadge} i18n={i18n} + platform={platform} id={reply.id} isInternalUser={isInternalUser} reply={reply} @@ -463,6 +466,7 @@ type ReplyOrReactionMessageProps = { deleteGroupStoryReplyForEveryone: (replyId: string) => void; getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; + platform: string; id: string; isInternalUser?: boolean; onContextMenu?: (ev: React.MouseEvent) => void; @@ -481,6 +485,7 @@ function ReplyOrReactionMessage({ deleteGroupStoryReplyForEveryone, containerElementRef, getPreferredBadge, + platform, shouldCollapseAbove, shouldCollapseBelow, showContactModal, @@ -549,6 +554,7 @@ function ReplyOrReactionMessage({ onContextMenu={onContextMenu} getPreferredBadge={getPreferredBadge} i18n={i18n} + platform={platform} id={reply.id} interactionMode="mouse" readStatus={reply.readStatus} diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 0c7133ada2ec..c508c9b679f9 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -98,7 +98,6 @@ import { Emojify } from './Emojify'; import { getPaymentEventDescription } from '../../messages/helpers'; import { PanelType } from '../../types/Panels'; import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser'; -import { isMacOS } from '../../OS'; const GUESS_METADATA_WIDTH_TIMESTAMP_SIZE = 16; const GUESS_METADATA_WIDTH_EXPIRE_TIMER_SIZE = 18; @@ -297,6 +296,7 @@ export type PropsHousekeeping = { getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; interactionMode: InteractionModeType; + platform: string; renderAudioAttachment: (props: AudioAttachmentProps) => JSX.Element; shouldCollapseAbove: boolean; shouldCollapseBelow: boolean; @@ -2577,6 +2577,7 @@ export class Message extends React.PureComponent { isSelected, isSelectMode, onKeyDown, + platform, renderMenu, shouldCollapseAbove, shouldCollapseBelow, @@ -2584,6 +2585,7 @@ export class Message extends React.PureComponent { onToggleSelect, onReplyToMessage, } = this.props; + const isMacOS = platform === 'darwin'; const { expired, expiring, isTargeted, imageBroken } = this.state; if (expired) { @@ -2622,7 +2624,7 @@ export class Message extends React.PureComponent { // We use `onClickCapture` here and preven default/stop propagation to // prevent other click handlers from firing. onClickCapture: event => { - if (isMacOS() ? event.metaKey : event.ctrlKey) { + if (isMacOS ? event.metaKey : event.ctrlKey) { event.preventDefault(); event.stopPropagation(); onToggleSelect(true, false); diff --git a/ts/components/conversation/MessageDetail.stories.tsx b/ts/components/conversation/MessageDetail.stories.tsx index 5424780de95d..009711dfa9cc 100644 --- a/ts/components/conversation/MessageDetail.stories.tsx +++ b/ts/components/conversation/MessageDetail.stories.tsx @@ -68,6 +68,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ getPreferredBadge: () => getFakeBadge(), i18n, + platform: 'darwin', interactionMode: 'keyboard', theme: ThemeType.light, diff --git a/ts/components/conversation/MessageDetail.tsx b/ts/components/conversation/MessageDetail.tsx index fa44bf7e5ef9..28a25b98b505 100644 --- a/ts/components/conversation/MessageDetail.tsx +++ b/ts/components/conversation/MessageDetail.tsx @@ -67,6 +67,7 @@ export type PropsData = { sentAt: number; i18n: LocalizerType; + platform: string; theme: ThemeType; getPreferredBadge: PreferredBadgeSelectorType; } & Pick; @@ -303,6 +304,7 @@ export class MessageDetail extends React.Component { kickOffAttachmentDownload, markAttachmentAsCorrupted, openGiftBadge, + platform, pushPanelForConversation, renderAudioAttachment, saveAttachment, @@ -346,6 +348,7 @@ export class MessageDetail extends React.Component { kickOffAttachmentDownload={kickOffAttachmentDownload} markAttachmentAsCorrupted={markAttachmentAsCorrupted} messageExpanded={noop} + platform={platform} showConversation={showConversation} openGiftBadge={openGiftBadge} pushPanelForConversation={pushPanelForConversation} diff --git a/ts/components/conversation/Quote.stories.tsx b/ts/components/conversation/Quote.stories.tsx index 951cc3dfc38b..08735c2953f5 100644 --- a/ts/components/conversation/Quote.stories.tsx +++ b/ts/components/conversation/Quote.stories.tsx @@ -44,6 +44,9 @@ export default { i18n: { defaultValue: i18n, }, + platform: { + defautlValue: 'darwin', + }, isFromMe: { control: { type: 'checkbox' }, defaultValue: false, @@ -103,6 +106,7 @@ const defaultMessageProps: TimelineMessagesProps = { ), getPreferredBadge: () => undefined, i18n, + platform: 'darwin', id: 'messageId', // renderingContext: 'storybook', interactionMode: 'keyboard', diff --git a/ts/components/conversation/Timeline.stories.tsx b/ts/components/conversation/Timeline.stories.tsx index 7d9c6203411f..1fcd2e4bed9b 100644 --- a/ts/components/conversation/Timeline.stories.tsx +++ b/ts/components/conversation/Timeline.stories.tsx @@ -344,6 +344,7 @@ const renderItem = ({ interactionMode="keyboard" isNextItemCallingNotification={false} theme={ThemeType.light} + platform="darwin" containerElementRef={containerElementRef} containerWidthBreakpoint={containerWidthBreakpoint} conversationId="" diff --git a/ts/components/conversation/TimelineItem.stories.tsx b/ts/components/conversation/TimelineItem.stories.tsx index 8e5606927a5c..d1ae525b09f0 100644 --- a/ts/components/conversation/TimelineItem.stories.tsx +++ b/ts/components/conversation/TimelineItem.stories.tsx @@ -61,6 +61,7 @@ const getDefaultProps = () => ({ isTargeted: false, interactionMode: 'keyboard' as const, theme: ThemeType.light, + platform: 'darwin', targetMessage: action('targetMessage'), toggleSelectMessage: action('toggleSelectMessage'), reactToMessage: action('reactToMessage'), diff --git a/ts/components/conversation/TimelineItem.tsx b/ts/components/conversation/TimelineItem.tsx index b9f623b4e226..2b7e8ba8eb33 100644 --- a/ts/components/conversation/TimelineItem.tsx +++ b/ts/components/conversation/TimelineItem.tsx @@ -151,6 +151,7 @@ type PropsLocalType = { isTargeted: boolean; targetMessage: (messageId: string, conversationId: string) => unknown; shouldRenderDateHeader: boolean; + platform: string; renderContact: SmartContactRendererType; renderUniversalTimerNotification: () => JSX.Element; i18n: LocalizerType; @@ -188,6 +189,7 @@ export class TimelineItem extends React.PureComponent { isNextItemCallingNotification, isTargeted, item, + platform, renderUniversalTimerNotification, returnToActiveCall, targetMessage, @@ -223,6 +225,7 @@ export class TimelineItem extends React.PureComponent { shouldHideMetadata={shouldHideMetadata} containerElementRef={containerElementRef} getPreferredBadge={getPreferredBadge} + platform={platform} i18n={i18n} theme={theme} /> diff --git a/ts/components/conversation/TimelineMessage.stories.tsx b/ts/components/conversation/TimelineMessage.stories.tsx index 4af46ce6d713..22f6f5a0cdb6 100644 --- a/ts/components/conversation/TimelineMessage.stories.tsx +++ b/ts/components/conversation/TimelineMessage.stories.tsx @@ -281,6 +281,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined), giftBadge: overrideProps.giftBadge, i18n, + platform: 'darwin', id: text('id', overrideProps.id || 'random-message-id'), // renderingContext: 'storybook', interactionMode: overrideProps.interactionMode || 'keyboard', diff --git a/ts/components/emoji/EmojiPicker.tsx b/ts/components/emoji/EmojiPicker.tsx index ca74e64c7447..eab15c438af6 100644 --- a/ts/components/emoji/EmojiPicker.tsx +++ b/ts/components/emoji/EmojiPicker.tsx @@ -24,7 +24,7 @@ import { Emoji } from './Emoji'; import { dataByCategory, search } from './lib'; import type { LocalizerType } from '../../types/Util'; import { isSingleGrapheme } from '../../util/grapheme'; -import { missingCaseError } from '../../util'; +import { missingCaseError } from '../../util/missingCaseError'; export type EmojiPickDataType = { skinTone?: number; diff --git a/ts/state/smart/EditHistoryMessagesModal.tsx b/ts/state/smart/EditHistoryMessagesModal.tsx index dcfb183ecbc7..163d94da552a 100644 --- a/ts/state/smart/EditHistoryMessagesModal.tsx +++ b/ts/state/smart/EditHistoryMessagesModal.tsx @@ -7,7 +7,7 @@ import type { GlobalModalsStateType } from '../ducks/globalModals'; import type { MessageAttributesType } from '../../model-types.d'; import type { StateType } from '../reducer'; import { EditHistoryMessagesModal } from '../../components/EditHistoryMessagesModal'; -import { getIntl } from '../selectors/user'; +import { getIntl, getPlatform } from '../selectors/user'; import { getMessagePropsSelector } from '../selectors/message'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { useConversationsActions } from '../ducks/conversations'; @@ -17,6 +17,7 @@ import { strictAssert } from '../../util/assert'; export function SmartEditHistoryMessagesModal(): JSX.Element { const i18n = useSelector(getIntl); + const platform = useSelector(getPlatform); const { closeEditHistoryModal } = useGlobalModalActions(); @@ -50,6 +51,7 @@ export function SmartEditHistoryMessagesModal(): JSX.Element { editHistoryMessages={editHistoryMessages} getPreferredBadge={getPreferredBadge} i18n={i18n} + platform={platform} kickOffAttachmentDownload={kickOffAttachmentDownload} showLightbox={showLightbox} /> diff --git a/ts/state/smart/MessageDetail.tsx b/ts/state/smart/MessageDetail.tsx index f1ca063427c2..cca3bb819a52 100644 --- a/ts/state/smart/MessageDetail.tsx +++ b/ts/state/smart/MessageDetail.tsx @@ -7,7 +7,12 @@ import { useSelector } from 'react-redux'; import type { Props as MessageDetailProps } from '../../components/conversation/MessageDetail'; import { MessageDetail } from '../../components/conversation/MessageDetail'; import { getContactNameColorSelector } from '../selectors/conversations'; -import { getIntl, getInteractionMode, getTheme } from '../selectors/user'; +import { + getIntl, + getInteractionMode, + getTheme, + getPlatform, +} from '../selectors/user'; import { getMessageDetails } from '../selectors/message'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { renderAudioAttachment } from './renderAudioAttachment'; @@ -27,6 +32,7 @@ export function SmartMessageDetail(): JSX.Element | null { const getContactNameColor = useSelector(getContactNameColorSelector); const getPreferredBadge = useSelector(getPreferredBadgeSelector); const i18n = useSelector(getIntl); + const platform = useSelector(getPlatform); const interactionMode = useSelector(getInteractionMode); const messageDetails = useSelector(getMessageDetails); const theme = useSelector(getTheme); @@ -76,6 +82,7 @@ export function SmartMessageDetail(): JSX.Element | null { errors={errors} getPreferredBadge={getPreferredBadge} i18n={i18n} + platform={platform} interactionMode={interactionMode} kickOffAttachmentDownload={kickOffAttachmentDownload} markAttachmentAsCorrupted={markAttachmentAsCorrupted} diff --git a/ts/state/smart/StoryViewer.tsx b/ts/state/smart/StoryViewer.tsx index 7b2d1079e5e7..bad08f96989f 100644 --- a/ts/state/smart/StoryViewer.tsx +++ b/ts/state/smart/StoryViewer.tsx @@ -18,7 +18,7 @@ import { getPreferredReactionEmoji, isInternalUser, } from '../selectors/items'; -import { getIntl } from '../selectors/user'; +import { getIntl, getPlatform } from '../selectors/user'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { getSelectedStoryData, @@ -56,6 +56,7 @@ export function SmartStoryViewer(): JSX.Element | null { const isWindowActive = useIsWindowActive(); const i18n = useSelector(getIntl); + const platform = useSelector(getPlatform); const getPreferredBadge = useSelector(getPreferredBadgeSelector); const preferredReactionEmoji = useSelector>( getPreferredReactionEmoji @@ -111,6 +112,7 @@ export function SmartStoryViewer(): JSX.Element | null { hasAllStoriesUnmuted={hasAllStoriesUnmuted} hasViewReceiptSetting={hasViewReceiptSetting} i18n={i18n} + platform={platform} isInternalUser={internalUser} saveAttachment={internalUser ? saveAttachment : asyncShouldNeverBeCalled} isSignalConversation={isSignalConversation({ diff --git a/ts/state/smart/TimelineItem.tsx b/ts/state/smart/TimelineItem.tsx index 262a37de92c2..318b6e6a7037 100644 --- a/ts/state/smart/TimelineItem.tsx +++ b/ts/state/smart/TimelineItem.tsx @@ -16,7 +16,12 @@ import { useLightboxActions } from '../ducks/lightbox'; import { useStoriesActions } from '../ducks/stories'; import { useCallingActions } from '../ducks/calling'; import { getPreferredBadgeSelector } from '../selectors/badges'; -import { getIntl, getInteractionMode, getTheme } from '../selectors/user'; +import { + getIntl, + getInteractionMode, + getTheme, + getPlatform, +} from '../selectors/user'; import { getTargetedMessage } from '../selectors/conversations'; import { getTimelineItem } from '../selectors/timeline'; import { @@ -67,6 +72,7 @@ export function SmartTimelineItem(props: ExternalProps): JSX.Element { const getPreferredBadge = useSelector(getPreferredBadgeSelector); const interactionMode = useSelector(getInteractionMode); const theme = useSelector(getTheme); + const platform = useSelector(getPlatform); const item = useProxySelector(getTimelineItem, messageId); const previousItem = useProxySelector(getTimelineItem, previousMessageId); const nextItem = useProxySelector(getTimelineItem, nextMessageId); @@ -166,6 +172,7 @@ export function SmartTimelineItem(props: ExternalProps): JSX.Element { i18n={i18n} interactionMode={interactionMode} theme={theme} + platform={platform} blockGroupLinkRequests={blockGroupLinkRequests} checkForAccount={checkForAccount} clearTargetedMessage={clearSelectedMessage}