diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index c7dd678c07..d9db8b4fed 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -43,7 +43,6 @@ $roomListCollapsedWidth: 68px; // Note: The 'room list' in this context is actually everything that isn't the tag // panel, such as the menu options, breadcrumbs, filtering, etc .mx_LeftPanel_roomListContainer { - background-color: $roomlist-bg-color; flex: 1 0 0; min-width: 0; // Create another flexbox (this time a column) for the room list components diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index a220c5d505..2e5ffbcd82 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -35,6 +35,16 @@ limitations under the License. height: 100%; } +.mx_BackdropPanel { + position: absolute; + top: 0; + left: 0; + width: 100%; + min-height: 100%; + z-index: 0; + opacity: .15; +} + .mx_MatrixToolbar { order: 1; diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index e64057d16c..21c1bed18f 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -27,6 +27,7 @@ $activeBorderColor: $secondary-fg-color; background-color: $groupFilterPanel-bg-color; padding: 0; margin: 0; + position: relative; // Create another flexbox so the Panel fills the container display: flex; diff --git a/res/themes/light/css/_mods.scss b/res/themes/light/css/_mods.scss index fbca58dfb1..15f6d4b0fe 100644 --- a/res/themes/light/css/_mods.scss +++ b/res/themes/light/css/_mods.scss @@ -4,27 +4,6 @@ // set the user avatar (if any) as a background so // it can be blurred by the tag panel and room list -@supports (backdrop-filter: none) { - .mx_LeftPanel { - background-image: var(--avatar-url, unset); - background-repeat: no-repeat; - background-size: cover; - background-position: left top; - } - - .mx_GroupFilterPanel { - backdrop-filter: blur($groupFilterPanel-background-blur-amount); - } - - .mx_SpacePanel { - backdrop-filter: blur($groupFilterPanel-background-blur-amount); - } - - .mx_LeftPanel .mx_LeftPanel_roomListContainer { - backdrop-filter: blur($roomlist-background-blur-amount); - } -} - .mx_RoomSublist_showNButton { background-color: transparent !important; } diff --git a/src/components/structures/BackdropPanel.tsx b/src/components/structures/BackdropPanel.tsx new file mode 100644 index 0000000000..daaae418c4 --- /dev/null +++ b/src/components/structures/BackdropPanel.tsx @@ -0,0 +1,50 @@ +import React, { createRef } from "react"; + +interface IProps { + width?: number; + height?: number; + backgroundImage?: ImageBitmap; + blur?: string; +} + + +export default class BackdropPanel extends React.PureComponent { + private canvasRef: React.RefObject = createRef(); + private ctx: CanvasRenderingContext2D; + + static defaultProps = { + blur: "60px", + } + + public componentDidMount() { + this.ctx = this.canvasRef.current.getContext("2d"); + } + + public componentDidUpdate() { + if (this.props.backgroundImage) { + requestAnimationFrame(this.refreshBackdropImage); + } + } + + private refreshBackdropImage = (): void => { + const { width, height, backgroundImage } = this.props; + this.canvasRef.current.width = width; + this.canvasRef.current.height = height; + + const destinationX = width - backgroundImage.width; + const destinationY = height - backgroundImage.height; + + this.ctx.filter = `blur(${this.props.blur})`; + this.ctx.drawImage( + backgroundImage, + Math.min(destinationX, 0), + Math.min(destinationY, 0), + Math.max(width, backgroundImage.width), + Math.max(height, backgroundImage.height), + ); + } + + public render() { + return ; + } +} diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index af22db1350..f71817480e 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -36,18 +36,18 @@ import SettingsStore from "../../settings/SettingsStore"; import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore"; import IndicatorScrollbar from "../structures/IndicatorScrollbar"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; -import { OwnProfileStore } from "../../stores/OwnProfileStore"; import RoomListNumResults from "../views/rooms/RoomListNumResults"; import LeftPanelWidget from "./LeftPanelWidget"; import {replaceableComponent} from "../../utils/replaceableComponent"; -import {mediaFromMxc} from "../../customisations/Media"; import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore"; import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager"; import UIStore from "../../stores/UIStore"; +import BackdropPanel from "./BackdropPanel"; interface IProps { isMinimized: boolean; resizeNotifier: ResizeNotifier; + backgroundImage?: ImageBitmap; } interface IState { @@ -85,16 +85,14 @@ export default class LeftPanel extends React.Component { BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate); RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate); - OwnProfileStore.instance.on(UPDATE_EVENT, this.onBackgroundImageUpdate); SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.updateActiveSpace); - this.bgImageWatcherRef = SettingsStore.watchSetting( - "RoomList.backgroundImage", null, this.onBackgroundImageUpdate); this.groupFilterPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => { this.setState({showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel")}); }); } public componentDidMount() { + UIStore.instance.trackElementDimensions("LeftPanel", this.ref.current); UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current); UIStore.instance.on("ListContainer", this.refreshStickyHeaders); // Using the passive option to not block the main thread @@ -104,10 +102,8 @@ export default class LeftPanel extends React.Component { public componentWillUnmount() { SettingsStore.unwatchSetting(this.groupFilterPanelWatcherRef); - SettingsStore.unwatchSetting(this.bgImageWatcherRef); BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate); - OwnProfileStore.instance.off(UPDATE_EVENT, this.onBackgroundImageUpdate); SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace); UIStore.instance.stopTrackingElementDimensions("ListContainer"); UIStore.instance.removeListener("ListContainer", this.refreshStickyHeaders); @@ -144,23 +140,6 @@ export default class LeftPanel extends React.Component { } }; - private onBackgroundImageUpdate = () => { - // Note: we do this in the LeftPanel as it uses this variable most prominently. - const avatarSize = 32; // arbitrary - let avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); - const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage"); - if (settingBgMxc) { - avatarUrl = mediaFromMxc(settingBgMxc).getSquareThumbnailHttp(avatarSize); - } - - const avatarUrlProp = `url(${avatarUrl})`; - if (!avatarUrl) { - document.body.style.removeProperty("--avatar-url"); - } else if (document.body.style.getPropertyValue("--avatar-url") !== avatarUrlProp) { - document.body.style.setProperty("--avatar-url", avatarUrlProp); - } - }; - private handleStickyHeaders(list: HTMLDivElement) { if (this.isDoingStickyHeaders) return; this.isDoingStickyHeaders = true; @@ -453,8 +432,15 @@ export default class LeftPanel extends React.Component { "mx_AutoHideScrollbar", ); + const panelDimensions = UIStore.instance.getElementDimensions("LeftPanel"); + return (
+ {leftLeftPanel}