mirror of https://github.com/vector-im/riot-web
Add user avatar to location sharing dialog (#7520)
parent
5ae166777c
commit
11c8e720b2
|
@ -21,69 +21,96 @@ limitations under the License.
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
|
||||||
|
|
||||||
#mx_LocationPicker_map {
|
#mx_LocationPicker_map {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
.maplibregl-ctrl.maplibregl-ctrl-group {
|
.maplibregl-ctrl.maplibregl-ctrl-group {
|
||||||
margin-top: 50px;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button.mx_LocationPicker_cancelButton {
|
.maplibregl-ctrl-bottom-right {
|
||||||
border: none;
|
bottom: 68px;
|
||||||
border-radius: 12px;
|
}
|
||||||
|
|
||||||
|
.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;
|
position: absolute;
|
||||||
top: -360px;
|
bottom: -3px;
|
||||||
right: 5px;
|
left: 12px;
|
||||||
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 {
|
.mx_LocationPicker_footer {
|
||||||
color: red;
|
position: absolute;
|
||||||
margin: auto;
|
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 React, { ReactElement } from 'react';
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import LocationPicker from './LocationPicker';
|
import LocationPicker from './LocationPicker';
|
||||||
|
@ -24,14 +24,14 @@ import { CollapsibleButton, ICollapsibleButtonProps } from '../rooms/Collapsible
|
||||||
import ContextMenu, { aboveLeftOf, useContextMenu, AboveLeftOf } from "../../structures/ContextMenu";
|
import ContextMenu, { aboveLeftOf, useContextMenu, AboveLeftOf } from "../../structures/ContextMenu";
|
||||||
|
|
||||||
interface IProps extends Pick<ICollapsibleButtonProps, "narrowMode"> {
|
interface IProps extends Pick<ICollapsibleButtonProps, "narrowMode"> {
|
||||||
room: Room;
|
sender: RoomMember;
|
||||||
shareLocation: (uri: string, ts: number) => boolean;
|
shareLocation: (uri: string, ts: number) => boolean;
|
||||||
menuPosition: AboveLeftOf;
|
menuPosition: AboveLeftOf;
|
||||||
narrowMode: boolean;
|
narrowMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LocationButton: React.FC<IProps> = (
|
export const LocationButton: React.FC<IProps> = (
|
||||||
{ shareLocation, menuPosition, narrowMode },
|
{ sender, shareLocation, menuPosition, narrowMode },
|
||||||
) => {
|
) => {
|
||||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||||
|
|
||||||
|
@ -45,7 +45,11 @@ export const LocationButton: React.FC<IProps> = (
|
||||||
onFinished={closeMenu}
|
onFinished={closeMenu}
|
||||||
managed={false}
|
managed={false}
|
||||||
>
|
>
|
||||||
<LocationPicker onChoose={shareLocation} onFinished={closeMenu} />
|
<LocationPicker
|
||||||
|
sender={sender}
|
||||||
|
onChoose={shareLocation}
|
||||||
|
onFinished={closeMenu}
|
||||||
|
/>
|
||||||
</ContextMenu>;
|
</ContextMenu>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,17 @@ limitations under the License.
|
||||||
import React, { SyntheticEvent } from 'react';
|
import React, { SyntheticEvent } from 'react';
|
||||||
import maplibregl from 'maplibre-gl';
|
import maplibregl from 'maplibre-gl';
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||||
|
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import MemberAvatar from '../avatars/MemberAvatar';
|
||||||
|
import MatrixClientContext from '../../../contexts/MatrixClientContext';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
sender: RoomMember;
|
||||||
onChoose(uri: string, ts: number): boolean;
|
onChoose(uri: string, ts: number): boolean;
|
||||||
onFinished(ev?: SyntheticEvent): void;
|
onFinished(ev?: SyntheticEvent): void;
|
||||||
}
|
}
|
||||||
|
@ -43,8 +47,11 @@ interface IState {
|
||||||
|
|
||||||
@replaceableComponent("views.location.LocationPicker")
|
@replaceableComponent("views.location.LocationPicker")
|
||||||
class LocationPicker extends React.Component<IProps, IState> {
|
class LocationPicker extends React.Component<IProps, IState> {
|
||||||
|
public static contextType = MatrixClientContext;
|
||||||
|
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||||
private map: maplibregl.Map;
|
private map: maplibregl.Map;
|
||||||
private geolocate: maplibregl.GeolocateControl;
|
private geolocate: maplibregl.GeolocateControl;
|
||||||
|
private marker: maplibregl.Marker;
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -55,6 +62,10 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getMarkerId = () => {
|
||||||
|
return "mx_MLocationPicker_marker";
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const config = SdkConfig.get();
|
const config = SdkConfig.get();
|
||||||
this.map = new maplibregl.Map({
|
this.map = new maplibregl.Map({
|
||||||
|
@ -74,6 +85,14 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
this.map.addControl(this.geolocate);
|
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) => {
|
this.map.on('error', (e) => {
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed to load map: check map_style_url in config.json "
|
"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) => {
|
private onGeolocate = (position: GeolocationPosition) => {
|
||||||
this.setState({ position });
|
this.setState({ position });
|
||||||
|
this.marker.setLngLat(
|
||||||
|
new maplibregl.LngLat(
|
||||||
|
position.coords.longitude,
|
||||||
|
position.coords.latitude,
|
||||||
|
),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onOk = () => {
|
private onOk = () => {
|
||||||
|
@ -134,6 +159,22 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -532,8 +532,8 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
if (SettingsStore.getValue("feature_location_share")) {
|
if (SettingsStore.getValue("feature_location_share")) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<LocationButton
|
<LocationButton
|
||||||
|
sender={this.props.room.getMember(MatrixClientPeg.get().getUserId())}
|
||||||
key="location"
|
key="location"
|
||||||
room={this.props.room}
|
|
||||||
shareLocation={this.shareLocation}
|
shareLocation={this.shareLocation}
|
||||||
menuPosition={menuPosition}
|
menuPosition={menuPosition}
|
||||||
narrowMode={this.state.narrowMode}
|
narrowMode={this.state.narrowMode}
|
||||||
|
|
Loading…
Reference in New Issue