From 3512ff8b80c3eea523e3ea6e05196920144bd575 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Thu, 10 Feb 2022 23:58:26 +0400 Subject: [PATCH] Fix Safari reactions --- src/components/chat/contextMenu.ts | 269 +---------------------- src/components/chat/reactionsMenu.ts | 290 +++++++++++++++++++++++++ src/environment/canUseTransferables.ts | 22 ++ src/lib/rlottie/lottieLoader.ts | 5 +- src/lib/rlottie/rlottie.worker.ts | 27 +-- src/lib/rlottie/rlottiePlayer.ts | 20 +- src/scss/partials/_button.scss | 22 +- src/scss/style.scss | 2 + 8 files changed, 350 insertions(+), 307 deletions(-) create mode 100644 src/components/chat/reactionsMenu.ts create mode 100644 src/environment/canUseTransferables.ts diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 9852498d..135e7c9f 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -29,278 +29,13 @@ import { Message, Poll, Chat as MTChat, MessageMedia, AvailableReaction } from " import PopupReportMessages from "../popups/reportMessages"; import assumeType from "../../helpers/assumeType"; import PopupSponsored from "../popups/sponsored"; -import Scrollable, { ScrollableBase, ScrollableX } from "../scrollable"; -import { wrapSticker } from "../wrappers"; -import RLottiePlayer from "../../lib/rlottie/rlottiePlayer"; -import getVisibleRect from "../../helpers/dom/getVisibleRect"; import ListenerSetter from "../../helpers/listenerSetter"; -import animationIntersector from "../animationIntersector"; import { getMiddleware } from "../../helpers/middleware"; -import noop from "../../helpers/noop"; -import callbackify from "../../helpers/callbackify"; -import rootScope from "../../lib/rootScope"; -import { fastRaf } from "../../helpers/schedulers"; -import lottieLoader from "../../lib/rlottie/lottieLoader"; import PeerTitle from "../peerTitle"; import StackedAvatars from "../stackedAvatars"; import { IS_APPLE } from "../../environment/userAgent"; import PopupReactedList from "../popups/reactedList"; - -const REACTIONS_CLASS_NAME = 'btn-menu-reactions'; -const REACTION_CLASS_NAME = REACTIONS_CLASS_NAME + '-reaction'; - -const REACTION_SIZE = 28; -const PADDING = 4; -const REACTION_CONTAINER_SIZE = REACTION_SIZE + PADDING * 2; - -type ChatReactionsMenuPlayers = { - select?: RLottiePlayer, - appear?: RLottiePlayer, - selectWrapper: HTMLElement, - appearWrapper: HTMLElement, - reaction: string -}; -export class ChatReactionsMenu { - public widthContainer: HTMLElement; - private container: HTMLElement; - private reactionsMap: Map; - private scrollable: ScrollableBase; - private animationGroup: string; - private middleware: ReturnType; - private message: Message.message; - - constructor( - private appReactionsManager: AppReactionsManager, - private type: 'horizontal' | 'vertical', - middleware: ChatReactionsMenu['middleware'] - ) { - const widthContainer = this.widthContainer = document.createElement('div'); - widthContainer.classList.add(REACTIONS_CLASS_NAME + '-container'); - widthContainer.classList.add(REACTIONS_CLASS_NAME + '-container-' + type); - - const reactionsContainer = this.container = document.createElement('div'); - reactionsContainer.classList.add(REACTIONS_CLASS_NAME); - - const reactionsScrollable = this.scrollable = type === 'vertical' ? new Scrollable(undefined) : new ScrollableX(undefined); - reactionsContainer.append(reactionsScrollable.container); - reactionsScrollable.onAdditionalScroll = this.onScroll; - reactionsScrollable.setListeners(); - - reactionsScrollable.container.classList.add('no-scrollbar'); - - ['big'].forEach(type => { - const bubble = document.createElement('div'); - bubble.classList.add(REACTIONS_CLASS_NAME + '-bubble', REACTIONS_CLASS_NAME + '-bubble-' + type); - reactionsContainer.append(bubble); - }); - - this.reactionsMap = new Map(); - this.animationGroup = 'CHAT-MENU-REACTIONS-' + Date.now(); - animationIntersector.setOverrideIdleGroup(this.animationGroup, true); - - if(!IS_TOUCH_SUPPORTED) { - reactionsContainer.addEventListener('mousemove', this.onMouseMove); - } - - attachClickEvent(reactionsContainer, (e) => { - const reactionDiv = findUpClassName(e.target, REACTION_CLASS_NAME); - if(!reactionDiv) return; - - const players = this.reactionsMap.get(reactionDiv); - if(!players) return; - - this.appReactionsManager.sendReaction(this.message, players.reaction); - }); - - widthContainer.append(reactionsContainer); - - this.middleware = middleware ?? getMiddleware(); - } - - public init(message: Message.message) { - this.message = message; - - const middleware = this.middleware.get(); - // const result = Promise.resolve(this.appReactionsManager.getAvailableReactionsForPeer(message.peerId)).then((res) => pause(1000).then(() => res)); - const result = this.appReactionsManager.getAvailableReactionsByMessage(message); - callbackify(result, (reactions) => { - if(!middleware() || !reactions.length) return; - reactions.forEach(reaction => { - this.renderReaction(reaction); - }); - - const setVisible = () => { - this.container.classList.add('is-visible'); - }; - - if(result instanceof Promise) { - fastRaf(setVisible); - } else { - setVisible(); - } - }); - } - - public cleanup() { - this.middleware.clean(); - this.scrollable.removeListeners(); - this.reactionsMap.clear(); - animationIntersector.setOverrideIdleGroup(this.animationGroup, false); - animationIntersector.checkAnimations(true, this.animationGroup, true); - } - - private onScroll = () => { - this.reactionsMap.forEach((players, div) => { - this.onScrollProcessItem(div, players); - }); - }; - - private renderReaction(reaction: AvailableReaction) { - const reactionDiv = document.createElement('div'); - reactionDiv.classList.add(REACTION_CLASS_NAME); - - const scaleContainer = document.createElement('div'); - scaleContainer.classList.add(REACTION_CLASS_NAME + '-scale'); - - const appearWrapper = document.createElement('div'); - let selectWrapper: HTMLElement;; - appearWrapper.classList.add(REACTION_CLASS_NAME + '-appear'); - - if(rootScope.settings.animationsEnabled) { - selectWrapper = document.createElement('div'); - selectWrapper.classList.add(REACTION_CLASS_NAME + '-select', 'hide'); - } - - const players: ChatReactionsMenuPlayers = { - selectWrapper, - appearWrapper, - reaction: reaction.reaction - }; - this.reactionsMap.set(reactionDiv, players); - - const middleware = this.middleware.get(); - - const hoverScale = IS_TOUCH_SUPPORTED ? 1 : 1.25; - const size = REACTION_SIZE * hoverScale; - - const options = { - width: size, - height: size, - skipRatio: 1, - needFadeIn: false, - withThumb: false, - group: this.animationGroup, - middleware - }; - - if(!rootScope.settings.animationsEnabled) { - delete options.needFadeIn; - delete options.withThumb; - - wrapSticker({ - doc: reaction.static_icon, - div: appearWrapper, - ...options - }); - } else { - let isFirst = true; - wrapSticker({ - doc: reaction.appear_animation, - div: appearWrapper, - play: true, - ...options - }).then(player => { - assumeType(player); - - players.appear = player; - - player.addEventListener('enterFrame', (frameNo) => { - if(player.maxFrame === frameNo) { - selectLoadPromise.then((selectPlayer) => { - assumeType(selectPlayer); - appearWrapper.classList.add('hide'); - selectWrapper.classList.remove('hide'); - - if(isFirst) { - players.select = selectPlayer; - isFirst = false; - } - }, noop); - } - }); - }, noop); - - const selectLoadPromise = wrapSticker({ - doc: reaction.select_animation, - div: selectWrapper, - ...options - }).then(player => { - assumeType(player); - - return lottieLoader.waitForFirstFrame(player); - }).catch(noop); - } - - scaleContainer.append(appearWrapper); - selectWrapper && scaleContainer.append(selectWrapper); - reactionDiv.append(scaleContainer); - this.scrollable.append(reactionDiv); - } - - private onScrollProcessItem(div: HTMLElement, players: ChatReactionsMenuPlayers) { - const scaleContainer = div.firstElementChild as HTMLElement; - const visibleRect = getVisibleRect(div, this.scrollable.container); - if(!visibleRect) { - if(!players.appearWrapper.classList.contains('hide') || !players.appear) { - return; - } - - if(players.select) { - players.select.stop(); - } - - players.appear.stop(); - players.appear.autoplay = true; - players.appearWrapper.classList.remove('hide'); - players.selectWrapper.classList.add('hide'); - scaleContainer.style.transform = ''; - } else if(visibleRect.overflow.left || visibleRect.overflow.right) { - const diff = Math.abs(visibleRect.rect.left - visibleRect.rect.right); - const scale = Math.min(diff ** 2 / REACTION_CONTAINER_SIZE ** 2, 1); - - scaleContainer.style.transform = `scale(${scale})`; - } else { - scaleContainer.style.transform = ''; - } - } - - private onMouseMove = (e: MouseEvent) => { - const reactionDiv = findUpClassName(e.target, REACTION_CLASS_NAME); - if(!reactionDiv) { - return; - } - - const players = this.reactionsMap.get(reactionDiv); - if(!players) { - return; - } - - // do not play select animation when appearing - if(!players.appear?.paused) { - return; - } - - const player = players.select; - if(!player) { - return; - } - - if(player.paused) { - player.autoplay = true; - player.restart(); - } - }; -} +import { ChatReactionsMenu } from "./reactionsMenu"; export default class ChatContextMenu { private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true, isSponsored?: true})[]; @@ -325,7 +60,7 @@ export default class ChatContextMenu { private viewerPeerId: PeerId; private middleware: ReturnType; - canOpenReactedList: boolean; + private canOpenReactedList: boolean; constructor( private attachTo: HTMLElement, diff --git a/src/components/chat/reactionsMenu.ts b/src/components/chat/reactionsMenu.ts new file mode 100644 index 00000000..3b0a8cca --- /dev/null +++ b/src/components/chat/reactionsMenu.ts @@ -0,0 +1,290 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport"; +import { IS_SAFARI } from "../../environment/userAgent"; +import assumeType from "../../helpers/assumeType"; +import callbackify from "../../helpers/callbackify"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import findUpClassName from "../../helpers/dom/findUpClassName"; +import getVisibleRect from "../../helpers/dom/getVisibleRect"; +import { getMiddleware } from "../../helpers/middleware"; +import noop from "../../helpers/noop"; +import { fastRaf } from "../../helpers/schedulers"; +import { Message, AvailableReaction } from "../../layer"; +import type { AppReactionsManager } from "../../lib/appManagers/appReactionsManager"; +import lottieLoader from "../../lib/rlottie/lottieLoader"; +import RLottiePlayer from "../../lib/rlottie/rlottiePlayer"; +import rootScope from "../../lib/rootScope"; +import animationIntersector from "../animationIntersector"; +import Scrollable, { ScrollableBase, ScrollableX } from "../scrollable"; +import { wrapSticker } from "../wrappers"; + +const REACTIONS_CLASS_NAME = 'btn-menu-reactions'; +const REACTION_CLASS_NAME = REACTIONS_CLASS_NAME + '-reaction'; + +const REACTION_SIZE = 28; +const PADDING = 4; +const REACTION_CONTAINER_SIZE = REACTION_SIZE + PADDING * 2; + +const CAN_USE_TRANSFORM = !IS_SAFARI; + +type ChatReactionsMenuPlayers = { + select?: RLottiePlayer, + appear?: RLottiePlayer, + selectWrapper: HTMLElement, + appearWrapper: HTMLElement, + reaction: string +}; +export class ChatReactionsMenu { + public widthContainer: HTMLElement; + private container: HTMLElement; + private reactionsMap: Map; + private scrollable: ScrollableBase; + private animationGroup: string; + private middleware: ReturnType; + private message: Message.message; + + constructor( + private appReactionsManager: AppReactionsManager, + private type: 'horizontal' | 'vertical', + middleware: ChatReactionsMenu['middleware'] + ) { + const widthContainer = this.widthContainer = document.createElement('div'); + widthContainer.classList.add(REACTIONS_CLASS_NAME + '-container'); + widthContainer.classList.add(REACTIONS_CLASS_NAME + '-container-' + type); + + const reactionsContainer = this.container = document.createElement('div'); + reactionsContainer.classList.add(REACTIONS_CLASS_NAME); + + const reactionsScrollable = this.scrollable = type === 'vertical' ? new Scrollable(undefined) : new ScrollableX(undefined); + reactionsContainer.append(reactionsScrollable.container); + reactionsScrollable.onAdditionalScroll = this.onScroll; + reactionsScrollable.setListeners(); + + reactionsScrollable.container.classList.add('no-scrollbar'); + + ['big'].forEach(type => { + const bubble = document.createElement('div'); + bubble.classList.add(REACTIONS_CLASS_NAME + '-bubble', REACTIONS_CLASS_NAME + '-bubble-' + type); + reactionsContainer.append(bubble); + }); + + this.reactionsMap = new Map(); + this.animationGroup = 'CHAT-MENU-REACTIONS-' + Date.now(); + animationIntersector.setOverrideIdleGroup(this.animationGroup, true); + + if(!IS_TOUCH_SUPPORTED) { + reactionsContainer.addEventListener('mousemove', this.onMouseMove); + } + + attachClickEvent(reactionsContainer, (e) => { + const reactionDiv = findUpClassName(e.target, REACTION_CLASS_NAME); + if(!reactionDiv) return; + + const players = this.reactionsMap.get(reactionDiv); + if(!players) return; + + this.appReactionsManager.sendReaction(this.message, players.reaction); + }); + + widthContainer.append(reactionsContainer); + + this.middleware = middleware ?? getMiddleware(); + } + + public init(message: Message.message) { + this.message = message; + + const middleware = this.middleware.get(); + // const result = Promise.resolve(this.appReactionsManager.getAvailableReactionsForPeer(message.peerId)).then((res) => pause(1000).then(() => res)); + const result = this.appReactionsManager.getAvailableReactionsByMessage(message); + callbackify(result, (reactions) => { + if(!middleware() || !reactions.length) return; + reactions.forEach(reaction => { + this.renderReaction(reaction); + }); + + const setVisible = () => { + this.container.classList.add('is-visible'); + }; + + if(result instanceof Promise) { + fastRaf(setVisible); + } else { + setVisible(); + } + }); + } + + public cleanup() { + this.middleware.clean(); + this.scrollable.removeListeners(); + this.reactionsMap.clear(); + animationIntersector.setOverrideIdleGroup(this.animationGroup, false); + animationIntersector.checkAnimations(true, this.animationGroup, true); + } + + private onScroll = () => { + this.reactionsMap.forEach((players, div) => { + this.onScrollProcessItem(div, players); + }); + }; + + private renderReaction(reaction: AvailableReaction) { + const reactionDiv = document.createElement('div'); + reactionDiv.classList.add(REACTION_CLASS_NAME); + + const scaleContainer = document.createElement('div'); + scaleContainer.classList.add(REACTION_CLASS_NAME + '-scale'); + + const appearWrapper = document.createElement('div'); + let selectWrapper: HTMLElement;; + appearWrapper.classList.add(REACTION_CLASS_NAME + '-appear'); + + if(rootScope.settings.animationsEnabled) { + selectWrapper = document.createElement('div'); + selectWrapper.classList.add(REACTION_CLASS_NAME + '-select', 'hide'); + } + + const players: ChatReactionsMenuPlayers = { + selectWrapper, + appearWrapper, + reaction: reaction.reaction + }; + this.reactionsMap.set(reactionDiv, players); + + const middleware = this.middleware.get(); + + const hoverScale = IS_TOUCH_SUPPORTED ? 1 : 1.25; + const size = REACTION_SIZE * hoverScale; + + const options = { + width: size, + height: size, + skipRatio: 1, + needFadeIn: false, + withThumb: false, + group: this.animationGroup, + middleware + }; + + if(!rootScope.settings.animationsEnabled) { + delete options.needFadeIn; + delete options.withThumb; + + wrapSticker({ + doc: reaction.static_icon, + div: appearWrapper, + ...options + }); + } else { + let isFirst = true; + wrapSticker({ + doc: reaction.appear_animation, + div: appearWrapper, + play: true, + ...options + }).then(player => { + assumeType(player); + + players.appear = player; + + player.addEventListener('enterFrame', (frameNo) => { + if(player.maxFrame === frameNo) { + selectLoadPromise.then((selectPlayer) => { + assumeType(selectPlayer); + appearWrapper.classList.add('hide'); + selectWrapper.classList.remove('hide'); + + if(isFirst) { + players.select = selectPlayer; + isFirst = false; + } + }, noop); + } + }); + }, noop); + + const selectLoadPromise = wrapSticker({ + doc: reaction.select_animation, + div: selectWrapper, + ...options + }).then(player => { + assumeType(player); + + return lottieLoader.waitForFirstFrame(player); + }).catch(noop); + } + + scaleContainer.append(appearWrapper); + selectWrapper && scaleContainer.append(selectWrapper); + reactionDiv.append(scaleContainer); + this.scrollable.append(reactionDiv); + } + + private onScrollProcessItem(div: HTMLElement, players: ChatReactionsMenuPlayers) { + // return; + + const scaleContainer = div.firstElementChild as HTMLElement; + const visibleRect = getVisibleRect(div, this.scrollable.container); + let transform: string; + if(!visibleRect) { + if(!players.appearWrapper.classList.contains('hide') || !players.appear) { + return; + } + + if(players.select) { + players.select.stop(); + } + + players.appear.stop(); + players.appear.autoplay = true; + players.appearWrapper.classList.remove('hide'); + players.selectWrapper.classList.add('hide'); + + transform = ''; + } else if(visibleRect.overflow.left || visibleRect.overflow.right) { + const diff = Math.abs(visibleRect.rect.left - visibleRect.rect.right); + const scale = Math.min(diff ** 2 / REACTION_CONTAINER_SIZE ** 2, 1); + + transform = 'scale(' + scale + ')'; + } else { + transform = ''; + } + + if(CAN_USE_TRANSFORM) { + scaleContainer.style.transform = transform; + } + } + + private onMouseMove = (e: MouseEvent) => { + const reactionDiv = findUpClassName(e.target, REACTION_CLASS_NAME); + if(!reactionDiv) { + return; + } + + const players = this.reactionsMap.get(reactionDiv); + if(!players) { + return; + } + + // do not play select animation when appearing + if(!players.appear?.paused) { + return; + } + + const player = players.select; + if(!player) { + return; + } + + if(player.paused) { + player.autoplay = true; + player.restart(); + } + }; +} diff --git a/src/environment/canUseTransferables.ts b/src/environment/canUseTransferables.ts new file mode 100644 index 00000000..a1d940f4 --- /dev/null +++ b/src/environment/canUseTransferables.ts @@ -0,0 +1,22 @@ +import { IS_SAFARI } from "./userAgent"; + +/* + * This is used as a workaround for a memory leak in Safari caused by using Transferable objects to + * transfer data between WebWorkers and the main thread. + * https://github.com/mapbox/mapbox-gl-js/issues/8771 + * + * This should be removed once the underlying Safari issue is fixed. + */ + +let CAN_USE_TRANSFERABLES: boolean; +if(!IS_SAFARI) CAN_USE_TRANSFERABLES = true; +else { + try { + const match = navigator.userAgent.match(/Version\/(.+?) /); + CAN_USE_TRANSFERABLES = +match[1] >= 14; + } catch(err) { + CAN_USE_TRANSFERABLES = false; + } +} + +export default CAN_USE_TRANSFERABLES; diff --git a/src/lib/rlottie/lottieLoader.ts b/src/lib/rlottie/lottieLoader.ts index 0cc6fade..842c4d67 100644 --- a/src/lib/rlottie/lottieLoader.ts +++ b/src/lib/rlottie/lottieLoader.ts @@ -185,7 +185,10 @@ export class LottieLoader { return; } - rlPlayer.clamped = frame; + if(rlPlayer.clamped !== undefined) { + rlPlayer.clamped = frame; + } + rlPlayer.renderFrame(frame, frameNo); }; diff --git a/src/lib/rlottie/rlottie.worker.ts b/src/lib/rlottie/rlottie.worker.ts index a3f5ea5c..6dcdf98c 100644 --- a/src/lib/rlottie/rlottie.worker.ts +++ b/src/lib/rlottie/rlottie.worker.ts @@ -4,6 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ +import CAN_USE_TRANSFERABLES from "../../environment/canUseTransferables"; import readBlobAsText from "../../helpers/blob/readBlobAsText"; import applyReplacements from "./applyReplacements"; @@ -192,29 +193,6 @@ const queryableFunctions = { } }; -/** - * Returns true when run in WebKit derived browsers. - * This is used as a workaround for a memory leak in Safari caused by using Transferable objects to - * transfer data between WebWorkers and the main thread. - * https://github.com/mapbox/mapbox-gl-js/issues/8771 - * - * This should be removed once the underlying Safari issue is fixed. - * - * @private - * @param scope {WindowOrWorkerGlobalScope} Since this function is used both on the main thread and WebWorker context, - * let the calling scope pass in the global scope object. - * @returns {boolean} - */ -let _isSafari: boolean = null; -function isSafari(scope: any) { - if(_isSafari === null) { - const userAgent = scope.navigator ? scope.navigator.userAgent : null; - _isSafari = !!scope.safari || - !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome')))); - } - return _isSafari; -} - function reply(...args: any[]) { if(arguments.length < 1) { throw new TypeError('reply - not enough arguments'); @@ -223,7 +201,8 @@ function reply(...args: any[]) { //if(arguments[0] === 'frame') return; args = Array.prototype.slice.call(arguments, 1); - if(isSafari(self)) { + + if(!CAN_USE_TRANSFERABLES) { postMessage({queryMethodListener: arguments[0], queryMethodArguments: args}); } else { const transfer: ArrayBuffer[] = []; diff --git a/src/lib/rlottie/rlottiePlayer.ts b/src/lib/rlottie/rlottiePlayer.ts index 3f0960b1..d3286e1a 100644 --- a/src/lib/rlottie/rlottiePlayer.ts +++ b/src/lib/rlottie/rlottiePlayer.ts @@ -4,6 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ +import CAN_USE_TRANSFERABLES from "../../environment/canUseTransferables"; import { IS_ANDROID, IS_APPLE_MOBILE, IS_APPLE, IS_SAFARI } from "../../environment/userAgent"; import EventListenerBase from "../../helpers/eventListenerBase"; import mediaSizes from "../../helpers/mediaSizes"; @@ -240,7 +241,10 @@ export default class RLottiePlayer extends EventListenerBase<{ this.context = this.canvas.getContext('2d'); - this.clamped = new Uint8ClampedArray(this.width * this.height * 4); + if(CAN_USE_TRANSFERABLES) { + this.clamped = new Uint8ClampedArray(this.width * this.height * 4); + } + this.imageData = new ImageData(this.width, this.height); if(this.name) { @@ -252,6 +256,10 @@ export default class RLottiePlayer extends EventListenerBase<{ } public clearCache() { + if(this.cachingDelta === Infinity) { + return; + } + if(this.cacheName && cache.getCacheCounter(this.cacheName) > 1) { // skip clearing because same sticker can be still visible return; } @@ -436,10 +444,8 @@ export default class RLottiePlayer extends EventListenerBase<{ const frame = this.frames.get(frameNo); if(frame) { this.renderFrame(frame, frameNo); - } else if(IS_SAFARI) { - this.sendQuery('renderFrame', frameNo); } else { - if(!this.clamped.length) { // fix detached + if(this.clamped && !this.clamped.length) { // fix detached this.clamped = new Uint8ClampedArray(this.width * this.height * 4); } @@ -454,6 +460,8 @@ export default class RLottiePlayer extends EventListenerBase<{ this.pause(false); return false; } + + return true; } private mainLoopForwards() { @@ -463,7 +471,7 @@ export default class RLottiePlayer extends EventListenerBase<{ this.requestFrame(frame); if((frame + skipDelta) > maxFrame) { - this.onLap(); + return this.onLap(); } return true; @@ -476,7 +484,7 @@ export default class RLottiePlayer extends EventListenerBase<{ this.requestFrame(frame); if((frame - skipDelta) < minFrame) { - this.onLap(); + return this.onLap(); } return true; diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index 0f99b528..68f68e9e 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -398,7 +398,7 @@ content: " "; pointer-events: none; border-radius: inherit; - background: linear-gradient(var(--inner-shadow-degree), var(--surface-color) 0%, transparent 1rem, transparent calc(100% - 1rem), var(--surface-color) 100%); + background: linear-gradient(var(--inner-shadow-degree), var(--surface-color) 0%, rgba(var(--surface-color-rgb), 0) 1rem, rgba(var(--surface-color-rgb), 0) calc(100% - 1rem), var(--surface-color) 100%); } .scrollable { @@ -433,25 +433,29 @@ &-scale { width: 100%; height: 100%; - transform: scale(1); + + html:not(.is-safari) & { + transform: scale(1); + } - @include animation-level(2) { + html:not(.is-safari) body.animation-level-2 & { transition: transform .1s linear; } } &-select { - html.no-touch & { + html.no-touch:not(.is-safari) & { transform: scale(1); + + &:hover, + &:active { + transform: scale(1.25); + } } - html.no-touch body.animation-level-2 & { + html.no-touch:not(.is-safari) body.animation-level-2 & { transition: transform var(--transition-standard-in); } - - @include hover() { - transform: scale(1.25); - } } .media-sticker-wrapper { diff --git a/src/scss/style.scss b/src/scss/style.scss index ac5cd81e..de1acb67 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -166,6 +166,7 @@ $chat-input-inner-padding-handhelds: .25rem; // --background-color: #fff; --background-color: var(--background-color-true); --border-color: #dfe1e5; + --surface-color-rgb: 255, 255, 255; --surface-color: #fff; --scrollbar-color: rgba(0, 0, 0, .2); --section-box-shadow-color: rgba(0, 0, 0, .06); @@ -238,6 +239,7 @@ $chat-input-inner-padding-handhelds: .25rem; // --background-color: #212121; --background-color: var(--background-color-true); --border-color: #0f0f0f; + --surface-color-rgb: 33, 33, 33; --surface-color: #212121; --scrollbar-color: rgba(255, 255, 255, .2); --section-box-shadow-color: rgba(0, 0, 0, .12);