Skip to content

Commit

Permalink
Geolocation attachment
Browse files Browse the repository at this point in the history
arnautov-anton committed Jan 7, 2025
1 parent 06b3bd3 commit 3b8f2e1
Showing 5 changed files with 88 additions and 32 deletions.
32 changes: 9 additions & 23 deletions examples/vite/src/App.tsx
Original file line number Diff line number Diff line change
@@ -20,7 +20,6 @@ import {
ChatView,
useChatContext,
useLiveLocationSharingManager,
Attachment,
} from 'stream-chat-react';
import 'stream-chat-react/css/v2/index.css';

@@ -90,7 +89,7 @@ const ShareLiveLocation = () => {
});
},
console.warn,
{ timeout: 200 },
{ timeout: 2000 },
);
}}
>
@@ -116,7 +115,10 @@ const watchLocationTimed: LiveLocationManagerConstructorParameters['watchLocatio
});
}, 5000);

return () => clearInterval(timer);
return () => {
clearInterval(timer);
console.log('cleanup');
};
};

const App = () => {
@@ -126,13 +128,11 @@ const App = () => {
userData: { id: userId },
});

const manager = useLiveLocationSharingManager({
client: chatClient ?? undefined,
watchLocation: watchLocationTimed,
useLiveLocationSharingManager({
client: chatClient,
watchLocation: watchLocationNormal,
});

// const s = useStateStore(manager?.state)

if (!chatClient) return <>Loading...</>;

return (
@@ -148,21 +148,7 @@ const App = () => {
showChannelSearch
additionalChannelSearchProps={{ searchForChannels: true }}
/>
<Channel
Attachment={(props) => {
const [attachment] = props.attachments ?? [];

if (attachment?.type === 'live_location') {
return (
<div style={{ padding: 25 }}>
lat: {attachment.latitude}, lng: {attachment.longitude}
</div>
);
}

return <Attachment {...props} />;
}}
>
<Channel>
<Window>
<ChannelHeader Avatar={ChannelAvatar} />
<MessageList returnAllReadData />
10 changes: 9 additions & 1 deletion src/components/Attachment/Attachment.tsx
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ import {
CardContainer,
FileContainer,
GalleryContainer,
GeolocationContainer,
ImageContainer,
MediaContainer,
UnsupportedAttachmentContainer,
@@ -38,10 +39,10 @@ const CONTAINER_MAP = {
audio: AudioContainer,
card: CardContainer,
file: FileContainer,
geolocation: GeolocationContainer,
media: MediaContainer,
unsupported: UnsupportedAttachmentContainer,
voiceRecording: VoiceRecordingContainer,
// geolocation: () => <div></div>,
} as const;

export const ATTACHMENT_GROUPS_ORDER = [
@@ -52,6 +53,7 @@ export const ATTACHMENT_GROUPS_ORDER = [
'audio',
'voiceRecording',
'file',
'geolocation',
'unsupported',
] as const;

@@ -72,6 +74,9 @@ export type AttachmentProps<
File?: React.ComponentType<FileAttachmentProps<StreamChatGenerics>>;
/** Custom UI component for displaying a gallery of image type attachments, defaults to and accepts same props as: [Gallery](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/Gallery.tsx) */
Gallery?: React.ComponentType<GalleryProps<StreamChatGenerics>>;
Geolocation?: React.ComponentType<{
attachment: StreamAttachment<StreamChatGenerics>;
}>;
/** Custom UI component for displaying an image type attachment, defaults to and accepts same props as: [Image](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/Image.tsx) */
Image?: React.ComponentType<ImageProps>;
/** Optional flag to signal that an attachment is a displayed as a part of a quoted message */
@@ -145,6 +150,7 @@ const renderGroupedAttachments = <
image: [],
// eslint-disable-next-line sort-keys
gallery: [],
geolocation: [],
voiceRecording: [],
},
);
@@ -184,6 +190,8 @@ const getAttachmentType = <
return 'voiceRecording';
} else if (isFileAttachment(attachment)) {
return 'file';
} else if (attachment.type === 'live_location' || attachment.type === 'static_location') {
return 'geolocation';
}

return 'unsupported';
12 changes: 12 additions & 0 deletions src/components/Attachment/AttachmentContainer.tsx
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import { Gallery as DefaultGallery, ImageComponent as DefaultImage } from '../Ga
import { Card as DefaultCard } from './Card';
import { FileAttachment as DefaultFile } from './FileAttachment';
import { UnsupportedAttachment as DefaultUnsupportedAttachment } from './UnsupportedAttachment';
import { Geolocation as DefaultGeolocation } from './Geolocation';
import {
AttachmentComponentType,
GalleryAttachment,
@@ -318,6 +319,17 @@ export const MediaContainer = <
);
};

export const GeolocationContainer = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>({
attachment,
Geolocation = DefaultGeolocation,
}: RenderAttachmentProps<StreamChatGenerics>) => (
<>
<Geolocation attachment={attachment} />
</>
);

export const UnsupportedAttachmentContainer = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>({
48 changes: 48 additions & 0 deletions src/components/Attachment/Geolocation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import type { Attachment, DefaultGenerics, ExtendableGenerics } from 'stream-chat';
import { useChatContext, useMessageContext } from '../../context';

export const Geolocation = <SCG extends ExtendableGenerics = DefaultGenerics>({
attachment,
}: {
attachment: Attachment<SCG>;
}) => {
const { channel } = useChatContext();
const { isMyMessage, message } = useMessageContext();

const stoppedSharing = !!attachment.stopped_sharing;
const expired: boolean =
typeof attachment.end_time === 'string' && Date.now() > new Date(attachment.end_time).getTime();

return (
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 10,
paddingBlock: 15,
paddingInline: 10,
width: 'auto',
}}
>
{attachment.type === 'live_location' && !stoppedSharing && !expired && isMyMessage() && (
<button
onClick={() =>
channel?.stopLiveLocationSharing({
attachments: message.attachments,
id: message.id,
type: message.type,
})
}
>
Stop sharing
</button>
)}
{/* TODO: {MAP} */}
<span>
lat: {attachment.latitude}, lng: {attachment.longitude}
</span>
{(stoppedSharing || expired) && <span style={{ fontSize: 12 }}>Location sharing ended</span>}
</div>
);
};
18 changes: 10 additions & 8 deletions src/components/Attachment/hooks/useLiveLocationSharingManager.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { LiveLocationManager } from 'stream-chat';
import type { LiveLocationManagerConstructorParameters } from 'stream-chat';
import { useEffect, useMemo } from 'react';
import type {
ExtendableGenerics,
LiveLocationManagerConstructorParameters,
StreamChat,
} from 'stream-chat';

type PartialKeys<T, K extends keyof T> = {
[L in keyof T]: L extends K ? T[L] | undefined : T[L];
};

export const useLiveLocationSharingManager = ({
export const useLiveLocationSharingManager = <SCG extends ExtendableGenerics>({
client,
retrieveAndDeserialize,
serializeAndStore,
watchLocation,
}: PartialKeys<LiveLocationManagerConstructorParameters, 'client'>) => {
}: Omit<LiveLocationManagerConstructorParameters<SCG>, 'client'> & {
client?: StreamChat<SCG> | null;
}) => {
const manager = useMemo(() => {
if (!client) return null;

return new LiveLocationManager({
return new LiveLocationManager<SCG>({
client,
retrieveAndDeserialize,
serializeAndStore,

0 comments on commit 3b8f2e1

Please sign in to comment.