session-desktop/ts/components/conversation/SessionRightPanel.tsx

376 lines
12 KiB
TypeScript
Raw Normal View History

import _ from 'lodash';
import React, { useEffect, useState } from 'react';
import { SessionIconButton } from '../icon';
// tslint:disable-next-line: no-submodule-imports
import { useDispatch, useSelector } from 'react-redux';
import useInterval from 'react-use/lib/useInterval';
import styled from 'styled-components';
import { Data } from '../../data/data';
import {
2021-10-25 08:04:51 +02:00
deleteAllMessagesByConvoIdWithConfirmation,
setDisappearingMessagesByConvoId,
showAddModeratorsByConvoId,
showInviteContactByConvoId,
showLeaveGroupByConvoId,
showRemoveModeratorsByConvoId,
showUpdateGroupMembersByConvoId,
showUpdateGroupNameByConvoId,
} from '../../interactions/conversationInteractions';
import { Constants } from '../../session';
import { closeRightPanel } from '../../state/ducks/conversations';
import { isRightPanelShowing } from '../../state/selectors/conversations';
import {
useSelectedConversationKey,
useSelectedDisplayNameInProfile,
useSelectedIsActive,
useSelectedIsBlocked,
useSelectedIsGroup,
useSelectedIsKickedFromGroup,
useSelectedIsLeft,
useSelectedIsPublic,
useSelectedSubscriberCount,
useSelectedWeAreAdmin,
} from '../../state/selectors/selectedConversation';
import { getTimerOptions } from '../../state/selectors/timerOptions';
import { AttachmentTypeWithPath } from '../../types/Attachment';
import { getAbsoluteAttachmentPath } from '../../types/MessageAttachment';
import { Avatar, AvatarSize } from '../avatar/Avatar';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
import { SessionDropdown } from '../basic/SessionDropdown';
import { SpacerLG } from '../basic/Text';
import { MediaItemType } from '../lightbox/LightboxGallery';
import { MediaGallery } from './media-gallery/MediaGallery';
async function getMediaGalleryProps(
2021-07-14 05:48:59 +02:00
conversationId: string
): Promise<{
documents: Array<MediaItemType>;
media: Array<MediaItemType>;
}> {
// We fetch more documents than media as they dont require to be loaded
// into memory right away. Revisit this once we have infinite scrolling:
const rawMedia = await Data.getMessagesWithVisualMediaAttachments(
2022-03-29 06:18:26 +02:00
conversationId,
Constants.CONVERSATION.DEFAULT_MEDIA_FETCH_COUNT
);
const rawDocuments = await Data.getMessagesWithFileAttachments(
2022-03-29 06:18:26 +02:00
conversationId,
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: getAbsoluteAttachmentPath(attachment.path),
thumbnailObjectUrl: thumbnail ? getAbsoluteAttachmentPath(thumbnail.path) : undefined,
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;
2020-01-13 05:50:18 +01:00
return {
contentType: attachment.contentType,
index: 0,
attachment,
messageTimestamp: timestamp || serverTimestamp || received_at || 0,
messageSender: source,
messageId: id,
2020-01-13 05:50:18 +01:00
};
});
return {
media,
documents: _.compact(documents), // remove null
};
}
2020-02-19 01:27:37 +01:00
2021-07-13 09:00:20 +02:00
const HeaderItem = () => {
const selectedConvoKey = useSelectedConversationKey();
2021-07-13 09:00:20 +02:00
const dispatch = useDispatch();
const isBlocked = useSelectedIsBlocked();
const isKickedFromGroup = useSelectedIsKickedFromGroup();
const left = useSelectedIsLeft();
const isGroup = useSelectedIsGroup();
2021-07-13 09:00:20 +02:00
if (!selectedConvoKey) {
2021-07-13 09:00:20 +02:00
return null;
}
2021-07-30 02:08:26 +02:00
const showInviteContacts = isGroup && !isKickedFromGroup && !isBlocked && !left;
2021-07-13 09:00:20 +02:00
return (
<div className="group-settings-header">
<SessionIconButton
iconType="chevron"
iconSize="medium"
2021-07-13 09:00:20 +02:00
iconRotation={270}
onClick={() => {
dispatch(closeRightPanel());
}}
style={{ position: 'absolute' }}
dataTestId="back-button-conversation-options"
2021-07-13 09:00:20 +02:00
/>
<Avatar size={AvatarSize.XL} pubkey={selectedConvoKey} />
{showInviteContacts && (
<SessionIconButton
iconType="addUser"
iconSize="medium"
onClick={() => {
if (selectedConvoKey) {
showInviteContactByConvoId(selectedConvoKey);
}
}}
dataTestId="add-user-button"
/>
)}
2021-07-13 09:00:20 +02:00
</div>
);
};
const StyledLeaveButton = styled.div`
width: 100%;
.session-button {
margin-top: auto;
width: 100%;
min-height: calc(var(--composition-container-height) + 1px); // include border in height
flex-shrink: 0;
align-items: center;
border-top: 1px solid var(--border-color);
border-radius: 0px;
&:not(.disabled) {
&:hover {
background-color: var(--button-solid-background-hover-color);
}
}
}
`;
const StyledGroupSettingsItem = styled.div`
display: flex;
align-items: center;
min-height: 3rem;
font-size: var(--font-size-md);
color: var(--right-panel-item-text-color);
background-color: var(--right-panel-item-background-color);
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
width: -webkit-fill-available;
padding: 0 var(--margins-md);
transition: var(--default-duration);
cursor: pointer;
&:hover {
background-color: var(--right-panel-item-background-hover-color);
}
`;
const StyledName = styled.h4`
padding-inline: var(--margins-md);
font-size: var(--font-size-md);
`;
// tslint:disable: cyclomatic-complexity
// tslint:disable: max-func-body-length
2021-07-14 05:48:59 +02:00
export const SessionRightPanelWithDetails = () => {
const [documents, setDocuments] = useState<Array<MediaItemType>>([]);
const [media, setMedia] = useState<Array<MediaItemType>>([]);
2021-07-13 09:00:20 +02:00
const selectedConvoKey = useSelectedConversationKey();
2021-07-13 09:00:20 +02:00
const isShowing = useSelector(isRightPanelShowing);
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 && selectedConvoKey) {
void getMediaGalleryProps(selectedConvoKey).then(results => {
2021-07-14 05:48:59 +02:00
if (isRunning) {
if (!_.isEqual(documents, results.documents)) {
2021-07-13 09:00:20 +02:00
setDocuments(results.documents);
2021-07-14 05:48:59 +02:00
}
if (!_.isEqual(media, results.media)) {
2021-07-13 09:00:20 +02:00
setMedia(results.media);
}
}
2021-07-14 05:48:59 +02:00
});
}
return () => {
isRunning = false;
return;
};
}, [isShowing, selectedConvoKey]);
useInterval(async () => {
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);
}
}
}, 10000);
2020-01-13 05:50:18 +01:00
if (!selectedConvoKey) {
2021-07-13 09:00:20 +02:00
return null;
2020-01-13 05:50:18 +01:00
}
2021-07-13 09:00:20 +02:00
const showMemberCount = !!(subscriberCount && subscriberCount > 0);
const commonNoShow = isKickedFromGroup || left || isBlocked || !isActive;
const hasDisappearingMessages = !isPublic && !commonNoShow;
const leaveGroupString = isPublic
? window.i18n('leaveGroup')
: isKickedFromGroup
? window.i18n('youGotKickedFromGroup')
: left
? window.i18n('youLeftTheGroup')
: window.i18n('leaveGroup');
2021-07-06 06:40:45 +02:00
const timerOptions = useSelector(getTimerOptions).timerOptions;
const disappearingMessagesOptions = timerOptions.map(option => {
return {
content: option.name,
2021-07-14 05:48:59 +02:00
onClick: () => {
void setDisappearingMessagesByConvoId(selectedConvoKey, option.value);
},
};
});
2021-07-30 02:08:26 +02:00
const showUpdateGroupNameButton =
isGroup && (!isPublic || (isPublic && weAreAdmin)) && !commonNoShow;
2021-07-13 09:00:20 +02:00
const showAddRemoveModeratorsButton = weAreAdmin && !commonNoShow && isPublic;
const showUpdateGroupMembersButton = !isPublic && isGroup && !commonNoShow;
const deleteConvoAction = isPublic
? () => {
deleteAllMessagesByConvoIdWithConfirmation(selectedConvoKey); // TODOLATER this does not delete the public group and showLeaveGroupByConvoId is not only working for closed groups
}
: () => {
showLeaveGroupByConvoId(selectedConvoKey);
};
return (
<div className="group-settings">
2021-07-13 09:00:20 +02:00
<HeaderItem />
<StyledName data-testid="right-panel-group-name">{displayNameInProfile}</StyledName>
{showMemberCount && (
<>
<SpacerLG />
<div role="button" className="subtle">
{window.i18n('members', [`${subscriberCount}`])}
</div>
<SpacerLG />
</>
)}
{showUpdateGroupNameButton && (
<StyledGroupSettingsItem
className="group-settings-item"
role="button"
onClick={async () => {
await showUpdateGroupNameByConvoId(selectedConvoKey);
}}
>
{isPublic ? window.i18n('editGroup') : window.i18n('editGroupName')}
</StyledGroupSettingsItem>
)}
{showAddRemoveModeratorsButton && (
<>
<StyledGroupSettingsItem
className="group-settings-item"
role="button"
onClick={() => {
showAddModeratorsByConvoId(selectedConvoKey);
}}
>
{window.i18n('addModerators')}
</StyledGroupSettingsItem>
<StyledGroupSettingsItem
className="group-settings-item"
role="button"
onClick={() => {
showRemoveModeratorsByConvoId(selectedConvoKey);
}}
>
{window.i18n('removeModerators')}
</StyledGroupSettingsItem>
</>
)}
{showUpdateGroupMembersButton && (
<StyledGroupSettingsItem
className="group-settings-item"
role="button"
onClick={async () => {
await showUpdateGroupMembersByConvoId(selectedConvoKey);
}}
>
{window.i18n('groupMembers')}
</StyledGroupSettingsItem>
)}
{hasDisappearingMessages && (
<SessionDropdown
label={window.i18n('disappearingMessages')}
options={disappearingMessagesOptions}
/>
)}
2021-07-14 05:48:59 +02:00
<MediaGallery documents={documents} media={media} />
{isGroup && (
// tslint:disable-next-line: use-simple-attributes
<StyledLeaveButton>
<SessionButton
text={leaveGroupString}
buttonColor={SessionButtonColor.Danger}
buttonType={SessionButtonType.Simple}
disabled={isKickedFromGroup || left}
onClick={deleteConvoAction}
/>
</StyledLeaveButton>
)}
</div>
);
};