Fix Safari reactions
This commit is contained in:
parent
513ed97988
commit
3512ff8b80
@ -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<HTMLElement, ChatReactionsMenuPlayers>;
|
||||
private scrollable: ScrollableBase;
|
||||
private animationGroup: string;
|
||||
private middleware: ReturnType<typeof getMiddleware>;
|
||||
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<RLottiePlayer>(player);
|
||||
|
||||
players.appear = player;
|
||||
|
||||
player.addEventListener('enterFrame', (frameNo) => {
|
||||
if(player.maxFrame === frameNo) {
|
||||
selectLoadPromise.then((selectPlayer) => {
|
||||
assumeType<RLottiePlayer>(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<RLottiePlayer>(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<typeof getMiddleware>;
|
||||
canOpenReactedList: boolean;
|
||||
private canOpenReactedList: boolean;
|
||||
|
||||
constructor(
|
||||
private attachTo: HTMLElement,
|
||||
|
290
src/components/chat/reactionsMenu.ts
Normal file
290
src/components/chat/reactionsMenu.ts
Normal file
@ -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<HTMLElement, ChatReactionsMenuPlayers>;
|
||||
private scrollable: ScrollableBase;
|
||||
private animationGroup: string;
|
||||
private middleware: ReturnType<typeof getMiddleware>;
|
||||
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<RLottiePlayer>(player);
|
||||
|
||||
players.appear = player;
|
||||
|
||||
player.addEventListener('enterFrame', (frameNo) => {
|
||||
if(player.maxFrame === frameNo) {
|
||||
selectLoadPromise.then((selectPlayer) => {
|
||||
assumeType<RLottiePlayer>(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<RLottiePlayer>(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();
|
||||
}
|
||||
};
|
||||
}
|
22
src/environment/canUseTransferables.ts
Normal file
22
src/environment/canUseTransferables.ts
Normal file
@ -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;
|
@ -185,7 +185,10 @@ export class LottieLoader {
|
||||
return;
|
||||
}
|
||||
|
||||
rlPlayer.clamped = frame;
|
||||
if(rlPlayer.clamped !== undefined) {
|
||||
rlPlayer.clamped = frame;
|
||||
}
|
||||
|
||||
rlPlayer.renderFrame(frame, frameNo);
|
||||
};
|
||||
|
||||
|
@ -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[] = [];
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user