diff --git a/ts/components/LightboxGallery.tsx b/ts/components/LightboxGallery.tsx
index 81c16bb7a..83f4a03bc 100644
--- a/ts/components/LightboxGallery.tsx
+++ b/ts/components/LightboxGallery.tsx
@@ -1,7 +1,7 @@
/**
* @prettier
*/
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import * as MIME from '../types/MIME';
import { Lightbox } from './Lightbox';
@@ -31,7 +31,7 @@ type Props = {
export const LightboxGallery = (props: Props) => {
const { media, onSave } = props;
- const [currentIndex, setCurrentIndex] = useState(0);
+ const [currentIndex, setCurrentIndex] = useState(-1);
const dispatch = useDispatch();
@@ -43,33 +43,26 @@ export const LightboxGallery = (props: Props) => {
const selectedMedia = media[currentIndex];
const firstIndex = 0;
const lastIndex = media.length - 1;
- const onPrevious =
- currentIndex > firstIndex
- ? () => {
- setCurrentIndex(Math.max(currentIndex - 1, 0));
- }
- : undefined;
- const onNext =
- currentIndex < lastIndex
- ? () => {
- setCurrentIndex(Math.min(currentIndex + 1, lastIndex));
- }
- : undefined;
-
- const handleSave = () => {
+
+ const hasPrevious = currentIndex > firstIndex;
+ const hasNext = currentIndex < lastIndex;
+
+ const onPrevious = useCallback(() => {
+ setCurrentIndex(Math.max(currentIndex - 1, 0));
+ }, [currentIndex]);
+
+ const onNext = useCallback(() => {
+ setCurrentIndex(Math.min(currentIndex + 1, lastIndex));
+ }, [currentIndex, lastIndex]);
+
+ const handleSave = useCallback(() => {
if (!onSave) {
return;
}
const mediaItem = media[currentIndex];
onSave(mediaItem);
- };
-
- const objectURL = selectedMedia?.objectURL || 'images/alert-outline.svg';
- const { attachment } = selectedMedia;
-
- const saveCallback = onSave ? handleSave : undefined;
- const captionCallback = attachment ? attachment.caption : undefined;
+ }, [currentIndex, media]);
useKey(
'ArrowRight',
@@ -88,17 +81,31 @@ export const LightboxGallery = (props: Props) => {
[currentIndex]
);
- useKey('Escape', () => {
- dispatch(showLightBox(undefined));
- });
+ useKey(
+ 'Escape',
+ () => {
+ dispatch(showLightBox(undefined));
+ },
+ undefined,
+ [currentIndex]
+ );
+ // just to avoid to render the first element during the first render when the user selected another item
+ if (currentIndex === -1) {
+ return null;
+ }
+ const objectURL = selectedMedia?.objectURL || 'images/alert-outline.svg';
+ const { attachment } = selectedMedia;
+ const saveCallback = onSave ? handleSave : undefined;
+ const caption = attachment?.caption;
return (
+ // tslint:disable: use-simple-attributes
);
diff --git a/ts/components/conversation/media-gallery/AttachmentSection.tsx b/ts/components/conversation/media-gallery/AttachmentSection.tsx
index 834dfe8af..4f2787933 100644
--- a/ts/components/conversation/media-gallery/AttachmentSection.tsx
+++ b/ts/components/conversation/media-gallery/AttachmentSection.tsx
@@ -14,7 +14,6 @@ type Props = {
const Items = (props: Props): JSX.Element => {
const { mediaItems, type } = props;
- const selectedConversationKey = useSelector(getSelectedConversationKey);
return (
<>
@@ -40,7 +39,6 @@ const Items = (props: Props): JSX.Element => {
shouldShowSeparator={shouldShowSeparator}
timestamp={messageTimestamp}
mediaItem={mediaItem}
- conversationId={selectedConversationKey as string}
/>
);
default:
diff --git a/ts/components/conversation/media-gallery/DocumentListItem.tsx b/ts/components/conversation/media-gallery/DocumentListItem.tsx
index 123073c37..36795c5f1 100644
--- a/ts/components/conversation/media-gallery/DocumentListItem.tsx
+++ b/ts/components/conversation/media-gallery/DocumentListItem.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
import classNames from 'classnames';
import moment from 'moment';
@@ -8,6 +8,8 @@ import { getDecryptedMediaUrl } from '../../../session/crypto/DecryptedAttachmen
import { sendDataExtractionNotification } from '../../../session/messages/outgoing/controlMessage/DataExtractionNotificationMessage';
import { AttachmentTypeWithPath, save } from '../../../types/Attachment';
import { MediaItemType } from '../../LightboxGallery';
+import { useSelector } from 'react-redux';
+import { getSelectedConversationKey } from '../../../state/selectors/conversations';
type Props = {
// Required
@@ -18,7 +20,6 @@ type Props = {
fileSize?: number | null;
shouldShowSeparator?: boolean;
mediaItem: MediaItemType;
- conversationId: string;
};
const saveAttachment = async ({
@@ -32,21 +33,35 @@ const saveAttachment = async ({
messageSender: string;
conversationId: string;
}) => {
- const timestamp = messageTimestamp;
attachment.url = await getDecryptedMediaUrl(attachment.url, attachment.contentType);
save({
attachment,
document,
getAbsolutePath: window.Signal.Migrations.getAbsoluteAttachmentPath,
- timestamp,
+ timestamp: messageTimestamp,
});
- await sendDataExtractionNotification(conversationId, messageSender, timestamp);
+ await sendDataExtractionNotification(conversationId, messageSender, messageTimestamp);
};
export const DocumentListItem = (props: Props) => {
const { shouldShowSeparator, fileName, fileSize, timestamp } = props;
const defaultShowSeparator = shouldShowSeparator === undefined ? true : shouldShowSeparator;
+ const selectedConversationKey = useSelector(getSelectedConversationKey) as string;
+
+ const saveAttachmentCallback = useCallback(() => {
+ void saveAttachment({
+ messageSender: props.mediaItem.messageSender,
+ messageTimestamp: props.mediaItem.messageTimestamp,
+ attachment: props.mediaItem.attachment,
+ conversationId: selectedConversationKey,
+ });
+ }, [
+ selectedConversationKey,
+ props.mediaItem.messageSender,
+ props.mediaItem.messageTimestamp,
+ props.mediaItem.attachment,
+ ]);
return (
{
{
- void saveAttachment({
- messageSender: props.mediaItem.messageSender,
- messageTimestamp: props.mediaItem.messageTimestamp,
- attachment: props.mediaItem.attachment,
- conversationId: props.conversationId,
- });
- }}
+ onClick={saveAttachmentCallback}
>
diff --git a/ts/components/conversation/media-gallery/MediaGallery.tsx b/ts/components/conversation/media-gallery/MediaGallery.tsx
index 847cd2558..efffb3caa 100644
--- a/ts/components/conversation/media-gallery/MediaGallery.tsx
+++ b/ts/components/conversation/media-gallery/MediaGallery.tsx
@@ -1,9 +1,8 @@
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import { AttachmentSection } from './AttachmentSection';
import { EmptyState } from './EmptyState';
-import { missingCaseError } from '../../../util/missingCaseError';
import { MediaItemType } from '../../LightboxGallery';
type Props = {
@@ -17,12 +16,10 @@ const Tab = ({
isSelected,
label,
onSelect,
- type,
}: {
isSelected: boolean;
label: string;
onSelect: () => void;
- type: TabType;
}) => {
return (
{
const type = selectedTab;
if (!mediaItems || mediaItems.length === 0) {
- const label = (() => {
- switch (type) {
- case 'media':
- return window.i18n('mediaEmptyState');
-
- case 'documents':
- return window.i18n('documentsEmptyState');
-
- default:
- throw missingCaseError(type);
- }
- })();
+ const label =
+ type === 'media' ? window.i18n('mediaEmptyState') : window.i18n('documentsEmptyState');
return
;
}
@@ -74,24 +61,22 @@ export const MediaGallery = (props: Props) => {
const isDocumentSelected = selectedTab === 'documents';
const isMediaSelected = selectedTab === 'media';
+ const setMediaTab = useCallback(() => {
+ setSelectedTab('media');
+ }, []);
+
+ const setDocumentsTab = useCallback(() => {
+ setSelectedTab('documents');
+ }, []);
+
return (
- {
- setSelectedTab('media');
- }}
- />
+
{
- setSelectedTab('documents');
- }}
+ onSelect={setDocumentsTab}
/>