Backdrop filter compatibility for Firefox and Safari
							parent
							
								
									27ee7c5836
								
							
						
					
					
						commit
						652ad3617d
					
				|  | @ -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", | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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" | ||||
|         />; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -47,7 +47,7 @@ import BackdropPanel from "./BackdropPanel"; | |||
| interface IProps { | ||||
|     isMinimized: boolean; | ||||
|     resizeNotifier: ResizeNotifier; | ||||
|     backgroundImage?: ImageBitmap; | ||||
|     backgroundImage?: CanvasImageSource; | ||||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ interface IState { | |||
|     usageLimitEventTs?: number; | ||||
|     useCompactLayout: boolean; | ||||
|     activeCalls: Array<MatrixCall>; | ||||
|     backgroundImage?: ImageBitmap; | ||||
|     backgroundImage?: CanvasImageSource; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -181,7 +181,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo | |||
| }); | ||||
| 
 | ||||
| interface IProps { | ||||
|     backgroundImage?: ImageBitmap; | ||||
|     backgroundImage?: CanvasImageSource; | ||||
| } | ||||
| 
 | ||||
| const SpacePanel = (props: IProps) => { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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" | ||||
|   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" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Germain Souquet
						Germain Souquet