import {
Channel,
ShareLocationDialog,
GeolocationMapProps,
ShareLocationDialogProps,
ShareGeolocationMapProps,
} from "stream-chat-react";
import "leaflet/dist/leaflet.css";
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet";
const GeolocationMap = ({ latitude, longitude }: GeolocationMapProps) => {
return (
<MapContainer
center={[latitude, longitude]}
zoom={13}
style={{ height: "300px", width: "100%" }}
>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<Marker position={[latitude, longitude]}>
<Popup>You are here</Popup>
</Marker>
</MapContainer>
);
};
const ShareGeolocationMap = ({
geolocationPositionError,
loadingLocation,
latitude,
longitude,
restartLocationWatching,
}: ShareGeolocationMapProps) => {
if (loadingLocation) {
return <div>Loading location...</div>;
}
if (!latitude || !longitude || geolocationPositionError) {
return (
<div>
<div>Could not retrieve the geolocation position.</div>
<button onClick={restartLocationWatching}>Retry</button>
</div>
);
}
return <GeolocationMap latitude={latitude} longitude={longitude} />;
};
const shareDurations = [60 * 1000, 60 * 60 * 1000, 8 * 60 * 60 * 1000];
const CustomShareLocationDialog = (props: ShareLocationDialogProps) => (
<ShareLocationDialog
{...props}
GeolocationMap={ShareGeolocationMap}
shareDurations={shareDurations}
/>
);
const App = () => {
return (
// ...
<Channel ShareLocationDialog={CustomShareLocationDialog}>
{/* ... */}
</Channel>
// ...
);
};
Location Sharing
The SDK supports location sharing. It supports the whole flow:
- Creation of a message with shared location (static or live)
- Rendering of a message with shared location
- Showing the latest message preview in the channel list
- Managing the reporting of live locations to the server
Sharing a location
Location sharing is performed by composing and sending a message with shared_location
property. There are two ways of sharing the location:
messageComposer.sendLocation()
- sends a message withshared_location
only (no text, file uploads etc.)messageComposer.locationComposer.setData()
- attaches location data to the message composition
The SDK exposes a default component SharedLocationDialog
that handles both scenarios. The dialog is displayed once user selects Location option from AttachmentSelector
.
To render the map in the dialog, a corresponding component has to be passed to the GeolocationMap
prop. In the example below, we are using react-leaflet to render the map. Custom array of durations can be provided too.
Location attachment preview
The location preview attached to the message composer can be customized
import {
AttachmentPreviewList,
AttachmentPreviewListProps,
GeolocationPreviewProps,
} from "stream-chat-react";
const CustomGeolocationPreview = (props: GeolocationPreviewProps) => {
// custom implementation
};
const CustomAttachmentPreviewList = (props) => (
<AttachmentPreviewList
{...props}
GeolocationPreview={CustomGeolocationPreview}
/>
);
const App = () => {
return (
// ...
<Channel AttachmentPreviewList={CustomAttachmentPreviewList}>
{/* ... */}
</Channel>
// ...
);
};
Rendering a location message
A message with location is rendered among the message attachments. The component that renders the shared location UI is Geolocation
and can be overridden using the same pattern as other attachments. In the example below we override the Geolocation
component to render the map.
import {
Attachment,
AttachmentProps,
Channel,
Geolocation,
GeolocationProps,
GeolocationMapProps,
} from "stream-chat-react";
import "leaflet/dist/leaflet.css";
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet";
const GeolocationMap = ({ latitude, longitude }: GeolocationMapProps) => {
return (
<MapContainer
center={[latitude, longitude]}
zoom={13}
style={{ height: "300px", width: "100%" }}
>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<Marker position={[latitude, longitude]}>
<Popup>You are here</Popup>
</Marker>
</MapContainer>
);
};
const CustomGeolocation = (props: GeolocationProps) => (
<Geolocation {...props} GeolocationMap={GeolocationMap} />
);
const CustomAttachment = (props: AttachmentProps) => (
<Attachment {...props} Geolocation={CustomGeolocation} />
);
const App = () => {
return (
// ...
<Channel Attachment={CustomAttachment}>{/* ... */}</Channel>
// ...
);
};
Own live location sharing can be stopped directly from the attachment:
The static location attachment just informs about the location at the given point in time.
Reporting live location updates
Live location updates are performed with LiveLocationManager
that should be initialized using the useLiveLocationSharingManager
hook. The hook should be called only at one place :
import { LiveLocationManagerConstructorParameters } from "stream-chat";
import { useLiveLocationSharingManager } from "stream-chat-react";
// retrieves the current position when emitted by watchPosition subscription
const watchLocationNormal: LiveLocationManagerConstructorParameters["watchLocation"] =
(handler) => {
const watch = navigator.geolocation.watchPosition((position) => {
handler({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
});
return () => navigator.geolocation.clearWatch(watch);
};
// retrieves the current position every 5 seconds
const watchLocationTimed: LiveLocationManagerConstructorParameters["watchLocation"] =
(handler) => {
const timer = setInterval(() => {
navigator.geolocation.getCurrentPosition((position) => {
handler({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
});
}, 5000);
return () => {
clearInterval(timer);
};
};
const Component = () => {
useLiveLocationSharingManager({
client: chatClient,
getDeviceId: getDeviceId,
watchLocation: watchLocationNormal,
});
};
The manager takes care of registering, unregistering and reporting the live location provided by watchLocation
function.