Page MenuHomePhabricator

D6863.diff
No OneTemporary

D6863.diff

diff --git a/web/modals/threads/gallery/thread-settings-media-gallery.css b/web/modals/threads/gallery/thread-settings-media-gallery.css
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/gallery/thread-settings-media-gallery.css
@@ -0,0 +1,24 @@
+div.container {
+ display: flex;
+ flex-wrap: wrap;
+ overflow-y: scroll;
+ justify-content: flex-start;
+ max-height: 700px;
+ padding: 10px;
+ margin-top: 10px;
+}
+
+div.mediaContainer {
+ flex: 0 1 31%;
+ width: 150px;
+ height: 200px;
+ margin: 5px;
+}
+
+img.media,
+video.media {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ cursor: pointer;
+}
diff --git a/web/modals/threads/gallery/thread-settings-media-gallery.react.js b/web/modals/threads/gallery/thread-settings-media-gallery.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/gallery/thread-settings-media-gallery.react.js
@@ -0,0 +1,115 @@
+// @flow
+
+import * as React from 'react';
+
+import { fetchThreadMedia } from 'lib/actions/thread-actions.js';
+import type { ThreadInfo } from 'lib/types/thread-types.js';
+import { useServerCall } from 'lib/utils/action-utils.js';
+
+import css from './thread-settings-media-gallery.css';
+import Tabs from '../../../components/tabs.react.js';
+import Modal from '../../modal.react.js';
+
+type MediaGalleryTab = 'All' | 'Images' | 'Videos';
+
+type ThreadSettingsMediaGalleryModalProps = {
+ +onClose: () => void,
+ +parentThreadInfo: ThreadInfo,
+ +limit: number,
+ +activeTab: MediaGalleryTab,
+};
+
+function ThreadSettingsMediaGalleryModal(
+ props: ThreadSettingsMediaGalleryModalProps,
+): React.Node {
+ const { onClose, parentThreadInfo, limit, activeTab } = props;
+ const { id: threadID } = parentThreadInfo;
+ const modalName = 'Media';
+
+ const callFetchThreadMedia = useServerCall(fetchThreadMedia);
+ const [mediaInfos, setMediaInfos] = React.useState([]);
+ const [tab, setTab] = React.useState<MediaGalleryTab>(activeTab);
+
+ React.useEffect(() => {
+ const fetchData = async () => {
+ const result = await callFetchThreadMedia({
+ threadID,
+ limit,
+ offset: 0,
+ });
+ setMediaInfos(result.media);
+ };
+ fetchData();
+ }, [callFetchThreadMedia, threadID, limit]);
+
+ const filteredMediaInfos = React.useMemo(() => {
+ if (tab === 'Images') {
+ return mediaInfos.filter(mediaInfo => mediaInfo.type === 'photo');
+ } else if (tab === 'Videos') {
+ return mediaInfos.filter(mediaInfo => mediaInfo.type === 'video');
+ }
+ return mediaInfos;
+ }, [tab, mediaInfos]);
+
+ const mediaCoverPhotos = React.useMemo(
+ () => filteredMediaInfos.map(media => media.thumbnailURI || media.uri),
+ [filteredMediaInfos],
+ );
+
+ const mediaGalleryItems = React.useMemo(
+ () =>
+ filteredMediaInfos.map((media, i) => (
+ <div key={i} className={css.mediaContainer}>
+ <img src={mediaCoverPhotos[i]} className={css.media} />
+ </div>
+ )),
+ [filteredMediaInfos, mediaCoverPhotos],
+ );
+
+ const handleScroll = React.useCallback(
+ async event => {
+ const container = event.target;
+ // Load more data when the user is within 1000 pixels of the end
+ const buffer = 1000;
+
+ if (
+ container.scrollHeight - container.scrollTop >
+ container.clientHeight + buffer
+ ) {
+ return;
+ }
+
+ const result = await callFetchThreadMedia({
+ threadID,
+ limit,
+ offset: mediaInfos.length,
+ });
+ setMediaInfos([...mediaInfos, ...result.media]);
+ },
+ [callFetchThreadMedia, threadID, limit, mediaInfos],
+ );
+
+ return (
+ <Modal name={modalName} onClose={onClose} size="large">
+ <Tabs.Container activeTab={tab} setTab={setTab}>
+ <Tabs.Item id="All" header="All">
+ <div className={css.container} onScroll={handleScroll}>
+ {mediaGalleryItems}
+ </div>
+ </Tabs.Item>
+ <Tabs.Item id="Images" header="Images">
+ <div className={css.container} onScroll={handleScroll}>
+ {mediaGalleryItems}
+ </div>
+ </Tabs.Item>
+ <Tabs.Item id="Videos" header="Videos">
+ <div className={css.container} onScroll={handleScroll}>
+ {mediaGalleryItems}
+ </div>
+ </Tabs.Item>
+ </Tabs.Container>
+ </Modal>
+ );
+}
+
+export default ThreadSettingsMediaGalleryModal;

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 24, 7:14 PM (19 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2577097
Default Alt Text
D6863.diff (4 KB)

Event Timeline