Backdrop filter compatibility for Firefox and Safari
This commit is contained in:
parent
27ee7c5836
commit
652ad3617d
9 changed files with 55 additions and 14 deletions
|
@ -60,6 +60,7 @@
|
||||||
"cheerio": "^1.0.0-rc.9",
|
"cheerio": "^1.0.0-rc.9",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"commonmark": "^0.29.3",
|
"commonmark": "^0.29.3",
|
||||||
|
"context-filter-polyfill": "^0.2.4",
|
||||||
"counterpart": "^0.18.6",
|
"counterpart": "^0.18.6",
|
||||||
"diff-dom": "^4.2.2",
|
"diff-dom": "^4.2.2",
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|
|
@ -26,6 +26,7 @@ $roomListCollapsedWidth: 68px;
|
||||||
// Create a row-based flexbox for the GroupFilterPanel and the room list
|
// Create a row-based flexbox for the GroupFilterPanel and the room list
|
||||||
display: flex;
|
display: flex;
|
||||||
contain: content;
|
contain: content;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.mx_LeftPanel_GroupFilterPanelContainer {
|
.mx_LeftPanel_GroupFilterPanelContainer {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { createRef } from "react";
|
import React, { createRef } from "react";
|
||||||
|
import "context-filter-polyfill";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
backgroundImage?: ImageBitmap;
|
backgroundImage?: CanvasImageSource;
|
||||||
blur?: string;
|
blur?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,20 +32,28 @@ export default class BackdropPanel extends React.PureComponent<IProps> {
|
||||||
this.canvasRef.current.width = width;
|
this.canvasRef.current.width = width;
|
||||||
this.canvasRef.current.height = height;
|
this.canvasRef.current.height = height;
|
||||||
|
|
||||||
const destinationX = width - backgroundImage.width;
|
const imageWidth = (backgroundImage as ImageBitmap).width
|
||||||
const destinationY = height - backgroundImage.height;
|
|| (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.filter = `blur(${this.props.blur})`;
|
||||||
this.ctx.drawImage(
|
this.ctx.drawImage(
|
||||||
backgroundImage,
|
backgroundImage,
|
||||||
Math.min(destinationX, 0),
|
Math.min(destinationX, 0),
|
||||||
Math.min(destinationY, 0),
|
Math.min(destinationY, 0),
|
||||||
Math.max(width, backgroundImage.width),
|
Math.max(width, imageWidth),
|
||||||
Math.max(height, backgroundImage.height),
|
Math.max(height, imageHeight),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return <canvas ref={this.canvasRef} className="mx_BackdropPanel" />;
|
return <canvas
|
||||||
|
ref={this.canvasRef}
|
||||||
|
className="mx_BackdropPanel"
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ import BackdropPanel from "./BackdropPanel";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
resizeNotifier: ResizeNotifier;
|
resizeNotifier: ResizeNotifier;
|
||||||
backgroundImage?: ImageBitmap;
|
backgroundImage?: CanvasImageSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
|
|
@ -124,7 +124,7 @@ interface IState {
|
||||||
usageLimitEventTs?: number;
|
usageLimitEventTs?: number;
|
||||||
useCompactLayout: boolean;
|
useCompactLayout: boolean;
|
||||||
activeCalls: Array<MatrixCall>;
|
activeCalls: Array<MatrixCall>;
|
||||||
backgroundImage?: ImageBitmap;
|
backgroundImage?: CanvasImageSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -181,7 +181,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
backgroundImage?: ImageBitmap;
|
backgroundImage?: CanvasImageSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpacePanel = (props: IProps) => {
|
const SpacePanel = (props: IProps) => {
|
||||||
|
|
|
@ -19,11 +19,12 @@ import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { User } from "matrix-js-sdk/src/models/user";
|
import { User } from "matrix-js-sdk/src/models/user";
|
||||||
import { throttle } from "lodash";
|
import { memoize, 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";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
|
import { getDrawable } from "../utils/drawable";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
|
@ -138,7 +139,7 @@ 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> {
|
public async getAvatarBitmap(avatarSize = 32): Promise<CanvasImageSource> {
|
||||||
let avatarUrl = this.getHttpAvatarUrl(avatarSize);
|
let avatarUrl = this.getHttpAvatarUrl(avatarSize);
|
||||||
const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage");
|
const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage");
|
||||||
if (settingBgMxc) {
|
if (settingBgMxc) {
|
||||||
|
@ -146,14 +147,14 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatarUrl) {
|
if (avatarUrl) {
|
||||||
const response = await fetch(avatarUrl);
|
return await this.buildBitmap(avatarUrl);
|
||||||
const blob = await response.blob();
|
|
||||||
return await createImageBitmap(blob);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private buildBitmap = memoize(getDrawable);
|
||||||
|
|
||||||
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) {
|
||||||
|
|
24
src/utils/drawable.ts
Normal file
24
src/utils/drawable.ts
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -2652,6 +2652,11 @@ content-type@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
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:
|
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"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue