stream-chat-react
Version:
React components to create chat conversations or livestream style chat
140 lines (139 loc) • 8.07 kB
JavaScript
import React, { useCallback, useEffect, useMemo, useState, } from 'react';
import { useChatContext, useTranslationContext } from '../../context';
import { useMessageComposer } from '../MessageInput';
import { ModalHeader } from '../Modal/ModalHeader';
import { SimpleSwitchField } from '../Form/SwitchField';
import { Dropdown, useDropdownContext } from '../Form/Dropdown';
const MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1000; // 1 minute;
const DEFAULT_SHARE_LOCATION_DURATIONS = [
15 * 60 * 1000, // 15 minutes
60 * 60 * 1000, // 1 hour
8 * 60 * 60 * 1000, // 8 hours
];
const DefaultGeolocationMap = () => null;
export const ShareLocationDialog = ({ close, GeolocationMap = DefaultGeolocationMap, shareDurations = DEFAULT_SHARE_LOCATION_DURATIONS, }) => {
const { client } = useChatContext();
const { t } = useTranslationContext();
const messageComposer = useMessageComposer();
const [durations, setDurations] = useState([]);
const [selectedDuration, setSelectedDuration] = useState(undefined);
const [geolocationPosition, setGeolocationPosition] = useState(null);
const [loadingLocation, setLoadingLocation] = useState(false);
const [geolocationPositionError, setGeolocationPositionError] = useState(undefined);
const validShareDurations = useMemo(() => shareDurations.filter((d) => d >= MIN_LIVE_LOCATION_SHARE_DURATION), [shareDurations]);
const openDropdownButtonProps = useMemo(() => ({
children: (() => (React.createElement("div", null, t('duration/Share Location', {
milliseconds: selectedDuration ?? durations[0],
}))))(), // todo: make it a component
}), [durations, selectedDuration, t]);
const getPosition = useCallback(() => new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition((position) => {
resolve(position);
}, (positionError) => {
console.warn(positionError);
reject(positionError);
}, { timeout: 1000 });
}), []);
const setupPositionWatching = useCallback(() => {
setLoadingLocation(true);
const watch = navigator.geolocation.watchPosition((position) => {
setGeolocationPosition(position);
setLoadingLocation(false);
setGeolocationPositionError(undefined);
}, (error) => {
setGeolocationPosition(null);
setLoadingLocation(false);
setGeolocationPositionError(error);
}, { timeout: 1000 });
return () => {
navigator.geolocation.clearWatch(watch);
};
}, []);
useEffect(() => setupPositionWatching(), [setupPositionWatching]);
return (React.createElement("div", { className: 'str-chat__dialog str-chat__share-location-dialog', "data-testid": 'share-location-dialog' },
React.createElement(ModalHeader, { close: close, title: t('Share Location') }),
React.createElement("div", { className: 'str-chat__dialog__body' },
React.createElement(GeolocationMap, { geolocationPositionError: geolocationPositionError, latitude: geolocationPosition?.coords.latitude, loadingLocation: loadingLocation, longitude: geolocationPosition?.coords.longitude, restartLocationWatching: setupPositionWatching }),
validShareDurations.length > 0 && (React.createElement("div", { className: 'str-chat__live-location-activation' },
React.createElement(SimpleSwitchField, { checked: durations.length > 0, "data-testid": 'share-location-dialog-live-location-switch', disabled: !geolocationPosition, labelText: t('Share live location for'), onChange: (e) => {
e.stopPropagation();
if (durations.length > 0) {
setDurations([]);
setSelectedDuration(undefined);
}
else {
setDurations(validShareDurations);
setSelectedDuration(validShareDurations[0]);
}
} }),
durations.length > 0 && (React.createElement(Dropdown, { openButtonProps: openDropdownButtonProps, placement: 'bottom-start' },
React.createElement(DurationDropdownItems, { durations: durations, selectDuration: setSelectedDuration })))))),
React.createElement("div", { className: 'str-chat__dialog__controls' },
React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--cancel', onClick: () => {
messageComposer.locationComposer.initState();
close();
} }, t('Cancel')),
React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--submit', disabled: !geolocationPosition, onClick: async () => {
let coords = geolocationPosition && {
latitude: geolocationPosition.coords.latitude,
longitude: geolocationPosition.coords.longitude,
};
if (!coords) {
coords = (await getPosition()).coords;
}
messageComposer.locationComposer.setData({
...coords,
durationMs: selectedDuration,
});
close();
}, type: 'submit' }, t('Attach')),
React.createElement("button", { className: 'str-chat__dialog__controls-button str-chat__dialog__controls-button--submit', disabled: !geolocationPosition, onClick: async () => {
let coords = geolocationPosition && {
latitude: geolocationPosition.coords.latitude,
longitude: geolocationPosition.coords.longitude,
};
if (!coords) {
try {
coords = (await getPosition()).coords;
}
catch (e) {
client.notifications.addError({
message: t('Failed to retrieve location'),
options: {
originalError: e instanceof Error ? e : undefined,
type: 'browser-api:location:get:failed',
},
origin: { emitter: 'ShareLocationDialog' },
});
return;
}
}
messageComposer.locationComposer.setData({
...coords,
durationMs: selectedDuration,
});
try {
await messageComposer.sendLocation();
}
catch (err) {
client.notifications.addError({
message: t('Failed to share location'),
options: {
originalError: err instanceof Error ? err : undefined,
type: 'api:location:share:failed',
},
origin: { emitter: 'ShareLocationDialog' },
});
return;
}
close();
}, type: 'submit' }, t('Share')))));
};
const DurationDropdownItems = ({ durations, selectDuration, }) => {
const { t } = useTranslationContext();
const { close } = useDropdownContext();
return durations.map((duration) => (React.createElement("button", { className: 'str-chat__live-location-sharing-duration-option', key: `duration-${duration}}`, onClick: () => {
selectDuration(duration);
close();
}, role: 'option' }, t('duration/Share Location', { milliseconds: duration }))));
};