Add user avatar to location sharing dialog (#7520)
parent
5ae166777c
commit
11c8e720b2
|
@ -21,69 +21,96 @@ limitations under the License.
|
|||
border-radius: 8px;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mx_LocationPicker_map {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
#mx_LocationPicker_map {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
|
||||
.maplibregl-ctrl.maplibregl-ctrl-group {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-bottom-right {
|
||||
bottom: 68px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LocationPicker_footer {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
|
||||
.mx_Dialog_buttons {
|
||||
text-align: center;
|
||||
|
||||
/* Note the `button` prefix and `not()` clauses are needed to make
|
||||
these selectors more specific than those in _common.scss. */
|
||||
|
||||
button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton) {
|
||||
margin: 0px 0px 16px 0px;
|
||||
min-width: 328px;
|
||||
min-height: 48px;
|
||||
.maplibregl-ctrl.maplibregl-ctrl-group {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
button.mx_LocationPicker_cancelButton {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
.maplibregl-ctrl-bottom-right {
|
||||
bottom: 68px;
|
||||
}
|
||||
|
||||
.maplibregl-user-location-accuracy-circle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.maplibregl-user-location-dot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_MLocationBody_markerBorder {
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
border-radius: 50%;
|
||||
background-color: $accent;
|
||||
filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.2));
|
||||
|
||||
.mx_BaseAvatar {
|
||||
margin-top: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_MLocationBody_pointer {
|
||||
position: absolute;
|
||||
top: -360px;
|
||||
right: 5px;
|
||||
background-color: $quinary-content;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0px;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
button.mx_LocationPicker_cancelButton::before {
|
||||
content: '';
|
||||
background-color: $primary-content;
|
||||
min-width: 8px;
|
||||
min-height: 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
margin: 4px 8px;
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
mask-position: center;
|
||||
mask-image: url('$(res)/img/cancel-small.svg');
|
||||
bottom: -3px;
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LocationPicker_error {
|
||||
color: red;
|
||||
margin: auto;
|
||||
.mx_LocationPicker_footer {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
|
||||
.mx_Dialog_buttons {
|
||||
text-align: center;
|
||||
|
||||
/* Note the `button` prefix and `not()` clauses are needed to make
|
||||
these selectors more specific than those in _common.scss. */
|
||||
|
||||
button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton) {
|
||||
margin: 0px 0px 16px 0px;
|
||||
min-width: 328px;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
button.mx_LocationPicker_cancelButton {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
position: absolute;
|
||||
top: -360px;
|
||||
right: 5px;
|
||||
background-color: $quinary-content;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0px;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
button.mx_LocationPicker_cancelButton::before {
|
||||
content: '';
|
||||
background-color: $primary-content;
|
||||
min-width: 8px;
|
||||
min-height: 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
margin: 4px 8px;
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
mask-position: center;
|
||||
mask-image: url('$(res)/img/cancel-small.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LocationPicker_error {
|
||||
color: red;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, { ReactElement } from 'react';
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import classNames from 'classnames';
|
||||
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import LocationPicker from './LocationPicker';
|
||||
|
@ -24,14 +24,14 @@ import { CollapsibleButton, ICollapsibleButtonProps } from '../rooms/Collapsible
|
|||
import ContextMenu, { aboveLeftOf, useContextMenu, AboveLeftOf } from "../../structures/ContextMenu";
|
||||
|
||||
interface IProps extends Pick<ICollapsibleButtonProps, "narrowMode"> {
|
||||
room: Room;
|
||||
sender: RoomMember;
|
||||
shareLocation: (uri: string, ts: number) => boolean;
|
||||
menuPosition: AboveLeftOf;
|
||||
narrowMode: boolean;
|
||||
}
|
||||
|
||||
export const LocationButton: React.FC<IProps> = (
|
||||
{ shareLocation, menuPosition, narrowMode },
|
||||
{ sender, shareLocation, menuPosition, narrowMode },
|
||||
) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
|
||||
|
@ -45,7 +45,11 @@ export const LocationButton: React.FC<IProps> = (
|
|||
onFinished={closeMenu}
|
||||
managed={false}
|
||||
>
|
||||
<LocationPicker onChoose={shareLocation} onFinished={closeMenu} />
|
||||
<LocationPicker
|
||||
sender={sender}
|
||||
onChoose={shareLocation}
|
||||
onFinished={closeMenu}
|
||||
/>
|
||||
</ContextMenu>;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,17 @@ limitations under the License.
|
|||
import React, { SyntheticEvent } from 'react';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import MemberAvatar from '../avatars/MemberAvatar';
|
||||
import MatrixClientContext from '../../../contexts/MatrixClientContext';
|
||||
|
||||
interface IProps {
|
||||
sender: RoomMember;
|
||||
onChoose(uri: string, ts: number): boolean;
|
||||
onFinished(ev?: SyntheticEvent): void;
|
||||
}
|
||||
|
@ -43,8 +47,11 @@ interface IState {
|
|||
|
||||
@replaceableComponent("views.location.LocationPicker")
|
||||
class LocationPicker extends React.Component<IProps, IState> {
|
||||
public static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
private map: maplibregl.Map;
|
||||
private geolocate: maplibregl.GeolocateControl;
|
||||
private marker: maplibregl.Marker;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -55,6 +62,10 @@ class LocationPicker extends React.Component<IProps, IState> {
|
|||
};
|
||||
}
|
||||
|
||||
private getMarkerId = () => {
|
||||
return "mx_MLocationPicker_marker";
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const config = SdkConfig.get();
|
||||
this.map = new maplibregl.Map({
|
||||
|
@ -74,6 +85,14 @@ class LocationPicker extends React.Component<IProps, IState> {
|
|||
});
|
||||
this.map.addControl(this.geolocate);
|
||||
|
||||
this.marker = new maplibregl.Marker({
|
||||
element: document.getElementById(this.getMarkerId()),
|
||||
anchor: 'bottom',
|
||||
offset: [0, -1],
|
||||
})
|
||||
.setLngLat(new maplibregl.LngLat(0, 0))
|
||||
.addTo(this.map);
|
||||
|
||||
this.map.on('error', (e) => {
|
||||
logger.error(
|
||||
"Failed to load map: check map_style_url in config.json "
|
||||
|
@ -100,6 +119,12 @@ class LocationPicker extends React.Component<IProps, IState> {
|
|||
|
||||
private onGeolocate = (position: GeolocationPosition) => {
|
||||
this.setState({ position });
|
||||
this.marker.setLngLat(
|
||||
new maplibregl.LngLat(
|
||||
position.coords.longitude,
|
||||
position.coords.latitude,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
private onOk = () => {
|
||||
|
@ -134,6 +159,22 @@ class LocationPicker extends React.Component<IProps, IState> {
|
|||
/>
|
||||
</form>
|
||||
</div>
|
||||
<div className="mx_MLocationBody_marker" id={this.getMarkerId()}>
|
||||
<div className="mx_MLocationBody_markerBorder">
|
||||
<MemberAvatar
|
||||
member={this.props.sender}
|
||||
width={27}
|
||||
height={27}
|
||||
viewUserOnClick={false}
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
className="mx_MLocationBody_pointer"
|
||||
src={require("../../../../res/img/location/pointer.svg")}
|
||||
width="9"
|
||||
height="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -532,8 +532,8 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
if (SettingsStore.getValue("feature_location_share")) {
|
||||
buttons.push(
|
||||
<LocationButton
|
||||
sender={this.props.room.getMember(MatrixClientPeg.get().getUserId())}
|
||||
key="location"
|
||||
room={this.props.room}
|
||||
shareLocation={this.shareLocation}
|
||||
menuPosition={menuPosition}
|
||||
narrowMode={this.state.narrowMode}
|
||||
|
|
Loading…
Reference in New Issue