Skip to content

Commit

Permalink
Add Link Payloads in feed composer (#189)
Browse files Browse the repository at this point in the history
* fix image postioning

* allow link preview uploads

* memoised to avoid multiple re renders

* update deps

* websockets upgradation to get event updates from all channel and post drives

* fix too much re rendering

* cleanups

* convert to 1 hour

* cleanups

* remove logs

* final

* fix bad audio cache fetching

* conversation page animated list on new conversation
  • Loading branch information
2002Bishwajeet authored Sep 11, 2024
1 parent eeade72 commit 63e9b7c
Show file tree
Hide file tree
Showing 17 changed files with 294 additions and 155 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build:libs": "npm run build --w packages/react-native-gifted-chat"
},
"dependencies": {
"@homebase-id/js-lib": "0.0.4-alpha.22",
"@homebase-id/js-lib": "0.0.4-alpha.23",
"axios": "1.7.5",
"patch-package": "8.0.0",
"react": "18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1681,7 +1681,7 @@ SPEC CHECKSUMS:
RNSVG: 50cf2c7018e57cf5d3522d98d0a3a4dd6bf9d093
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9
Yoga: d17d2cc8105eed528474683b42e2ea310e1daf61
Yoga: 805bf71192903b20fc14babe48080582fee65a80
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c

PODFILE CHECKSUM: f32ae1d81504ba80ec01963a7150115ccbbf36fb
Expand Down
29 changes: 25 additions & 4 deletions packages/mobile/src/components/Chat/Link-Preview-Bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useLinkPreview } from '../../hooks/links/useLinkPreview';

import { Text } from '../ui/Text/Text';
import { memo, useCallback, useEffect, useState } from 'react';
import { View } from 'react-native';
import { ActivityIndicator, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { Close } from '../ui/Icons/icons';
import { LinkPreview } from '@homebase-id/js-lib/media';
Expand All @@ -17,7 +17,7 @@ export type LinkPreviewProps = {
export const LinkPreviewBar = memo(
({ textToSearchIn, onDismiss, onLinkData }: LinkPreviewProps) => {
const link = textToSearchIn.match(/(https?:\/\/[^\s]+)/g)?.[0];
const { data } = useLinkPreview(link).get;
const { data, isLoading } = useLinkPreview(link).get;
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
Expand All @@ -36,10 +36,31 @@ export const LinkPreviewBar = memo(
onDismiss?.();
}, [onDismiss]);

if (isLoading) {
return (
<View
style={{
display: 'flex',
flexDirection: 'row',
borderWidth: 1,
borderColor: 'gray',
borderRadius: 15,
justifyContent: 'center',
padding: 8,
marginLeft: 8,
marginRight: 8,
marginBottom: 4,
position: 'relative',
}}
>
<ActivityIndicator size="small" color="gray" />
</View>
);
}

if (!isVisible || !data) {
return null;
}

const { title, description, imageUrl } = data;
return (
<Animated.View
Expand All @@ -53,7 +74,7 @@ export const LinkPreviewBar = memo(
source={{ uri: imageUrl }}
style={{
width: 50,
height: 50,
// height: 50,
}}
/>
)}
Expand Down
86 changes: 60 additions & 26 deletions packages/mobile/src/components/Chat/MediaMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Dimensions, GestureResponderEvent } from 'react-native';
import { memo } from 'react';
import { Dimensions, GestureResponderEvent, StyleProp, ViewStyle } from 'react-native';
import { memo, useCallback, useMemo } from 'react';

import { MessageImageProps } from 'react-native-gifted-chat';
import { ChatDrive } from '../../provider/chat/ConversationProvider';
Expand All @@ -18,49 +18,83 @@ const MediaMessage = memo(
props: MessageImageProps<ChatMessageIMessage>;
onLongPress: (e: GestureResponderEvent, message: ChatMessageIMessage) => void;
}) => {
const navigation = useNavigation<NavigationProp<ChatStackParamList>>();
const longPress = useCallback(
(e: GestureResponderEvent, message: ChatMessageIMessage) => onLongPress?.(e, message),
[onLongPress]
);
if (!props.currentMessage || !props.currentMessage.fileMetadata.payloads?.length) return null;
return (
<InnerMediaMessage
currentMessage={props.currentMessage}
containerStyle={props.containerStyle}
onLongPress={longPress}
/>
);
}
);

const InnerMediaMessage = memo(
({
currentMessage,
containerStyle,
onLongPress,
}: {
currentMessage: ChatMessageIMessage;
containerStyle?: StyleProp<ViewStyle>;
onLongPress: (e: GestureResponderEvent, message: ChatMessageIMessage) => void;
}) => {
const navigation = useNavigation<NavigationProp<ChatStackParamList>>();
const { width, height } = Dimensions.get('screen');
const { currentMessage } = props;
const payloads = currentMessage.fileMetadata.payloads;
const isMe = currentMessage.fileMetadata.senderOdinId === '';
const onClick = (currIndex?: number) => {
navigation.navigate('PreviewMedia', {
fileId: currentMessage.fileId,
payloads: payloads,
senderOdinId: currentMessage.fileMetadata.senderOdinId,
createdAt: currentMessage.fileMetadata.created,
previewThumbnail: currentMessage.fileMetadata.appData.previewThumbnail,
currIndex: currIndex || 0,
targetDrive: ChatDrive,
});
};

if (payloads.length === 1) {
const previewThumbnail = currentMessage.fileMetadata.appData.previewThumbnail;
const onClick = useCallback(
(currIndex?: number) => {
navigation.navigate('PreviewMedia', {
fileId: currentMessage.fileId,
payloads: payloads,
senderOdinId: currentMessage.fileMetadata.senderOdinId,
createdAt: currentMessage.fileMetadata.created,
previewThumbnail: currentMessage.fileMetadata.appData.previewThumbnail,
currIndex: currIndex || 0,
targetDrive: ChatDrive,
});
},
[currentMessage, navigation, payloads]
);
const previewThumbnail = currentMessage.fileMetadata.appData.previewThumbnail;

const aspectRatio =
(previewThumbnail?.pixelWidth || 1) / (previewThumbnail?.pixelHeight || 1);
const aspectRatio = useMemo(
() => (previewThumbnail?.pixelWidth || 1) / (previewThumbnail?.pixelHeight || 1),
[previewThumbnail]
);

const { width: newWidth, height: newHeight } = calculateScaledDimensions(
previewThumbnail?.pixelWidth || 300,
previewThumbnail?.pixelHeight || 300,
{ width: width * 0.8, height: height * 0.68 }
);
const { width: newWidth, height: newHeight } = useMemo(
() =>
calculateScaledDimensions(
previewThumbnail?.pixelWidth || 300,
previewThumbnail?.pixelHeight || 300,
{ width: width * 0.8, height: height * 0.68 }
),
[previewThumbnail, width, height]
);

if (payloads.length === 1) {
return (
<MediaItem
payload={payloads[0]}
targetDrive={ChatDrive}
fileId={currentMessage.fileId}
previewThumbnail={currentMessage.fileMetadata.appData.previewThumbnail}
previewThumbnail={
payloads[0]?.previewThumbnail || currentMessage.fileMetadata.appData.previewThumbnail
}
imageSize={{
width: newWidth,
height: newHeight,
}}
position={isMe ? 'right' : 'left'}
fit={'contain'}
containerStyle={props.containerStyle}
containerStyle={containerStyle}
onLongPress={(e) => onLongPress(e, currentMessage)}
style={{
borderRadius: 10,
Expand Down
2 changes: 0 additions & 2 deletions packages/mobile/src/components/ui/Media/MediaGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export const MediaGallery = memo(
({
fileId,
payloads,
previewThumbnail,
onLongPress,
onClick,
targetDrive,
Expand Down Expand Up @@ -164,7 +163,6 @@ export const MediaItem = memo(
const isAudio = payload.contentType?.startsWith('audio');
const isImage = payload.contentType?.startsWith('image');
const isLink = payload.key === CHAT_LINKS_PAYLOAD_KEY || payload.key === POST_LINKS_PAYLOAD_KEY;

if (!payload.contentType || !payload.key || !fileId) {
if (isImage && (payload as NewPayloadDescriptor).pendingFile) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,24 @@ export const useAudio = (props?: OdinAudioProps) => {
if (fileId === undefined || fileId === '' || !drive || !payloadKey) {
return null;
}

const cachedAudio = getFromCache(fileId, payloadKey, drive);
if (cachedAudio) return cachedAudio;

const audioBlob = await getPayloadBytes(dotYouClient, drive, fileId, payloadKey, {
lastModified,
});

if (!audioBlob) return null;
return {
url: audioBlob.uri,
type: audioBlob.type,
};
};

const getFromCache = async (
const getFromCache = (
fileId: string,
payloadKey: string,
drive: TargetDrive
): Promise<AudioData | null> => {
): AudioData | null => {
const queryKey = ['audio', drive?.alias, fileId, payloadKey];
const cachedEntries = queryClient.getQueryCache().find<AudioData | null>({
queryKey,
Expand Down
12 changes: 8 additions & 4 deletions packages/mobile/src/hooks/chat/useLiveChatProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { processInbox } from '@homebase-id/js-lib/peer';

import { useNotificationSubscriber } from '../useNotificationSubscriber';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { stringGuidsEqual } from '@homebase-id/js-lib/helpers';
import { getConversationQueryOptions, useConversation } from './useConversation';
Expand Down Expand Up @@ -46,7 +46,7 @@ import {
} from '../notifications/usePushNotifications';
import { insertNewReaction, removeReaction } from './useChatReaction';
import { useNotification } from '../notifications/useNotification';
import { BlogConfig } from '@homebase-id/js-lib/public';
import { useDriveSubscriber } from '../drive/useDriveSubscriber';

const MINUTE_IN_MS = 60000;
const isDebug = false; // The babel plugin to remove console logs would remove any if they get to production
Expand Down Expand Up @@ -146,6 +146,8 @@ const useChatWebsocket = (isEnabled: boolean) => {
} = useConversation();
const { add } = useNotification();
const queryClient = useQueryClient();
const { data: subscribedDrives, isFetched } = useDriveSubscriber();


const [chatMessagesQueue, setChatMessagesQueue] = useState<HomebaseFile<ChatMessage>[]>([]);

Expand Down Expand Up @@ -308,8 +310,10 @@ const useChatWebsocket = (isEnabled: boolean) => {
}
}, [processQueue, chatMessagesQueue]);



return useNotificationSubscriber(
isEnabled ? handler : undefined,
isEnabled && isFetched ? handler : undefined,
[
'fileAdded',
'fileModified',
Expand All @@ -318,7 +322,7 @@ const useChatWebsocket = (isEnabled: boolean) => {
'statisticsChanged',
'appNotificationAdded',
],
[ChatDrive, BlogConfig.FeedDrive],
subscribedDrives || [],
() => {
queryClient.invalidateQueries({ queryKey: ['process-inbox'] });
}
Expand Down
23 changes: 23 additions & 0 deletions packages/mobile/src/hooks/drive/useDriveSubscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getDrivesByType } from '@homebase-id/js-lib/core';
import { BlogConfig } from '@homebase-id/js-lib/public';
import { useQuery } from '@tanstack/react-query';
import { useDotYouClientContext } from 'feed-app-common';
import { ChatDrive } from '../../provider/chat/ConversationProvider';

const PAGE_SIZE = 100;

export const useDriveSubscriber = () => {
const dotyouClient = useDotYouClientContext();
const fetchPostsDrives = async () => {
const pagedDrives = await getDrivesByType(dotyouClient, BlogConfig.DriveType, 1, PAGE_SIZE);
return pagedDrives.results.map((drive) => drive.targetDriveInfo);
};
return useQuery({
queryKey: ['drive-subscriber'],
select: (data) => [ChatDrive, BlogConfig.FeedDrive, BlogConfig.PublicChannelDrive, ...data],
queryFn: fetchPostsDrives,
refetchOnMount: false,
refetchOnWindowFocus: true,
staleTime: 1000 * 60 * 60,
});
};
Loading

0 comments on commit 63e9b7c

Please sign in to comment.