fix: resolved missing selected conversation hooks

still need to fix more issues
pull/2861/head
William Grant 2 years ago
parent 10561e4e3c
commit aad3a3aba2

@ -10,7 +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/conversations';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
export const DraggableCallWindow = styled.div`
position: absolute;

@ -7,10 +7,8 @@ import {
declineConversationWithConfirm,
} from '../../interactions/conversationInteractions';
import { getConversationController } from '../../session/conversations';
import {
hasSelectedConversationIncomingMessages,
useSelectedConversationKey,
} from '../../state/selectors/conversations';
import { hasSelectedConversationIncomingMessages } from '../../state/selectors/conversations';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
import { ConversationRequestExplanation } from './SubtleNotification';

@ -21,9 +21,7 @@ import { StateType } from '../../../state/reducer';
import {
getMentionsInput,
getQuotedMessage,
getSelectedCanWrite,
getSelectedConversation,
getSelectedConversationKey,
} from '../../../state/selectors/conversations';
import { AttachmentType } from '../../../types/Attachment';
import { processNewAttachment } from '../../../types/MessageAttachment';
@ -58,6 +56,10 @@ import {
renderUserMentionRow,
styleForCompositionBoxSuggestions,
} from './UserMentions';
import {
getSelectedCanWrite,
getSelectedConversationKey,
} from '../../../state/selectors/selectedConversation';
export interface ReplyingToMessageProps {
convoId: string;

@ -4,7 +4,6 @@ import { useDispatch, useSelector } from 'react-redux';
import {
isMessageDetailView,
isMessageSelectionMode,
useSelectedConversationKey,
} from '../../../state/selectors/conversations';
import { closeMessageDetailsView, openRightPanel } from '../../../state/ducks/conversations';
@ -14,6 +13,7 @@ import { ConversationHeaderMenu } from '../../menu/ConversationHeaderMenu';
import { AvatarHeader, BackButton, CallButton, TripleDotsMenu } from './ConversationHeaderItems';
import { SelectionOverlay } from './ConversationHeaderSelectionOverlay';
import { ConversationHeaderTitle } from './ConversationHeaderTitle';
import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation';
export const ConversationHeaderWithDetails = () => {
const isSelectionMode = useSelector(isMessageSelectionMode);

@ -14,7 +14,7 @@ import {
useSelectedIsNoteToSelf,
useSelectedIsPrivate,
useSelectedIsPrivateFriend,
} from '../../../state/selectors/conversations';
} from '../../../state/selectors/selectedConversation';
const TripleDotContainer = styled.div`
user-select: none;

@ -5,11 +5,11 @@ import {
deleteMessagesByIdForEveryone,
} from '../../../interactions/conversations/unsendingInteractions';
import { resetSelectedMessageIds } from '../../../state/ducks/conversations';
import { getSelectedMessageIds } from '../../../state/selectors/conversations';
import {
getSelectedMessageIds,
useSelectedConversationKey,
useSelectedIsPublic,
} from '../../../state/selectors/conversations';
} from '../../../state/selectors/selectedConversation';
import {
SessionButton,
SessionButtonColor,

@ -4,8 +4,8 @@ import { useDispatch, useSelector } from 'react-redux';
import { useConversationUsername } from '../../../hooks/useParamSelector';
import { closeRightPanel, openRightPanel } from '../../../state/ducks/conversations';
import { resetRightOverlayMode, setRightOverlayMode } from '../../../state/ducks/section';
import { isRightPanelShowing } from '../../../state/selectors/conversations';
import {
isRightPanelShowing,
useSelectedConversationKey,
useSelectedExpirationType,
useSelectedExpireTimer,
@ -16,7 +16,7 @@ import {
useSelectedMembers,
useSelectedNotificationSetting,
useSelectedSubscriberCount,
} from '../../../state/selectors/conversations';
} from '../../../state/selectors/selectedConversation';
import { ExpirationTimerOptions } from '../../../util/expiringMessages';
import { ConversationHeaderSubtitle } from './ConversationHeaderSubtitle';

@ -6,7 +6,7 @@ import moment from 'moment';
import formatFileSize from 'filesize';
import { saveAttachmentToDisk } from '../../../util/attachmentsUtil';
import { MediaItemType } from '../../lightbox/LightboxGallery';
import { useSelectedConversationKey } from '../../../state/selectors/conversations';
import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation';
type Props = {
// Required

@ -8,7 +8,10 @@ import {
useMessageAuthor,
useMessageDirection,
} from '../../../../state/selectors';
import { useSelectedIsGroup, useSelectedIsPublic } from '../../../../state/selectors/conversations';
import {
useSelectedIsGroup,
useSelectedIsPublic,
} from '../../../../state/selectors/selectedConversation';
import { Flex } from '../../../basic/Flex';
import { ContactName } from '../../ContactName';

@ -21,7 +21,7 @@ import {
getSelectedCanWrite,
useSelectedConversationKey,
useSelectedIsPublic,
} from '../../../../state/selectors/conversations';
} from '../../../../state/selectors/selectedConversation';
import { Avatar, AvatarSize, CrownIcon } from '../../../avatar/Avatar';
// tslint:disable: use-simple-attributes

@ -23,14 +23,14 @@ import {
toggleSelectedMessageId,
} from '../../../../state/ducks/conversations';
import { StateType } from '../../../../state/reducer';
import { getMessageContextMenuProps } from '../../../../state/selectors/conversations';
import {
getMessageContextMenuProps,
useSelectedConversationKey,
useSelectedIsBlocked,
useSelectedIsPublic,
useSelectedWeAreAdmin,
useSelectedWeAreModerator,
} from '../../../../state/selectors/conversations';
} from '../../../../state/selectors/selectedConversation';
import { saveAttachmentToDisk } from '../../../../util/attachmentsUtil';
import { Reactions } from '../../../../util/reactions';
import { SessionContextMenuContainer } from '../../../SessionContextMenuContainer';

@ -3,7 +3,7 @@ import React, { ReactElement, useEffect, useState } from 'react';
import styled from 'styled-components';
import { useMessageReactsPropsById } from '../../../../hooks/useParamSelector';
import { MessageRenderingProps } from '../../../../models/messageType';
import { useSelectedIsGroup } from '../../../../state/selectors/conversations';
import { useSelectedIsGroup } from '../../../../state/selectors/selectedConversation';
import { SortedReactionList } from '../../../../types/Reaction';
import { nativeEmojiData } from '../../../../util/emoji';
import { Flex } from '../../../basic/Flex';

@ -11,7 +11,7 @@ import { PubKey } from '../../../../session/types';
import {
useSelectedIsPrivate,
useSelectedIsPublic,
} from '../../../../state/selectors/conversations';
} from '../../../../state/selectors/selectedConversation';
import { ContactName } from '../../ContactName';
import { MessageBody } from './MessageBody';

@ -4,7 +4,7 @@ import { useQuoteAuthorName } from '../../../../../hooks/useParamSelector';
import { PubKey } from '../../../../../session/types';
import { ContactName } from '../../../ContactName';
import { QuoteProps } from './Quote';
import { useSelectedIsPublic } from '../../../../../state/selectors/conversations';
import { useSelectedIsPublic } from '../../../../../state/selectors/selectedConversation';
const StyledQuoteAuthor = styled.div<{ isIncoming: boolean }>`
color: ${props =>

@ -5,7 +5,7 @@ import { MIME } from '../../../../../types';
import { GoogleChrome } from '../../../../../util';
import { MessageBody } from '../MessageBody';
import { QuoteProps } from './Quote';
import { useSelectedIsGroup } from '../../../../../state/selectors/conversations';
import { useSelectedIsGroup } from '../../../../../state/selectors/selectedConversation';
const StyledQuoteText = styled.div<{ isIncoming: boolean }>`
display: -webkit-box;

@ -19,8 +19,8 @@ import {
getQuotedMessageToAnimate,
getShowScrollButton,
getYoungestMessageId,
useSelectedConversationKey,
} from '../../../../state/selectors/conversations';
import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation';
import { getIsAppFocused } from '../../../../state/selectors/section';
import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer';

@ -9,7 +9,7 @@ import {
useSelectedConversationKey,
useSelectedDisplayNameInProfile,
useSelectedNickname,
} from '../../../../../state/selectors/conversations';
} from '../../../../../state/selectors/selectedConversation';
import { LocalizerKeys } from '../../../../../types/LocalizerKeys';
import { SessionIconType } from '../../../../icon';
import { ExpirableReadableMessage } from '../ExpirableReadableMessage';

@ -14,7 +14,7 @@ import {
useSelectedExpireTimer,
useSelectedIsGroup,
useSelectedWeAreAdmin,
} from '../../../../../state/selectors/conversations';
} from '../../../../../state/selectors/selectedConversation';
import {
DEFAULT_TIMER_OPTION,
DisappearingMessageConversationType,

@ -8,7 +8,6 @@ import {
MessageModelPropsWithConvoProps,
MessageModelPropsWithoutConvoProps,
MessagePropsDetails,
PropsForExpiringMessage,
PropsForQuote,
QuoteLookupType,
ReduxConversationType,
@ -25,11 +24,7 @@ import { MessageTextSelectorProps } from '../../components/conversation/message/
import { GenericReadableMessageSelectorProps } from '../../components/conversation/message/message-item/GenericReadableMessage';
import { LightBoxOptions } from '../../components/conversation/SessionConversation';
import { hasValidIncomingRequestValues } from '../../models/conversation';
import {
CONVERSATION_PRIORITIES,
ConversationTypeEnum,
isOpenOrClosedGroup,
} from '../../models/conversationAttributes';
import { CONVERSATION_PRIORITIES, isOpenOrClosedGroup } from '../../models/conversationAttributes';
import { getConversationController } from '../../session/conversations';
import { UserUtils } from '../../session/utils';
import { LocalizerType } from '../../types/Util';
@ -37,19 +32,13 @@ import { BlockedNumberController } from '../../util';
import { Storage } from '../../util/storage';
import { getIntl } from './user';
import { filter, isEmpty, isNumber, isString, pick, sortBy, toNumber } from 'lodash';
import { useSelector } from 'react-redux';
import { filter, isEmpty, isNumber, pick, sortBy, toNumber } from 'lodash';
import { MessageReactsSelectorProps } from '../../components/conversation/message/message-content/MessageReactions';
import { processQuoteAttachment } from '../../models/message';
import { isUsAnySogsFromCache } from '../../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import { PubKey } from '../../session/types';
import { DisappearingMessageConversationSetting } from '../../util/expiringMessages';
import {
getCanWrite,
getModerators,
getModeratorsOutsideRedux,
getSubscriberCount,
} from './sogsRoomInfo';
import { getSelectedConversationKey } from './selectedConversation';
import { getModeratorsOutsideRedux } from './sogsRoomInfo';
export const getConversations = (state: StateType): ConversationsStateType => state.conversations;
@ -349,10 +338,6 @@ const _getGlobalUnreadCount = (sortedConversations: Array<ReduxConversationType>
return globalUnreadCount;
};
export const getSelectedConversationKey = (state: StateType): string | undefined => {
return state.conversations.selectedConversation;
};
export const _getSortedConversations = (
lookup: ConversationLookupType,
comparator: (left: ReduxConversationType, right: ReduxConversationType) => number
@ -919,29 +904,6 @@ export const getMessageAttachmentProps = createSelector(getMessagePropsByMessage
return msgProps;
});
export const getMessageExpirationProps = createSelector(getMessagePropsByMessageId, (props):
| PropsForExpiringMessage
| undefined => {
if (!props || isEmpty(props)) {
return undefined;
}
const msgProps: PropsForExpiringMessage = {
...pick(props.propsForMessage, [
'convoId',
'direction',
'receivedAt',
'isUnread',
'expirationTimestamp',
'expirationLength',
'isExpired',
]),
messageId: props.propsForMessage.id,
};
return msgProps;
});
export const getIsMessageSelected = createSelector(
getMessagePropsByMessageId,
getSelectedMessageIds,
@ -1004,6 +966,9 @@ export const getGenericReadableMessageSelectorProps = createSelector(
'convoId',
'direction',
'conversationType',
'expirationLength',
'expirationTimestamp',
'isExpired',
'isUnread',
'receivedAt',
'isKickedFromGroup',
@ -1026,337 +991,3 @@ export const getIsSelectedConvoInitialLoadingInProgress = (state: StateType): bo
export function getCurrentlySelectedConversationOutsideRedux() {
return window?.inboxStore?.getState().conversations.selectedConversation as string | undefined;
}
/**
* Selected conversation selectors & hooks
*
*/
/**
* 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;
};
const getSelectedIsApproved = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.isApproved) || false;
};
const getSelectedApprovedMe = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.didApproveMe) || 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;
};
/**
* 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 canWriteSogs = getCanWrite(state, selectedConvoPubkey);
const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo;
return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWriteSogs));
}
/**
* 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 getSelectedGroupMembers = (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 useSelectedIsApproved() {
return useSelector(getSelectedIsApproved);
}
export function useSelectedApprovedMe() {
return useSelector(getSelectedApprovedMe);
}
/**
* Returns true if the given arguments corresponds to a private contact which is approved both sides. i.e. a friend.
*/
export function isPrivateAndFriend({
approvedMe,
isApproved,
isPrivate,
}: {
isPrivate: boolean;
isApproved: boolean;
approvedMe: boolean;
}) {
return isPrivate && isApproved && approvedMe;
}
/**
* Returns true if the selected conversation is private and is approved both sides
*/
export function useSelectedIsPrivateFriend() {
const isPrivate = useSelectedIsPrivate();
const isApproved = useSelectedIsApproved();
const approvedMe = useSelectedApprovedMe();
return isPrivateAndFriend({ isPrivate, isApproved, approvedMe });
}
export function useSelectedIsActive() {
return useSelector(getIsSelectedActive);
}
export function useSelectedIsNoteToSelf() {
return useSelector(getIsSelectedNoteToSelf);
}
export function useSelectedMembers() {
return useSelector(getSelectedGroupMembers);
}
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 useSelectedExpireTimer(): number | undefined {
return useSelector((state: StateType) => getSelectedConversation(state)?.expireTimer);
}
export function useSelectedExpirationType(): string | undefined {
return useSelector((state: StateType) => getSelectedConversation(state)?.expirationType);
}
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);
}
/**
* For a private chat, this returns the (xxxx...xxxx) shortened pubkey
* If this is a private chat, but somehow, we have no pubkey, this returns the localized `anonymous` string
* Otherwise, this returns the localized `unknown` string
*/
export function useSelectedShortenedPubkeyOrFallback() {
const isPrivate = useSelectedIsPrivate();
const selected = useSelectedConversationKey();
if (isPrivate && selected) {
return PubKey.shorten(selected);
}
if (isPrivate) {
return window.i18n('anonymous');
}
return window.i18n('unknown');
}
/**
* That's a very convoluted way to say "nickname or profile name or shortened pubkey or ("Anonymous" or "unknown" depending on the type of conversation).
* This also returns the localized "Note to Self" if the conversation is the note to self.
*/
export function useSelectedNicknameOrProfileNameOrShortenedPubkey() {
const nickname = useSelectedNickname();
const profileName = useSelectedDisplayNameInProfile();
const shortenedPubkey = useSelectedShortenedPubkeyOrFallback();
const isMe = useSelectedIsNoteToSelf();
if (isMe) {
return window.i18n('noteToSelf');
}
return nickname || profileName || shortenedPubkey;
}
export function useSelectedWeAreAdmin() {
return useSelector((state: StateType) => getSelectedConversation(state)?.weAreAdmin || false);
}
export const getSelectedConversationExpirationModes = createSelector(
getSelectedConversation,
(convo: ReduxConversationType | undefined) => {
if (!convo) {
return null;
}
let modes = DisappearingMessageConversationSetting;
// TODO legacy messages support will be removed in a future release
// TODO remove legacy mode
modes = modes.slice(0, -1);
// Note to Self and Closed Groups only support deleteAfterSend
const isClosedGroup = !convo.isPrivate && !convo.isPublic;
if (convo?.isMe || isClosedGroup) {
modes = [modes[0], modes[2]];
}
const modesWithDisabledState: Record<string, boolean> = {};
if (modes && modes.length > 1) {
modes.forEach(mode => {
modesWithDisabledState[mode] = isClosedGroup ? !convo.weAreAdmin : false;
});
}
return modesWithDisabledState;
}
);
// TODO legacy messages support will be removed in a future release
export const getSelectedConversationExpirationModesWithLegacy = createSelector(
getSelectedConversation,
(convo: ReduxConversationType | undefined) => {
// this just won't happen
if (!convo) {
return null;
}
let modes = DisappearingMessageConversationSetting;
// Note to Self and Closed Groups only support deleteAfterSend and legacy modes
const isClosedGroup = !convo.isPrivate && !convo.isPublic;
if (convo?.isMe || isClosedGroup) {
modes = [modes[0], ...modes.slice(2)];
}
// Legacy mode is the 2nd option in the UI
modes = [modes[0], modes[modes.length - 1], ...modes.slice(1, modes.length - 1)];
// TODO it would be nice to type those with something else that string but it causes a lot of issues
const modesWithDisabledState: Record<string, boolean> = {};
// The new modes are disabled by default
if (modes && modes.length > 1) {
modes.forEach(mode => {
modesWithDisabledState[mode] = Boolean(
(mode !== 'legacy' && mode !== 'off') || (isClosedGroup && !convo.weAreAdmin)
);
});
}
return modesWithDisabledState;
}
);
/**
* Only for communities.
* @returns true if the selected convo is a community and we are one of the moderators
*/
export function useSelectedWeAreModerator() {
// TODO might be something to memoize let's see
const isPublic = useSelectedIsPublic();
const selectedConvoKey = useSelectedConversationKey();
const us = UserUtils.getOurPubKeyStrFromCache();
const mods = useSelector((state: StateType) => getModerators(state, selectedConvoKey));
const weAreModerator = mods.includes(us);
return isPublic && isString(selectedConvoKey) && weAreModerator;
}

@ -0,0 +1,343 @@
import { isString } from 'lodash';
import { useSelector } from 'react-redux';
import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes';
import { PubKey } from '../../session/types';
import { UserUtils } from '../../session/utils';
import { StateType } from '../reducer';
import { getCanWrite, getModerators, getSubscriberCount } from './sogsRoomInfo';
import { getIsMessageSelectionMode, getSelectedConversation } from './conversations';
import { DisappearingMessageConversationSetting } from '../../util/expiringMessages';
/**
* 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;
};
const getSelectedIsApproved = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.isApproved) || false;
};
const getSelectedApprovedMe = (state: StateType): boolean => {
return Boolean(getSelectedConversation(state)?.didApproveMe) || 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;
};
/**
* 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 canWriteSogs = getCanWrite(state, selectedConvoPubkey);
const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo;
return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWriteSogs));
}
/**
* 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 getSelectedGroupMembers = (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);
};
export const getSelectedConversationExpirationModes = (state: StateType) => {
const convo = getSelectedConversation(state);
if (!convo) {
return undefined;
}
let modes = DisappearingMessageConversationSetting;
// TODO legacy messages support will be removed in a future release
// TODO remove legacy mode
modes = modes.slice(0, -1);
// Note to Self and Closed Groups only support deleteAfterSend
const isClosedGroup = !convo.isPrivate && !convo.isPublic;
if (convo?.isMe || isClosedGroup) {
modes = [modes[0], modes[2]];
}
const modesWithDisabledState: Record<string, boolean> = {};
if (modes && modes.length > 1) {
modes.forEach(mode => {
modesWithDisabledState[mode] = isClosedGroup ? !convo.weAreAdmin : false;
});
}
return modesWithDisabledState;
};
// TODO legacy messages support will be removed in a future release
export const getSelectedConversationExpirationModesWithLegacy = (state: StateType) => {
const convo = getSelectedConversation(state);
if (!convo) {
return undefined;
}
let modes = DisappearingMessageConversationSetting;
// Note to Self and Closed Groups only support deleteAfterSend and legacy modes
const isClosedGroup = !convo.isPrivate && !convo.isPublic;
if (convo?.isMe || isClosedGroup) {
modes = [modes[0], ...modes.slice(2)];
}
// Legacy mode is the 2nd option in the UI
modes = [modes[0], modes[modes.length - 1], ...modes.slice(1, modes.length - 1)];
// TODO it would be nice to type those with something else that string but it causes a lot of issues
const modesWithDisabledState: Record<string, boolean> = {};
// The new modes are disabled by default
if (modes && modes.length > 1) {
modes.forEach(mode => {
modesWithDisabledState[mode] = Boolean(
(mode !== 'legacy' && mode !== 'off') || (isClosedGroup && !convo.weAreAdmin)
);
});
}
return modesWithDisabledState;
};
// ============== 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 useSelectedIsApproved() {
return useSelector(getSelectedIsApproved);
}
export function useSelectedApprovedMe() {
return useSelector(getSelectedApprovedMe);
}
/**
* Returns true if the given arguments corresponds to a private contact which is approved both sides. i.e. a friend.
*/
export function isPrivateAndFriend({
approvedMe,
isApproved,
isPrivate,
}: {
isPrivate: boolean;
isApproved: boolean;
approvedMe: boolean;
}) {
return isPrivate && isApproved && approvedMe;
}
/**
* Returns true if the selected conversation is private and is approved both sides
*/
export function useSelectedIsPrivateFriend() {
const isPrivate = useSelectedIsPrivate();
const isApproved = useSelectedIsApproved();
const approvedMe = useSelectedApprovedMe();
return isPrivateAndFriend({ isPrivate, isApproved, approvedMe });
}
export function useSelectedIsActive() {
return useSelector(getIsSelectedActive);
}
export function useSelectedIsNoteToSelf() {
return useSelector(getIsSelectedNoteToSelf);
}
export function useSelectedMembers() {
return useSelector(getSelectedGroupMembers);
}
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 useSelectedExpireTimer(): number | undefined {
return useSelector((state: StateType) => getSelectedConversation(state)?.expireTimer);
}
export function useSelectedExpirationType(): string | undefined {
return useSelector((state: StateType) => getSelectedConversation(state)?.expirationType);
}
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);
}
/**
* For a private chat, this returns the (xxxx...xxxx) shortened pubkey
* If this is a private chat, but somehow, we have no pubkey, this returns the localized `anonymous` string
* Otherwise, this returns the localized `unknown` string
*/
export function useSelectedShortenedPubkeyOrFallback() {
const isPrivate = useSelectedIsPrivate();
const selected = useSelectedConversationKey();
if (isPrivate && selected) {
return PubKey.shorten(selected);
}
if (isPrivate) {
return window.i18n('anonymous');
}
return window.i18n('unknown');
}
/**
* That's a very convoluted way to say "nickname or profile name or shortened pubkey or ("Anonymous" or "unknown" depending on the type of conversation).
* This also returns the localized "Note to Self" if the conversation is the note to self.
*/
export function useSelectedNicknameOrProfileNameOrShortenedPubkey() {
const nickname = useSelectedNickname();
const profileName = useSelectedDisplayNameInProfile();
const shortenedPubkey = useSelectedShortenedPubkeyOrFallback();
const isMe = useSelectedIsNoteToSelf();
if (isMe) {
return window.i18n('noteToSelf');
}
return nickname || profileName || shortenedPubkey;
}
export function useSelectedWeAreAdmin() {
return useSelector((state: StateType) => getSelectedConversation(state)?.weAreAdmin || false);
}
/**
* Only for communities.
* @returns true if the selected convo is a community and we are one of the moderators
*/
export function useSelectedWeAreModerator() {
// TODO might be something to memoize let's see
const isPublic = useSelectedIsPublic();
const selectedConvoKey = useSelectedConversationKey();
const us = UserUtils.getOurPubKeyStrFromCache();
const mods = useSelector((state: StateType) => getModerators(state, selectedConvoKey));
const weAreModerator = mods.includes(us);
return isPublic && isString(selectedConvoKey) && weAreModerator;
}
export function useIsMessageSelectionMode() {
return useSelector(getIsMessageSelectionMode);
}
Loading…
Cancel
Save