import React, { useEffect, useState } from 'react'; import { SessionIconButton, SessionIconSize, SessionIconType } from '../icon'; import { Avatar, AvatarSize } from '../../Avatar'; import { SessionButton, SessionButtonColor, SessionButtonType } from '../SessionButton'; import { SessionDropdown } from '../SessionDropdown'; import { MediaGallery } from '../../conversation/media-gallery/MediaGallery'; import _ from 'lodash'; import { Constants } from '../../../session'; import { ConversationAvatar } from '../usingClosedConversationDetails'; import { AttachmentTypeWithPath } from '../../../types/Attachment'; import { useTheme } from 'styled-components'; import { getMessagesWithFileAttachments, getMessagesWithVisualMediaAttachments, } from '../../../data/data'; import { SpacerLG } from '../../basic/Text'; import { deleteMessagesByConvoIdWithConfirmation, setDisappearingMessagesByConvoId, showAddModeratorsByConvoId, showInviteContactByConvoId, showLeaveGroupByConvoId, showRemoveModeratorsByConvoId, showUpdateGroupMembersByConvoId, showUpdateGroupNameByConvoId, } from '../../../interactions/conversationInteractions'; import { MediaItemType } from '../../LightboxGallery'; // tslint:disable-next-line: no-submodule-imports import useInterval from 'react-use/lib/useInterval'; import { useDispatch, useSelector } from 'react-redux'; import { getTimerOptions } from '../../../state/selectors/timerOptions'; import { getSelectedConversation, isRightPanelShowing, } from '../../../state/selectors/conversations'; import { useMembersAvatars } from '../../../hooks/useMembersAvatar'; import { closeRightPanel } from '../../../state/ducks/conversations'; async function getMediaGalleryProps( conversationId: string ): Promise<{ documents: Array; media: Array; }> { // We fetch more documents than media as they don’t require to be loaded // into memory right away. Revisit this once we have infinite scrolling: const rawMedia = await getMessagesWithVisualMediaAttachments(conversationId, { limit: Constants.CONVERSATION.DEFAULT_MEDIA_FETCH_COUNT, }); const rawDocuments = await getMessagesWithFileAttachments(conversationId, { limit: Constants.CONVERSATION.DEFAULT_DOCUMENTS_FETCH_COUNT, }); const media = _.flatten( rawMedia.map(attributes => { const { attachments, source, id, timestamp, serverTimestamp, received_at } = attributes; return (attachments || []) .filter( (attachment: AttachmentTypeWithPath) => attachment.thumbnail && !attachment.pending && !attachment.error ) .map((attachment: AttachmentTypeWithPath, index: number) => { const { thumbnail } = attachment; const mediaItem: MediaItemType = { objectURL: window.Signal.Migrations.getAbsoluteAttachmentPath(attachment.path), thumbnailObjectUrl: thumbnail ? window.Signal.Migrations.getAbsoluteAttachmentPath(thumbnail.path) : null, contentType: attachment.contentType || '', index, messageTimestamp: timestamp || serverTimestamp || received_at || 0, messageSender: source, messageId: id, attachment, }; return mediaItem; }); }) ); // Unlike visual media, only one non-image attachment is supported const documents = rawDocuments.map(attributes => { // this is to not fail if the attachment is invalid (could be a Long Attachment type which is not supported) if (!attributes.attachments?.length) { // window?.log?.info( // 'Got a message with an empty list of attachment. Skipping...' // ); return null; } const attachment = attributes.attachments[0]; const { source, id, timestamp, serverTimestamp, received_at } = attributes; return { contentType: attachment.contentType, index: 0, attachment, messageTimestamp: timestamp || serverTimestamp || received_at || 0, messageSender: source, messageId: id, }; }); return { media, documents: _.compact(documents), // remove null }; } const HeaderItem = () => { const selectedConversation = useSelector(getSelectedConversation); const theme = useTheme(); const dispatch = useDispatch(); const memberDetails = useMembersAvatars(selectedConversation); if (!selectedConversation) { return null; } const { avatarPath, isPublic, id, isGroup, isKickedFromGroup, profileName, phoneNumber, isBlocked, left, name, } = selectedConversation; const showInviteContacts = isGroup && !isKickedFromGroup && !isBlocked && !left; const userName = name || profileName || phoneNumber; return (
{ dispatch(closeRightPanel()); }} theme={theme} />
{showInviteContacts && ( { if (selectedConversation) { showInviteContactByConvoId(selectedConversation.id); } }} theme={theme} /> )}
); }; // tslint:disable: cyclomatic-complexity // tslint:disable: max-func-body-length export const SessionRightPanelWithDetails = () => { const [documents, setDocuments] = useState>([]); const [media, setMedia] = useState>([]); const selectedConversation = useSelector(getSelectedConversation); const isShowing = useSelector(isRightPanelShowing); useEffect(() => { let isRunning = true; if (isShowing && selectedConversation) { void getMediaGalleryProps(selectedConversation.id).then(results => { if (isRunning) { if (!_.isEqual(documents, results.documents)) { setDocuments(results.documents); } if (!_.isEqual(media, results.media)) { setMedia(results.media); } } }); } return () => { isRunning = false; return; }; }, [isShowing, selectedConversation?.id]); useInterval(async () => { if (isShowing && selectedConversation) { const results = await getMediaGalleryProps(selectedConversation.id); if (results.documents.length !== documents.length || results.media.length !== media.length) { setDocuments(results.documents); setMedia(results.media); } } }, 10000); if (!selectedConversation) { return null; } const { id, subscriberCount, name, isKickedFromGroup, left, isPublic, weAreAdmin, isBlocked, isGroup, } = selectedConversation; const showMemberCount = !!(subscriberCount && subscriberCount > 0); const commonNoShow = isKickedFromGroup || left || isBlocked; const hasDisappearingMessages = !isPublic && !commonNoShow; const leaveGroupString = isPublic ? window.i18n('leaveGroup') : isKickedFromGroup ? window.i18n('youGotKickedFromGroup') : left ? window.i18n('youLeftTheGroup') : window.i18n('leaveGroup'); const timerOptions = useSelector(getTimerOptions).timerOptions; const disappearingMessagesOptions = timerOptions.map(option => { return { content: option.name, onClick: () => { void setDisappearingMessagesByConvoId(id, option.value); }, }; }); const showUpdateGroupNameButton = isGroup && (!isPublic || (isPublic && weAreAdmin)) && !commonNoShow; const showAddRemoveModeratorsButton = weAreAdmin && !commonNoShow && isPublic; const showUpdateGroupMembersButton = !isPublic && isGroup && !commonNoShow; const deleteConvoAction = isPublic ? () => { deleteMessagesByConvoIdWithConfirmation(id); } : () => { showLeaveGroupByConvoId(id); }; return (

{name}

{showMemberCount && ( <>
{window.i18n('members', subscriberCount)}
)} {showUpdateGroupNameButton && (
{ await showUpdateGroupNameByConvoId(id); }} > {isPublic ? window.i18n('editGroup') : window.i18n('editGroupName')}
)} {showAddRemoveModeratorsButton && ( <>
{ showAddModeratorsByConvoId(id); }} > {window.i18n('addModerators')}
{ showRemoveModeratorsByConvoId(id); }} > {window.i18n('removeModerators')}
)} {showUpdateGroupMembersButton && (
{ await showUpdateGroupMembersByConvoId(id); }} > {window.i18n('groupMembers')}
)} {hasDisappearingMessages && ( )} {isGroup && ( // tslint:disable-next-line: use-simple-attributes )}
); };