Location Sharing

The SDK supports location sharing. It supports the whole flow:

  1. Creation of a message with shared location (static or live)
  2. Rendering of a message with shared location
  3. Showing the latest message preview in the channel list
  4. 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 with shared_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.

ShareLocationDialog without map
ShareLocationDialog with map
ShareLocationDialog with live location durations

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.

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 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:

Preview of live location attachment
Preview of live location attachment

The static location attachment just informs about the location at the given point in time.

Preview of static location attachment

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.

© Getstream.io, Inc. All Rights Reserved.