Simple static location sharing (#7135)
Adds maplibre as a dependency, and behind a labs flag, lets users send and receive [MSC3488](https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md) style location shares - with backwards compatibility with old school `m.location` `msgtype` location shares too. For this to work, you have to define a valid maptile server and API in your config.json's `map_style_url`.
This commit is contained in:
parent
eb05044bc4
commit
1262021417
17 changed files with 703 additions and 3 deletions
|
@ -48,6 +48,7 @@ import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInse
|
|||
import { Action } from "../../../dispatcher/actions";
|
||||
import EditorModel from "../../../editor/model";
|
||||
import EmojiPicker from '../emojipicker/EmojiPicker';
|
||||
import LocationPicker from '../location/LocationPicker';
|
||||
import UIStore, { UI_EVENTS } from '../../../stores/UIStore';
|
||||
import Modal from "../../../Modal";
|
||||
import { RelationType } from 'matrix-js-sdk/src/@types/event';
|
||||
|
@ -55,6 +56,9 @@ import RoomContext from '../../../contexts/RoomContext';
|
|||
import { POLL_START_EVENT_TYPE } from "../../../polls/consts";
|
||||
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||
import PollCreateDialog from "../elements/PollCreateDialog";
|
||||
import { MsgType } from "matrix-js-sdk/src/@types/event";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import LocationShareType from "../location/LocationShareType";
|
||||
import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdatedPayload";
|
||||
|
||||
let instanceCount = 0;
|
||||
|
@ -77,7 +81,7 @@ function SendButton(props: ISendButtonProps) {
|
|||
|
||||
interface IEmojiButtonProps {
|
||||
addEmoji: (unicode: string) => boolean;
|
||||
menuPosition: any; // TODO: Types
|
||||
menuPosition: AboveLeftOf;
|
||||
narrowMode: boolean;
|
||||
}
|
||||
|
||||
|
@ -114,6 +118,46 @@ const EmojiButton: React.FC<IEmojiButtonProps> = ({ addEmoji, menuPosition, narr
|
|||
</React.Fragment>;
|
||||
};
|
||||
|
||||
interface ILocationButtonProps {
|
||||
room: Room;
|
||||
shareLocation: (uri: string, ts: number, type: LocationShareType, description: string) => boolean;
|
||||
menuPosition: AboveLeftOf;
|
||||
narrowMode: boolean;
|
||||
}
|
||||
|
||||
const LocationButton: React.FC<ILocationButtonProps> = ({ shareLocation, menuPosition, narrowMode }) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
|
||||
let contextMenu;
|
||||
if (menuDisplayed) {
|
||||
const position = menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect());
|
||||
contextMenu = <ContextMenu {...position} onFinished={closeMenu} managed={false}>
|
||||
<LocationPicker onChoose={shareLocation} onFinished={closeMenu} />
|
||||
</ContextMenu>;
|
||||
}
|
||||
|
||||
const className = classNames(
|
||||
"mx_MessageComposer_button",
|
||||
"mx_MessageComposer_location",
|
||||
{
|
||||
"mx_MessageComposer_button_highlight": menuDisplayed,
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: replace ContextMenuTooltipButton with a unified representation of
|
||||
// the header buttons and the right panel buttons
|
||||
return <React.Fragment>
|
||||
<AccessibleTooltipButton
|
||||
className={className}
|
||||
onClick={openMenu}
|
||||
title={!narrowMode && _t('Share location')}
|
||||
label={narrowMode ? _t('Share location') : null}
|
||||
/>
|
||||
|
||||
{ contextMenu }
|
||||
</React.Fragment>;
|
||||
};
|
||||
|
||||
interface IUploadButtonProps {
|
||||
roomId: string;
|
||||
relation?: IEventRelation | null;
|
||||
|
@ -447,6 +491,25 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
return true;
|
||||
};
|
||||
|
||||
private shareLocation = (uri: string, ts: number, type: LocationShareType, description: string): boolean => {
|
||||
if (!uri) return false;
|
||||
try {
|
||||
const text = `${description ? description : 'Location'} at ${uri} as of ${new Date(ts).toISOString()}`;
|
||||
// noinspection ES6MissingAwait - we don't care if it fails, it'll get queued.
|
||||
MatrixClientPeg.get().sendMessage(this.props.room.roomId, {
|
||||
"body": text,
|
||||
"msgtype": MsgType.Location,
|
||||
"geo_uri": uri,
|
||||
"org.matrix.msc3488.location": { uri, description },
|
||||
"org.matrix.msc3488.ts": ts,
|
||||
// TODO: MSC1767 fallbacks for text & thumbnail
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error("Error sending location:", e);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
private sendMessage = async () => {
|
||||
if (this.state.haveRecording && this.voiceRecordingButton.current) {
|
||||
// There shouldn't be any text message to send when a voice recording is active, so
|
||||
|
@ -514,6 +577,17 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
relation={this.props.relation}
|
||||
/>,
|
||||
);
|
||||
if (SettingsStore.getValue("feature_location_share")) {
|
||||
buttons.push(
|
||||
<LocationButton
|
||||
key="location"
|
||||
room={this.props.room}
|
||||
shareLocation={this.shareLocation}
|
||||
menuPosition={menuPosition}
|
||||
narrowMode={this.state.narrowMode}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
buttons.push(
|
||||
<EmojiButton key="emoji_button" addEmoji={this.addEmoji} menuPosition={menuPosition} narrowMode={this.state.narrowMode} />,
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue