diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1671605..f81fbce 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,13 +12,13 @@ jobs: lint: runs-on: ubuntu-latest steps: - - name: Checkout + - name: checkout uses: actions/checkout@v3 - - name: Setup + - name: setup uses: ./.github/actions/setup - - name: Lint files + - name: lint run: yarn lint - name: Typecheck files @@ -27,10 +27,10 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Checkout + - name: checkout uses: actions/checkout@v3 - - name: Setup + - name: setup uses: ./.github/actions/setup - name: Run unit tests @@ -39,11 +39,24 @@ jobs: build-library: runs-on: ubuntu-latest steps: - - name: Checkout + - name: checkout uses: actions/checkout@v3 - - name: Setup + - name: setup uses: ./.github/actions/setup - - name: Build package + - name: build package run: yarn prepare + + build-documentation: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: setup + uses: ./.github/actions/setup + + - name: build documentation + run: yarn docs docs:build diff --git a/example/src/gallery/GalleryExample.tsx b/example/src/gallery/GalleryExample.tsx index 273a686..551da7d 100644 --- a/example/src/gallery/GalleryExample.tsx +++ b/example/src/gallery/GalleryExample.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { View } from 'react-native'; -import { useSharedValue } from 'react-native-reanimated'; +import { useSharedValue, withTiming } from 'react-native-reanimated'; import { stackTransition, Gallery, @@ -8,12 +8,15 @@ import { } from 'react-native-zoom-toolkit'; import { getAssetsAsync, + MediaType, requestPermissionsAsync, type Asset, } from 'expo-media-library'; import GalleryImage from './GalleryImage'; import { StyleSheet } from 'react-native'; +import VideoControls from './controls/VideoControls'; +import GalleryVideo from './GalleryVideo'; type SizeVector = { width: number; height: number }; @@ -23,28 +26,54 @@ const GalleryExample = () => { const [assets, setAssets] = useState([]); const [scales, setScales] = useState([]); + const progress = useSharedValue(0); + const opacityControls = useSharedValue(0); const activeIndex = useSharedValue(0); + // This value is used to prevent the timer to keep updating the current position + // when the user is dragging the bar to a position of their desire + const isSeeking = useSharedValue(false); + const renderItem = useCallback( (item: Asset, index: number) => { + if (item.mediaType === MediaType.video) { + return ( + + ); + } + return ( ); }, - [activeIndex] + [activeIndex, progress, isSeeking] ); const keyExtractor = useCallback((item, index) => `${item.uri}-${index}`, []); - const customTransition = useCallback(stackTransition, []); + // Toogle video controls opacity if the current item is a video + const onTap = useCallback(() => { + const isVideo = assets[activeIndex.value]?.mediaType === MediaType.video; + if (!isVideo) return; + + const toValue = opacityControls.value > 0 ? 0 : 1; + opacityControls.value = withTiming(toValue); + }, [assets, activeIndex, opacityControls]); + useEffect(() => { const requestAssets = async () => { const { granted } = await requestPermissionsAsync(); if (granted) { const page = await getAssetsAsync({ first: 100, - mediaType: 'photo', + mediaType: ['photo', 'video'], sortBy: 'creationTime', }); @@ -76,8 +105,17 @@ const GalleryExample = () => { onIndexChange={(idx) => { activeIndex.value = idx; }} + onTap={onTap} customTransition={customTransition} /> + + ); }; diff --git a/example/src/gallery/GalleryImage.tsx b/example/src/gallery/GalleryImage.tsx index f1e55bc..724907b 100644 --- a/example/src/gallery/GalleryImage.tsx +++ b/example/src/gallery/GalleryImage.tsx @@ -8,7 +8,7 @@ import { } from 'react-native-reanimated'; import { type Asset } from 'expo-media-library'; -import { getAspectRatioSize } from 'react-native-zoom-toolkit'; +import { calculateItemSize } from './utils/utils'; type GalleryImageProps = { asset: Asset; @@ -24,21 +24,11 @@ const GalleryImage: React.FC = ({ const [downScale, setDownScale] = useState(true); const { width, height } = useWindowDimensions(); - const phoneRatio = width / height; - const pictureRatio = asset.width / asset.height; - let size = getAspectRatioSize({ - aspectRatio: pictureRatio, - width: height > width ? width : undefined, - height: height > width ? undefined : height, - }); - - if (pictureRatio > phoneRatio && phoneRatio > 1) { - size = getAspectRatioSize({ aspectRatio: pictureRatio, width }); - } - - if (pictureRatio < phoneRatio && phoneRatio < 1) { - size = getAspectRatioSize({ aspectRatio: pictureRatio, height }); - } + const size = calculateItemSize( + { width: asset.width, height: asset.height }, + { width, height }, + width / height + ); const wrapper = (active: number) => { if (index === active) setDownScale(false); diff --git a/example/src/gallery/GalleryVideo.tsx b/example/src/gallery/GalleryVideo.tsx new file mode 100644 index 0000000..9fe40a6 --- /dev/null +++ b/example/src/gallery/GalleryVideo.tsx @@ -0,0 +1,97 @@ +import React, { useEffect, useRef } from 'react'; +import { useWindowDimensions } from 'react-native'; +import { ResizeMode, Video, type AVPlaybackStatus } from 'expo-av'; +import type { Asset } from 'expo-media-library'; + +import { calculateItemSize } from './utils/utils'; +import { + listenToPauseVideoEvent, + listenToPlayVideoEvent, + listenToSeekVideoEvent, + listenToStopVideoEvent, +} from './utils/emitter'; +import { type SharedValue } from 'react-native-reanimated'; + +type GalleryVideoProps = { + asset: Asset; + index: number; + isLooping: boolean; + isSeeking: SharedValue; + progress: SharedValue; +}; + +const GalleryVideo: React.FC = ({ + asset, + index, + isLooping, + isSeeking, + progress, +}) => { + const videoRef = useRef