diff --git a/res/css/_components.scss b/res/css/_components.scss index a5ff7e233f8..4865a8be158 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -4,6 +4,7 @@ @import "./_font-sizes.scss"; @import "./_font-weights.scss"; @import "./_spacing.scss"; +@import "./components/views/beacon/_LeftPanelLiveShareWarning.scss"; @import "./components/views/location/_LocationShareMenu.scss"; @import "./components/views/location/_MapError.scss"; @import "./components/views/location/_ShareDialogButtons.scss"; diff --git a/res/css/components/views/beacon/_LeftPanelLiveShareWarning.scss b/res/css/components/views/beacon/_LeftPanelLiveShareWarning.scss new file mode 100644 index 00000000000..0ee60a65f27 --- /dev/null +++ b/res/css/components/views/beacon/_LeftPanelLiveShareWarning.scss @@ -0,0 +1,31 @@ +/* +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_LeftPanelLiveShareWarning { + width: 100%; + box-sizing: border-box; + + padding: $spacing-4; + text-align: center; + + background-color: $accent; + color: #fff; + font-size: $font-10px; + + // panel backdrops overlay the whole sidepanel + // go above to get hover for title + z-index: 1; +} diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 1113a5d9efd..f663d5a70bb 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -23,13 +23,20 @@ $roomListCollapsedWidth: 68px; } } -.mx_LeftPanel_wrapper { +.mx_LeftPanel_outerWrapper { display: flex; + flex-direction: column; max-width: 50%; position: relative; // Contain the amount of layers rendered by constraining what actually needs re-layering via css contain: layout paint; +} + +.mx_LeftPanel_wrapper { + display: flex; + flex-direction: row; + flex: 1; .mx_LeftPanel_wrapper--user { background-color: $roomlist-bg-color; diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index a95bfa9eb9f..d0ee7d92ebd 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -63,7 +63,7 @@ limitations under the License. } /* not the left panel, and not the resize handle, so the roomview/groupview/... */ -.mx_MatrixChat > :not(.mx_LeftPanel):not(.mx_SpacePanel):not(.mx_ResizeHandle):not(.mx_LeftPanel_wrapper) { +.mx_MatrixChat > :not(.mx_LeftPanel):not(.mx_SpacePanel):not(.mx_ResizeHandle):not(.mx_LeftPanel_outerWrapper) { background-color: $background; flex: 1 1 0; diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 4967954cdd6..2194d2f6e5d 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -75,6 +75,7 @@ import RightPanelStore from '../../stores/right-panel/RightPanelStore'; import { TimelineRenderingType } from "../../contexts/RoomContext"; import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts"; import { SwitchSpacePayload } from "../../dispatcher/payloads/SwitchSpacePayload"; +import LeftPanelLiveShareWarning from '../views/beacon/LeftPanelLiveShareWarning'; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -694,8 +695,10 @@ class LoggedInView extends React.Component { >
-
- { SettingsStore.getValue('TagPanel.enableTagPanel') && +
+ +
+ { SettingsStore.getValue('TagPanel.enableTagPanel') && (
{ { SettingsStore.getValue("feature_custom_tags") ? : null }
) - } - { SpaceStore.spacesEnabled ? <> + } + { SpaceStore.spacesEnabled ? <> + + + : null } - - : null } - -
- +
+ +
diff --git a/src/components/views/beacon/LeftPanelLiveShareWarning.tsx b/src/components/views/beacon/LeftPanelLiveShareWarning.tsx new file mode 100644 index 00000000000..d0be4f087eb --- /dev/null +++ b/src/components/views/beacon/LeftPanelLiveShareWarning.tsx @@ -0,0 +1,50 @@ +/* +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 classNames from 'classnames'; +import React from 'react'; + +import { useEventEmitterState } from '../../../hooks/useEventEmitter'; +import { _t } from '../../../languageHandler'; +import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore'; +import { Icon as LiveLocationIcon } from '../../../../res/img/location/live-location.svg'; + +interface Props { + isMinimized?: boolean; +} + +const LeftPanelLiveShareWarning: React.FC = ({ isMinimized }) => { + const hasLiveBeacons = useEventEmitterState( + OwnBeaconStore.instance, + OwnBeaconStoreEvent.LivenessChange, + () => OwnBeaconStore.instance.hasLiveBeacons(), + ); + + if (!hasLiveBeacons) { + return null; + } + + return
+ { isMinimized ? : _t('You are sharing your live location') } +
; +}; + +export default LeftPanelLiveShareWarning; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 26b1a012b3c..28e98bba00f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2956,6 +2956,7 @@ "Beta": "Beta", "Leave the beta": "Leave the beta", "Join the beta": "Join the beta", + "You are sharing your live location": "You are sharing your live location", "Avatar": "Avatar", "This room is public": "This room is public", "Away": "Away", diff --git a/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx b/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx new file mode 100644 index 00000000000..ed452281053 --- /dev/null +++ b/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx @@ -0,0 +1,80 @@ +/* +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 from 'react'; +import { mocked } from 'jest-mock'; +import { mount } from 'enzyme'; + +import '../../../skinned-sdk'; +import LeftPanelLiveShareWarning from '../../../../src/components/views/beacon/LeftPanelLiveShareWarning'; +import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../../src/stores/OwnBeaconStore'; +import { flushPromises } from '../../../test-utils'; + +jest.mock('../../../../src/stores/OwnBeaconStore', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const EventEmitter = require("events"); + class MockOwnBeaconStore extends EventEmitter { + public hasLiveBeacons = jest.fn().mockReturnValue(false); + } + return { + // @ts-ignore + ...jest.requireActual('../../../../src/stores/OwnBeaconStore'), + OwnBeaconStore: { + instance: new MockOwnBeaconStore() as unknown as OwnBeaconStore, + }, + }; +}, +); + +describe('', () => { + const defaultProps = {}; + const getComponent = (props = {}) => + mount(); + + it('renders nothing when user has no live beacons', () => { + const component = getComponent(); + expect(component.html()).toBe(null); + }); + + describe('when user has live beacons', () => { + beforeEach(() => { + mocked(OwnBeaconStore.instance).hasLiveBeacons.mockReturnValue(true); + }); + it('renders correctly when not minimized', () => { + const component = getComponent(); + expect(component).toMatchSnapshot(); + }); + + it('renders correctly when minimized', () => { + const component = getComponent({ isMinimized: true }); + expect(component).toMatchSnapshot(); + }); + + it('removes itself when user stops having live beacons', async () => { + const component = getComponent({ isMinimized: true }); + // started out rendered + expect(component.html()).toBeTruthy(); + + mocked(OwnBeaconStore.instance).hasLiveBeacons.mockReturnValue(false); + OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.LivenessChange, false); + + await flushPromises(); + component.setProps({}); + + expect(component.html()).toBe(null); + }); + }); +}); diff --git a/test/components/views/beacon/__snapshots__/LeftPanelLiveShareWarning-test.tsx.snap b/test/components/views/beacon/__snapshots__/LeftPanelLiveShareWarning-test.tsx.snap new file mode 100644 index 00000000000..9854191fa3d --- /dev/null +++ b/test/components/views/beacon/__snapshots__/LeftPanelLiveShareWarning-test.tsx.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` when user has live beacons renders correctly when minimized 1`] = ` + +
+
+
+ +`; + +exports[` when user has live beacons renders correctly when not minimized 1`] = ` + +
+ You are sharing your live location +
+
+`;