diff --git a/examples/SampleApp/ios/Podfile.lock b/examples/SampleApp/ios/Podfile.lock index 61fc07eb9..48003e3ab 100644 --- a/examples/SampleApp/ios/Podfile.lock +++ b/examples/SampleApp/ios/Podfile.lock @@ -3584,7 +3584,7 @@ SPEC CHECKSUMS: op-sqlite: a7e46cfdaebeef219fd0e939332967af9fe6d406 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 - RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 + RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f RCTDeprecation: 300c5eb91114d4339b0bb39505d0f4824d7299b7 RCTRequired: e0446b01093475b7082fbeee5d1ef4ad1fe20ac4 RCTTypeSafety: cb974efcdc6695deedf7bf1eb942f2a0603a063f diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx index 93a4a100d..bd88cac06 100644 --- a/package/src/components/Channel/Channel.tsx +++ b/package/src/components/Channel/Channel.tsx @@ -179,6 +179,7 @@ import { AttachmentUploadProgressIndicator as AttachmentUploadProgressIndicatorD import { AudioAttachmentUploadPreview as AudioAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview'; import { FileAttachmentUploadPreview as FileAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/FileAttachmentUploadPreview'; import { ImageAttachmentUploadPreview as ImageAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview'; +import { VideoAttachmentUploadPreview as VideoAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview'; import { AudioRecorder as AudioRecorderDefault } from '../MessageInput/components/AudioRecorder/AudioRecorder'; import { AudioRecordingButton as AudioRecordingButtonDefault } from '../MessageInput/components/AudioRecorder/AudioRecordingButton'; import { AudioRecordingInProgress as AudioRecordingInProgressDefault } from '../MessageInput/components/AudioRecorder/AudioRecordingInProgress'; @@ -758,7 +759,7 @@ const ChannelWithContext = (props: PropsWithChildren) = UnreadMessagesNotification = UnreadMessagesNotificationDefault, AttachmentUploadProgressIndicator = AttachmentUploadProgressIndicatorDefault, UrlPreview = CardDefault, - VideoAttachmentUploadPreview = FileAttachmentUploadPreviewDefault, + VideoAttachmentUploadPreview = VideoAttachmentUploadPreviewDefault, VideoThumbnail = VideoThumbnailDefault, isOnline, maximumMessageLimit, diff --git a/package/src/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.tsx b/package/src/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.tsx new file mode 100644 index 000000000..643e9fbb2 --- /dev/null +++ b/package/src/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.tsx @@ -0,0 +1,91 @@ +import React, { useMemo } from 'react'; + +import { StyleSheet, Text, View } from 'react-native'; + +import { LocalImageAttachment, LocalVideoAttachment } from 'stream-chat'; + +import { FileAttachmentUploadPreview } from './FileAttachmentUploadPreview'; +import { ImageAttachmentUploadPreview } from './ImageAttachmentUploadPreview'; + +import { useTheme } from '../../../../contexts/themeContext/ThemeContext'; +import { Recorder } from '../../../../icons'; +import { primitives } from '../../../../theme'; +import { UploadAttachmentPreviewProps } from '../../../../types/types'; +import { formatMsToMinSec } from '../../../../utils/utils'; + +export type VideoAttachmentUploadPreviewProps> = + UploadAttachmentPreviewProps>; + +export const VideoAttachmentUploadPreview = ({ + attachment, + handleRetry, + removeAttachments, +}: VideoAttachmentUploadPreviewProps) => { + const styles = useStyles(); + + const durationLabel = useMemo( + () => (attachment.duration ? formatMsToMinSec(attachment.duration) : undefined), + [attachment.duration], + ); + + return attachment.localMetadata.previewUri ? ( + <> + + {durationLabel ? ( + + + {durationLabel} + + ) : null} + + ) : ( + + ); +}; + +const useStyles = () => { + const { + theme: { + semantics, + messageInput: { + videoAttachmentUploadPreview: { durationContainer, durationText }, + }, + }, + } = useTheme(); + + const { badgeBgInverse, badgeText } = semantics; + + return useMemo( + () => + StyleSheet.create({ + durationContainer: { + position: 'absolute', + left: 8, + bottom: 8, + borderRadius: primitives.radiusMax, + backgroundColor: badgeBgInverse, + paddingVertical: primitives.spacingXxs, + paddingHorizontal: primitives.spacingXs, + flexDirection: 'row', + alignItems: 'center', + ...durationContainer, + }, + durationText: { + fontSize: primitives.typographyFontSizeXxs, + fontWeight: primitives.typographyFontWeightBold, + color: badgeText, + marginLeft: primitives.spacingXxs, + ...durationText, + }, + }), + [badgeBgInverse, badgeText, durationContainer, durationText], + ); +}; diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index 89a322028..5bcb403e3 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -39,6 +39,7 @@ import type { AttachmentUploadProgressIndicatorProps } from '../../components/Me import { AudioAttachmentUploadPreviewProps } from '../../components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview'; import { FileAttachmentUploadPreviewProps } from '../../components/MessageInput/components/AttachmentPreview/FileAttachmentUploadPreview'; import { ImageAttachmentUploadPreviewProps } from '../../components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview'; +import { VideoAttachmentUploadPreviewProps } from '../../components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview'; import type { AudioRecorderProps } from '../../components/MessageInput/components/AudioRecorder/AudioRecorder'; import type { AudioRecordingButtonProps } from '../../components/MessageInput/components/AudioRecorder/AudioRecordingButton'; import type { AudioRecordingInProgressProps } from '../../components/MessageInput/components/AudioRecorder/AudioRecordingInProgress'; @@ -251,7 +252,7 @@ export type InputMessageInputContextValue = { AudioAttachmentUploadPreview: React.ComponentType; ImageAttachmentUploadPreview: React.ComponentType; FileAttachmentUploadPreview: React.ComponentType; - VideoAttachmentUploadPreview: React.ComponentType; + VideoAttachmentUploadPreview: React.ComponentType; /** * Custom UI component to display the remaining cooldown a user will have to wait before diff --git a/package/src/contexts/themeContext/utils/theme.ts b/package/src/contexts/themeContext/utils/theme.ts index bf6467abd..5c1ba99a3 100644 --- a/package/src/contexts/themeContext/utils/theme.ts +++ b/package/src/contexts/themeContext/utils/theme.ts @@ -411,10 +411,8 @@ export type Theme = { overlay: ViewStyle; }; videoAttachmentUploadPreview: { - recorderIconContainer: ViewStyle; - recorderIcon: IconProps; - itemContainer: ViewStyle; - upload: ImageStyle; + durationContainer: ViewStyle; + durationText: TextStyle; }; wrapper: ViewStyle; linkPreviewList: { @@ -1160,6 +1158,10 @@ export const defaultTheme: Theme = { upload: {}, wrapper: {}, }, + videoAttachmentUploadPreview: { + durationContainer: {}, + durationText: {}, + }, inputBox: {}, inputBoxContainer: {}, inputBoxWrapper: {}, @@ -1222,12 +1224,6 @@ export const defaultTheme: Theme = { indicatorColor: '', overlay: {}, }, - videoAttachmentUploadPreview: { - itemContainer: {}, - recorderIcon: {}, - recorderIconContainer: {}, - upload: {}, - }, wrapper: {}, linkPreviewList: { linkContainer: {}, diff --git a/package/src/utils/utils.ts b/package/src/utils/utils.ts index 22a0d6766..99b9f054b 100644 --- a/package/src/utils/utils.ts +++ b/package/src/utils/utils.ts @@ -234,6 +234,17 @@ export const getDurationLabelFromDuration = (duration: number) => { return durationLabel; }; +export const formatMsToMinSec = (ms: number) => { + const totalSeconds = Math.max(0, Math.floor(ms / 1000)); + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + + const mm = minutes; // no padding for minutes + const ss = String(seconds).padStart(2, '0'); + + return `${mm}m ${ss}s`.replace(/^0m\s/, ''); +}; + /** * Utility to escape special characters in a string. * @param text