210 lines
5.6 KiB
TypeScript
210 lines
5.6 KiB
TypeScript
![]() |
/**
|
|||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|||
|
*
|
|||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
* you may not use this file except in compliance with the License.
|
|||
|
* You may obtain a copy of the License at
|
|||
|
*
|
|||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
*
|
|||
|
* Unless required by applicable law or agreed to in writing, software
|
|||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
* See the License for the specific language governing permissions and
|
|||
|
* limitations under the License.
|
|||
|
*/
|
|||
|
import {DemoVideoGalleryQuery} from '@/common/components/gallery/__generated__/DemoVideoGalleryQuery.graphql';
|
|||
|
import VideoGalleryUploadVideo from '@/common/components/gallery/VideoGalleryUploadPhoto';
|
|||
|
import VideoPhoto from '@/common/components/gallery/VideoPhoto';
|
|||
|
import useScreenSize from '@/common/screen/useScreenSize';
|
|||
|
import {VideoData} from '@/demo/atoms';
|
|||
|
import {DEMO_SHORT_NAME} from '@/demo/DemoConfig';
|
|||
|
import {fontSize, fontWeight, spacing} from '@/theme/tokens.stylex';
|
|||
|
import stylex from '@stylexjs/stylex';
|
|||
|
import {useMemo} from 'react';
|
|||
|
import PhotoAlbum, {Photo, RenderPhotoProps} from 'react-photo-album';
|
|||
|
import {graphql, useLazyLoadQuery} from 'react-relay';
|
|||
|
import {useLocation, useNavigate} from 'react-router-dom';
|
|||
|
|
|||
|
const styles = stylex.create({
|
|||
|
container: {
|
|||
|
display: 'flex',
|
|||
|
flexDirection: 'column',
|
|||
|
marginHorizontal: spacing[1],
|
|||
|
height: '100%',
|
|||
|
lineHeight: 1.2,
|
|||
|
paddingTop: spacing[8],
|
|||
|
},
|
|||
|
headerContainer: {
|
|||
|
marginBottom: spacing[8],
|
|||
|
fontWeight: fontWeight['medium'],
|
|||
|
fontSize: fontSize['2xl'],
|
|||
|
'@media screen and (max-width: 768px)': {
|
|||
|
marginTop: spacing[0],
|
|||
|
marginBottom: spacing[8],
|
|||
|
marginHorizontal: spacing[4],
|
|||
|
fontSize: fontSize['xl'],
|
|||
|
},
|
|||
|
},
|
|||
|
albumContainer: {
|
|||
|
flex: '1 1 0%',
|
|||
|
width: '100%',
|
|||
|
overflowY: 'auto',
|
|||
|
},
|
|||
|
});
|
|||
|
|
|||
|
type Props = {
|
|||
|
showUploadInGallery?: boolean;
|
|||
|
onSelect?: (video: VideoPhotoData) => void;
|
|||
|
onUpload: (video: VideoData) => void;
|
|||
|
onUploadStart?: () => void;
|
|||
|
onUploadError?: (error: Error) => void;
|
|||
|
};
|
|||
|
|
|||
|
type VideoPhotoData = Photo &
|
|||
|
VideoData & {
|
|||
|
poster: string;
|
|||
|
isUploadOption: boolean;
|
|||
|
};
|
|||
|
|
|||
|
export default function DemoVideoGallery({
|
|||
|
showUploadInGallery = false,
|
|||
|
onSelect,
|
|||
|
onUpload,
|
|||
|
onUploadStart,
|
|||
|
onUploadError,
|
|||
|
}: Props) {
|
|||
|
const navigate = useNavigate();
|
|||
|
const location = useLocation();
|
|||
|
const {isMobile: isMobileScreenSize} = useScreenSize();
|
|||
|
|
|||
|
const data = useLazyLoadQuery<DemoVideoGalleryQuery>(
|
|||
|
graphql`
|
|||
|
query DemoVideoGalleryQuery {
|
|||
|
videos {
|
|||
|
edges {
|
|||
|
node {
|
|||
|
id
|
|||
|
path
|
|||
|
posterPath
|
|||
|
url
|
|||
|
posterUrl
|
|||
|
height
|
|||
|
width
|
|||
|
posterUrl
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
`,
|
|||
|
{},
|
|||
|
);
|
|||
|
|
|||
|
const allVideos: VideoPhotoData[] = useMemo(() => {
|
|||
|
return data.videos.edges.map(video => {
|
|||
|
return {
|
|||
|
src: video.node.url,
|
|||
|
path: video.node.path,
|
|||
|
poster: video.node.posterPath,
|
|||
|
posterPath: video.node.posterPath,
|
|||
|
url: video.node.url,
|
|||
|
posterUrl: video.node.posterUrl,
|
|||
|
width: video.node.width,
|
|||
|
height: video.node.height,
|
|||
|
isUploadOption: false,
|
|||
|
} as VideoPhotoData;
|
|||
|
});
|
|||
|
}, [data.videos.edges]);
|
|||
|
|
|||
|
const shareableVideos: VideoPhotoData[] = useMemo(() => {
|
|||
|
const filteredVideos = [...allVideos];
|
|||
|
|
|||
|
if (showUploadInGallery) {
|
|||
|
const uploadOption = {
|
|||
|
src: '',
|
|||
|
width: 1280,
|
|||
|
height: 720,
|
|||
|
poster: '',
|
|||
|
isUploadOption: true,
|
|||
|
} as VideoPhotoData;
|
|||
|
filteredVideos.unshift(uploadOption);
|
|||
|
}
|
|||
|
|
|||
|
return filteredVideos;
|
|||
|
}, [allVideos, showUploadInGallery]);
|
|||
|
|
|||
|
const renderPhoto = ({
|
|||
|
photo: video,
|
|||
|
imageProps,
|
|||
|
}: RenderPhotoProps<VideoPhotoData>) => {
|
|||
|
const {style} = imageProps;
|
|||
|
const {url, posterUrl} = video;
|
|||
|
|
|||
|
return video.isUploadOption ? (
|
|||
|
<VideoGalleryUploadVideo
|
|||
|
style={style}
|
|||
|
onUpload={handleUploadVideo}
|
|||
|
onUploadError={onUploadError}
|
|||
|
onUploadStart={onUploadStart}
|
|||
|
/>
|
|||
|
) : (
|
|||
|
<VideoPhoto
|
|||
|
src={url}
|
|||
|
poster={posterUrl}
|
|||
|
style={style}
|
|||
|
onClick={() => {
|
|||
|
navigate(location.pathname, {
|
|||
|
state: {
|
|||
|
video,
|
|||
|
},
|
|||
|
});
|
|||
|
onSelect?.(video);
|
|||
|
}}
|
|||
|
/>
|
|||
|
);
|
|||
|
};
|
|||
|
|
|||
|
function handleUploadVideo(video: VideoData) {
|
|||
|
navigate(location.pathname, {
|
|||
|
state: {
|
|||
|
video,
|
|||
|
},
|
|||
|
});
|
|||
|
onUpload?.(video);
|
|||
|
}
|
|||
|
|
|||
|
const descriptionStyle = 'text-sm md:text-base text-gray-400 leading-snug';
|
|||
|
|
|||
|
return (
|
|||
|
<div {...stylex.props(styles.container)}>
|
|||
|
<div {...stylex.props(styles.albumContainer)}>
|
|||
|
<div className="pt-0 md:px-16 md:pt-8 md:pb-8">
|
|||
|
<div {...stylex.props(styles.headerContainer)}>
|
|||
|
<h3 className="mb-2">
|
|||
|
Select a video to try{' '}
|
|||
|
<span className="hidden md:inline">
|
|||
|
with the {DEMO_SHORT_NAME}
|
|||
|
</span>
|
|||
|
</h3>
|
|||
|
<p className={descriptionStyle}>
|
|||
|
You’ll be able to download what you make.
|
|||
|
</p>
|
|||
|
</div>
|
|||
|
|
|||
|
<PhotoAlbum<VideoPhotoData>
|
|||
|
layout="rows"
|
|||
|
photos={shareableVideos}
|
|||
|
targetRowHeight={isMobileScreenSize ? 120 : 200}
|
|||
|
rowConstraints={{
|
|||
|
singleRowMaxHeight: isMobileScreenSize ? 120 : 240,
|
|||
|
maxPhotos: 3,
|
|||
|
}}
|
|||
|
renderPhoto={renderPhoto}
|
|||
|
spacing={4}
|
|||
|
/>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
);
|
|||
|
}
|