signal-desktop/ts/components/conversation/media-gallery/MediaGallery.tsx

186 lines
4.9 KiB
TypeScript
Raw Normal View History

2023-01-03 19:55:46 +00:00
// Copyright 2018 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
2022-12-20 17:50:23 +00:00
import React, { useEffect, useRef } from 'react';
2018-04-11 15:57:31 +00:00
2018-04-13 00:56:05 +00:00
import moment from 'moment';
2022-12-20 17:50:23 +00:00
import type { ItemClickEvent } from './types/ItemClickEvent';
import type { LocalizerType } from '../../../types/Util';
import type { MediaItemType } from '../../../types/MediaItem';
import type { SaveAttachmentActionCreatorType } from '../../../state/ducks/conversations';
2018-04-15 01:11:40 +00:00
import { AttachmentSection } from './AttachmentSection';
2018-04-26 23:06:48 +00:00
import { EmptyState } from './EmptyState';
2022-12-20 17:50:23 +00:00
import { Tabs } from '../../Tabs';
import { getMessageTimestamp } from '../../../util/getMessageTimestamp';
import { groupMediaItemsByDate } from './groupMediaItemsByDate';
2018-04-26 23:06:48 +00:00
import { missingCaseError } from '../../../util/missingCaseError';
2018-04-12 20:23:26 +00:00
2022-12-20 17:50:23 +00:00
enum TabViews {
Media = 'Media',
Documents = 'Documents',
}
export type Props = {
2022-12-20 17:50:23 +00:00
conversationId: string;
documents: Array<MediaItemType>;
2019-01-14 21:49:58 +00:00
i18n: LocalizerType;
2022-12-20 17:50:23 +00:00
loadMediaItems: (id: string) => unknown;
media: Array<MediaItemType>;
2022-12-20 17:50:23 +00:00
saveAttachment: SaveAttachmentActionCreatorType;
showLightboxWithMedia: (
selectedIndex: number,
2022-12-20 17:50:23 +00:00
media: Array<MediaItemType>
) => void;
};
2018-04-11 15:57:31 +00:00
2018-04-13 00:56:05 +00:00
const MONTH_FORMAT = 'MMMM YYYY';
2018-04-12 20:23:26 +00:00
2022-12-20 17:50:23 +00:00
function MediaSection({
2018-04-12 20:23:26 +00:00
type,
2022-12-20 17:50:23 +00:00
i18n,
media,
documents,
saveAttachment,
showLightboxWithMedia,
}: Pick<
Props,
'i18n' | 'media' | 'documents' | 'showLightboxWithMedia' | 'saveAttachment'
> & { type: 'media' | 'documents' }): JSX.Element {
const mediaItems = type === 'media' ? media : documents;
if (!mediaItems || mediaItems.length === 0) {
const label = (() => {
switch (type) {
case 'media':
2023-03-30 00:03:25 +00:00
return i18n('icu:mediaEmptyState');
2022-12-20 17:50:23 +00:00
case 'documents':
2023-03-30 00:03:25 +00:00
return i18n('icu:documentsEmptyState');
2022-12-20 17:50:23 +00:00
default:
throw missingCaseError(type);
}
2022-12-20 17:50:23 +00:00
})();
2018-04-12 20:23:26 +00:00
2022-12-20 17:50:23 +00:00
return <EmptyState data-test="EmptyState" label={label} />;
2020-09-14 19:51:27 +00:00
}
2022-12-20 17:50:23 +00:00
const now = Date.now();
const sections = groupMediaItemsByDate(now, mediaItems).map(section => {
const first = section.mediaItems[0];
const { message } = first;
const date = moment(getMessageTimestamp(message));
function getHeader(): string {
switch (section.type) {
case 'yearMonth':
return date.format(MONTH_FORMAT);
case 'today':
2023-03-30 00:03:25 +00:00
return i18n('icu:today');
case 'yesterday':
2023-03-30 00:03:25 +00:00
return i18n('icu:yesterday');
case 'thisWeek':
2023-03-30 00:03:25 +00:00
return i18n('icu:thisWeek');
case 'thisMonth':
2023-03-30 00:03:25 +00:00
return i18n('icu:thisMonth');
default:
throw missingCaseError(section);
}
}
const header = getHeader();
2018-04-12 20:23:26 +00:00
2018-04-11 15:57:31 +00:00
return (
2022-12-20 17:50:23 +00:00
<AttachmentSection
key={header}
header={header}
i18n={i18n}
type={type}
mediaItems={section.mediaItems}
onItemClick={(event: ItemClickEvent) => {
switch (event.type) {
case 'documents': {
saveAttachment(event.attachment, event.message.sent_at);
break;
}
case 'media': {
showLightboxWithMedia(event.index, media);
2022-12-20 17:50:23 +00:00
break;
}
default:
throw new TypeError(`Unknown attachment type: '${event.type}'`);
}
}}
/>
2018-04-11 15:57:31 +00:00
);
2022-12-20 17:50:23 +00:00
});
2018-04-12 20:23:26 +00:00
2022-12-20 17:50:23 +00:00
return <div className="module-media-gallery__sections">{sections}</div>;
}
export function MediaGallery({
conversationId,
documents,
i18n,
loadMediaItems,
media,
saveAttachment,
showLightboxWithMedia,
}: Props): JSX.Element {
const focusRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
focusRef.current?.focus();
}, []);
useEffect(() => {
loadMediaItems(conversationId);
}, [conversationId, loadMediaItems]);
return (
<div className="module-media-gallery" tabIndex={-1} ref={focusRef}>
<Tabs
initialSelectedTab={TabViews.Media}
tabs={[
{
id: TabViews.Media,
2023-03-30 00:03:25 +00:00
label: i18n('icu:media'),
2022-12-20 17:50:23 +00:00
},
{
id: TabViews.Documents,
2023-03-30 00:03:25 +00:00
label: i18n('icu:documents'),
2022-12-20 17:50:23 +00:00
},
]}
>
{({ selectedTab }) => (
<div className="module-media-gallery__content">
{selectedTab === TabViews.Media && (
<MediaSection
documents={documents}
i18n={i18n}
media={media}
saveAttachment={saveAttachment}
showLightboxWithMedia={showLightboxWithMedia}
type="media"
/>
)}
{selectedTab === TabViews.Documents && (
<MediaSection
documents={documents}
i18n={i18n}
media={media}
saveAttachment={saveAttachment}
showLightboxWithMedia={showLightboxWithMedia}
type="documents"
/>
)}
</div>
)}
</Tabs>
</div>
);
2018-04-11 15:57:31 +00:00
}