Skip to content

Commit

Permalink
fix: quote image gallery (#31415)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliajforesti committed Jan 15, 2024
1 parent afd5fdd commit e3252f5
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/smooth-kangaroos-mate.md
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

fix: quote image gallery
15 changes: 3 additions & 12 deletions apps/meteor/client/components/ImageGallery/ImageGallery.tsx
@@ -1,3 +1,4 @@
import type { IUpload } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Box, ButtonGroup, IconButton, Palette, Throbber } from '@rocket.chat/fuselage';
import React, { useRef, useState } from 'react';
Expand All @@ -14,8 +15,6 @@ import 'swiper/modules/keyboard/keyboard.min.css';
import 'swiper/modules/zoom/zoom.min.css';

import { usePreventPropagation } from '../../hooks/usePreventPropagation';
import ImageGalleryLoader from './ImageGalleryLoader';
import { useImageGallery } from './hooks/useImageGallery';

const swiperStyle = css`
.swiper {
Expand Down Expand Up @@ -107,7 +106,7 @@ const swiperStyle = css`
}
`;

const ImageGallery = () => {
export const ImageGallery = ({ images, onClose, loadMore }: { images: IUpload[]; onClose: () => void; loadMore?: () => void }) => {
const swiperRef = useRef<SwiperRef>(null);
const [, setSwiperInst] = useState<SwiperClass>();
const [zoomScale, setZoomScale] = useState(1);
Expand All @@ -126,18 +125,12 @@ const ImageGallery = () => {

const preventPropagation = usePreventPropagation();

const { isLoading, loadMore, images, onClose } = useImageGallery();

if (isLoading) {
return <ImageGalleryLoader onClose={onClose} />;
}

return createPortal(
<FocusScope contain restoreFocus autoFocus>
<Box className={swiperStyle}>
<div className='swiper-container' onClick={onClose}>
<ButtonGroup className='rcx-swiper-controls' onClick={preventPropagation}>
{zoomScale !== 1 && <IconButton icon='arrow-collapse' title='Resize' rcx-swiper-zoom-out onClick={handleResize} />}
{zoomScale !== 1 && <IconButton small icon='arrow-collapse' title='Resize' rcx-swiper-zoom-out onClick={handleResize} />}
<IconButton small icon='h-bar' title='Zoom out' rcx-swiper-zoom-out onClick={handleZoomOut} disabled={zoomScale === 1} />
<IconButton small icon='plus' title='Zoom in' rcx-swiper-zoom-in onClick={handleZoomIn} />
<IconButton small icon='cross' title='Close' aria-label='Close gallery' className='rcx-swiper-close-button' onClick={onClose} />
Expand Down Expand Up @@ -176,5 +169,3 @@ const ImageGallery = () => {
document.body,
);
};

export default ImageGallery;
26 changes: 26 additions & 0 deletions apps/meteor/client/components/ImageGallery/ImageGalleryError.tsx
@@ -0,0 +1,26 @@
import { css } from '@rocket.chat/css-in-js';
import { IconButton, ModalBackdrop } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';
import { createPortal } from 'react-dom';

import GenericError from '../GenericError/GenericError';

const closeButtonStyle = css`
position: absolute;
z-index: 10;
top: 10px;
right: 10px;
`;

export const ImageGalleryError = ({ onClose }: { onClose: () => void }) => {
const t = useTranslation();

return createPortal(
<ModalBackdrop display='flex' justifyContent='center' color='pure-white'>
<GenericError buttonAction={onClose} buttonTitle={t('Close')} />
<IconButton icon='cross' aria-label='Close gallery' className={closeButtonStyle} onClick={onClose} />
</ModalBackdrop>,
document.body,
);
};
Expand Up @@ -10,13 +10,11 @@ const closeButtonStyle = css`
right: 10px;
`;

const ImageGalleryLoader = ({ onClose }: { onClose: () => void }) =>
export const ImageGalleryLoading = ({ onClose }: { onClose: () => void }) =>
createPortal(
<ModalBackdrop display='flex' justifyContent='center' color='pure-white'>
<IconButton icon='cross' aria-label='Close gallery' className={closeButtonStyle} onClick={onClose} />
<Throbber inheritColor />
</ModalBackdrop>,
document.body,
);

export default ImageGalleryLoader;

This file was deleted.

4 changes: 3 additions & 1 deletion apps/meteor/client/components/ImageGallery/index.ts
@@ -1 +1,3 @@
export { default } from './ImageGallery';
export * from './ImageGallery';
export * from './ImageGalleryError';
export * from './ImageGalleryLoading';
Expand Up @@ -70,7 +70,7 @@ export const QuoteAttachment = ({ attachment }: QuoteAttachmentProps): ReactElem
{attachment.md ? <MessageContentBody md={attachment.md} /> : attachment.text.substring(attachment.text.indexOf('\n') + 1)}
{attachment.attachments && (
<AttachmentInner>
<Attachments attachments={attachment.attachments} />
<Attachments attachments={attachment.attachments} id={attachment.attachments[0]?.title_link} />
</AttachmentInner>
)}
</AttachmentDetails>
Expand Down
34 changes: 20 additions & 14 deletions apps/meteor/client/providers/ImageGalleryProvider.tsx
@@ -1,42 +1,48 @@
import React, { type ReactNode, useEffect, useState } from 'react';

import ImageGallery from '../components/ImageGallery/ImageGallery';
import { ImageGallery } from '../components/ImageGallery';
import { ImageGalleryContext } from '../contexts/ImageGalleryContext';
import ImageGalleryData from '../views/room/ImageGallery/ImageGalleryData';

type ImageGalleryProviderProps = {
children: ReactNode;
};

const ImageGalleryProvider = ({ children }: ImageGalleryProviderProps) => {
const [imageId, setImageId] = useState<string>();
const [quotedImageUrl, setQuotedImageUrl] = useState<string>();

useEffect(() => {
document.addEventListener('click', (event: Event) => {
const handleImageClick = (event: Event) => {
const target = event?.target as HTMLElement | null;

if (target?.closest('.rcx-attachment__details')) {
return setQuotedImageUrl(target.dataset.id);
}
if (target?.classList.contains('gallery-item')) {
return setImageId(target.dataset.id || target?.parentElement?.parentElement?.dataset.id);
const id = target.closest('.gallery-item-container')?.getAttribute('data-id') || undefined;
return setImageId(target.dataset.id || id);
}

if (target?.classList.contains('gallery-item-container')) {
return setImageId(target.dataset.id);
}
if (
target?.classList.contains('gallery-item') &&
target?.parentElement?.parentElement?.classList.contains('gallery-item-container')
) {
return setImageId(target.dataset.id || target?.parentElement?.parentElement?.dataset.id);
if (target?.classList.contains('rcx-avatar__element') && target.closest('.gallery-item')) {
const avatarTarget = target.closest('.gallery-item-container')?.getAttribute('data-id') || undefined;
return setImageId(avatarTarget);
}
};
document.addEventListener('click', handleImageClick);

if (target?.classList.contains('rcx-avatar__element') && target?.parentElement?.classList.contains('gallery-item')) {
return setImageId(target.dataset.id || target?.parentElement?.parentElement?.dataset.id);
}
});
return () => document.removeEventListener('click', handleImageClick);
}, []);

return (
<ImageGalleryContext.Provider value={{ imageId: imageId || '', isOpen: !!imageId, onClose: () => setImageId(undefined) }}>
{children}
{!!imageId && <ImageGallery />}
{!!quotedImageUrl && (
<ImageGallery images={[{ _id: quotedImageUrl, url: quotedImageUrl }]} onClose={() => setQuotedImageUrl(undefined)} />
)}
{!!imageId && <ImageGalleryData />}
</ImageGalleryContext.Provider>
);
};
Expand Down
28 changes: 28 additions & 0 deletions apps/meteor/client/views/room/ImageGallery/ImageGalleryData.tsx
@@ -0,0 +1,28 @@
import React, { useContext, useMemo } from 'react';

import { ImageGallery, ImageGalleryError, ImageGalleryLoading } from '../../../components/ImageGallery';
import { ImageGalleryContext } from '../../../contexts/ImageGalleryContext';
import { useRecordList } from '../../../hooks/lists/useRecordList';
import { useRoom } from '../contexts/RoomContext';
import { useImagesList } from './hooks/useImagesList';

const ImageGalleryData = () => {
const { _id: rid } = useRoom();

const { imageId, onClose } = useContext(ImageGalleryContext);

const { filesList, loadMoreItems } = useImagesList(useMemo(() => ({ roomId: rid, startingFromId: imageId }), [imageId, rid]));
const { phase, items: images, error } = useRecordList(filesList);

if (error) {
return <ImageGalleryError onClose={onClose} />;
}

if (phase === 'loading') {
return <ImageGalleryLoading onClose={onClose} />;
}

return <ImageGallery images={images} loadMore={() => loadMoreItems(images.length - 1)} onClose={onClose} />;
};

export default ImageGalleryData;
Expand Up @@ -2,9 +2,9 @@ import type { ChannelsImagesProps } from '@rocket.chat/rest-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useCallback, useEffect, useState } from 'react';

import { useScrollableRecordList } from '../../../hooks/lists/useScrollableRecordList';
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
import { ImagesList } from '../../../lib/lists/ImagesList';
import { useScrollableRecordList } from '../../../../hooks/lists/useScrollableRecordList';
import { useComponentDidUpdate } from '../../../../hooks/useComponentDidUpdate';
import { ImagesList } from '../../../../lib/lists/ImagesList';

export const useImagesList = (
options: ChannelsImagesProps,
Expand Down

0 comments on commit e3252f5

Please sign in to comment.