diff --git a/res/css/_components.scss b/res/css/_components.scss index d1576d6ec8e..953f12d73ef 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -6,6 +6,7 @@ @import "./_spacing.scss"; @import "./components/views/beacon/_BeaconListItem.scss"; @import "./components/views/beacon/_BeaconStatus.scss"; +@import "./components/views/beacon/_BeaconStatusTooltip.scss"; @import "./components/views/beacon/_BeaconViewDialog.scss"; @import "./components/views/beacon/_DialogOwnBeaconStatus.scss"; @import "./components/views/beacon/_DialogSidebar.scss"; @@ -143,7 +144,6 @@ @import "./views/elements/_AddressSelector.scss"; @import "./views/elements/_AddressTile.scss"; @import "./views/elements/_CopyableText.scss"; -@import "./views/elements/_SearchWarning.scss"; @import "./views/elements/_DesktopCapturerSourcePicker.scss"; @import "./views/elements/_DialPadBackspaceButton.scss"; @import "./views/elements/_DirectorySearchBox.scss"; @@ -172,6 +172,7 @@ @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_RoomAliasField.scss"; @import "./views/elements/_SSOButtons.scss"; +@import "./views/elements/_SearchWarning.scss"; @import "./views/elements/_ServerPicker.scss"; @import "./views/elements/_SettingsFlag.scss"; @import "./views/elements/_Slider.scss"; diff --git a/res/css/components/views/beacon/_BeaconListItem.scss b/res/css/components/views/beacon/_BeaconListItem.scss index 60311a4466f..dd99192cf56 100644 --- a/res/css/components/views/beacon/_BeaconListItem.scss +++ b/res/css/components/views/beacon/_BeaconListItem.scss @@ -40,6 +40,7 @@ limitations under the License. .mx_BeaconListItem_info { flex: 1 1 0; + width: 0; display: flex; flex-direction: column; align-items: stretch; diff --git a/res/css/components/views/beacon/_BeaconStatus.scss b/res/css/components/views/beacon/_BeaconStatus.scss index 4dd3d325475..95c41749111 100644 --- a/res/css/components/views/beacon/_BeaconStatus.scss +++ b/res/css/components/views/beacon/_BeaconStatus.scss @@ -46,14 +46,15 @@ limitations under the License. } .mx_BeaconStatus_description { - flex: 1; + flex: 1 1 0; display: flex; flex-direction: column; line-height: $font-14px; padding-right: $spacing-8; - // TODO handle text-overflow + white-space: nowrap; + overflow: hidden; } .mx_BeaconStatus_expiryTime { @@ -62,4 +63,6 @@ limitations under the License. .mx_BeaconStatus_label { margin-bottom: 2px; + overflow: hidden; + text-overflow: ellipsis; } diff --git a/res/css/components/views/beacon/_BeaconStatusTooltip.scss b/res/css/components/views/beacon/_BeaconStatusTooltip.scss new file mode 100644 index 00000000000..07b3a43cc01 --- /dev/null +++ b/res/css/components/views/beacon/_BeaconStatusTooltip.scss @@ -0,0 +1,37 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_BeaconStatusTooltip { + position: absolute; + top: 42px; + max-width: 150px; + height: 38px; + box-sizing: content-box; + padding-top: $spacing-8; + + // override copyable text style to make compact + .mx_CopyableText_copyButton { + margin-left: 0 !important; + } +} + +.mx_BeaconStatusTooltip_inner { + position: relative; + height: 100%; + border-radius: 4px; + background: $menu-bg-color; + box-shadow: 4px 4px 12px 0 $menu-box-shadow-color; +} diff --git a/src/components/views/beacon/BeaconMarker.tsx b/src/components/views/beacon/BeaconMarker.tsx index f7f284b88ed..644482e48b3 100644 --- a/src/components/views/beacon/BeaconMarker.tsx +++ b/src/components/views/beacon/BeaconMarker.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useContext } from 'react'; +import React, { ReactNode, useContext } from 'react'; import maplibregl from 'maplibre-gl'; import { Beacon, @@ -29,12 +29,13 @@ import SmartMarker from '../location/SmartMarker'; interface Props { map: maplibregl.Map; beacon: Beacon; + tooltip?: ReactNode; } /** * Updates a map SmartMarker with latest location from given beacon */ -const BeaconMarker: React.FC = ({ map, beacon }) => { +const BeaconMarker: React.FC = ({ map, beacon, tooltip }) => { const latestLocationState = useEventEmitterState( beacon, BeaconEvent.LocationUpdate, @@ -58,6 +59,7 @@ const BeaconMarker: React.FC = ({ map, beacon }) => { id={beacon.identifier} geoUri={geoUri} roomMember={markerRoomMember} + tooltip={tooltip} useMemberColor />; }; diff --git a/src/components/views/beacon/BeaconStatusTooltip.tsx b/src/components/views/beacon/BeaconStatusTooltip.tsx new file mode 100644 index 00000000000..bc9f3609395 --- /dev/null +++ b/src/components/views/beacon/BeaconStatusTooltip.tsx @@ -0,0 +1,61 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { useContext } from 'react'; +import { Beacon } from 'matrix-js-sdk/src/matrix'; +import { LocationAssetType } from 'matrix-js-sdk/src/@types/location'; + +import MatrixClientContext from '../../../contexts/MatrixClientContext'; +import CopyableText from '../elements/CopyableText'; +import BeaconStatus from './BeaconStatus'; +import { BeaconDisplayStatus } from './displayStatus'; + +interface Props { + beacon: Beacon; +} + +const useBeaconName = (beacon: Beacon): string => { + const matrixClient = useContext(MatrixClientContext); + + if (beacon.beaconInfo.assetType !== LocationAssetType.Self) { + return beacon.beaconInfo.description; + } + const room = matrixClient.getRoom(beacon.roomId); + const member = room?.getMember(beacon.beaconInfoOwner); + + return member?.rawDisplayName || beacon.beaconInfoOwner; +}; + +const BeaconStatusTooltip: React.FC = ({ beacon }) => { + const label = useBeaconName(beacon); + + return
+ + beacon.latestLocationState?.uri} + /> + +
; +}; + +export default BeaconStatusTooltip; diff --git a/src/components/views/beacon/BeaconViewDialog.tsx b/src/components/views/beacon/BeaconViewDialog.tsx index e6c4a423fe9..a7cdb242d37 100644 --- a/src/components/views/beacon/BeaconViewDialog.tsx +++ b/src/components/views/beacon/BeaconViewDialog.tsx @@ -37,6 +37,7 @@ import { _t } from '../../../languageHandler'; import AccessibleButton from '../elements/AccessibleButton'; import DialogSidebar from './DialogSidebar'; import DialogOwnBeaconStatus from './DialogOwnBeaconStatus'; +import BeaconStatusTooltip from './BeaconStatusTooltip'; interface IProps extends IDialogProps { roomId: Room['roomId']; @@ -103,6 +104,7 @@ const BeaconViewDialog: React.FC = ({ key={beacon.identifier} map={map} beacon={beacon} + tooltip={} />) } diff --git a/src/components/views/location/Marker.tsx b/src/components/views/location/Marker.tsx index bacade71cf4..075d2d092ef 100644 --- a/src/components/views/location/Marker.tsx +++ b/src/components/views/location/Marker.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { ReactNode, useState } from 'react'; import classNames from 'classnames'; import { RoomMember } from 'matrix-js-sdk/src/matrix'; @@ -28,12 +28,39 @@ interface Props { roomMember?: RoomMember; // use member text color as background useMemberColor?: boolean; + tooltip?: ReactNode; } +/** + * Wrap with tooltip handlers when + * tooltip is truthy + */ +const OptionalTooltip: React.FC<{ + tooltip?: ReactNode; children: ReactNode; +}> = ({ tooltip, children }) => { + const [isVisible, setIsVisible] = useState(false); + if (!tooltip) { + return <>{ children }; + } + + const show = () => setIsVisible(true); + const hide = () => setIsVisible(false); + const toggleVisibility = (e: React.MouseEvent) => { + // stop map from zooming in on click + e.stopPropagation(); + setIsVisible(!isVisible); + }; + + return
+ { children } + { isVisible && tooltip } +
; +}; + /** * Generic location marker */ -const Marker = React.forwardRef(({ id, roomMember, useMemberColor }, ref) => { +const Marker = React.forwardRef(({ id, roomMember, useMemberColor, tooltip }, ref) => { const memberColorClass = useMemberColor && roomMember ? getUserNameColorClass(roomMember.userId) : ''; return
(({ id, roomMember, useMem "mx_Marker_defaultColor": !memberColorClass, })} > -
- { roomMember ? - - : - } -
+ +
+ { roomMember ? + + : + } +
+
; }); diff --git a/src/components/views/location/SmartMarker.tsx b/src/components/views/location/SmartMarker.tsx index c4a7c61e32b..13b7ef093ea 100644 --- a/src/components/views/location/SmartMarker.tsx +++ b/src/components/views/location/SmartMarker.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { ReactNode, useCallback, useEffect, useState } from 'react'; import maplibregl from 'maplibre-gl'; import { RoomMember } from 'matrix-js-sdk/src/matrix'; @@ -64,12 +64,13 @@ interface SmartMarkerProps { roomMember?: RoomMember; // use member text color as background useMemberColor?: boolean; + tooltip?: ReactNode; } /** * Generic location marker */ -const SmartMarker: React.FC = ({ id, map, geoUri, roomMember, useMemberColor }) => { +const SmartMarker: React.FC = ({ id, map, geoUri, roomMember, useMemberColor, tooltip }) => { const { onElementRef } = useMapMarker(map, geoUri); return ( @@ -84,6 +85,7 @@ const SmartMarker: React.FC = ({ id, map, geoUri, roomMember, id={id} roomMember={roomMember} useMemberColor={useMemberColor} + tooltip={tooltip} /> ); diff --git a/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap b/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap index e590cbcd9f3..a79a47b5892 100644 --- a/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap +++ b/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap @@ -148,83 +148,86 @@ exports[` renders marker when beacon has location 1`] = ` className="mx_Marker mx_Username_color4" id="!room:server_@alice:server" > -
- +
- - - + A + + - - - -
+ title="@alice:server" + /> + + +
+
+ diff --git a/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap b/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap index 8a1910a5820..a9f7a03f07d 100644 --- a/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap +++ b/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap @@ -57,13 +57,15 @@ exports[` renders map correctly 1`] = ` className="mx_Marker mx_Marker_defaultColor" id="mx_LocationViewDialog_$2-marker" > -
+
-
+ className="mx_Marker_border" + > +
+
+
diff --git a/test/components/views/location/__snapshots__/Marker-test.tsx.snap b/test/components/views/location/__snapshots__/Marker-test.tsx.snap index b7596d1af8a..71391f9c08f 100644 --- a/test/components/views/location/__snapshots__/Marker-test.tsx.snap +++ b/test/components/views/location/__snapshots__/Marker-test.tsx.snap @@ -8,13 +8,15 @@ exports[` renders with location icon when no room member 1`] = ` className="mx_Marker mx_Marker_defaultColor" id="abc123" > -
+
-
+ className="mx_Marker_border" + > +
+
+
`; diff --git a/test/components/views/location/__snapshots__/SmartMarker-test.tsx.snap b/test/components/views/location/__snapshots__/SmartMarker-test.tsx.snap index d20c9bcd6ce..cfcba2e0dbe 100644 --- a/test/components/views/location/__snapshots__/SmartMarker-test.tsx.snap +++ b/test/components/views/location/__snapshots__/SmartMarker-test.tsx.snap @@ -24,13 +24,15 @@ exports[` creates a marker on mount 1`] = `
-
+
-
+ className="mx_Marker_border" + > +
+
+
@@ -61,13 +63,15 @@ exports[` removes marker on unmount 1`] = `
-
+
-
+ className="mx_Marker_border" + > +
+
+
diff --git a/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap b/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap index cc742223eba..58a413fdb78 100644 --- a/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap +++ b/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap @@ -167,13 +167,15 @@ exports[`MLocationBody without error renders map correctly 1`] = className="mx_Marker mx_Marker_defaultColor" id="mx_MLocationBody_$2_1f9acffa-marker" > -
+
-
+ className="mx_Marker_border" + > +
+
+