diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 71bc554cb..56cd35866 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -41,7 +41,7 @@ "unreadMessages": "Unread Messages", "debugLogExplanation": "This log will be saved to your desktop.", "reportIssue": "Report a Bug", - "markAllAsRead": "Mark All as Read", + "markAllAsRead": "Mark Read", "incomingError": "Error handling incoming message", "media": "Media", "mediaEmptyState": "No media", @@ -79,7 +79,7 @@ "typingAlt": "Typing animation for this conversation", "contactAvatarAlt": "Avatar for contact $name$", "downloadAttachment": "Download Attachment", - "replyToMessage": "Reply to message", + "replyToMessage": "Reply", "replyingToMessage": "Replying to:", "originalMessageNotFound": "Original message not found", "you": "You", @@ -107,13 +107,10 @@ "deleteMessagesConfirmation": "Permanently delete the messages in this conversation?", "hideConversation": "Hide Conversation", "hideNoteToSelfConfirmation": "Are you sure you want to hide your Note to Self conversation?", - "hideConversationFailed": "Failed to hide the Conversation!", - "hideConversationFailedPleaseTryAgain": "Unable to hide the conversation, please try again", "deleteConversation": "Delete Conversation", "deleteConversationConfirmation": "Are you sure you want to delete your conversation with $name$?", "deleteConversationFailed": "Failed to delete the Conversation!", "deleteConversationFailedPleaseTryAgain": "Unable to delete the conversation, please try again", - "hiding": "Hiding...", "leaving": "Leaving...", "deleted": "$count$ deleted", "messageDeletedPlaceholder": "This message has been deleted", @@ -126,6 +123,7 @@ "groupMembers": "Members", "moreInformation": "More information", "failed": "Failed", + "failedToSendMessage": "Failed to send message", "read": "Read", "resend": "Resend", "clear": "Clear", diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index c7df9722e..349b85b70 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -554,7 +554,8 @@ justify-content: center; align-items: center; flex-grow: 1; - font-size: 28px; + margin-top: var(--margins-sm); + font-size: 16px; } // Module: Conversation List Item diff --git a/ts/components/conversation/message/message-content/ClickToTrustSender.tsx b/ts/components/conversation/message/message-content/ClickToTrustSender.tsx index 25afee8a7..d54733a1a 100644 --- a/ts/components/conversation/message/message-content/ClickToTrustSender.tsx +++ b/ts/components/conversation/message/message-content/ClickToTrustSender.tsx @@ -106,6 +106,9 @@ export const ClickToTrustSender = (props: { messageId: string }) => { }) ); }, + onClickClose: () => { + window.inboxStore?.dispatch(updateConfirmModal(null)); + }, }) ); }; diff --git a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx index 0a4d9ddc3..c9941095e 100644 --- a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx @@ -132,7 +132,7 @@ export const MessageContentWithStatuses = (props: Props) => { onDoubleClickCapture={onDoubleClickReplyToMessage} dataTestId={dataTestId} > - + {!isDetailView && } diff --git a/ts/components/conversation/message/message-content/MessageStatus.tsx b/ts/components/conversation/message/message-content/MessageStatus.tsx index ea7ce2ebd..41b9386fb 100644 --- a/ts/components/conversation/message/message-content/MessageStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageStatus.tsx @@ -30,12 +30,11 @@ type Props = { * - if the message is incoming: do not show anything (3) * - if the message is outgoing: show the text for the last message, or a message sending, or in the error state. (4) */ -export const MessageStatus = (props: Props) => { - const { messageId, isDetailView, dataTestId } = props; - const status = useMessageStatus(props.messageId); - const selected = useMessageExpirationPropsById(props.messageId); +export const MessageStatus = ({ isDetailView, messageId, dataTestId }: Props) => { + const status = useMessageStatus(messageId); + const selected = useMessageExpirationPropsById(messageId); - if (!props.messageId || !selected || isDetailView) { + if (!messageId || !selected || isDetailView) { return null; } const isIncoming = selected.direction === 'incoming'; @@ -79,15 +78,15 @@ const MessageStatusContainer = styled.div<{ isIncoming: boolean; isGroup: boolea props.isGroup || !props.isIncoming ? 'var(--width-avatar-group-msg-list)' : 0}; `; -const StyledStatusText = styled.div` - color: var(--text-secondary-color); +const StyledStatusText = styled.div<{ textColor: string }>` font-size: small; + color: ${props => props.textColor}; `; -const TextDetails = ({ text }: { text: string }) => { +const TextDetails = ({ text, textColor }: { text: string; textColor: string }) => { return ( <> - {text} + {text} ); @@ -154,7 +153,7 @@ const MessageStatusSending = ({ dataTestId }: Omit) => { isIncoming={false} isGroup={false} > - + ); @@ -193,7 +192,7 @@ const MessageStatusSent = ({ dataTestId, messageId }: Omit - + ); @@ -221,7 +220,7 @@ const MessageStatusRead = ({ isIncoming={isIncoming} isGroup={isGroup} > - + ); @@ -243,7 +242,7 @@ const MessageStatusError = ({ dataTestId }: Omit) => { isIncoming={false} isGroup={isGroup} > - + ); diff --git a/ts/components/conversation/message/message-item/InteractionNotification.tsx b/ts/components/conversation/message/message-item/InteractionNotification.tsx index e4a5d0553..be06bee1d 100644 --- a/ts/components/conversation/message/message-item/InteractionNotification.tsx +++ b/ts/components/conversation/message/message-item/InteractionNotification.tsx @@ -38,7 +38,7 @@ export const InteractionNotification = (props: PropsForInteractionNotification) switch (interactionType) { case ConversationInteractionType.Hide: - text = window.i18n('hideConversationFailedPleaseTryAgain'); + // this can't happen break; case ConversationInteractionType.Leave: text = isCommunity diff --git a/ts/components/conversation/right-panel/RightPanel.tsx b/ts/components/conversation/right-panel/RightPanel.tsx index e164c0fb2..8ca45ab0c 100644 --- a/ts/components/conversation/right-panel/RightPanel.tsx +++ b/ts/components/conversation/right-panel/RightPanel.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { useRightOverlayMode } from '../../../hooks/useUI'; +import { isRtlBody } from '../../../util/i18n'; import { Flex } from '../../basic/Flex'; import { OverlayRightPanelSettings } from './overlay/OverlayRightPanelSettings'; import { OverlayDisappearingMessages } from './overlay/disappearing-messages/OverlayDisappearingMessages'; @@ -113,6 +114,8 @@ const ClosableOverlay = () => { }; export const RightPanel = () => { + const isRtlMode = isRtlBody(); + return ( { width={'var(--right-panel-width)'} height={'var(--right-panel-height)'} className="right-panel" + style={{ direction: isRtlMode ? 'rtl' : 'initial' }} > diff --git a/ts/components/conversation/right-panel/overlay/OverlayRightPanelSettings.tsx b/ts/components/conversation/right-panel/overlay/OverlayRightPanelSettings.tsx index d83a019dd..c08aa5498 100644 --- a/ts/components/conversation/right-panel/overlay/OverlayRightPanelSettings.tsx +++ b/ts/components/conversation/right-panel/overlay/OverlayRightPanelSettings.tsx @@ -42,7 +42,6 @@ import { AttachmentTypeWithPath } from '../../../../types/Attachment'; import { getAbsoluteAttachmentPath } from '../../../../types/MessageAttachment'; import { Avatar, AvatarSize } from '../../../avatar/Avatar'; import { Flex } from '../../../basic/Flex'; -import { SessionButton, SessionButtonColor, SessionButtonType } from '../../../basic/SessionButton'; import { SpacerMD } from '../../../basic/Text'; import { PanelButtonGroup, PanelIconButton } from '../../../buttons'; import { MediaItemType } from '../../../lightbox/LightboxGallery'; @@ -183,25 +182,6 @@ const HeaderItem = () => { ); }; -const StyledLeaveButton = styled.div` - width: 100%; - .session-button { - margin-top: auto; - width: 100%; - min-height: calc(var(--composition-container-height) + 1px); // include border in height - flex-shrink: 0; - align-items: center; - border-top: 1px solid var(--border-color); - border-radius: 0px; - - &:not(.disabled) { - &:hover { - background-color: var(--button-solid-background-hover-color); - } - } - } -`; - const StyledName = styled.h4` padding-inline: var(--margins-md); font-size: var(--font-size-md); @@ -359,15 +339,14 @@ export const OverlayRightPanelSettings = () => { {isGroup && ( - - - + void deleteConvoAction()} + color={'var(--danger-color)'} + iconType={'delete'} + /> )} diff --git a/ts/components/conversation/right-panel/overlay/message-info/components/AttachmentInfo.tsx b/ts/components/conversation/right-panel/overlay/message-info/components/AttachmentInfo.tsx index 555416c89..44344b5a6 100644 --- a/ts/components/conversation/right-panel/overlay/message-info/components/AttachmentInfo.tsx +++ b/ts/components/conversation/right-panel/overlay/message-info/components/AttachmentInfo.tsx @@ -10,7 +10,8 @@ type Props = { const StyledLabelContainer = styled(Flex)` div { - flex-grow: 1; + // we want 2 items per row and that's the easiest to make it happen + min-width: 50%; } `; @@ -23,7 +24,7 @@ export const AttachmentInfo = (props: Props) => { label={`${window.i18n('fileId')}:`} info={attachment?.id ? String(attachment.id) : window.i18n('notApplicable')} /> - + { label={`${window.i18n('fileSize')}:`} info={attachment?.fileSize ? String(attachment.fileSize) : window.i18n('notApplicable')} /> - - { + window.inboxStore?.dispatch(updateConfirmModal(null)); + }, }) ); return; @@ -122,6 +125,9 @@ async function deleteEverythingAndNetworkData() { await deleteDbLocally(); window.restart(); }, + onClickClose: () => { + window.inboxStore?.dispatch(updateConfirmModal(null)); + }, }) ); return; diff --git a/ts/components/dialog/SessionConfirm.tsx b/ts/components/dialog/SessionConfirm.tsx index a964a3612..932a8a66c 100644 --- a/ts/components/dialog/SessionConfirm.tsx +++ b/ts/components/dialog/SessionConfirm.tsx @@ -1,5 +1,6 @@ import { shell } from 'electron'; import React, { Dispatch, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { useLastMessage } from '../../hooks/useParamSelector'; import { MessageInteraction } from '../../interactions'; @@ -69,6 +70,7 @@ export interface SessionConfirmDialogProps { } export const SessionConfirm = (props: SessionConfirmDialogProps) => { + const dispatch = useDispatch(); const { title = '', message = '', @@ -113,7 +115,7 @@ export const SessionConfirm = (props: SessionConfirmDialogProps) => { } if (closeAfterInput) { - window.inboxStore?.dispatch(updateConfirmModal(null)); + dispatch(updateConfirmModal(null)); } }; @@ -137,13 +139,9 @@ export const SessionConfirm = (props: SessionConfirmDialogProps) => { * Performs specified on close action then removes the modal. */ const onClickCancelHandler = () => { - if (onClickCancel) { - onClickCancel(); - } + onClickCancel?.(); - if (onClickClose) { - onClickClose(); - } + onClickClose?.(); }; return ( diff --git a/ts/components/leftpane/conversation-list-item/InteractionItem.tsx b/ts/components/leftpane/conversation-list-item/InteractionItem.tsx index 6a2ef801f..0e7b0031f 100644 --- a/ts/components/leftpane/conversation-list-item/InteractionItem.tsx +++ b/ts/components/leftpane/conversation-list-item/InteractionItem.tsx @@ -58,14 +58,7 @@ export const InteractionItem = (props: InteractionItemProps) => { switch (interactionType) { case ConversationInteractionType.Hide: - errorText = window.i18n('hideConversationFailed'); - text = - interactionStatus === ConversationInteractionStatus.Error - ? errorText - : interactionStatus === ConversationInteractionStatus.Start || - interactionStatus === ConversationInteractionStatus.Loading - ? window.i18n('hiding') - : text; + // if it's hidden or pending hiding, we don't show any text break; case ConversationInteractionType.Leave: errorText = isCommunity diff --git a/ts/components/settings/section/CategoryPrivacy.tsx b/ts/components/settings/section/CategoryPrivacy.tsx index 4e0f5be3b..59e836d52 100644 --- a/ts/components/settings/section/CategoryPrivacy.tsx +++ b/ts/components/settings/section/CategoryPrivacy.tsx @@ -32,6 +32,9 @@ async function toggleLinkPreviews(isToggleOn: boolean, forceUpdate: () => void) await window.setSettingValue(SettingsKey.settingsLinkPreview, newValue); forceUpdate(); }, + onClickClose: () => { + window.inboxStore?.dispatch(updateConfirmModal(null)); + }, }) ); } else { diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index de0509504..98d18929d 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -294,12 +294,6 @@ async function leaveGroupOrCommunityByConvoId( onClickClose?: () => void ) { try { - await updateConversationInteractionState({ - conversationId, - type: ConversationInteractionType.Leave, - status: ConversationInteractionStatus.Start, - }); - if (onClickClose) { onClickClose(); } @@ -308,13 +302,20 @@ async function leaveGroupOrCommunityByConvoId( await getConversationController().deleteCommunity(conversationId, { fromSyncMessage: false, }); - } else { - await getConversationController().deleteClosedGroup(conversationId, { - fromSyncMessage: false, - sendLeaveMessage: true, - forceDeleteLocal, - }); + return; } + // for groups, we have a "leaving..." state that we don't need for communities. + // that's because communities can be left always, whereas for groups we need to send a leave message (and so have some encryption keypairs) + await updateConversationInteractionState({ + conversationId, + type: ConversationInteractionType.Leave, + status: ConversationInteractionStatus.Start, + }); + await getConversationController().deleteClosedGroup(conversationId, { + fromSyncMessage: false, + sendLeaveMessage: true, + forceDeleteLocal, + }); await clearConversationInteractionState({ conversationId }); } catch (err) { window.log.warn(`showLeaveGroupByConvoId error: ${err}`); diff --git a/ts/interactions/conversations/unsendingInteractions.ts b/ts/interactions/conversations/unsendingInteractions.ts index 50f055248..c73c77117 100644 --- a/ts/interactions/conversations/unsendingInteractions.ts +++ b/ts/interactions/conversations/unsendingInteractions.ts @@ -347,6 +347,8 @@ export async function deleteMessagesByIdForEveryone( const messageCount = selectedMessages.length; const moreThanOne = messageCount > 1; + const closeDialog = () => window.inboxStore?.dispatch(updateConfirmModal(null)); + window.inboxStore?.dispatch( updateConfirmModal({ title: window.i18n('deleteForEveryone'), @@ -359,8 +361,9 @@ export async function deleteMessagesByIdForEveryone( await doDeleteSelectedMessages({ selectedMessages, conversation, deleteForEveryone: true }); // explicitly close modal for this case. - window.inboxStore?.dispatch(updateConfirmModal(null)); + closeDialog(); }, + onClickCancel: closeDialog, closeAfterInput: false, }) ); @@ -374,6 +377,7 @@ export async function deleteMessagesById(messageIds: Array, conversation const messageCount = selectedMessages.length; const moreThanOne = selectedMessages.length > 1; + const closeDialog = () => window.inboxStore?.dispatch(updateConfirmModal(null)); window.inboxStore?.dispatch( updateConfirmModal({ @@ -398,6 +402,7 @@ export async function deleteMessagesById(messageIds: Array, conversation window.inboxStore?.dispatch(resetRightOverlayMode()); }, closeAfterInput: false, + onClickClose: closeDialog, }) ); } diff --git a/ts/models/message.ts b/ts/models/message.ts index 7b7bfe827..92fb02abd 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -1435,7 +1435,8 @@ export class MessageModel extends Backbone.Model { switch (interactionType) { case ConversationInteractionType.Hide: - return window.i18n('hideConversationFailed'); + // there is no text for hiding changes + return ''; case ConversationInteractionType.Leave: return isCommunity ? window.i18n('leaveCommunityFailed') diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index a9cb4daa3..761586f78 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -519,7 +519,6 @@ async function leaveClosedGroup(groupId: string, fromSyncMessage: boolean) { }); window?.log?.info(`We are leaving the group ${groupId}. Sending our leaving message.`); - // if we do not have a keypair for that group, we can't send our leave message, so just skip the message sending part const wasSent = await getMessageQueue().sendToPubKeyNonDurably({ message: ourLeavingMessage, diff --git a/ts/session/conversations/createClosedGroup.ts b/ts/session/conversations/createClosedGroup.ts index f1a6ad23f..e30afa86a 100644 --- a/ts/session/conversations/createClosedGroup.ts +++ b/ts/session/conversations/createClosedGroup.ts @@ -116,7 +116,6 @@ async function sendToGroupMembers( const allInvitesSent = _.every(inviteResults, inviteResult => inviteResult !== false); if (allInvitesSent) { - // if (true) { if (isRetry) { const invitesTitle = inviteResults.length > 1 @@ -128,6 +127,9 @@ async function sendToGroupMembers( title: invitesTitle, message: window.i18n('closedGroupInviteSuccessMessage'), hideCancel: true, + onClickClose: () => { + window.inboxStore?.dispatch(updateConfirmModal(null)); + }, }) ); } @@ -167,6 +169,9 @@ async function sendToGroupMembers( ); } }, + onClickClose: () => { + window.inboxStore?.dispatch(updateConfirmModal(null)); + }, }) ); diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts index fa889bc46..87a80c1ac 100644 --- a/ts/types/LocalizerKeys.ts +++ b/ts/types/LocalizerKeys.ts @@ -202,6 +202,7 @@ export type LocalizerKeys = | 'failedResolveOns' | 'failedToAddAsModerator' | 'failedToRemoveFromModerator' + | 'failedToSendMessage' | 'faq' | 'fileId' | 'fileSize' @@ -221,14 +222,11 @@ export type LocalizerKeys = | 'hide' | 'hideBanner' | 'hideConversation' - | 'hideConversationFailed' - | 'hideConversationFailedPleaseTryAgain' | 'hideMenuBarDescription' | 'hideMenuBarTitle' | 'hideNoteToSelfConfirmation' | 'hideRequestBanner' | 'hideRequestBannerDescription' - | 'hiding' | 'iAmSure' | 'image' | 'imageAttachmentAlt'