chore: move selected convo selectors to another file

This commit is contained in:
Audric Ackermann 2023-03-24 16:48:50 +11:00
parent c3e9d503e4
commit 0050352470
34 changed files with 467 additions and 447 deletions

View file

@ -3,7 +3,6 @@ import { useSelector } from 'react-redux';
import Draggable, { DraggableData, DraggableEvent } from 'react-draggable';
import styled from 'styled-components';
import { getSelectedConversationKey } from '../../state/selectors/conversations';
import { getHasOngoingCall, getHasOngoingCallWith } from '../../state/selectors/call';
import { openConversationWithMessages } from '../../state/ducks/conversations';
import { Avatar, AvatarSize } from '../avatar/Avatar';
@ -11,6 +10,7 @@ import { useVideoCallEventsListener } from '../../hooks/useVideoEventListener';
import { VideoLoadingSpinner } from './InConversationCallContainer';
import { getSection } from '../../state/selectors/section';
import { SectionType } from '../../state/ducks/section';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
export const DraggableCallWindow = styled.div`
position: absolute;
@ -58,7 +58,7 @@ const CenteredAvatarInDraggable = styled.div`
export const DraggableCallContainer = () => {
const ongoingCallProps = useSelector(getHasOngoingCallWith);
const selectedConversationKey = useSelector(getSelectedConversationKey);
const selectedConversationKey = useSelectedConversationKey();
const hasOngoingCall = useSelector(getHasOngoingCall);
const selectedSection = useSelector(getSection);

View file

@ -6,15 +6,6 @@ import { contextMenu } from 'react-contexify';
import styled from 'styled-components';
import { ConversationNotificationSettingType } from '../../models/conversationAttributes';
import {
getConversationHeaderTitleProps,
getCurrentNotificationSettingText,
getCurrentSubscriberCount,
getIsSelectedActive,
getIsSelectedBlocked,
getIsSelectedNoteToSelf,
getIsSelectedPrivate,
getSelectedConversationIsPublic,
getSelectedConversationKey,
getSelectedMessageIds,
isMessageDetailView,
isMessageSelectionMode,
@ -50,6 +41,19 @@ import { SessionIconButton } from '../icon';
import { ConversationHeaderMenu } from '../menu/ConversationHeaderMenu';
import { Flex } from '../basic/Flex';
import { ExpirationTimerOptions } from '../../util/expiringMessages';
import {
useSelectedConversationKey,
useSelectedIsActive,
useSelectedIsBlocked,
useSelectedIsGroup,
useSelectedIsKickedFromGroup,
useSelectedisNoteToSelf,
useSelectedIsPrivate,
useSelectedIsPublic,
useSelectedMembers,
useSelectedNotificationSetting,
useSelectedSubscriberCount,
} from '../../state/selectors/selectedConversation';
export interface TimerOption {
name: string;
@ -58,8 +62,8 @@ export interface TimerOption {
const SelectionOverlay = () => {
const selectedMessageIds = useSelector(getSelectedMessageIds);
const selectedConversationKey = useSelector(getSelectedConversationKey);
const isPublic = useSelector(getSelectedConversationIsPublic);
const selectedConversationKey = useSelectedConversationKey();
const isPublic = useSelectedIsPublic();
const dispatch = useDispatch();
const { i18n } = window;
@ -200,11 +204,11 @@ const BackButton = (props: { onGoBack: () => void; showBackButton: boolean }) =>
};
const CallButton = () => {
const isPrivate = useSelector(getIsSelectedPrivate);
const isBlocked = useSelector(getIsSelectedBlocked);
const activeAt = useSelector(getIsSelectedActive);
const isMe = useSelector(getIsSelectedNoteToSelf);
const selectedConvoKey = useSelector(getSelectedConversationKey);
const isPrivate = useSelectedIsPrivate();
const isBlocked = useSelectedIsBlocked();
const activeAt = useSelectedIsActive();
const isMe = useSelectedisNoteToSelf();
const selectedConvoKey = useSelectedConversationKey();
const hasIncomingCall = useSelector(getHasIncomingCall);
const hasOngoingCall = useSelector(getHasOngoingCall);
@ -266,18 +270,22 @@ export const ConversationHeaderSubtitle = (props: { text?: string | null }): JSX
const ConversationHeaderTitle = () => {
const dispatch = useDispatch();
const headerTitleProps = useSelector(getConversationHeaderTitleProps);
const notificationSetting = useSelector(getCurrentNotificationSettingText);
const notificationSetting = useSelectedNotificationSetting();
const isRightPanelOn = useSelector(isRightPanelShowing);
const convoName = useConversationUsername(headerTitleProps?.conversationKey);
const subscriberCount = useSelector(getCurrentSubscriberCount);
const subscriberCount = useSelectedSubscriberCount();
const selectedConvoKey = useSelectedConversationKey();
const convoName = useConversationUsername(selectedConvoKey);
if (!headerTitleProps) {
const isPublic = useSelectedIsPublic();
const isKickedFromGroup = useSelectedIsKickedFromGroup();
const isMe = useSelectedisNoteToSelf();
const isGroup = useSelectedIsGroup();
const members = useSelectedMembers();
if (!selectedConvoKey) {
return null;
}
const { isGroup, isPublic, members, isMe, isKickedFromGroup } = headerTitleProps;
const { i18n } = window;
if (isMe) {
@ -331,15 +339,15 @@ const ConversationHeaderTitle = () => {
export const ConversationHeaderWithDetails = () => {
const isSelectionMode = useSelector(isMessageSelectionMode);
const isMessageDetailOpened = useSelector(isMessageDetailView);
const selectedConvoKey = useSelector(getSelectedConversationKey);
const selectedConvoKey = useSelectedConversationKey();
const dispatch = useDispatch();
const isKickedFromGroup = useIsKickedFromGroup(selectedConvoKey);
const expireTimerSetting = useExpireTimer(selectedConvoKey);
if (!selectedConvoKey) {
return null;
}
const isKickedFromGroup = useIsKickedFromGroup(selectedConvoKey);
const expireTimerSetting = useExpireTimer(selectedConvoKey);
const expirationSettingName = expireTimerSetting
? ExpirationTimerOptions.getName(expireTimerSetting || 0)
: undefined;

View file

@ -7,10 +7,8 @@ import {
declineConversationWithConfirm,
} from '../../interactions/conversationInteractions';
import { getConversationController } from '../../session/conversations';
import {
getSelectedConversation,
hasSelectedConversationIncomingMessages,
} from '../../state/selectors/conversations';
import { hasSelectedConversationIncomingMessages } from '../../state/selectors/conversations';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
const handleDeclineConversationRequest = (convoId: string) => {
@ -44,12 +42,12 @@ const ConversationBannerRow = styled.div`
`;
export const ConversationMessageRequestButtons = () => {
const selectedConversation = useSelector(getSelectedConversation);
const selectedConvoId = useSelectedConversationKey();
const hasIncomingMessages = useSelector(hasSelectedConversationIncomingMessages);
const isIncomingMessageRequest = useIsRequest(selectedConversation?.id);
const isIncomingMessageRequest = useIsRequest(selectedConvoId);
if (!selectedConversation || !hasIncomingMessages) {
if (!selectedConvoId || !hasIncomingMessages) {
return null;
}
@ -62,7 +60,7 @@ export const ConversationMessageRequestButtons = () => {
<ConversationBannerRow>
<SessionButton
onClick={async () => {
await handleAcceptConversationRequest(selectedConversation.id);
await handleAcceptConversationRequest(selectedConvoId);
}}
text={window.i18n('accept')}
dataTestId="accept-message-request"
@ -71,7 +69,7 @@ export const ConversationMessageRequestButtons = () => {
buttonColor={SessionButtonColor.Danger}
text={window.i18n('decline')}
onClick={() => {
handleDeclineConversationRequest(selectedConversation.id);
handleDeclineConversationRequest(selectedConvoId);
}}
dataTestId="decline-message-request"
/>

View file

@ -2,10 +2,8 @@ import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { useIsRequest } from '../../hooks/useParamSelector';
import {
getSelectedConversation,
hasSelectedConversationIncomingMessages,
} from '../../state/selectors/conversations';
import { hasSelectedConversationIncomingMessages } from '../../state/selectors/conversations';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
const ConversationRequestTextBottom = styled.div`
display: flex;
@ -22,8 +20,8 @@ const ConversationRequestTextInner = styled.div`
`;
export const ConversationRequestinfo = () => {
const selectedConversation = useSelector(getSelectedConversation);
const isIncomingMessageRequest = useIsRequest(selectedConversation?.id);
const selectedConversation = useSelectedConversationKey();
const isIncomingMessageRequest = useIsRequest(selectedConversation);
const showMsgRequestUI = selectedConversation && isIncomingMessageRequest;
const hasIncomingMessages = useSelector(hasSelectedConversationIncomingMessages);

View file

@ -15,19 +15,19 @@ import {
import {
getOldBottomMessageId,
getOldTopMessageId,
getSelectedConversationKey,
getSortedMessagesTypesOfSelectedConversation,
} from '../../state/selectors/conversations';
import { GroupUpdateMessage } from './message/message-item/GroupUpdateMessage';
import { MessageRequestResponse } from './message/message-item/MessageRequestResponse';
import { MessageDateBreak } from './message/message-item/DateBreak';
import { GroupInvitation } from './message/message-item/GroupInvitation';
import { GroupUpdateMessage } from './message/message-item/GroupUpdateMessage';
import { Message } from './message/message-item/Message';
import { MessageRequestResponse } from './message/message-item/MessageRequestResponse';
import { CallNotification } from './message/message-item/notification-bubble/CallNotification';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification';
import { SessionLastSeenIndicator } from './SessionLastSeenIndicator';
import { TimerNotification } from './TimerNotification';
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification';
function isNotTextboxEvent(e: KeyboardEvent) {
return (e?.target as any)?.type === undefined;
@ -46,7 +46,7 @@ export const SessionMessagesList = (props: {
onEndPressed: () => void;
}) => {
const messagesProps = useSelector(getSortedMessagesTypesOfSelectedConversation);
const convoKey = useSelector(getSelectedConversationKey);
const convoKey = useSelectedConversationKey();
const [didScroll, setDidScroll] = useState(false);
const oldTopMessageId = useSelector(getOldTopMessageId);

View file

@ -17,12 +17,14 @@ import {
import { StateType } from '../../state/reducer';
import {
getQuotedMessageToAnimate,
getSelectedConversation,
getSelectedConversationKey,
getSortedMessagesOfSelectedConversation,
} from '../../state/selectors/conversations';
import { TypingBubble } from './TypingBubble';
import styled from 'styled-components';
import {
getSelectedConversation,
getSelectedConversationKey,
} from '../../state/selectors/selectedConversation';
export type SessionMessageListProps = {
messageContainerRef: React.RefObject<HTMLDivElement>;

View file

@ -17,11 +17,7 @@ import {
} from '../../interactions/conversationInteractions';
import { Constants } from '../../session';
import { closeRightPanel } from '../../state/ducks/conversations';
import {
getCurrentSubscriberCount,
getSelectedConversation,
isRightPanelShowing,
} from '../../state/selectors/conversations';
import { isRightPanelShowing } from '../../state/selectors/conversations';
import { getTimerOptions } from '../../state/selectors/timerOptions';
import { AttachmentTypeWithPath } from '../../types/Attachment';
import { Avatar, AvatarSize } from '../avatar/Avatar';
@ -32,6 +28,18 @@ import { MediaItemType } from '../lightbox/LightboxGallery';
import { MediaGallery } from './media-gallery/MediaGallery';
import { getAbsoluteAttachmentPath } from '../../types/MessageAttachment';
import styled from 'styled-components';
import {
useSelectedConversationKey,
useSelectedDisplayNameInProfile,
useSelectedIsActive,
useSelectedIsBlocked,
useSelectedIsGroup,
useSelectedIsKickedFromGroup,
useSelectedIsLeft,
useSelectedIsPublic,
useSelectedSubscriberCount,
useSelectedWeAreAdmin,
} from '../../state/selectors/selectedConversation';
async function getMediaGalleryProps(
conversationId: string
@ -107,13 +115,16 @@ async function getMediaGalleryProps(
}
const HeaderItem = () => {
const selectedConversation = useSelector(getSelectedConversation);
const selectedConvoKey = useSelectedConversationKey();
const dispatch = useDispatch();
const isBlocked = useSelectedIsBlocked();
const isKickedFromGroup = useSelectedIsKickedFromGroup();
const left = useSelectedIsLeft();
const isGroup = useSelectedIsGroup();
if (!selectedConversation) {
if (!selectedConvoKey) {
return null;
}
const { id, isGroup, isKickedFromGroup, isBlocked, left } = selectedConversation;
const showInviteContacts = isGroup && !isKickedFromGroup && !isBlocked && !left;
@ -129,14 +140,14 @@ const HeaderItem = () => {
style={{ position: 'absolute' }}
dataTestId="back-button-conversation-options"
/>
<Avatar size={AvatarSize.XL} pubkey={id} />
<Avatar size={AvatarSize.XL} pubkey={selectedConvoKey} />
{showInviteContacts && (
<SessionIconButton
iconType="addUser"
iconSize="medium"
onClick={() => {
if (selectedConversation) {
showInviteContactByConvoId(selectedConversation.id);
if (selectedConvoKey) {
showInviteContactByConvoId(selectedConvoKey);
}
}}
dataTestId="add-user-button"
@ -191,15 +202,24 @@ export const SessionRightPanelWithDetails = () => {
const [documents, setDocuments] = useState<Array<MediaItemType>>([]);
const [media, setMedia] = useState<Array<MediaItemType>>([]);
const selectedConversation = useSelector(getSelectedConversation);
const selectedConvoKey = useSelectedConversationKey();
const isShowing = useSelector(isRightPanelShowing);
const subscriberCount = useSelector(getCurrentSubscriberCount);
const subscriberCount = useSelectedSubscriberCount();
const isActive = useSelectedIsActive();
const displayNameInProfile = useSelectedDisplayNameInProfile();
const isBlocked = useSelectedIsBlocked();
const isKickedFromGroup = useSelectedIsKickedFromGroup();
const left = useSelectedIsLeft();
const isGroup = useSelectedIsGroup();
const isPublic = useSelectedIsPublic();
const weAreAdmin = useSelectedWeAreAdmin();
useEffect(() => {
let isRunning = true;
if (isShowing && selectedConversation) {
void getMediaGalleryProps(selectedConversation.id).then(results => {
if (isShowing && selectedConvoKey) {
void getMediaGalleryProps(selectedConvoKey).then(results => {
if (isRunning) {
if (!_.isEqual(documents, results.documents)) {
setDocuments(results.documents);
@ -216,11 +236,11 @@ export const SessionRightPanelWithDetails = () => {
isRunning = false;
return;
};
}, [isShowing, selectedConversation?.id]);
}, [isShowing, selectedConvoKey]);
useInterval(async () => {
if (isShowing && selectedConversation) {
const results = await getMediaGalleryProps(selectedConversation.id);
if (isShowing && selectedConvoKey) {
const results = await getMediaGalleryProps(selectedConvoKey);
if (results.documents.length !== documents.length || results.media.length !== media.length) {
setDocuments(results.documents);
setMedia(results.media);
@ -228,23 +248,12 @@ export const SessionRightPanelWithDetails = () => {
}
}, 10000);
if (!selectedConversation) {
if (!selectedConvoKey) {
return null;
}
const {
id,
displayNameInProfile,
isKickedFromGroup,
left,
isPublic,
weAreAdmin,
isBlocked,
isGroup,
activeAt,
} = selectedConversation;
const showMemberCount = !!(subscriberCount && subscriberCount > 0);
const commonNoShow = isKickedFromGroup || left || isBlocked || !activeAt;
const commonNoShow = isKickedFromGroup || left || isBlocked || !isActive;
const hasDisappearingMessages = !isPublic && !commonNoShow;
const leaveGroupString = isPublic
? window.i18n('leaveGroup')
@ -260,7 +269,7 @@ export const SessionRightPanelWithDetails = () => {
return {
content: option.name,
onClick: () => {
void setDisappearingMessagesByConvoId(id, option.value);
void setDisappearingMessagesByConvoId(selectedConvoKey, option.value);
},
};
});
@ -272,10 +281,10 @@ export const SessionRightPanelWithDetails = () => {
const deleteConvoAction = isPublic
? () => {
deleteAllMessagesByConvoIdWithConfirmation(id);
deleteAllMessagesByConvoIdWithConfirmation(selectedConvoKey); // TODO this does not delete the public group and showLeaveGroupByConvoId is not only working for closed groups
}
: () => {
showLeaveGroupByConvoId(id);
showLeaveGroupByConvoId(selectedConvoKey);
};
return (
<div className="group-settings">
@ -295,7 +304,7 @@ export const SessionRightPanelWithDetails = () => {
className="group-settings-item"
role="button"
onClick={async () => {
await showUpdateGroupNameByConvoId(id);
await showUpdateGroupNameByConvoId(selectedConvoKey);
}}
>
{isPublic ? window.i18n('editGroup') : window.i18n('editGroupName')}
@ -307,7 +316,7 @@ export const SessionRightPanelWithDetails = () => {
className="group-settings-item"
role="button"
onClick={() => {
showAddModeratorsByConvoId(id);
showAddModeratorsByConvoId(selectedConvoKey);
}}
>
{window.i18n('addModerators')}
@ -316,7 +325,7 @@ export const SessionRightPanelWithDetails = () => {
className="group-settings-item"
role="button"
onClick={() => {
showRemoveModeratorsByConvoId(id);
showRemoveModeratorsByConvoId(selectedConvoKey);
}}
>
{window.i18n('removeModerators')}
@ -329,7 +338,7 @@ export const SessionRightPanelWithDetails = () => {
className="group-settings-item"
role="button"
onClick={async () => {
await showUpdateGroupMembersByConvoId(id);
await showUpdateGroupMembersByConvoId(selectedConvoKey);
}}
>
{window.i18n('groupMembers')}

View file

@ -1,21 +1,21 @@
import React from 'react';
import { isImageTypeSupported, isVideoTypeSupported } from '../../util/GoogleChrome';
import { Image } from './Image';
import { StagedGenericAttachment } from './StagedGenericAttachment';
import { StagedPlaceholderAttachment } from './StagedPlaceholderAttachment';
import { useDispatch } from 'react-redux';
import {
removeAllStagedAttachmentsInConversation,
removeStagedAttachmentInConversation,
} from '../../state/ducks/stagedAttachments';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
import {
areAllAttachmentsVisual,
AttachmentType,
getUrl,
isVideoAttachment,
} from '../../types/Attachment';
import { useDispatch, useSelector } from 'react-redux';
import {
removeAllStagedAttachmentsInConversation,
removeStagedAttachmentInConversation,
} from '../../state/ducks/stagedAttachments';
import { getSelectedConversationKey } from '../../state/selectors/conversations';
import { isImageTypeSupported, isVideoTypeSupported } from '../../util/GoogleChrome';
import { Image } from './Image';
import { StagedGenericAttachment } from './StagedGenericAttachment';
import { StagedPlaceholderAttachment } from './StagedPlaceholderAttachment';
type Props = {
attachments: Array<AttachmentType>;
@ -30,7 +30,7 @@ export const StagedAttachmentList = (props: Props) => {
const { attachments, onAddAttachment, onClickAttachment } = props;
const dispatch = useDispatch();
const conversationKey = useSelector(getSelectedConversationKey);
const conversationKey = useSelectedConversationKey();
const onRemoveAllStaged = () => {
if (!conversationKey) {

View file

@ -31,13 +31,7 @@ import { ToastUtils } from '../../../session/utils';
import { ReduxConversationType } from '../../../state/ducks/conversations';
import { removeAllStagedAttachmentsInConversation } from '../../../state/ducks/stagedAttachments';
import { StateType } from '../../../state/reducer';
import {
getIsTypingEnabled,
getMentionsInput,
getQuotedMessage,
getSelectedConversation,
getSelectedConversationKey,
} from '../../../state/selectors/conversations';
import { getMentionsInput, getQuotedMessage } from '../../../state/selectors/conversations';
import { AttachmentUtil } from '../../../util';
import { Flex } from '../../basic/Flex';
import { CaptionEditor } from '../../CaptionEditor';
@ -57,6 +51,11 @@ import { renderEmojiQuickResultRow, searchEmojiForQuery } from './EmojiQuickResu
import { LinkPreviews } from '../../../util/linkPreviews';
import styled from 'styled-components';
import { FixedBaseEmoji } from '../../../types/Reaction';
import {
getSelectedCanWrite,
getSelectedConversation,
getSelectedConversationKey,
} from '../../../state/selectors/selectedConversation';
export interface ReplyingToMessageProps {
convoId: string;
@ -1084,7 +1083,7 @@ const mapStateToProps = (state: StateType) => {
quotedMessageProps: getQuotedMessage(state),
selectedConversation: getSelectedConversation(state),
selectedConversationKey: getSelectedConversationKey(state),
typingEnabled: getIsTypingEnabled(state),
typingEnabled: getSelectedCanWrite(state),
};
};

View file

@ -1,13 +1,12 @@
import React, { useCallback } from 'react';
import classNames from 'classnames';
import React, { useCallback } from 'react';
import moment from 'moment';
// tslint:disable-next-line:match-default-export-name
import formatFileSize from 'filesize';
import { useSelector } from 'react-redux';
import { getSelectedConversationKey } from '../../../state/selectors/conversations';
import { saveAttachmentToDisk } from '../../../util/attachmentsUtil';
import { MediaItemType } from '../../lightbox/LightboxGallery';
import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation';
type Props = {
// Required
@ -24,7 +23,11 @@ export const DocumentListItem = (props: Props) => {
const { shouldShowSeparator, fileName, fileSize, timestamp } = props;
const defaultShowSeparator = shouldShowSeparator === undefined ? true : shouldShowSeparator;
const selectedConversationKey = useSelector(getSelectedConversationKey) as string;
const selectedConversationKey = useSelectedConversationKey();
if (!selectedConversationKey) {
throw new Error('DocumentListItem: selectedConversationKey was not set');
}
const saveAttachmentCallback = useCallback(() => {
void saveAttachmentToDisk({

View file

@ -3,11 +3,11 @@ import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { MessageRenderingProps } from '../../../../models/messageType';
import { PubKey } from '../../../../session/types';
import { getMessageAuthorProps } from '../../../../state/selectors/conversations';
import {
getMessageAuthorProps,
getSelectedConversationIsGroup,
isPublicGroupConversation,
} from '../../../../state/selectors/conversations';
useSelectedIsGroup,
useSelectedIsPublic,
} from '../../../../state/selectors/selectedConversation';
import { Flex } from '../../../basic/Flex';
import { ContactName } from '../../ContactName';
@ -27,8 +27,9 @@ const StyledAuthorContainer = styled(Flex)`
export const MessageAuthorText = (props: Props) => {
const selected = useSelector(state => getMessageAuthorProps(state as any, props.messageId));
const isPublic = useSelector(isPublicGroupConversation);
const isGroup = useSelector(getSelectedConversationIsGroup);
const isPublic = useSelectedIsPublic();
const isGroup = useSelectedIsGroup();
if (!selected) {
return null;
}

View file

@ -8,12 +8,12 @@ import { getSodiumRenderer } from '../../../../session/crypto';
import { PubKey } from '../../../../session/types';
import { openConversationWithMessages } from '../../../../state/ducks/conversations';
import { updateUserDetailsModal } from '../../../../state/ducks/modalDialog';
import { getMessageAvatarProps } from '../../../../state/selectors/conversations';
import {
getIsTypingEnabled,
getMessageAvatarProps,
getSelectedConversationIsGroup,
getSelectedConversationKey,
} from '../../../../state/selectors/conversations';
getSelectedCanWrite,
useSelectedConversationKey,
useSelectedIsGroup,
} from '../../../../state/selectors/selectedConversation';
import { Avatar, AvatarSize, CrownIcon } from '../../../avatar/Avatar';
// tslint:disable: use-simple-attributes
@ -37,10 +37,10 @@ export const MessageAvatar = (props: Props) => {
const dispatch = useDispatch();
const avatarProps = useSelector(state => getMessageAvatarProps(state as any, messageId));
const selectedConvoKey = useSelector(getSelectedConversationKey);
const isSelectedGroup = useSelector(getSelectedConversationIsGroup);
const selectedConvoKey = useSelectedConversationKey();
const isSelectedGroup = useSelectedIsGroup();
const isTypingEnabled = useSelector(getIsTypingEnabled);
const isTypingEnabled = useSelector(getSelectedCanWrite);
if (!avatarProps) {
return null;

View file

@ -1,16 +1,15 @@
import { isEmpty, isEqual } from 'lodash';
import React, { ReactElement, useEffect, useState } from 'react';
import styled from 'styled-components';
import { MessageRenderingProps } from '../../../../models/messageType';
import { isEmpty, isEqual } from 'lodash';
import { SortedReactionList } from '../../../../types/Reaction';
import { StyledPopupContainer } from '../reactions/ReactionPopup';
import { Flex } from '../../../basic/Flex';
import { nativeEmojiData } from '../../../../util/emoji';
import { Reaction, ReactionProps } from '../reactions/Reaction';
import { SessionIcon } from '../../../icon';
import { useMessageReactsPropsById } from '../../../../hooks/useParamSelector';
import { getSelectedConversationIsGroup } from '../../../../state/selectors/conversations';
import { useSelector } from 'react-redux';
import { MessageRenderingProps } from '../../../../models/messageType';
import { useSelectedIsGroup } from '../../../../state/selectors/selectedConversation';
import { SortedReactionList } from '../../../../types/Reaction';
import { nativeEmojiData } from '../../../../util/emoji';
import { Flex } from '../../../basic/Flex';
import { SessionIcon } from '../../../icon';
import { Reaction, ReactionProps } from '../reactions/Reaction';
import { StyledPopupContainer } from '../reactions/ReactionPopup';
export const popupXDefault = -81;
export const popupYDefault = -90;
@ -164,7 +163,7 @@ export const MessageReactions = (props: Props): ReactElement => {
const msgProps = useMessageReactsPropsById(messageId);
const inGroup = useSelector(getSelectedConversationIsGroup);
const inGroup = useSelectedIsGroup();
useEffect(() => {
if (msgProps?.sortedReacts && !isEqual(reactions, msgProps?.sortedReacts)) {

View file

@ -1,22 +1,19 @@
import React, { useState } from 'react';
import classNames from 'classnames';
import React, { useState } from 'react';
import * as MIME from '../../../../../ts/types/MIME';
import * as GoogleChrome from '../../../../../ts/util/GoogleChrome';
import { useSelector } from 'react-redux';
import { noop } from 'lodash';
import { useDisableDrag } from '../../../../hooks/useDisableDrag';
import { useEncryptedFileFetch } from '../../../../hooks/useEncryptedFileFetch';
import { PubKey } from '../../../../session/types';
import {
getSelectedConversationKey,
isPublicGroupConversation,
} from '../../../../state/selectors/conversations';
useSelectedIsPrivate,
useSelectedIsPublic,
} from '../../../../state/selectors/selectedConversation';
import { ContactName } from '../../ContactName';
import { MessageBody } from './MessageBody';
import { useIsPrivate } from '../../../../hooks/useParamSelector';
export type QuotePropsWithoutListener = {
attachment?: QuotedAttachmentType;
@ -229,8 +226,7 @@ export const QuoteText = (
) => {
const { text, attachment, isIncoming } = props;
const convoId = useSelector(getSelectedConversationKey);
const isGroup = !useIsPrivate(convoId);
const isGroup = !useSelectedIsPrivate();
if (text) {
return (
@ -345,7 +341,7 @@ export const Quote = (props: QuotePropsWithListener) => {
setImageBroken(true);
};
const isPublic = useSelector(isPublicGroupConversation);
const isPublic = useSelectedIsPublic();
if (!validateQuote(props)) {
return null;

View file

@ -17,11 +17,11 @@ import {
getMostRecentMessageId,
getOldestMessageId,
getQuotedMessageToAnimate,
getSelectedConversationKey,
getShowScrollButton,
getYoungestMessageId,
} from '../../../../state/selectors/conversations';
import { getIsAppFocused } from '../../../../state/selectors/section';
import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation';
import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer';
type ReadableMessageProps = {
@ -63,7 +63,7 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
const isAppFocused = useSelector(getIsAppFocused);
const dispatch = useDispatch();
const selectedConversationKey = useSelector(getSelectedConversationKey);
const selectedConversationKey = useSelectedConversationKey();
const loadedMessagesLength = useSelector(getLoadedMessagesLength);
const mostRecentMessageId = useSelector(getMostRecentMessageId);
const oldestMessageId = useSelector(getOldestMessageId);

View file

@ -1,12 +1,15 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { PubKey } from '../../../../../session/types';
import {
CallNotificationType,
PropsForCallNotification,
} from '../../../../../state/ducks/conversations';
import { getSelectedConversation } from '../../../../../state/selectors/conversations';
import {
useSelectedConversationKey,
useSelectedDisplayNameInProfile,
useSelectedNickname,
} from '../../../../../state/selectors/selectedConversation';
import { LocalizerKeys } from '../../../../../types/LocalizerKeys';
import { SessionIconType } from '../../../../icon';
import { ReadableMessage } from '../ReadableMessage';
@ -37,13 +40,13 @@ const style: StyleType = {
export const CallNotification = (props: PropsForCallNotification) => {
const { messageId, receivedAt, isUnread, notificationType } = props;
const selectedConvoId = useSelectedConversationKey();
const selectedConvoProps = useSelector(getSelectedConversation);
const displayNameInProfile = useSelectedDisplayNameInProfile();
const nickname = useSelectedNickname();
const displayName =
selectedConvoProps?.nickname ||
selectedConvoProps?.displayNameInProfile ||
(selectedConvoProps?.id && PubKey.shorten(selectedConvoProps?.id));
nickname || displayNameInProfile || (selectedConvoId && PubKey.shorten(selectedConvoId));
const styleItem = style[notificationType];
const notificationText = window.i18n(styleItem.notificationTextKey, [displayName || 'Unknown']);

View file

@ -76,7 +76,7 @@ export const Reaction = (props: ReactionProps): ReactElement => {
const reactionRef = useRef<HTMLDivElement>(null);
const { docX, elW } = useMouse(reactionRef);
const gutterWidth = 380;
const gutterWidth = 380; // TODO make this a variable which can be shared in CSS and JS
const tooltipMidPoint = POPUP_WIDTH / 2; // px
const [tooltipPosition, setTooltipPosition] = useState<TipPosition>('center');

View file

@ -1,6 +1,6 @@
import { isEmpty, isEqual } from 'lodash';
import React, { ReactElement, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { Data } from '../../data/data';
import { useMessageReactsPropsById, useWeAreModerator } from '../../hooks/useParamSelector';
@ -11,7 +11,7 @@ import {
updateReactListModal,
updateUserDetailsModal,
} from '../../state/ducks/modalDialog';
import { getSelectedConversationIsPublic } from '../../state/selectors/conversations';
import { useSelectedIsPublic } from '../../state/selectors/selectedConversation';
import { SortedReactionList } from '../../types/Reaction';
import { nativeEmojiData } from '../../util/emoji';
import { Reactions } from '../../util/reactions';
@ -228,7 +228,7 @@ export const ReactListModal = (props: Props): ReactElement => {
const [senders, setSenders] = useState<Array<string>>([]);
const msgProps = useMessageReactsPropsById(messageId);
const isPublic = useSelector(getSelectedConversationIsPublic);
const isPublic = useSelectedIsPublic();
const weAreModerator = useWeAreModerator(msgProps?.convoId);
const me = UserUtils.getOurPubKeyStrFromCache();

View file

@ -5,25 +5,24 @@ import { contextMenu } from 'react-contexify';
import { Avatar, AvatarSize } from '../../avatar/Avatar';
import { createPortal } from 'react-dom';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import {
openConversationWithMessages,
ReduxConversationType,
} from '../../../state/ducks/conversations';
import { updateUserDetailsModal } from '../../../state/ducks/modalDialog';
import _ from 'lodash';
import { useSelector } from 'react-redux';
import _, { isNil } from 'lodash';
import {
useAvatarPath,
useConversationUsername,
useHasUnread,
useIsBlocked,
useIsPrivate,
useIsSelectedConversation,
useMentionedUs,
} from '../../../hooks/useParamSelector';
import { isSearching } from '../../../state/selectors/search';
import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation';
import { MemoConversationListItemContextMenu } from '../../menu/ConversationListItemContextMenu';
import { ConversationListItemHeaderItem } from './HeaderItem';
import { MessageItem } from './MessageItem';
@ -85,7 +84,9 @@ const ConversationListItem = (props: Props) => {
let hasUnreadMentionedUs = useMentionedUs(conversationId);
let isBlocked = useIsBlocked(conversationId);
const isSearch = useSelector(isSearching);
const isSelectedConvo = useIsSelectedConversation(conversationId);
const selectedConvo = useSelectedConversationKey();
const isSelectedConvo = conversationId === selectedConvo && !isNil(selectedConvo);
if (isSearch) {
// force isBlocked and hasUnreadMentionedUs to be false, we just want to display the row without any special style when showing search results

View file

@ -3,10 +3,7 @@ import React from 'react';
import { SpacerLG } from '../../basic/Text';
import { useDispatch, useSelector } from 'react-redux';
import {
getConversationRequests,
getSelectedConversation,
} from '../../../state/selectors/conversations';
import { getConversationRequests } from '../../../state/selectors/conversations';
import { MemoConversationListItemWithDetails } from '../conversation-list-item/ConversationListItem';
import styled from 'styled-components';
import { SessionButton, SessionButtonColor } from '../../basic/SessionButton';
@ -20,6 +17,7 @@ import {
resetConversationExternal,
} from '../../../state/ducks/conversations';
import { updateConfirmModal } from '../../../state/ducks/modalDialog';
import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation';
const MessageRequestListPlaceholder = styled.div`
color: var(--conversation-tab-text-color);
@ -55,7 +53,7 @@ export const OverlayMessageRequest = () => {
}
const convoRequestCount = useSelector(getConversationRequests).length;
const messageRequests = useSelector(getConversationRequests);
const selectedConversation = useSelector(getSelectedConversation);
const selectedConvoId = useSelectedConversationKey();
const buttonText = window.i18n('clearAll');
@ -94,7 +92,7 @@ export const OverlayMessageRequest = () => {
await convoModel.setIsApproved(false);
// if we're looking at the convo to decline, close the convo
if (selectedConversation?.id === id) {
if (selectedConvoId === id) {
dispatch(resetConversationExternal());
}
return true;

View file

@ -6,12 +6,12 @@ import React, { useCallback, useEffect, useState } from 'react';
import { Lightbox } from './Lightbox';
// tslint:disable-next-line: no-submodule-imports
import { useDispatch } from 'react-redux';
import useKey from 'react-use/lib/useKey';
import { AttachmentTypeWithPath } from '../../types/Attachment';
import { useDispatch, useSelector } from 'react-redux';
import { showLightBox } from '../../state/ducks/conversations';
import { getSelectedConversationKey } from '../../state/selectors/conversations';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
import { MIME } from '../../types';
import { AttachmentTypeWithPath } from '../../types/Attachment';
import { saveAttachmentToDisk } from '../../util/attachmentsUtil';
export interface MediaItemType {
@ -33,7 +33,11 @@ type Props = {
export const LightboxGallery = (props: Props) => {
const { media } = props;
const [currentIndex, setCurrentIndex] = useState(-1);
const selectedConversation = useSelector(getSelectedConversationKey) as string;
const selectedConversation = useSelectedConversationKey();
if (!selectedConversation) {
throw new Error('LightboxGallery: selectedConversation is undefined');
}
const dispatch = useDispatch();

View file

@ -24,9 +24,8 @@ import {
} from './Menu';
import _ from 'lodash';
import { ContextConversationId } from '../leftpane/conversation-list-item/ConversationListItem';
import { getSelectedConversationKey } from '../../state/selectors/conversations';
import { useSelector } from 'react-redux';
import { SessionContextMenuContainer } from '../SessionContextMenuContainer';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
export type PropsConversationHeaderMenu = {
triggerId: string;
@ -35,7 +34,7 @@ export type PropsConversationHeaderMenu = {
export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
const { triggerId } = props;
const selectedConversation = useSelector(getSelectedConversationKey);
const selectedConversation = useSelectedConversationKey();
if (!selectedConversation) {
throw new Error('selectedConversation must be set for a header to be visible!');

View file

@ -1,13 +1,10 @@
import { isEmpty, isNil, pick } from 'lodash';
import { isEmpty, pick } from 'lodash';
import { useSelector } from 'react-redux';
import { ConversationModel } from '../models/conversation';
import { PubKey } from '../session/types';
import { UserUtils } from '../session/utils';
import { StateType } from '../state/reducer';
import {
getMessageReactsProps,
getSelectedConversationKey,
} from '../state/selectors/conversations';
import { getMessageReactsProps } from '../state/selectors/conversations';
export function useAvatarPath(convoId: string | undefined) {
const convoProps = useConversationPropsById(convoId);
@ -220,8 +217,3 @@ export function useMentionedUs(conversationId?: string): boolean {
export function useIsTyping(conversationId?: string): boolean {
return useConversationPropsById(conversationId)?.isTyping || false;
}
export function useIsSelectedConversation(conversation?: string): boolean {
const selectedConvo = useSelector(getSelectedConversationKey);
return !isNil(selectedConvo) && !isNil(conversation) && selectedConvo === conversation;
}

View file

@ -9,11 +9,11 @@ import {
InputItem,
removeVideoEventsListener,
} from '../session/utils/calling/CallManager';
import { getSelectedConversationKey } from '../state/selectors/conversations';
import { getCallIsInFullScreen, getHasOngoingCallWithPubkey } from '../state/selectors/call';
import { useSelectedConversationKey } from '../state/selectors/selectedConversation';
export function useVideoCallEventsListener(uniqueId: string, onSame: boolean) {
const selectedConversationKey = useSelector(getSelectedConversationKey);
const selectedConversationKey = useSelectedConversationKey();
const ongoingCallPubkey = useSelector(getHasOngoingCallWithPubkey);
const isFullScreen = useSelector(getCallIsInFullScreen);

View file

@ -190,8 +190,9 @@ export function showLeaveGroupByConvoId(conversationId: string) {
const title = window.i18n('leaveGroup');
const message = window.i18n('leaveGroupConfirmation');
const ourPK = UserUtils.getOurPubKeyStrFromCache();
const isAdmin = (conversation.get('groupAdmins') || []).includes(ourPK);
const isAdmin = (conversation.get('groupAdmins') || []).includes(
UserUtils.getOurPubKeyStrFromCache()
);
const isClosedGroup = conversation.isClosedGroup() || false;
// if this is not a closed group, or we are not admin, we can just show a confirmation dialog

View file

@ -1296,8 +1296,8 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
return window.i18n('titleIsNow', [groupUpdate.name]);
}
if (groupUpdate.joined && groupUpdate.joined.length) {
const names = groupUpdate.joined.map((pubKey: string) =>
getConversationController().getContactProfileNameOrShortenedPubKey(pubKey)
const names = groupUpdate.joined.map(
getConversationController().getContactProfileNameOrShortenedPubKey
);
if (names.length > 1) {

View file

@ -143,43 +143,43 @@ async function handleContactsUpdate(result: IncomingConfResult): Promise<Incomin
continue;
}
const existingConvo = await getConversationController().getOrCreateAndWait(
const contactConvo = await getConversationController().getOrCreateAndWait(
wrapperConvo.id,
ConversationTypeEnum.PRIVATE
);
if (wrapperConvo.id && existingConvo) {
if (wrapperConvo.id && contactConvo) {
let changes = false;
// the display name set is handled in `updateProfileOfContact`
if (wrapperConvo.nickname !== existingConvo.getNickname()) {
await existingConvo.setNickname(wrapperConvo.nickname || null, false);
if (wrapperConvo.nickname !== contactConvo.getNickname()) {
await contactConvo.setNickname(wrapperConvo.nickname || null, false);
changes = true;
}
if (!wrapperConvo.hidden && !existingConvo.isHidden()) {
existingConvo.set({ hidden: false });
if (!wrapperConvo.hidden && !contactConvo.isHidden()) {
contactConvo.set({ hidden: false });
changes = true;
}
if (Boolean(wrapperConvo.approved) !== Boolean(existingConvo.isApproved())) {
await existingConvo.setIsApproved(Boolean(wrapperConvo.approved), false);
if (Boolean(wrapperConvo.approved) !== Boolean(contactConvo.isApproved())) {
await contactConvo.setIsApproved(Boolean(wrapperConvo.approved), false);
changes = true;
}
if (Boolean(wrapperConvo.approvedMe) !== Boolean(existingConvo.didApproveMe())) {
await existingConvo.setDidApproveMe(Boolean(wrapperConvo.approvedMe), false);
if (Boolean(wrapperConvo.approvedMe) !== Boolean(contactConvo.didApproveMe())) {
await contactConvo.setDidApproveMe(Boolean(wrapperConvo.approvedMe), false);
changes = true;
}
if (Boolean(wrapperConvo.approvedMe) !== Boolean(existingConvo.didApproveMe())) {
await existingConvo.setDidApproveMe(Boolean(wrapperConvo.approvedMe), false);
if (Boolean(wrapperConvo.approvedMe) !== Boolean(contactConvo.didApproveMe())) {
await contactConvo.setDidApproveMe(Boolean(wrapperConvo.approvedMe), false);
changes = true;
}
//TODO priority means more than just isPinned but has an order logic in it too
const shouldBePinned = wrapperConvo.priority > 0;
if (shouldBePinned !== Boolean(existingConvo.isPinned())) {
await existingConvo.setIsPinned(shouldBePinned, false);
if (shouldBePinned !== Boolean(contactConvo.isPinned())) {
await contactConvo.setIsPinned(shouldBePinned, false);
changes = true;
}
@ -188,12 +188,12 @@ async function handleContactsUpdate(result: IncomingConfResult): Promise<Incomin
// make sure to write the changes to the database now as the `AvatarDownloadJob` below might take some time before getting run
if (changes) {
await existingConvo.commit();
await contactConvo.commit();
}
// we still need to handle the `name` (synchronous) and the `profilePicture` (asynchronous)
await ProfileManager.updateProfileOfContact(
existingConvo.id,
contactConvo.id,
wrapperConvo.name,
wrapperConvo.profilePicture?.url || null,
wrapperConvo.profilePicture?.key || null
@ -273,20 +273,20 @@ async function handleCommunitiesUpdate() {
fromWrapper.roomCasePreserved
);
const existingConvo = getConversationController().get(convoId);
if (fromWrapper && existingConvo) {
const communityConvo = getConversationController().get(convoId);
if (fromWrapper && communityConvo) {
let changes = false;
//TODO priority means more than just isPinned but has an order logic in it too
const shouldBePinned = fromWrapper.priority > 0;
if (shouldBePinned !== Boolean(existingConvo.isPinned())) {
await existingConvo.setIsPinned(shouldBePinned, false);
if (shouldBePinned !== Boolean(communityConvo.isPinned())) {
await communityConvo.setIsPinned(shouldBePinned, false);
changes = true;
}
// make sure to write the changes to the database now as the `AvatarDownloadJob` below might take some time before getting run
if (changes) {
await existingConvo.commit();
await communityConvo.commit();
}
}
}
@ -340,8 +340,8 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
for (let index = 0; index < allLegacyGroupsInWrapper.length; index++) {
const fromWrapper = allLegacyGroupsInWrapper[index];
const convo = getConversationController().get(fromWrapper.pubkeyHex);
if (!convo) {
const legacyGroupConvo = getConversationController().get(fromWrapper.pubkeyHex);
if (!legacyGroupConvo) {
// this should not happen as we made sure to create them before
window.log.warn(
'could not find legacy group which should already be there:',
@ -360,8 +360,9 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
members,
admins,
activeAt:
!!convo.get('active_at') && convo.get('active_at') < latestEnvelopeTimestamp
? convo.get('active_at')
!!legacyGroupConvo.get('active_at') &&
legacyGroupConvo.get('active_at') < latestEnvelopeTimestamp
? legacyGroupConvo.get('active_at')
: latestEnvelopeTimestamp,
weWereJustAdded: false, // TODO to remove
};
@ -369,16 +370,16 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
await ClosedGroup.updateOrCreateClosedGroup(groupDetails);
let changes = false;
if (convo.isPinned() !== fromWrapper.priority > 0) {
await convo.setIsPinned(fromWrapper.priority > 0, false);
if (legacyGroupConvo.isPinned() !== fromWrapper.priority > 0) {
await legacyGroupConvo.setIsPinned(fromWrapper.priority > 0, false);
changes = true;
}
if (!!convo.isHidden() !== !!fromWrapper.hidden) {
convo.set({ hidden: !!fromWrapper.hidden });
if (!!legacyGroupConvo.isHidden() !== !!fromWrapper.hidden) {
legacyGroupConvo.set({ hidden: !!fromWrapper.hidden });
changes = true;
}
if (convo.get('expireTimer') !== fromWrapper.disappearingTimerSeconds) {
await convo.updateExpireTimer(
if (legacyGroupConvo.get('expireTimer') !== fromWrapper.disappearingTimerSeconds) {
await legacyGroupConvo.updateExpireTimer(
fromWrapper.disappearingTimerSeconds,
undefined,
latestEnvelopeTimestamp,
@ -390,45 +391,23 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
}
if (changes) {
await convo.commit();
await legacyGroupConvo.commit();
}
// save the encryption keypair if needed
if (!isEmpty(fromWrapper.encPubkey) && !isEmpty(fromWrapper.encSeckey)) {
const inWrapperKeypair: HexKeyPair = {
publicHex: toHex(fromWrapper.encPubkey),
privateHex: toHex(fromWrapper.encSeckey),
};
try {
const inWrapperKeypair: HexKeyPair = {
publicHex: toHex(fromWrapper.encPubkey),
privateHex: toHex(fromWrapper.encSeckey),
};
await addKeyPairToCacheAndDBIfNeeded(fromWrapper.pubkeyHex, inWrapperKeypair);
await addKeyPairToCacheAndDBIfNeeded(fromWrapper.pubkeyHex, inWrapperKeypair);
} catch (e) {
window.log.warn('failed to save keypair for legacugroup', fromWrapper.pubkeyHex);
}
}
}
// // if the convos already exists, make sure to update the fields if needed
// for (let index = 0; index < allCommunitiesInWrapper.length; index++) {
// const fromWrapper = allCommunitiesInWrapper[index];
// const convoId = OpenGroupUtils.getOpenGroupV2ConversationId(
// fromWrapper.baseUrl,
// fromWrapper.roomCasePreserved
// );
// const existingConvo = getConversationController().get(convoId);
// if (fromWrapper && existingConvo) {
// let changes = false;
// //TODO priority means more than just isPinned but has an order logic in it too
// const shouldBePinned = fromWrapper.priority > 0;
// if (shouldBePinned !== Boolean(existingConvo.isPinned())) {
// await existingConvo.setIsPinned(shouldBePinned, false);
// changes = true;
// }
// // make sure to write the changes to the database now as the `AvatarDownloadJob` below might take some time before getting run
// if (changes) {
// await existingConvo.commit();
// }
// }
// }
}
async function handleUserGroupsUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {

View file

@ -87,22 +87,19 @@ export class ConversationController {
throw new Error('getConversationController().get() needs complete initial fetch');
}
let conversation = this.conversations.get(id);
if (conversation) {
return conversation;
if (this.conversations.get(id)) {
return this.conversations.get(id) as ConversationModel;
}
conversation = this.conversations.add({
const conversation = this.conversations.add({
id,
type,
});
const create = async () => {
try {
const saveDetails = await Data.saveConversation(conversation.attributes);
if (saveDetails) {
await conversation.refreshInMemoryDetails(saveDetails);
}
// this saves to DB and to the required wrapper
await conversation.commit();
} catch (error) {
window?.log?.error(
'Conversation save failed! ',
@ -114,25 +111,22 @@ export class ConversationController {
throw error;
}
return conversation;
};
window?.inboxStore?.dispatch(
conversationActions.conversationAdded({
id: conversation.id,
data: conversation.getConversationModelProps(),
})
);
conversation.initialPromise = create();
conversation.initialPromise.then(() => {
if (window?.inboxStore) {
window.inboxStore?.dispatch(
conversationActions.conversationAdded({
id: conversation.id,
data: conversation.getConversationModelProps(),
})
);
}
if (!conversation.isPublic() && conversation.isActive()) {
// NOTE: we request snodes updating the cache, but ignore the result
void getSwarmFor(id);
}
});
return conversation;
};
conversation.initialPromise = create();
return conversation;
}

View file

@ -2,7 +2,8 @@ import { createSelector } from '@reduxjs/toolkit';
import { CallStateType, CallStatusEnum } from '../ducks/call';
import { ConversationsStateType, ReduxConversationType } from '../ducks/conversations';
import { StateType } from '../reducer';
import { getConversations, getSelectedConversationKey } from './conversations';
import { getConversations } from './conversations';
import { getSelectedConversationKey } from './selectedConversation';
const getCallState = (state: StateType): CallStateType => state.call;

View file

@ -1,6 +1,5 @@
import { createSelector } from '@reduxjs/toolkit';
import { StateType } from '../reducer';
import {
ConversationLookupType,
ConversationsStateType,
@ -11,12 +10,8 @@ import {
ReduxConversationType,
SortedMessageModelProps,
} from '../ducks/conversations';
import { StateType } from '../reducer';
import { getIntl } from './user';
import { BlockedNumberController } from '../../util';
import { ConversationModel } from '../../models/conversation';
import { LocalizerType } from '../../types/Util';
import { ConversationHeaderTitleProps } from '../../components/conversation/ConversationHeader';
import { ReplyingToMessageProps } from '../../components/conversation/composition/CompositionBox';
import { MessageAttachmentSelectorProps } from '../../components/conversation/message/message-content/MessageAttachment';
import { MessageAuthorSelectorProps } from '../../components/conversation/message/message-content/MessageAuthorText';
@ -30,14 +25,19 @@ import { MessageStatusSelectorProps } from '../../components/conversation/messag
import { MessageTextSelectorProps } from '../../components/conversation/message/message-content/MessageText';
import { GenericReadableMessageSelectorProps } from '../../components/conversation/message/message-item/GenericReadableMessage';
import { LightBoxOptions } from '../../components/conversation/SessionConversation';
import { ConversationModel } from '../../models/conversation';
import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes';
import { getConversationController } from '../../session/conversations';
import { UserUtils } from '../../session/utils';
import { LocalizerType } from '../../types/Util';
import { BlockedNumberController } from '../../util';
import { Storage } from '../../util/storage';
import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes';
import { getIntl } from './user';
import { MessageReactsSelectorProps } from '../../components/conversation/message/message-content/MessageReactions';
import { filter, isEmpty, pick, sortBy } from 'lodash';
import { getCanWrite, getModeratorsOutsideRedux, getSubscriberCount } from './sogsRoomInfo';
import { MessageReactsSelectorProps } from '../../components/conversation/message/message-content/MessageReactions';
import { getModeratorsOutsideRedux } from './sogsRoomInfo';
import { getSelectedConversation, getSelectedConversationKey } from './selectedConversation';
export const getConversations = (state: StateType): ConversationsStateType => state.conversations;
@ -52,80 +52,6 @@ export const getConversationsCount = createSelector(getConversationLookup, (stat
return Object.values(state).length;
});
export const getSelectedConversationKey = createSelector(
getConversations,
(state: ConversationsStateType): string | undefined => {
return state.selectedConversation;
}
);
export const getSelectedConversation = createSelector(
getConversations,
(state: ConversationsStateType): ReduxConversationType | undefined => {
return state.selectedConversation
? state.conversationLookup[state.selectedConversation]
: undefined;
}
);
export const getSelectedConversationIsPublic = createSelector(
getSelectedConversation,
(state: ReduxConversationType | undefined): boolean => {
return state?.isPublic || false;
}
);
export function getIsTypingEnabled(state: StateType) {
const selectedConvoPubkey = getSelectedConversationKey(state);
if (!selectedConvoPubkey) {
return false;
}
const selectedConvo = state.conversations.conversationLookup[selectedConvoPubkey];
if (!selectedConvo) {
return false;
}
const canWrite = getCanWrite(state, selectedConvoPubkey);
const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo;
return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWrite));
}
/**
* Returns true if the current conversation selected is a group conversation.
* Returns false if the current conversation selected is not a group conversation, or none are selected
*/
export const getSelectedConversationIsGroup = createSelector(
getSelectedConversation,
(state: ReduxConversationType | undefined): boolean => {
const type = state?.type;
return type ? isOpenOrClosedGroup(type) : false;
}
);
/**
* Returns true if the current conversation selected is a closed group and false otherwise.
*/
export const isClosedGroupConversation = createSelector(
getSelectedConversation,
(state: ReduxConversationType | undefined): boolean => {
return (
(state?.type === ConversationTypeEnum.GROUP && !state.isPublic) ||
state?.type === ConversationTypeEnum.GROUPV3 ||
false
);
}
);
/**
* Returns true if the current conversation selected is a public group and false otherwise.
*/
export const isPublicGroupConversation = createSelector(
getSelectedConversation,
(state: ReduxConversationType | undefined): boolean => {
return (state?.type === ConversationTypeEnum.GROUP && state.isPublic) || false;
}
);
export const getOurPrimaryConversation = createSelector(
getConversations,
(state: ConversationsStateType): ReduxConversationType =>
@ -547,85 +473,6 @@ export const getGlobalUnreadMessageCount = createSelector(getLeftPaneLists, (sta
return state.globalUnreadCount;
});
export const getConversationHeaderTitleProps = (
state: StateType
): ConversationHeaderTitleProps | undefined => {
const convo = getSelectedConversation(state);
if (!convo) {
return undefined;
}
return {
isKickedFromGroup: !!convo.isKickedFromGroup,
conversationKey: convo.id,
isMe: !!convo.isMe,
members: convo.members || [],
isPublic: !!convo.isPublic,
isGroup: isOpenOrClosedGroup(convo.type),
currentNotificationSetting: convo.currentNotificationSetting,
};
};
export const getCurrentSubscriberCount = (state: StateType): number | undefined => {
const convo = getSelectedConversation(state);
if (!convo) {
return undefined;
}
return getSubscriberCount(state, convo.id);
};
/**
* Returns the formatted text for notification setting.
*/
export const getCurrentNotificationSettingText = createSelector(getSelectedConversation, (state):
| string
| undefined => {
if (!state) {
return undefined;
}
switch (state.currentNotificationSetting) {
case 'all':
return window.i18n('notificationForConvo_all');
case 'mentions_only':
return window.i18n('notificationForConvo_mentions_only');
case 'disabled':
return window.i18n('notificationForConvo_disabled');
default:
return window.i18n('notificationForConvo_all');
}
});
export const getIsSelectedPrivate = createSelector(
getSelectedConversation,
(selectedProps): boolean => {
return selectedProps?.isPrivate || false;
}
);
export const getIsSelectedBlocked = createSelector(
getSelectedConversation,
(selectedProps): boolean => {
return selectedProps?.isBlocked || false;
}
);
/**
* Returns true if the currently selected conversation is active (has an active_at field > 0)
*/
export const getIsSelectedActive = createSelector(
getSelectedConversation,
(selectedProps): boolean => {
return Boolean(selectedProps?.activeAt);
}
);
export const getIsSelectedNoteToSelf = createSelector(
getSelectedConversation,
(selectedProps): boolean => {
return selectedProps?.isMe || false;
}
);
export const isMessageDetailView = createSelector(
getConversations,
(state: ConversationsStateType): boolean => state.messageDetailProps !== undefined

View file

@ -4,8 +4,9 @@ import { createSelector } from '@reduxjs/toolkit';
import { StateType } from '../reducer';
import { SearchStateType } from '../ducks/search';
import { getConversationLookup, getSelectedConversationKey } from './conversations';
import { getConversationLookup } from './conversations';
import { ConversationLookupType } from '../ducks/conversations';
import { getSelectedConversationKey } from './selectedConversation';
export const getSearch = (state: StateType): SearchStateType => state.search;

View file

@ -0,0 +1,185 @@
import { useSelector } from 'react-redux';
import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes';
import { ReduxConversationType } from '../ducks/conversations';
import { StateType } from '../reducer';
import { getCanWrite, getSubscriberCount } from './sogsRoomInfo';
/**
* Returns the formatted text for notification setting.
*/
const getCurrentNotificationSettingText = (state: StateType): string | undefined => {
if (!state) {
return undefined;
}
const currentNotificationSetting = getSelectedConversation(state)?.currentNotificationSetting;
switch (currentNotificationSetting) {
case 'all':
return window.i18n('notificationForConvo_all');
case 'mentions_only':
return window.i18n('notificationForConvo_mentions_only');
case 'disabled':
return window.i18n('notificationForConvo_disabled');
default:
return window.i18n('notificationForConvo_all');
}
};
const getIsSelectedPrivate = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.isPrivate) || false;
};
const getIsSelectedBlocked = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.isBlocked) || false;
};
/**
* Returns true if the currently selected conversation is active (has an active_at field > 0)
*/
const getIsSelectedActive = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.activeAt) || false;
};
const getIsSelectedNoteToSelf = (state: StateType): boolean => {
return getSelectedConversation(state)?.isMe || false;
};
export const getSelectedConversationKey = (state: StateType): string | undefined => {
return state.conversations.selectedConversation;
};
export const getSelectedConversation = (state: StateType): ReduxConversationType | undefined => {
const selected = getSelectedConversationKey(state);
return selected ? state.conversations.conversationLookup[selected] : undefined;
};
/**
* Returns true if the current conversation selected is a public group and false otherwise.
*/
export const getSelectedConversationIsPublic = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.isPublic) || false;
};
/**
* Returns true if the current conversation selected can be typed into
*/
export function getSelectedCanWrite(state: StateType) {
const selectedConvoPubkey = getSelectedConversationKey(state);
if (!selectedConvoPubkey) {
return false;
}
const selectedConvo = getSelectedConversation(state);
if (!selectedConvo) {
return false;
}
const canWrite = getCanWrite(state, selectedConvoPubkey);
const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo;
return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWrite));
}
/**
* Returns true if the current conversation selected is a group conversation.
* Returns false if the current conversation selected is not a group conversation, or none are selected
*/
const getSelectedConversationIsGroup = (state: StateType): boolean => {
const selected = getSelectedConversation(state);
if (!selected || !selected.type) {
return false;
}
return selected.type ? isOpenOrClosedGroup(selected.type) : false;
};
/**
* Returns true if the current conversation selected is a closed group and false otherwise.
*/
export const isClosedGroupConversation = (state: StateType): boolean => {
const selected = getSelectedConversation(state);
if (!selected) {
return false;
}
return (
(selected.type === ConversationTypeEnum.GROUP && !selected.isPublic) ||
selected.type === ConversationTypeEnum.GROUPV3 ||
false
);
};
const getGroupMembers = (state: StateType): Array<string> => {
const selected = getSelectedConversation(state);
if (!selected) {
return [];
}
return selected.members || [];
};
const getSelectedSubscriberCount = (state: StateType): number | undefined => {
const convo = getSelectedConversation(state);
if (!convo) {
return undefined;
}
return getSubscriberCount(state, convo.id);
};
// ============== SELECTORS RELEVANT TO SELECTED/OPENED CONVERSATION ==============
export function useSelectedConversationKey() {
return useSelector(getSelectedConversationKey);
}
export function useSelectedIsGroup() {
return useSelector(getSelectedConversationIsGroup);
}
export function useSelectedIsPublic() {
return useSelector(getSelectedConversationIsPublic);
}
export function useSelectedIsPrivate() {
return useSelector(getIsSelectedPrivate);
}
export function useSelectedIsBlocked() {
return useSelector(getIsSelectedBlocked);
}
export function useSelectedIsActive() {
return useSelector(getIsSelectedActive);
}
export function useSelectedisNoteToSelf() {
return useSelector(getIsSelectedNoteToSelf);
}
export function useSelectedMembers() {
return useSelector(getGroupMembers);
}
export function useSelectedSubscriberCount() {
return useSelector(getSelectedSubscriberCount);
}
export function useSelectedNotificationSetting() {
return useSelector(getCurrentNotificationSettingText);
}
export function useSelectedIsKickedFromGroup() {
return useSelector(
(state: StateType) => Boolean(getSelectedConversation(state)?.isKickedFromGroup) || false
);
}
export function useSelectedIsLeft() {
return useSelector((state: StateType) => Boolean(getSelectedConversation(state)?.left) || false);
}
export function useSelectedNickname() {
return useSelector((state: StateType) => getSelectedConversation(state)?.nickname);
}
export function useSelectedDisplayNameInProfile() {
return useSelector((state: StateType) => getSelectedConversation(state)?.displayNameInProfile);
}
export function useSelectedWeAreAdmin() {
return useSelector((state: StateType) => getSelectedConversation(state)?.weAreAdmin || false);
}

View file

@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { StagedAttachmentType } from '../../components/conversation/composition/CompositionBox';
import { StagedAttachmentsStateType } from '../ducks/stagedAttachments';
import { StateType } from '../reducer';
import { getSelectedConversationKey } from './conversations';
import { getSelectedConversationKey } from './selectedConversation';
export const getStagedAttachmentsState = (state: StateType): StagedAttachmentsStateType =>
state.stagedAttachments;

View file

@ -1,21 +1,23 @@
import { connect } from 'react-redux';
import { SessionConversation } from '../../components/conversation/SessionConversation';
import { mapDispatchToProps } from '../actions';
import { StateType } from '../reducer';
import { getTheme } from '../selectors/theme';
import { getHasOngoingCallWithFocusedConvo } from '../selectors/call';
import {
getIsSelectedConvoInitialLoadingInProgress,
getLightBoxOptions,
getSelectedConversation,
getSelectedConversationKey,
getSelectedMessageIds,
getSortedMessagesOfSelectedConversation,
isMessageDetailView,
isRightPanelShowing,
} from '../selectors/conversations';
import { getOurNumber } from '../selectors/user';
import {
getSelectedConversation,
getSelectedConversationKey,
} from '../selectors/selectedConversation';
import { getStagedAttachmentsForCurrentConversation } from '../selectors/stagedAttachments';
import { getHasOngoingCallWithFocusedConvo } from '../selectors/call';
import { SessionConversation } from '../../components/conversation/SessionConversation';
import { getTheme } from '../selectors/theme';
import { getOurNumber } from '../selectors/user';
const mapStateToProps = (state: StateType) => {
return {