session-desktop/ts/state/selectors/selectedConversation.ts

308 lines
9.5 KiB
TypeScript

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';
/**
* 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;
const readOnlySogs = isPublic && !canWriteSogs;
const isBlindedAndDisabledMsgRequests = getSelectedBlindedDisabledMsgRequests(state); // true if isPrivate, blinded and explicitely disabled msgreq
return !(
isBlocked ||
isKickedFromGroup ||
left ||
readOnlySogs ||
isBlindedAndDisabledMsgRequests
);
}
function getSelectedBlindedDisabledMsgRequests(state: StateType) {
const selectedConvoPubkey = getSelectedConversationKey(state);
if (!selectedConvoPubkey) {
return false;
}
const selectedConvo = getSelectedConversation(state);
if (!selectedConvo) {
return false;
}
const { blocksSogsMsgReqsTimestamp, isPrivate } = selectedConvo;
const isBlindedAndDisabledMsgRequests = Boolean(
isPrivate && PubKey.isBlinded(selectedConvoPubkey) && blocksSogsMsgReqsTimestamp
);
return isBlindedAndDisabledMsgRequests;
}
/**
* 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);
}
export function useSelectedHasDisabledBlindedMsgRequests() {
return useSelector(getSelectedBlindedDisabledMsgRequests);
}
/**
* 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 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);
}