Location hooks
These hooks give a component the live location manager and follow the location shared in one message. They're exported from stream-chat-react-native and stream-chat-expo, with LiveLocationManagerProvider, the component that creates the manager.
Location sharing, and why live locations need a manager, is explained on Location sharing. The location being shared from the composer is in locationComposer.state: see Composer hooks.
Each entry gives the signature, then:
- Returns: what the hook gives back.
- Reads: the store or the context it reads.
- Re-renders: what makes the component render again.
- Needs: where the component must sit, and what the hook does outside that place.
LiveLocationManagerProvider
Creates the manager that sends the user's position to every live location they're sharing.
LiveLocationManagerProvider(props: PropsWithChildren<{
watchLocation: WatchLocation;
getDeviceId?: () => string;
}>)The props:
watchLocation:(handler) => unsubscribe. It starts watching the device's position and callshandlerwith each{ latitude, longitude }. The manager calls it while at least one live share is active, and calls the returned function when none is left.getDeviceId: the ID of the device the shares come from. By defaultreact-native-<platform>-<user ID>, such asreact-native-ios-cleo.
It creates a LiveLocationManager for the client of Chat, starts it with LiveLocationManager.init(), and disposes of it when it unmounts. A new watchLocation or getDeviceId creates a new manager, so declare them at module scope. It needs Chat above it.
Render it once, around the part of the app that stays mounted while the user is signed in. Two managers would both send each position. The SampleApp wraps its navigator in it, with a watchLocation built on @react-native-community/geolocation:
import type { PropsWithChildren } from "react";
import Geolocation from "@react-native-community/geolocation";
import type { WatchLocation } from "stream-chat";
import { LiveLocationManagerProvider } from "stream-chat-react-native";
const watchLocation: WatchLocation = (handler) => {
const watchId = Geolocation.watchPosition(
({ coords }) =>
handler({ latitude: coords.latitude, longitude: coords.longitude }),
(error) => console.warn("Watching the location failed", error),
{ enableHighAccuracy: true },
);
return () => Geolocation.clearWatch(watchId);
};
export const LiveLocationRoot = ({ children }: PropsWithChildren) => (
<LiveLocationManagerProvider watchLocation={watchLocation}>
{children}
</LiveLocationManagerProvider>
);The app asks for location permission itself, before the user shares a live location.
useLiveLocationManagerContext
Returns the live location manager.
useLiveLocationManagerContext(): { liveLocationManager: LiveLocationManager | null }- Returns:
{ liveLocationManager }, the managerLiveLocationManagerProvidercreated.nulloutside the provider. - Reads: the context of
LiveLocationManagerProvider. No store. The manager keeps its own state inliveLocationManager.state. - Re-renders: when the provider renders, since it passes a new value each time.
- Needs: nothing. Never throws.
The manager's state holds two fields:
ready: whether the manager has loaded the user's active live locations.messages: aMapfrom message ID to each live location the user is sharing, with itsend_at.
import { Text } from "react-native";
import type { LiveLocationManagerState } from "stream-chat";
import {
useLiveLocationManagerContext,
useStateStore,
} from "stream-chat-react-native";
const sharesSelector = ({ messages }: LiveLocationManagerState) => ({
messages,
});
export const LiveLocationStatus = () => {
const { liveLocationManager } = useLiveLocationManagerContext();
const { messages } =
useStateStore(liveLocationManager?.state, sharesSelector) ?? {};
if (!messages?.size) return null;
return <Text>Sharing your live location in {messages.size} messages</Text>;
};useHandleLiveLocationEvents
Follows the location shared in one message.
useHandleLiveLocationEvents(params: {
channel: Channel;
messageId: string;
onLocationUpdate?: (location: SharedLocationResponse) => void;
}): {
isLiveLocationStopped: boolean | null;
locationResponse: SharedLocationResponse | undefined;
}- Returns:
locationResponse: the message'sshared_locationfrom the latestmessage.updatedevent.undefineduntil the first one arrives, so start frommessage.shared_locationand prefer this value once it's set.isLiveLocationStopped:trueonce an update arrives whoseend_athas passed.nullbefore that. A live location that expires without a further update staysnull, so compareend_atwith the current time as well.
- Reads: the
message.updatedevents of the channel formessageId. It callsonLocationUpdatewith each new location. - Re-renders: when an update for the message arrives.
- Needs:
Chat. Throws outside it.
A new onLocationUpdate function subscribes again, so define it with useCallback. The SDK's components don't call this hook. The SampleApp's map screen uses it to move the marker of a live location.
A location shown in a message is message.shared_location, and changes with the message's message.updated event.