You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-03 00:33:22 +03:00 
			
		
		
		
	Move backdrop filter to a canvas based solution
This commit is contained in:
		@@ -43,7 +43,6 @@ $roomListCollapsedWidth: 68px;
 | 
				
			|||||||
    // Note: The 'room list' in this context is actually everything that isn't the tag
 | 
					    // Note: The 'room list' in this context is actually everything that isn't the tag
 | 
				
			||||||
    // panel, such as the menu options, breadcrumbs, filtering, etc
 | 
					    // panel, such as the menu options, breadcrumbs, filtering, etc
 | 
				
			||||||
    .mx_LeftPanel_roomListContainer {
 | 
					    .mx_LeftPanel_roomListContainer {
 | 
				
			||||||
        background-color: $roomlist-bg-color;
 | 
					 | 
				
			||||||
        flex: 1 0 0;
 | 
					        flex: 1 0 0;
 | 
				
			||||||
        min-width: 0;
 | 
					        min-width: 0;
 | 
				
			||||||
        // Create another flexbox (this time a column) for the room list components
 | 
					        // Create another flexbox (this time a column) for the room list components
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,16 @@ limitations under the License.
 | 
				
			|||||||
    height: 100%;
 | 
					    height: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mx_BackdropPanel {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    min-height: 100%;
 | 
				
			||||||
 | 
					    z-index: 0;
 | 
				
			||||||
 | 
					    opacity: .15;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.mx_MatrixToolbar {
 | 
					.mx_MatrixToolbar {
 | 
				
			||||||
    order: 1;
 | 
					    order: 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ $activeBorderColor: $secondary-fg-color;
 | 
				
			|||||||
    background-color: $groupFilterPanel-bg-color;
 | 
					    background-color: $groupFilterPanel-bg-color;
 | 
				
			||||||
    padding: 0;
 | 
					    padding: 0;
 | 
				
			||||||
    margin: 0;
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create another flexbox so the Panel fills the container
 | 
					    // Create another flexbox so the Panel fills the container
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,27 +4,6 @@
 | 
				
			|||||||
// set the user avatar (if any) as a background so
 | 
					// set the user avatar (if any) as a background so
 | 
				
			||||||
// it can be blurred by the tag panel and room list
 | 
					// 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 {
 | 
					.mx_RoomSublist_showNButton {
 | 
				
			||||||
    background-color: transparent !important;
 | 
					    background-color: transparent !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										50
									
								
								src/components/structures/BackdropPanel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/components/structures/BackdropPanel.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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<IProps> {
 | 
				
			||||||
 | 
					    private canvasRef: React.RefObject<HTMLCanvasElement> = 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 <canvas ref={this.canvasRef} className="mx_BackdropPanel" />;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -36,18 +36,18 @@ import SettingsStore from "../../settings/SettingsStore";
 | 
				
			|||||||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore";
 | 
					import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore";
 | 
				
			||||||
import IndicatorScrollbar from "../structures/IndicatorScrollbar";
 | 
					import IndicatorScrollbar from "../structures/IndicatorScrollbar";
 | 
				
			||||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
 | 
					import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
 | 
				
			||||||
import { OwnProfileStore } from "../../stores/OwnProfileStore";
 | 
					 | 
				
			||||||
import RoomListNumResults from "../views/rooms/RoomListNumResults";
 | 
					import RoomListNumResults from "../views/rooms/RoomListNumResults";
 | 
				
			||||||
import LeftPanelWidget from "./LeftPanelWidget";
 | 
					import LeftPanelWidget from "./LeftPanelWidget";
 | 
				
			||||||
import {replaceableComponent} from "../../utils/replaceableComponent";
 | 
					import {replaceableComponent} from "../../utils/replaceableComponent";
 | 
				
			||||||
import {mediaFromMxc} from "../../customisations/Media";
 | 
					 | 
				
			||||||
import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
 | 
					import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
 | 
				
			||||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
 | 
					import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
 | 
				
			||||||
import UIStore from "../../stores/UIStore";
 | 
					import UIStore from "../../stores/UIStore";
 | 
				
			||||||
 | 
					import BackdropPanel from "./BackdropPanel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IProps {
 | 
					interface IProps {
 | 
				
			||||||
    isMinimized: boolean;
 | 
					    isMinimized: boolean;
 | 
				
			||||||
    resizeNotifier: ResizeNotifier;
 | 
					    resizeNotifier: ResizeNotifier;
 | 
				
			||||||
 | 
					    backgroundImage?: ImageBitmap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IState {
 | 
					interface IState {
 | 
				
			||||||
@@ -85,16 +85,14 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
 | 
					        BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
 | 
				
			||||||
        RoomListStore.instance.on(LISTS_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);
 | 
					        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.groupFilterPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => {
 | 
				
			||||||
            this.setState({showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel")});
 | 
					            this.setState({showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel")});
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public componentDidMount() {
 | 
					    public componentDidMount() {
 | 
				
			||||||
 | 
					        UIStore.instance.trackElementDimensions("LeftPanel", this.ref.current);
 | 
				
			||||||
        UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
 | 
					        UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
 | 
				
			||||||
        UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
 | 
					        UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
 | 
				
			||||||
        // Using the passive option to not block the main thread
 | 
					        // Using the passive option to not block the main thread
 | 
				
			||||||
@@ -104,10 +102,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public componentWillUnmount() {
 | 
					    public componentWillUnmount() {
 | 
				
			||||||
        SettingsStore.unwatchSetting(this.groupFilterPanelWatcherRef);
 | 
					        SettingsStore.unwatchSetting(this.groupFilterPanelWatcherRef);
 | 
				
			||||||
        SettingsStore.unwatchSetting(this.bgImageWatcherRef);
 | 
					 | 
				
			||||||
        BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
 | 
					        BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
 | 
				
			||||||
        RoomListStore.instance.off(LISTS_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);
 | 
					        SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
 | 
				
			||||||
        UIStore.instance.stopTrackingElementDimensions("ListContainer");
 | 
					        UIStore.instance.stopTrackingElementDimensions("ListContainer");
 | 
				
			||||||
        UIStore.instance.removeListener("ListContainer", this.refreshStickyHeaders);
 | 
					        UIStore.instance.removeListener("ListContainer", this.refreshStickyHeaders);
 | 
				
			||||||
@@ -144,23 +140,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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) {
 | 
					    private handleStickyHeaders(list: HTMLDivElement) {
 | 
				
			||||||
        if (this.isDoingStickyHeaders) return;
 | 
					        if (this.isDoingStickyHeaders) return;
 | 
				
			||||||
        this.isDoingStickyHeaders = true;
 | 
					        this.isDoingStickyHeaders = true;
 | 
				
			||||||
@@ -453,8 +432,15 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
				
			|||||||
            "mx_AutoHideScrollbar",
 | 
					            "mx_AutoHideScrollbar",
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const panelDimensions = UIStore.instance.getElementDimensions("LeftPanel");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div className={containerClasses} ref={this.ref}>
 | 
					            <div className={containerClasses} ref={this.ref}>
 | 
				
			||||||
 | 
					                <BackdropPanel
 | 
				
			||||||
 | 
					                    backgroundImage={this.props.backgroundImage}
 | 
				
			||||||
 | 
					                    width={panelDimensions?.width}
 | 
				
			||||||
 | 
					                    height={panelDimensions?.height}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
                {leftLeftPanel}
 | 
					                {leftLeftPanel}
 | 
				
			||||||
                <aside className="mx_LeftPanel_roomListContainer">
 | 
					                <aside className="mx_LeftPanel_roomListContainer">
 | 
				
			||||||
                    {this.renderHeader()}
 | 
					                    {this.renderHeader()}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,10 @@ import {replaceableComponent} from "../../utils/replaceableComponent";
 | 
				
			|||||||
import CallHandler, { CallHandlerEvent } from '../../CallHandler';
 | 
					import CallHandler, { CallHandlerEvent } from '../../CallHandler';
 | 
				
			||||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
 | 
					import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
 | 
				
			||||||
import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall';
 | 
					import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall';
 | 
				
			||||||
 | 
					import { OwnProfileStore } from '../../stores/OwnProfileStore';
 | 
				
			||||||
 | 
					import { UPDATE_EVENT } from "../../stores/AsyncStore";
 | 
				
			||||||
 | 
					import { mediaFromMxc } from "../../customisations/Media";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// We need to fetch each pinned message individually (if we don't already have it)
 | 
					// 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.
 | 
					// so each pinned message may trigger a request. Limit the number per room for sanity.
 | 
				
			||||||
@@ -120,6 +124,7 @@ interface IState {
 | 
				
			|||||||
    usageLimitEventTs?: number;
 | 
					    usageLimitEventTs?: number;
 | 
				
			||||||
    useCompactLayout: boolean;
 | 
					    useCompactLayout: boolean;
 | 
				
			||||||
    activeCalls: Array<MatrixCall>;
 | 
					    activeCalls: Array<MatrixCall>;
 | 
				
			||||||
 | 
					    backgroundImage?: ImageBitmap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -198,6 +203,8 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
				
			|||||||
        this.resizer = this._createResizer();
 | 
					        this.resizer = this._createResizer();
 | 
				
			||||||
        this.resizer.attach();
 | 
					        this.resizer.attach();
 | 
				
			||||||
        this._loadResizerPreferences();
 | 
					        this._loadResizerPreferences();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        OwnProfileStore.instance.on(UPDATE_EVENT, this.refreshBackgroundImage);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentWillUnmount() {
 | 
					    componentWillUnmount() {
 | 
				
			||||||
@@ -206,10 +213,17 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
				
			|||||||
        this._matrixClient.removeListener("accountData", this.onAccountData);
 | 
					        this._matrixClient.removeListener("accountData", this.onAccountData);
 | 
				
			||||||
        this._matrixClient.removeListener("sync", this.onSync);
 | 
					        this._matrixClient.removeListener("sync", this.onSync);
 | 
				
			||||||
        this._matrixClient.removeListener("RoomState.events", this.onRoomStateEvents);
 | 
					        this._matrixClient.removeListener("RoomState.events", this.onRoomStateEvents);
 | 
				
			||||||
 | 
					        OwnProfileStore.instance.off(UPDATE_EVENT, this.refreshBackgroundImage);
 | 
				
			||||||
        SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
 | 
					        SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
 | 
				
			||||||
        this.resizer.detach();
 | 
					        this.resizer.detach();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private refreshBackgroundImage = async (): Promise<void> => {
 | 
				
			||||||
 | 
					        this.setState({
 | 
				
			||||||
 | 
					            backgroundImage: await OwnProfileStore.instance.getAvatarBitmap(),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private onCallsChanged = () => {
 | 
					    private onCallsChanged = () => {
 | 
				
			||||||
        this.setState({
 | 
					        this.setState({
 | 
				
			||||||
            activeCalls: CallHandler.sharedInstance().getAllActiveCalls(),
 | 
					            activeCalls: CallHandler.sharedInstance().getAllActiveCalls(),
 | 
				
			||||||
@@ -633,10 +647,13 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
				
			|||||||
                >
 | 
					                >
 | 
				
			||||||
                    <ToastContainer />
 | 
					                    <ToastContainer />
 | 
				
			||||||
                    <div ref={this._resizeContainer} className={bodyClasses}>
 | 
					                    <div ref={this._resizeContainer} className={bodyClasses}>
 | 
				
			||||||
                        { SettingsStore.getValue("feature_spaces") ? <SpacePanel /> : null }
 | 
					                        { SettingsStore.getValue("feature_spaces")
 | 
				
			||||||
 | 
					                            ? <SpacePanel backgroundImage={this.state.backgroundImage} />
 | 
				
			||||||
 | 
					                            : null }
 | 
				
			||||||
                        <LeftPanel
 | 
					                        <LeftPanel
 | 
				
			||||||
                            isMinimized={this.props.collapseLhs || false}
 | 
					                            isMinimized={this.props.collapseLhs || false}
 | 
				
			||||||
                            resizeNotifier={this.props.resizeNotifier}
 | 
					                            resizeNotifier={this.props.resizeNotifier}
 | 
				
			||||||
 | 
					                            backgroundImage={this.state.backgroundImage}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        <ResizeHandle />
 | 
					                        <ResizeHandle />
 | 
				
			||||||
                        { pageElement }
 | 
					                        { pageElement }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import React, { Dispatch, ReactNode, SetStateAction, useEffect, useState } from "react";
 | 
					import React, { Dispatch, ReactNode, SetStateAction, useEffect, useRef, useState } from "react";
 | 
				
			||||||
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
 | 
					import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
					import { Room } from "matrix-js-sdk/src/models/room";
 | 
				
			||||||
@@ -43,6 +43,8 @@ import { Key } from "../../../Keyboard";
 | 
				
			|||||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
 | 
					import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
 | 
				
			||||||
import { NotificationState } from "../../../stores/notifications/NotificationState";
 | 
					import { NotificationState } from "../../../stores/notifications/NotificationState";
 | 
				
			||||||
import SettingsStore from "../../../settings/SettingsStore";
 | 
					import SettingsStore from "../../../settings/SettingsStore";
 | 
				
			||||||
 | 
					import BackdropPanel from "../../structures/BackdropPanel";
 | 
				
			||||||
 | 
					import UIStore from "../../../stores/UIStore";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IButtonProps {
 | 
					interface IButtonProps {
 | 
				
			||||||
    space?: Room;
 | 
					    space?: Room;
 | 
				
			||||||
@@ -178,7 +180,11 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
 | 
				
			|||||||
    </div>;
 | 
					    </div>;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SpacePanel = () => {
 | 
					interface IProps {
 | 
				
			||||||
 | 
					    backgroundImage?: ImageBitmap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SpacePanel = (props: IProps) => {
 | 
				
			||||||
    // We don't need the handle as we position the menu in a constant location
 | 
					    // We don't need the handle as we position the menu in a constant location
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
    const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<void>();
 | 
					    const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<void>();
 | 
				
			||||||
@@ -261,6 +267,15 @@ const SpacePanel = () => {
 | 
				
			|||||||
        openMenu();
 | 
					        openMenu();
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const ref: React.RefObject<HTMLUListElement> = useRef(null);
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        UIStore.instance.trackElementDimensions("SpacePanel", ref.current);
 | 
				
			||||||
 | 
					        return () => {
 | 
				
			||||||
 | 
					            UIStore.instance.stopTrackingElementDimensions("SpacePanel");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, []);
 | 
				
			||||||
 | 
					    const panelDimensions = UIStore.instance.getElementDimensions("SpacePanel");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <DragDropContext onDragEnd={result => {
 | 
					        <DragDropContext onDragEnd={result => {
 | 
				
			||||||
            if (!result.destination) return; // dropped outside the list
 | 
					            if (!result.destination) return; // dropped outside the list
 | 
				
			||||||
@@ -271,7 +286,13 @@ const SpacePanel = () => {
 | 
				
			|||||||
                    <ul
 | 
					                    <ul
 | 
				
			||||||
                        className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
 | 
					                        className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
 | 
				
			||||||
                        onKeyDown={onKeyDownHandler}
 | 
					                        onKeyDown={onKeyDownHandler}
 | 
				
			||||||
 | 
					                        ref={ref}
 | 
				
			||||||
                    >
 | 
					                    >
 | 
				
			||||||
 | 
					                        <BackdropPanel
 | 
				
			||||||
 | 
					                            backgroundImage={props.backgroundImage}
 | 
				
			||||||
 | 
					                            width={panelDimensions?.width}
 | 
				
			||||||
 | 
					                            height={panelDimensions?.height}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                        <Droppable droppableId="top-level-spaces">
 | 
					                        <Droppable droppableId="top-level-spaces">
 | 
				
			||||||
                            {(provided, snapshot) => (
 | 
					                            {(provided, snapshot) => (
 | 
				
			||||||
                                <AutoHideScrollbar
 | 
					                                <AutoHideScrollbar
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import { throttle } from "lodash";
 | 
				
			|||||||
import { MatrixClientPeg } from "../MatrixClientPeg";
 | 
					import { MatrixClientPeg } from "../MatrixClientPeg";
 | 
				
			||||||
import { _t } from "../languageHandler";
 | 
					import { _t } from "../languageHandler";
 | 
				
			||||||
import {mediaFromMxc} from "../customisations/Media";
 | 
					import {mediaFromMxc} from "../customisations/Media";
 | 
				
			||||||
 | 
					import SettingsStore from "../settings/SettingsStore";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IState {
 | 
					interface IState {
 | 
				
			||||||
    displayName?: string;
 | 
					    displayName?: string;
 | 
				
			||||||
@@ -137,6 +138,22 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
        await this.updateState({displayName: profileInfo.displayname, avatarUrl: profileInfo.avatar_url});
 | 
					        await this.updateState({displayName: profileInfo.displayname, avatarUrl: profileInfo.avatar_url});
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async getAvatarBitmap(avatarSize = 32): Promise<ImageBitmap> {
 | 
				
			||||||
 | 
					        let avatarUrl = this.getHttpAvatarUrl(avatarSize);
 | 
				
			||||||
 | 
					        const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage");
 | 
				
			||||||
 | 
					        if (settingBgMxc) {
 | 
				
			||||||
 | 
					            avatarUrl = mediaFromMxc(settingBgMxc).getSquareThumbnailHttp(avatarSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (avatarUrl) {
 | 
				
			||||||
 | 
					            const response = await fetch(avatarUrl);
 | 
				
			||||||
 | 
					            const blob = await response.blob();
 | 
				
			||||||
 | 
					            return await createImageBitmap(blob);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private onStateEvents = throttle(async (ev: MatrixEvent) => {
 | 
					    private onStateEvents = throttle(async (ev: MatrixEvent) => {
 | 
				
			||||||
        const myUserId = MatrixClientPeg.get().getUserId();
 | 
					        const myUserId = MatrixClientPeg.get().getUserId();
 | 
				
			||||||
        if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) {
 | 
					        if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user