import React from 'react'; import { Avatar, AvatarSize } from '../Avatar'; import { SessionIconButton } from '../session/icon'; import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton'; import { MemoConversationHeaderMenu } from '../session/menu/ConversationHeaderMenu'; import { contextMenu } from 'react-contexify'; import styled from 'styled-components'; import { ConversationNotificationSettingType } from '../../models/conversation'; import { getConversationHeaderProps, getConversationHeaderTitleProps, getCurrentNotificationSettingText, getIsSelectedNoteToSelf, getIsSelectedPrivate, getSelectedConversationIsPublic, getSelectedConversationKey, getSelectedMessageIds, isMessageDetailView, isMessageSelectionMode, isRightPanelShowing, } from '../../state/selectors/conversations'; import { useDispatch, useSelector } from 'react-redux'; import { deleteMessagesById, deleteMessagesByIdForEveryone, } from '../../interactions/conversations/unsendingInteractions'; import { closeMessageDetailsView, closeRightPanel, openRightPanel, resetSelectedMessageIds, } from '../../state/ducks/conversations'; import { callRecipient } from '../../interactions/conversationInteractions'; import { getHasIncomingCall, getHasOngoingCall } from '../../state/selectors/call'; import { useConversationUsername } from '../../hooks/useParamSelector'; export interface TimerOption { name: string; value: number; } export type ConversationHeaderProps = { conversationKey: string; name?: string; profileName?: string; avatarPath: string | null; isMe: boolean; isGroup: boolean; isPrivate: boolean; isPublic: boolean; weAreAdmin: boolean; // We might not always have the full list of members, // e.g. for open groups where we could have thousands // of members. We'll keep this for now (for closed chats) members: Array; // not equal members.length (see above) subscriberCount?: number; expirationSettingName?: string; currentNotificationSetting: ConversationNotificationSettingType; hasNickname: boolean; isBlocked: boolean; isKickedFromGroup: boolean; left: boolean; }; const SelectionOverlay = () => { const selectedMessageIds = useSelector(getSelectedMessageIds); const selectedConversationKey = useSelector(getSelectedConversationKey); const isPublic = useSelector(getSelectedConversationIsPublic); const dispatch = useDispatch(); const { i18n } = window; function onCloseOverlay() { dispatch(resetSelectedMessageIds()); } function onDeleteSelectedMessages() { if (selectedConversationKey) { void deleteMessagesById(selectedMessageIds, selectedConversationKey); } } function onDeleteSelectedMessagesForEveryone() { if (selectedConversationKey) { void deleteMessagesByIdForEveryone(selectedMessageIds, selectedConversationKey); } } const isOnlyServerDeletable = isPublic; const deleteMessageButtonText = i18n('delete'); const deleteForEveroneMessageButtonText = i18n('deleteForEveryone'); return (
{!isOnlyServerDeletable && ( )}
); }; const TripleDotsMenu = (props: { triggerId: string; showBackButton: boolean }) => { const { showBackButton } = props; if (showBackButton) { return null; } return (
{ contextMenu.show({ id: props.triggerId, event: e, }); }} >
); }; const ExpirationLength = (props: { expirationSettingName?: string }) => { const { expirationSettingName } = props; if (!expirationSettingName) { return null; } return (
{expirationSettingName}
); }; const AvatarHeader = (props: { pubkey: string; showBackButton: boolean; onAvatarClick?: (pubkey: string) => void; }) => { const { pubkey, onAvatarClick, showBackButton } = props; return ( { // do not allow right panel to appear if another button is shown on the SessionConversation if (onAvatarClick && !showBackButton) { onAvatarClick(pubkey); } }} pubkey={pubkey} /> ); }; const BackButton = (props: { onGoBack: () => void; showBackButton: boolean }) => { const { onGoBack, showBackButton } = props; if (!showBackButton) { return null; } return ( ); }; const CallButton = () => { const isPrivate = useSelector(getIsSelectedPrivate); const isMe = useSelector(getIsSelectedNoteToSelf); const selectedConvoKey = useSelector(getSelectedConversationKey); const hasIncomingCall = useSelector(getHasIncomingCall); const hasOngoingCall = useSelector(getHasOngoingCall); const canCall = !(hasIncomingCall || hasOngoingCall); if (!isPrivate || isMe || !selectedConvoKey) { return null; } return ( { void callRecipient(selectedConvoKey, canCall); }} /> ); }; export const StyledSubtitleContainer = styled.div` display: flex; flex-direction: row; align-items: center; justify-content: center; span:last-child { margin-bottom: 0; } `; export type ConversationHeaderTitleProps = { conversationKey: string; profileName?: string; isMe: boolean; isGroup: boolean; isPublic: boolean; members: Array; subscriberCount?: number; isKickedFromGroup: boolean; name?: string; currentNotificationSetting?: ConversationNotificationSettingType; }; const ConversationHeaderTitle = () => { const headerTitleProps = useSelector(getConversationHeaderTitleProps); const notificationSetting = useSelector(getCurrentNotificationSettingText); const isRightPanelOn = useSelector(isRightPanelShowing); const convoName = useConversationUsername(headerTitleProps?.conversationKey); const dispatch = useDispatch(); if (!headerTitleProps) { return null; } const { isGroup, isPublic, members, subscriberCount, isMe, isKickedFromGroup } = headerTitleProps; const { i18n } = window; if (isMe) { return
{i18n('noteToSelf')}
; } let memberCount = 0; if (isGroup) { if (isPublic) { memberCount = subscriberCount || 0; } else { memberCount = members.length; } } let memberCountText = ''; if (isGroup && memberCount > 0 && !isKickedFromGroup) { const count = String(memberCount); memberCountText = i18n('members', [count]); } const notificationSubtitle = notificationSetting ? window.i18n('notificationSubtitle', [notificationSetting]) : null; const fullTextSubtitle = memberCountText ? `${memberCountText} ● ${notificationSubtitle}` : `${notificationSubtitle}`; return (
{ if (isRightPanelOn) { dispatch(closeRightPanel()); } else { dispatch(openRightPanel()); } }} role="button" > {convoName}
); }; /** * The subtitle beneath a conversation title when looking at a conversation screen. * @param props props for subtitle. Text to be displayed * @returns JSX Element of the subtitle of conversation header */ export const ConversationHeaderSubtitle = (props: { text?: string | null }): JSX.Element | null => { const { text } = props; if (!text) { return null; } return {text}; }; export const ConversationHeaderWithDetails = () => { const headerProps = useSelector(getConversationHeaderProps); const isSelectionMode = useSelector(isMessageSelectionMode); const isMessageDetailOpened = useSelector(isMessageDetailView); const dispatch = useDispatch(); if (!headerProps) { return null; } const { isKickedFromGroup, expirationSettingName, avatarPath, name, profileName, isMe, isPublic, currentNotificationSetting, hasNickname, weAreAdmin, isBlocked, left, conversationKey, isPrivate, isGroup, } = headerProps; const triggerId = 'conversation-header'; return (
{ dispatch(closeMessageDetailsView()); }} showBackButton={isMessageDetailOpened} />
{!isKickedFromGroup && } {!isSelectionMode && ( <> { dispatch(openRightPanel()); }} pubkey={conversationKey} showBackButton={isMessageDetailOpened} /> )}
{isSelectionMode && }
); };