Backdrop filter compatibility for Firefox and Safari

pull/21833/head
Germain Souquet 2021-06-25 09:20:03 +01:00
parent 27ee7c5836
commit 652ad3617d
9 changed files with 55 additions and 14 deletions

View File

@ -60,6 +60,7 @@
"cheerio": "^1.0.0-rc.9",
"classnames": "^2.2.6",
"commonmark": "^0.29.3",
"context-filter-polyfill": "^0.2.4",
"counterpart": "^0.18.6",
"diff-dom": "^4.2.2",
"diff-match-patch": "^1.0.5",

View File

@ -26,6 +26,7 @@ $roomListCollapsedWidth: 68px;
// Create a row-based flexbox for the GroupFilterPanel and the room list
display: flex;
contain: content;
position: relative;
.mx_LeftPanel_GroupFilterPanelContainer {
flex-grow: 0;

View File

@ -1,9 +1,10 @@
import React, { createRef } from "react";
import "context-filter-polyfill";
interface IProps {
width?: number;
height?: number;
backgroundImage?: ImageBitmap;
backgroundImage?: CanvasImageSource;
blur?: string;
}
@ -31,20 +32,28 @@ export default class BackdropPanel extends React.PureComponent<IProps> {
this.canvasRef.current.width = width;
this.canvasRef.current.height = height;
const destinationX = width - backgroundImage.width;
const destinationY = height - backgroundImage.height;
const imageWidth = (backgroundImage as ImageBitmap).width
|| (backgroundImage as HTMLImageElement).naturalWidth;
const imageHeight = (backgroundImage as ImageBitmap).height
|| (backgroundImage as HTMLImageElement).naturalHeight;
const destinationX = width - imageWidth;
const destinationY = height - imageHeight;
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),
Math.max(width, imageWidth),
Math.max(height, imageHeight),
);
}
public render() {
return <canvas ref={this.canvasRef} className="mx_BackdropPanel" />;
return <canvas
ref={this.canvasRef}
className="mx_BackdropPanel"
/>;
}
}

View File

@ -47,7 +47,7 @@ import BackdropPanel from "./BackdropPanel";
interface IProps {
isMinimized: boolean;
resizeNotifier: ResizeNotifier;
backgroundImage?: ImageBitmap;
backgroundImage?: CanvasImageSource;
}
interface IState {

View File

@ -124,7 +124,7 @@ interface IState {
usageLimitEventTs?: number;
useCompactLayout: boolean;
activeCalls: Array<MatrixCall>;
backgroundImage?: ImageBitmap;
backgroundImage?: CanvasImageSource;
}
/**

View File

@ -181,7 +181,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
});
interface IProps {
backgroundImage?: ImageBitmap;
backgroundImage?: CanvasImageSource;
}
const SpacePanel = (props: IProps) => {

View File

@ -19,11 +19,12 @@ import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import defaultDispatcher from "../dispatcher/dispatcher";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { User } from "matrix-js-sdk/src/models/user";
import { throttle } from "lodash";
import { memoize, throttle } from "lodash";
import { MatrixClientPeg } from "../MatrixClientPeg";
import { _t } from "../languageHandler";
import {mediaFromMxc} from "../customisations/Media";
import SettingsStore from "../settings/SettingsStore";
import { getDrawable } from "../utils/drawable";
interface IState {
displayName?: string;
@ -138,7 +139,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
await this.updateState({displayName: profileInfo.displayname, avatarUrl: profileInfo.avatar_url});
};
public async getAvatarBitmap(avatarSize = 32): Promise<ImageBitmap> {
public async getAvatarBitmap(avatarSize = 32): Promise<CanvasImageSource> {
let avatarUrl = this.getHttpAvatarUrl(avatarSize);
const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage");
if (settingBgMxc) {
@ -146,14 +147,14 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
}
if (avatarUrl) {
const response = await fetch(avatarUrl);
const blob = await response.blob();
return await createImageBitmap(blob);
return await this.buildBitmap(avatarUrl);
} else {
return null;
}
}
private buildBitmap = memoize(getDrawable);
private onStateEvents = throttle(async (ev: MatrixEvent) => {
const myUserId = MatrixClientPeg.get().getUserId();
if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) {

24
src/utils/drawable.ts Normal file
View File

@ -0,0 +1,24 @@
/**
* Fetch an image using the best available method based on browser compatibility
* @param url the URL of the image to fetch
* @returns a canvas drawable object
*/
export async function getDrawable(url: string): Promise<CanvasImageSource> {
if ('createImageBitmap' in window) {
const response = await fetch(url);
const blob = await response.blob();
return await createImageBitmap(blob);
} else {
return new Promise((resolve, reject) => {
const img = document.createElement("img");
img.crossOrigin = "anonymous";
img.onload = function() {
resolve(img);
}
img.onerror = function(e) {
reject(e);
}
img.src = url;
});
}
}

View File

@ -2652,6 +2652,11 @@ content-type@^1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
context-filter-polyfill@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/context-filter-polyfill/-/context-filter-polyfill-0.2.4.tgz#ecf88d3197e7c3a47e9a7ae2d5167b703945a5d4"
integrity sha512-LDZ3WiTzo6kIeJM7j8kPSgZf+gbD1cV1GaLyYO8RWvAg25cO3zUo3d2KizO0w9hAezNwz7tTbuWKpPdvLWzKqQ==
convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"