Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package/expo-package/src/optionalDependencies/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ if (videoPackage) {
}
// expo-av
else if (ExpoAVVideoComponent) {
Video = ({ onPlaybackStatusUpdate, paused, resizeMode, style, uri, videoRef }) => {
Video = ({ onPlaybackStatusUpdate, paused, resizeMode, style, uri, videoRef, rate }) => {
// This is done so that the audio of the video is not muted when the phone is in silent mode for iOS.
useEffect(() => {
const initializeSound = async () => {
Expand All @@ -107,6 +107,7 @@ else if (ExpoAVVideoComponent) {
uri,
}}
style={[style]}
playbackRate={rate}
/>
);
};
Expand Down
15 changes: 14 additions & 1 deletion package/native-package/src/optionalDependencies/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@ import React from 'react';
import AudioVideoPlayer from './AudioVideo';

export const Video = AudioVideoPlayer
? ({ onBuffer, onEnd, onLoad, onProgress, paused, repeat, resizeMode, style, uri, videoRef }) => (
? ({
onBuffer,
onEnd,
onLoad,
onProgress,
paused,
repeat,
resizeMode,
style,
uri,
videoRef,
rate,
}) => (
<AudioVideoPlayer
ignoreSilentSwitch={'ignore'}
onBuffer={onBuffer}
Expand All @@ -20,6 +32,7 @@ export const Video = AudioVideoPlayer
uri,
}}
style={style}
rate={rate}
/>
)
: null;
8 changes: 4 additions & 4 deletions package/src/components/Attachment/Audio/PlayPauseButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { useMemo } from 'react';
import { Pressable, PressableProps, StyleProp, StyleSheet, ViewStyle } from 'react-native';

import { useTheme } from '../../../contexts';
import { NewPause } from '../../../icons/NewPause';
import { NewPlay } from '../../../icons/NewPlay';
import { Pause } from '../../../icons/Pause';
import { Play } from '../../../icons/Play';
import { primitives } from '../../../theme';
import { buttonSizes } from '../../ui/Button/constants';

Expand Down Expand Up @@ -45,9 +45,9 @@ export const PlayPauseButton = ({
{...rest}
>
{isPlaying ? (
<NewPause fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
<Pause fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
) : (
<NewPlay fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
<Play fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
)}
</Pressable>
);
Expand Down
195 changes: 59 additions & 136 deletions package/src/components/ImageGallery/ImageGallery.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { Image, ImageStyle, StyleSheet, ViewStyle } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

Expand All @@ -11,24 +11,8 @@ import Animated, {
withTiming,
} from 'react-native-reanimated';

import BottomSheet from '@gorhom/bottom-sheet';

import { AnimatedGalleryImage } from './components/AnimatedGalleryImage';
import { AnimatedGalleryVideo } from './components/AnimatedGalleryVideo';
import {
ImageGalleryFooter,
ImageGalleryFooterCustomComponentProps,
} from './components/ImageGalleryFooter';
import {
ImageGalleryHeader,
ImageGalleryHeaderCustomComponentProps,
} from './components/ImageGalleryHeader';
import { ImageGalleryOverlay } from './components/ImageGalleryOverlay';
import { ImageGalleryGridImageComponents, ImageGrid } from './components/ImageGrid';
import {
ImageGalleryGridHandleCustomComponentProps,
ImageGridHandle,
} from './components/ImageGridHandle';

import { useImageGalleryGestures } from './hooks/useImageGalleryGestures';

Expand All @@ -43,9 +27,21 @@ import {
import { useTheme } from '../../contexts/themeContext/ThemeContext';
import { useStateStore } from '../../hooks';
import { useViewport } from '../../hooks/useViewport';
import { IconProps } from '../../icons/utils/base';
import { ImageGalleryState } from '../../state-store/image-gallery-state-store';
import { FileTypes } from '../../types/types';
import { dismissKeyboard } from '../KeyboardCompatibleView/KeyboardControllerAvoidingView';
import { BottomSheetModal } from '../UIComponents';

export type ImageGalleryActionHandler = () => Promise<void> | void;

export type ImageGalleryActionItem = {
action: ImageGalleryActionHandler;
Icon: React.ComponentType<IconProps>;
id: 'showInChat' | 'save' | 'reply' | 'delete' | string;
label: string;
type: 'destructive' | 'standard';
};

const MARGIN = 32;

Expand All @@ -60,68 +56,31 @@ export enum IsSwiping {
FALSE,
}

export type ImageGalleryCustomComponents = {
/**
* Override props for following UI components, which are part of [image gallery](https://github.com/GetStream/stream-chat-react-native/wiki/Cookbook-v3.0#gallery-components).
*
* - [ImageGalleryFooter](#ImageGalleryFooter)
*
* - [ImageGrid](#ImageGrid)
*
* - [ImageGridHandle](#ImageGridHandle)
*
* - [ImageGalleryHeader](#ImageGalleryHeader)
*
* e.g.,
*
* ```js
* {
* footer: {
* ShareIcon: CustomShareIconComponent
* },
* grid: {
* avatarComponent: CustomAvatarComponent
* },
* gridHandle: {
* centerComponent: CustomCenterComponent
* },
* header: {
* CloseIcon: CustomCloseButtonComponent
* },
* }
* ```
* @overrideType object
*/
imageGalleryCustomComponents?: {
footer?: ImageGalleryFooterCustomComponentProps;
grid?: ImageGalleryGridImageComponents;
gridHandle?: ImageGalleryGridHandleCustomComponentProps;
header?: ImageGalleryHeaderCustomComponentProps;
};
};

const imageGallerySelector = (state: ImageGalleryState) => ({
assets: state.assets,
currentIndex: state.currentIndex,
});

type ImageGalleryWithContextProps = Pick<
ImageGalleryProviderProps,
| 'imageGalleryCustomComponents'
| 'imageGalleryGridSnapPoints'
| 'imageGalleryGridHandleHeight'
| 'numberOfImageGalleryGridColumns'
| 'ImageGalleryHeader'
| 'ImageGalleryFooter'
| 'ImageGalleryVideoControls'
| 'ImageGalleryGrid'
> &
Pick<OverlayContextValue, 'overlayOpacity'>;

export const ImageGalleryWithContext = (props: ImageGalleryWithContextProps) => {
const {
imageGalleryGridHandleHeight,
imageGalleryGridSnapPoints,
imageGalleryCustomComponents,
numberOfImageGalleryGridColumns,
overlayOpacity,
ImageGalleryHeader,
ImageGalleryFooter,
ImageGalleryVideoControls,
ImageGalleryGrid,
} = props;
const [isGridViewVisible, setIsGridViewVisible] = useState(false);
const {
theme: {
colors: { white_snow },
Expand All @@ -143,22 +102,6 @@ export const ImageGalleryWithContext = (props: ImageGalleryWithContextProps) =>

const halfScreenHeight = fullWindowHeight / 2;
const quarterScreenHeight = fullWindowHeight / 4;
const snapPoints = React.useMemo(
() => [(fullWindowHeight * 3) / 4, fullWindowHeight - (imageGalleryGridHandleHeight ?? 0)],
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
);

/**
* BottomSheetModal ref
*/
const bottomSheetModalRef = useRef<BottomSheet>(null);

/**
* BottomSheetModal state
*/
const [currentBottomSheetIndex, setCurrentBottomSheetIndex] = useState(0);
const animatedBottomSheetIndex = useSharedValue(0);

/**
* Fade animation for screen, it is always rendered with pointerEvents
Expand Down Expand Up @@ -322,30 +265,16 @@ export const ImageGalleryWithContext = (props: ImageGalleryWithContextProps) =>
* Functions toclose BottomSheetModal with image grid
*/
const closeGridView = () => {
if (bottomSheetModalRef.current?.close) {
bottomSheetModalRef.current.close();
}
setIsGridViewVisible(false);
};

/**
* Function to open BottomSheetModal with image grid
*/
const openGridView = () => {
if (bottomSheetModalRef.current?.snapToIndex) {
bottomSheetModalRef.current.snapToIndex(0);
}
setIsGridViewVisible(true);
};

const MemoizedImageGridHandle = useCallback(
() => (
<ImageGridHandle
closeGridView={closeGridView}
{...imageGalleryCustomComponents?.gridHandle}
/>
),
[imageGalleryCustomComponents?.gridHandle],
);

return (
<Animated.View
accessibilityLabel='Image Gallery'
Expand Down Expand Up @@ -403,42 +332,34 @@ export const ImageGalleryWithContext = (props: ImageGalleryWithContextProps) =>
</Animated.View>
</Animated.View>
</GestureDetector>
<ImageGalleryHeader
opacity={headerFooterOpacity}
visible={headerFooterVisible}
{...imageGalleryCustomComponents?.header}
/>

<ImageGalleryFooter
accessibilityLabel={'Image Gallery Footer'}
opacity={headerFooterOpacity}
openGridView={openGridView}
visible={headerFooterVisible}
{...imageGalleryCustomComponents?.footer}
/>

<ImageGalleryOverlay
animatedBottomSheetIndex={animatedBottomSheetIndex}
closeGridView={closeGridView}
currentBottomSheetIndex={currentBottomSheetIndex}
/>
<BottomSheet
animatedIndex={animatedBottomSheetIndex}
enablePanDownToClose={true}
handleComponent={MemoizedImageGridHandle}
// @ts-ignore
handleHeight={imageGalleryGridHandleHeight}
index={-1}
onChange={(index: number) => setCurrentBottomSheetIndex(index)}
ref={bottomSheetModalRef}
snapPoints={imageGalleryGridSnapPoints || snapPoints}
>
<ImageGrid
closeGridView={closeGridView}
numberOfImageGalleryGridColumns={numberOfImageGalleryGridColumns}
{...imageGalleryCustomComponents?.grid}
{ImageGalleryHeader ? (
<ImageGalleryHeader opacity={headerFooterOpacity} visible={headerFooterVisible} />
) : null}

{ImageGalleryFooter ? (
<ImageGalleryFooter
accessibilityLabel={'Image Gallery Footer'}
opacity={headerFooterOpacity}
openGridView={openGridView}
visible={headerFooterVisible}
ImageGalleryVideoControls={ImageGalleryVideoControls}
/>
</BottomSheet>
) : null}

<BottomSheetModal
height={350}
onClose={() => {
setIsGridViewVisible(false);
}}
visible={isGridViewVisible}
>
{ImageGalleryGrid ? (
<ImageGalleryGrid
closeGridView={closeGridView}
numberOfImageGalleryGridColumns={numberOfImageGalleryGridColumns}
/>
) : null}
</BottomSheetModal>
</Animated.View>
);
};
Expand All @@ -447,19 +368,21 @@ export type ImageGalleryProps = Partial<ImageGalleryWithContextProps>;

export const ImageGallery = (props: ImageGalleryProps) => {
const {
imageGalleryCustomComponents,
imageGalleryGridHandleHeight,
imageGalleryGridSnapPoints,
numberOfImageGalleryGridColumns,
ImageGalleryHeader,
ImageGalleryFooter,
ImageGalleryVideoControls,
ImageGalleryGrid,
} = useImageGalleryContext();
const { overlayOpacity } = useOverlayContext();
return (
<ImageGalleryWithContext
imageGalleryCustomComponents={imageGalleryCustomComponents}
imageGalleryGridHandleHeight={imageGalleryGridHandleHeight}
imageGalleryGridSnapPoints={imageGalleryGridSnapPoints}
numberOfImageGalleryGridColumns={numberOfImageGalleryGridColumns}
overlayOpacity={overlayOpacity}
ImageGalleryHeader={ImageGalleryHeader}
ImageGalleryFooter={ImageGalleryFooter}
ImageGalleryVideoControls={ImageGalleryVideoControls}
ImageGalleryGrid={ImageGalleryGrid}
{...props}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import duration from 'dayjs/plugin/duration';

import { LocalMessage } from 'stream-chat';

import { ImageGalleryFooter as ImageGalleryFooterDefault } from '../../../components/ImageGallery/components/ImageGalleryFooter';
import { ImageGalleryHeader as ImageGalleryHeaderDefault } from '../../../components/ImageGallery/components/ImageGalleryHeader';
import { ImageGalleryVideoControl as ImageGalleryVideoControlDefault } from '../../../components/ImageGallery/components/ImageGalleryVideoControl';
import { ImageGalleryGrid as ImageGalleryGridDefault } from '../../../components/ImageGallery/components/ImageGrid';
import {
ImageGalleryContext,
ImageGalleryContextValue,
Expand Down Expand Up @@ -58,7 +62,15 @@ const ImageGalleryComponent = (props: ImageGalleryProps & { message: LocalMessag
return (
<OverlayProvider value={{ overlayOpacity: { value: 1 } as SharedValue<number> }}>
<ImageGalleryContext.Provider
value={{ imageGalleryStateStore } as unknown as ImageGalleryContextValue}
value={
{
imageGalleryStateStore,
ImageGalleryHeader: ImageGalleryHeaderDefault,
ImageGalleryFooter: ImageGalleryFooterDefault,
ImageGalleryVideoControls: ImageGalleryVideoControlDefault,
ImageGalleryGrid: ImageGalleryGridDefault,
} as unknown as ImageGalleryContextValue
}
>
<ImageGallery {...props} />
</ImageGalleryContext.Provider>
Expand Down
Loading
Loading