Sticker viewer
This commit is contained in:
parent
b9e6151d5c
commit
3266d1d4c6
@ -16,7 +16,7 @@ import appMediaPlaybackController from './appMediaPlaybackController';
|
|||||||
|
|
||||||
export type AnimationItemGroup = '' | 'none' | 'chat' | 'lock' |
|
export type AnimationItemGroup = '' | 'none' | 'chat' | 'lock' |
|
||||||
'STICKERS-POPUP' | 'emoticons-dropdown' | 'STICKERS-SEARCH' | 'GIFS-SEARCH' |
|
'STICKERS-POPUP' | 'emoticons-dropdown' | 'STICKERS-SEARCH' | 'GIFS-SEARCH' |
|
||||||
`CHAT-MENU-REACTIONS-${number}` | 'INLINE-HELPER' | 'GENERAL-SETTINGS';
|
`CHAT-MENU-REACTIONS-${number}` | 'INLINE-HELPER' | 'GENERAL-SETTINGS' | 'STICKER-VIEWER';
|
||||||
export interface AnimationItem {
|
export interface AnimationItem {
|
||||||
el: HTMLElement,
|
el: HTMLElement,
|
||||||
group: AnimationItemGroup,
|
group: AnimationItemGroup,
|
||||||
@ -201,7 +201,11 @@ export class AnimationIntersector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setOnlyOnePlayableGroup(group: AnimationItemGroup) {
|
public getOnlyOnePlayableGroup() {
|
||||||
|
return this.onlyOnePlayableGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setOnlyOnePlayableGroup(group: AnimationItemGroup = '') {
|
||||||
this.onlyOnePlayableGroup = group;
|
this.onlyOnePlayableGroup = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +111,7 @@ import getAlbumText from '../../lib/appManagers/utils/messages/getAlbumText';
|
|||||||
import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount';
|
import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount';
|
||||||
import PopupPayment from '../popups/payment';
|
import PopupPayment from '../popups/payment';
|
||||||
import isInDOM from '../../helpers/dom/isInDOM';
|
import isInDOM from '../../helpers/dom/isInDOM';
|
||||||
|
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
|
||||||
|
|
||||||
const USE_MEDIA_TAILS = false;
|
const USE_MEDIA_TAILS = false;
|
||||||
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
|
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
|
||||||
@ -1234,9 +1235,6 @@ export default class ChatBubbles {
|
|||||||
needFadeIn: false
|
needFadeIn: false
|
||||||
}).then(({render}) => render).then((player) => {
|
}).then(({render}) => render).then((player) => {
|
||||||
assumeType<RLottiePlayer>(player);
|
assumeType<RLottiePlayer>(player);
|
||||||
if(!middleware()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
player.addEventListener('firstFrame', () => {
|
player.addEventListener('firstFrame', () => {
|
||||||
if(!middleware()) {
|
if(!middleware()) {
|
||||||
@ -1254,7 +1252,7 @@ export default class ChatBubbles {
|
|||||||
this.managers.appReactionsManager.sendReaction(message, availableReaction.reaction);
|
this.managers.appReactionsManager.sendReaction(message, availableReaction.reaction);
|
||||||
this.unhoverPrevious();
|
this.unhoverPrevious();
|
||||||
}, {listenerSetter: this.listenerSetter});
|
}, {listenerSetter: this.listenerSetter});
|
||||||
});
|
}, noop);
|
||||||
});
|
});
|
||||||
} else if(hoverReaction.dataset.loaded) {
|
} else if(hoverReaction.dataset.loaded) {
|
||||||
this.setHoverVisible(hoverReaction, true);
|
this.setHoverVisible(hoverReaction, true);
|
||||||
@ -4035,7 +4033,7 @@ export default class ChatBubbles {
|
|||||||
noPremium: messageMedia?.pFlags?.nopremium
|
noPremium: messageMedia?.pFlags?.nopremium
|
||||||
});
|
});
|
||||||
|
|
||||||
if(isInUnread || isOutgoing/* || true */) {
|
if(getStickerEffectThumb(doc) && (isInUnread || isOutgoing)/* || true */) {
|
||||||
this.observer.observe(bubble, this.stickerEffectObserverCallback);
|
this.observer.observe(bubble, this.stickerEffectObserverCallback);
|
||||||
}
|
}
|
||||||
} else if(doc.type === 'video' || doc.type === 'gif' || doc.type === 'round'/* && doc.size <= 20e6 */) {
|
} else if(doc.type === 'video' || doc.type === 'gif' || doc.type === 'round'/* && doc.size <= 20e6 */) {
|
||||||
|
@ -33,7 +33,7 @@ export default class PopupStickers extends PopupElement {
|
|||||||
this.title.append(i18n('Loading'));
|
this.title.append(i18n('Loading'));
|
||||||
|
|
||||||
this.addEventListener('close', () => {
|
this.addEventListener('close', () => {
|
||||||
animationIntersector.setOnlyOnePlayableGroup('');
|
animationIntersector.setOnlyOnePlayableGroup();
|
||||||
});
|
});
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
@ -12,7 +12,8 @@ const SetTransition = (
|
|||||||
forwards: boolean,
|
forwards: boolean,
|
||||||
duration: number,
|
duration: number,
|
||||||
onTransitionEnd?: () => void,
|
onTransitionEnd?: () => void,
|
||||||
useRafs?: number
|
useRafs?: number,
|
||||||
|
onTransitionStart?: () => void
|
||||||
) => {
|
) => {
|
||||||
const {timeout, raf} = element.dataset;
|
const {timeout, raf} = element.dataset;
|
||||||
if(timeout !== undefined) {
|
if(timeout !== undefined) {
|
||||||
@ -36,7 +37,7 @@ const SetTransition = (
|
|||||||
if(useRafs && rootScope.settings.animationsEnabled && duration) {
|
if(useRafs && rootScope.settings.animationsEnabled && duration) {
|
||||||
element.dataset.raf = '' + window.requestAnimationFrame(() => {
|
element.dataset.raf = '' + window.requestAnimationFrame(() => {
|
||||||
delete element.dataset.raf;
|
delete element.dataset.raf;
|
||||||
SetTransition(element, className, forwards, duration, onTransitionEnd, useRafs - 1);
|
SetTransition(element, className, forwards, duration, onTransitionEnd, useRafs - 1, onTransitionStart);
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -54,9 +55,10 @@ const SetTransition = (
|
|||||||
|
|
||||||
element.classList.remove('animating');
|
element.classList.remove('animating');
|
||||||
|
|
||||||
onTransitionEnd && onTransitionEnd();
|
onTransitionEnd?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onTransitionStart?.();
|
||||||
if(!rootScope.settings.animationsEnabled || !duration) {
|
if(!rootScope.settings.animationsEnabled || !duration) {
|
||||||
element.classList.remove('animating', 'backwards');
|
element.classList.remove('animating', 'backwards');
|
||||||
afterTimeout();
|
afterTimeout();
|
||||||
|
@ -16,6 +16,7 @@ import findUpClassName from '../../helpers/dom/findUpClassName';
|
|||||||
import renderImageFromUrl from '../../helpers/dom/renderImageFromUrl';
|
import renderImageFromUrl from '../../helpers/dom/renderImageFromUrl';
|
||||||
import getImageFromStrippedThumb from '../../helpers/getImageFromStrippedThumb';
|
import getImageFromStrippedThumb from '../../helpers/getImageFromStrippedThumb';
|
||||||
import getPreviewURLFromThumb from '../../helpers/getPreviewURLFromThumb';
|
import getPreviewURLFromThumb from '../../helpers/getPreviewURLFromThumb';
|
||||||
|
import makeError from '../../helpers/makeError';
|
||||||
import onMediaLoad from '../../helpers/onMediaLoad';
|
import onMediaLoad from '../../helpers/onMediaLoad';
|
||||||
import {isSavingLottiePreview, saveLottiePreview} from '../../helpers/saveLottiePreview';
|
import {isSavingLottiePreview, saveLottiePreview} from '../../helpers/saveLottiePreview';
|
||||||
import throttle from '../../helpers/schedulers/throttle';
|
import throttle from '../../helpers/schedulers/throttle';
|
||||||
@ -33,7 +34,7 @@ import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
|
|||||||
import rootScope from '../../lib/rootScope';
|
import rootScope from '../../lib/rootScope';
|
||||||
import type {ThumbCache} from '../../lib/storages/thumbs';
|
import type {ThumbCache} from '../../lib/storages/thumbs';
|
||||||
import webpWorkerController from '../../lib/webp/webpWorkerController';
|
import webpWorkerController from '../../lib/webp/webpWorkerController';
|
||||||
import {SendMessageEmojiInteractionData} from '../../types';
|
import {Awaited, SendMessageEmojiInteractionData} from '../../types';
|
||||||
import {getEmojiToneIndex} from '../../vendor/emoji';
|
import {getEmojiToneIndex} from '../../vendor/emoji';
|
||||||
import animationIntersector, {AnimationItemGroup} from '../animationIntersector';
|
import animationIntersector, {AnimationItemGroup} from '../animationIntersector';
|
||||||
import LazyLoadQueue from '../lazyLoadQueue';
|
import LazyLoadQueue from '../lazyLoadQueue';
|
||||||
@ -42,12 +43,12 @@ import {hideToast, toastNew} from '../toast';
|
|||||||
import wrapStickerAnimation from './stickerAnimation';
|
import wrapStickerAnimation from './stickerAnimation';
|
||||||
|
|
||||||
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp#L40
|
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp#L40
|
||||||
const STICKER_EFFECT_MULTIPLIER = 1 + 0.245 * 2;
|
export const STICKER_EFFECT_MULTIPLIER = 1 + 0.245 * 2;
|
||||||
const EMOJI_EFFECT_MULTIPLIER = 3;
|
const EMOJI_EFFECT_MULTIPLIER = 3;
|
||||||
|
|
||||||
const locksUrls: {[docId: string]: string} = {};
|
const locksUrls: {[docId: string]: string} = {};
|
||||||
|
|
||||||
export default async function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn, needUpscale, skipRatio, static: asStatic, managers = rootScope.managers, fullThumb, isOut, noPremium, withLock}: {
|
export default async function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn, needUpscale, skipRatio, static: asStatic, managers = rootScope.managers, fullThumb, isOut, noPremium, withLock, relativeEffect, loopEffect}: {
|
||||||
doc: MyDocument,
|
doc: MyDocument,
|
||||||
div: HTMLElement,
|
div: HTMLElement,
|
||||||
middleware?: () => boolean,
|
middleware?: () => boolean,
|
||||||
@ -69,7 +70,9 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
fullThumb?: PhotoSize | VideoSize,
|
fullThumb?: PhotoSize | VideoSize,
|
||||||
isOut?: boolean,
|
isOut?: boolean,
|
||||||
noPremium?: boolean,
|
noPremium?: boolean,
|
||||||
withLock?: boolean
|
withLock?: boolean,
|
||||||
|
relativeEffect?: boolean,
|
||||||
|
loopEffect?: boolean
|
||||||
}) {
|
}) {
|
||||||
const stickerType = doc.sticker;
|
const stickerType = doc.sticker;
|
||||||
if(stickerType === 1) {
|
if(stickerType === 1) {
|
||||||
@ -305,8 +308,11 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const middlewareError = makeError('MIDDLEWARE');
|
||||||
const load = async() => {
|
const load = async() => {
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) {
|
||||||
|
throw middlewareError;
|
||||||
|
}
|
||||||
|
|
||||||
if(stickerType === 2 && !asStatic) {
|
if(stickerType === 2 && !asStatic) {
|
||||||
/* if(doc.id === '1860749763008266301') {
|
/* if(doc.id === '1860749763008266301') {
|
||||||
@ -325,7 +331,7 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
// console.timeEnd('download sticker' + doc.id);
|
// console.timeEnd('download sticker' + doc.id);
|
||||||
// console.log('loaded sticker:', doc, div/* , blob */);
|
// console.log('loaded sticker:', doc, div/* , blob */);
|
||||||
if(middleware && !middleware()) {
|
if(middleware && !middleware()) {
|
||||||
throw new Error('wrapSticker 2 middleware');
|
throw middlewareError;
|
||||||
}
|
}
|
||||||
|
|
||||||
const animation = await lottieLoader.loadAnimationWorker({
|
const animation = await lottieLoader.loadAnimationWorker({
|
||||||
@ -355,7 +361,7 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cb = () => {
|
const cb = () => {
|
||||||
if(element && element !== animation.canvas) {
|
if(element && element !== animation.canvas && element.tagName !== 'DIV') {
|
||||||
element.remove();
|
element.remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -513,6 +519,9 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
|
|
||||||
if(play) {
|
if(play) {
|
||||||
(media as HTMLVideoElement).autoplay = true;
|
(media as HTMLVideoElement).autoplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(loop) {
|
||||||
(media as HTMLVideoElement).loop = true;
|
(media as HTMLVideoElement).loop = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -528,16 +537,17 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
media.classList.add('fade-in');
|
media.classList.add('fade-in');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<void>(async(resolve, reject) => {
|
return new Promise<HTMLVideoElement | HTMLImageElement>(async(resolve, reject) => {
|
||||||
const r = async() => {
|
const r = async() => {
|
||||||
if(middleware && !middleware()) return resolve();
|
if(middleware && !middleware()) {
|
||||||
|
reject(middlewareError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const onLoad = () => {
|
const onLoad = () => {
|
||||||
sequentialDom.mutateElement(div, () => {
|
sequentialDom.mutateElement(div, () => {
|
||||||
div.append(media);
|
div.append(media);
|
||||||
if(thumbImage) {
|
thumbImage && thumbImage.classList.add('fade-out');
|
||||||
thumbImage.classList.add('fade-out');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(stickerType === 3 && !isSavingLottiePreview(doc, toneIndex)) {
|
if(stickerType === 3 && !isSavingLottiePreview(doc, toneIndex)) {
|
||||||
// const perf = performance.now();
|
// const perf = performance.now();
|
||||||
@ -555,14 +565,12 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
animationIntersector.addAnimation(media as HTMLVideoElement, group);
|
animationIntersector.addAnimation(media as HTMLVideoElement, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve();
|
resolve(media as any);
|
||||||
|
|
||||||
if(needFadeIn) {
|
if(needFadeIn) {
|
||||||
media.addEventListener('animationend', () => {
|
media.addEventListener('animationend', () => {
|
||||||
media.classList.remove('fade-in');
|
media.classList.remove('fade-in');
|
||||||
if(thumbImage) {
|
thumbImage?.remove();
|
||||||
thumbImage.remove();
|
|
||||||
}
|
|
||||||
}, {once: true});
|
}, {once: true});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -589,13 +597,13 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
promise = appDownloadManager.downloadMediaURL({media: doc, queueId: lazyLoadQueue?.queueId});
|
promise = appDownloadManager.downloadMediaURL({media: doc, queueId: lazyLoadQueue?.queueId});
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then(r, resolve);
|
promise.then(r, reject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadPromise: Promise<RLottiePlayer | void> = lazyLoadQueue && (!downloaded || isAnimated) ?
|
const loadPromise: Promise<Awaited<ReturnType<typeof load>> | void> = lazyLoadQueue && (!downloaded || isAnimated) ?
|
||||||
(lazyLoadQueue.push({div, load}), Promise.resolve()) :
|
(lazyLoadQueue.push({div, load}), Promise.resolve()) :
|
||||||
load();
|
load();
|
||||||
|
|
||||||
@ -614,21 +622,25 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
|||||||
middleware,
|
middleware,
|
||||||
isOut,
|
isOut,
|
||||||
width,
|
width,
|
||||||
loadPromise
|
loadPromise,
|
||||||
|
relativeEffect,
|
||||||
|
loopEffect
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {render: loadPromise};
|
return {render: loadPromise};
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachStickerEffectHandler({container, doc, managers, middleware, isOut, width, loadPromise}: {
|
function attachStickerEffectHandler({container, doc, managers, middleware, isOut, width, loadPromise, relativeEffect, loopEffect}: {
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
doc: MyDocument,
|
doc: MyDocument,
|
||||||
managers: AppManagers,
|
managers: AppManagers,
|
||||||
middleware: () => boolean,
|
middleware: () => boolean,
|
||||||
isOut: boolean,
|
isOut: boolean,
|
||||||
width: number,
|
width: number,
|
||||||
loadPromise: Promise<any>
|
loadPromise: Promise<any>,
|
||||||
|
relativeEffect?: boolean,
|
||||||
|
loopEffect?: boolean
|
||||||
}) {
|
}) {
|
||||||
managers.appStickersManager.preloadSticker(doc.id, true);
|
managers.appStickersManager.preloadSticker(doc.id, true);
|
||||||
|
|
||||||
@ -660,10 +672,12 @@ function attachStickerEffectHandler({container, doc, managers, middleware, isOut
|
|||||||
size: width * STICKER_EFFECT_MULTIPLIER,
|
size: width * STICKER_EFFECT_MULTIPLIER,
|
||||||
target: container,
|
target: container,
|
||||||
play: true,
|
play: true,
|
||||||
fullThumb: getStickerEffectThumb(doc)
|
fullThumb: getStickerEffectThumb(doc),
|
||||||
|
relativeEffect,
|
||||||
|
loopEffect
|
||||||
});
|
});
|
||||||
|
|
||||||
if(isOut !== undefined && !isOut) {
|
if(isOut !== undefined && !isOut/* && !relativeEffect */) {
|
||||||
animationDiv.classList.add('reflect-x');
|
animationDiv.classList.add('reflect-x');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,9 @@ export default function wrapStickerAnimation({
|
|||||||
play,
|
play,
|
||||||
managers,
|
managers,
|
||||||
fullThumb,
|
fullThumb,
|
||||||
withRandomOffset
|
withRandomOffset,
|
||||||
|
relativeEffect,
|
||||||
|
loopEffect
|
||||||
}: {
|
}: {
|
||||||
size: number,
|
size: number,
|
||||||
doc: MyDocument,
|
doc: MyDocument,
|
||||||
@ -37,7 +39,9 @@ export default function wrapStickerAnimation({
|
|||||||
play: boolean,
|
play: boolean,
|
||||||
managers?: AppManagers,
|
managers?: AppManagers,
|
||||||
fullThumb?: PhotoSize | VideoSize,
|
fullThumb?: PhotoSize | VideoSize,
|
||||||
withRandomOffset?: boolean
|
withRandomOffset?: boolean,
|
||||||
|
relativeEffect?: boolean,
|
||||||
|
loopEffect?: boolean
|
||||||
}) {
|
}) {
|
||||||
const animationDiv = document.createElement('div');
|
const animationDiv = document.createElement('div');
|
||||||
animationDiv.classList.add('emoji-animation');
|
animationDiv.classList.add('emoji-animation');
|
||||||
@ -59,7 +63,7 @@ export default function wrapStickerAnimation({
|
|||||||
middleware,
|
middleware,
|
||||||
withThumb: false,
|
withThumb: false,
|
||||||
needFadeIn: false,
|
needFadeIn: false,
|
||||||
loop: false,
|
loop: !!loopEffect,
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
play,
|
play,
|
||||||
@ -71,7 +75,7 @@ export default function wrapStickerAnimation({
|
|||||||
assumeType<RLottiePlayer>(_animation);
|
assumeType<RLottiePlayer>(_animation);
|
||||||
animation = _animation;
|
animation = _animation;
|
||||||
animation.addEventListener('enterFrame', (frameNo) => {
|
animation.addEventListener('enterFrame', (frameNo) => {
|
||||||
if(frameNo === animation.maxFrame || !isInDOM(target)) {
|
if((!loopEffect && frameNo === animation.maxFrame) || !isInDOM(target)) {
|
||||||
unmountAnimation();
|
unmountAnimation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -92,7 +96,6 @@ export default function wrapStickerAnimation({
|
|||||||
|
|
||||||
const randomOffsetX = withRandomOffset ? generateRandomSigned(16) : 0;
|
const randomOffsetX = withRandomOffset ? generateRandomSigned(16) : 0;
|
||||||
const randomOffsetY = withRandomOffset ? generateRandomSigned(4) : 0;
|
const randomOffsetY = withRandomOffset ? generateRandomSigned(4) : 0;
|
||||||
const stableOffsetX = /* size / 8 */16 * (side === 'right' ? 1 : -1);
|
|
||||||
const setPosition = () => {
|
const setPosition = () => {
|
||||||
if(!isInDOM(target)) {
|
if(!isInDOM(target)) {
|
||||||
unmountAnimation();
|
unmountAnimation();
|
||||||
@ -100,35 +103,46 @@ export default function wrapStickerAnimation({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rect = target.getBoundingClientRect();
|
const rect = target.getBoundingClientRect();
|
||||||
/* const boxWidth = Math.max(rect.width, rect.height);
|
|
||||||
const boxHeight = Math.max(rect.width, rect.height);
|
const factor = rect.width / 200;
|
||||||
const x = rect.left + ((boxWidth - size) / 2);
|
const stableOffsetX = side === 'center' ? 0 : 16 * (side === 'right' ? 1 : -1) * factor;
|
||||||
const y = rect.top + ((boxHeight - size) / 2); */
|
// const stableOffsetY = side === 'center' ? 0 : -50 * factor;
|
||||||
|
const stableOffsetY = side === 'center' ? 0 : 0 * factor;
|
||||||
|
|
||||||
const rectX = side === 'right' ? rect.right : rect.left;
|
const rectX = side === 'right' ? rect.right : rect.left;
|
||||||
|
const rectY = rect.top;
|
||||||
|
|
||||||
const addOffsetX = side === 'center' ? (rect.width - size) / 2 : (side === 'right' ? -size : 0) + stableOffsetX + randomOffsetX;
|
const addOffsetX = (side === 'center' ? (rect.width - size) / 2 : (side === 'right' ? -size : 0)) + stableOffsetX + randomOffsetX;
|
||||||
|
const addOffsetY = (side === 'center' || true ? (rect.height - size) / 2 : 0) + stableOffsetY + randomOffsetY;
|
||||||
const x = rectX + addOffsetX;
|
const x = rectX + addOffsetX;
|
||||||
// const y = rect.bottom - size + size / 4;
|
const y = rectY + addOffsetY;
|
||||||
const y = rect.top + ((rect.height - size) / 2) + (side === 'center' ? 0 : randomOffsetY);
|
|
||||||
// animationDiv.style.transform = `translate(${x}px, ${y}px)`;
|
|
||||||
|
|
||||||
if(y <= -size || y >= windowSize.height) {
|
if(y <= -size || y >= windowSize.height) {
|
||||||
unmountAnimation();
|
unmountAnimation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(relativeEffect) {
|
||||||
|
if(side !== 'center') animationDiv.style[side] = Math.abs(stableOffsetX) * -1 + 'px';
|
||||||
|
else animationDiv.style.left = addOffsetX + 'px';
|
||||||
|
animationDiv.style.top = addOffsetY + 'px';
|
||||||
|
} else {
|
||||||
animationDiv.style.top = y + 'px';
|
animationDiv.style.top = y + 'px';
|
||||||
animationDiv.style.left = x + 'px';
|
animationDiv.style.left = x + 'px';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onScroll = throttleWithRaf(setPosition);
|
const onScroll = throttleWithRaf(setPosition);
|
||||||
|
|
||||||
appImManager.chat.bubbles.scrollable.container.addEventListener('scroll', onScroll);
|
appImManager.chat.bubbles.scrollable.container.addEventListener('scroll', onScroll);
|
||||||
|
|
||||||
setPosition();
|
setPosition();
|
||||||
|
|
||||||
|
if(relativeEffect) {
|
||||||
|
animationDiv.classList.add('is-relative');
|
||||||
|
target.parentElement.append(animationDiv);
|
||||||
|
} else {
|
||||||
appImManager.emojiAnimationContainer.append(animationDiv);
|
appImManager.emojiAnimationContainer.append(animationDiv);
|
||||||
|
}
|
||||||
|
|
||||||
return {animationDiv, stickerPromise};
|
return {animationDiv, stickerPromise};
|
||||||
}
|
}
|
||||||
|
2
src/global.d.ts
vendored
2
src/global.d.ts
vendored
@ -30,7 +30,7 @@ declare global {
|
|||||||
type FiltersError = 'PINNED_DIALOGS_TOO_MUCH';
|
type FiltersError = 'PINNED_DIALOGS_TOO_MUCH';
|
||||||
|
|
||||||
type LocalFileError = ApiFileManagerError | ReferenceError | StorageError;
|
type LocalFileError = ApiFileManagerError | ReferenceError | StorageError;
|
||||||
type LocalErrorType = LocalFileError | NetworkerError | FiltersError | 'UNKNOWN' | 'NO_DOC';
|
type LocalErrorType = LocalFileError | NetworkerError | FiltersError | 'UNKNOWN' | 'NO_DOC' | 'MIDDLEWARE';
|
||||||
|
|
||||||
type ServerErrorType = 'FILE_REFERENCE_EXPIRED' | 'SESSION_REVOKED' | 'AUTH_KEY_DUPLICATED' |
|
type ServerErrorType = 'FILE_REFERENCE_EXPIRED' | 'SESSION_REVOKED' | 'AUTH_KEY_DUPLICATED' |
|
||||||
'SESSION_PASSWORD_NEEDED' | 'CONNECTION_NOT_INITED' | 'ERROR_EMPTY' | 'MTPROTO_CLUSTER_INVALID' |
|
'SESSION_PASSWORD_NEEDED' | 'CONNECTION_NOT_INITED' | 'ERROR_EMPTY' | 'MTPROTO_CLUSTER_INVALID' |
|
||||||
|
@ -55,7 +55,7 @@ import {CallType} from '../calls/types';
|
|||||||
import {Modify, SendMessageEmojiInteractionData} from '../../types';
|
import {Modify, SendMessageEmojiInteractionData} from '../../types';
|
||||||
import htmlToSpan from '../../helpers/dom/htmlToSpan';
|
import htmlToSpan from '../../helpers/dom/htmlToSpan';
|
||||||
import getVisibleRect from '../../helpers/dom/getVisibleRect';
|
import getVisibleRect from '../../helpers/dom/getVisibleRect';
|
||||||
import {simulateClickEvent} from '../../helpers/dom/clickEvent';
|
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
|
||||||
import PopupCall from '../../components/call';
|
import PopupCall from '../../components/call';
|
||||||
import copy from '../../helpers/object/copy';
|
import copy from '../../helpers/object/copy';
|
||||||
import getObjectKeysAndSort from '../../helpers/object/getObjectKeysAndSort';
|
import getObjectKeysAndSort from '../../helpers/object/getObjectKeysAndSort';
|
||||||
@ -92,6 +92,15 @@ import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount
|
|||||||
import findUpClassName from '../../helpers/dom/findUpClassName';
|
import findUpClassName from '../../helpers/dom/findUpClassName';
|
||||||
import {CLICK_EVENT_NAME} from '../../helpers/dom/clickEvent';
|
import {CLICK_EVENT_NAME} from '../../helpers/dom/clickEvent';
|
||||||
import PopupPayment from '../../components/popups/payment';
|
import PopupPayment from '../../components/popups/payment';
|
||||||
|
import {getMiddleware} from '../../helpers/middleware';
|
||||||
|
import {wrapSticker} from '../../components/wrappers';
|
||||||
|
import windowSize from '../../helpers/windowSize';
|
||||||
|
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
|
||||||
|
import {makeMediaSize} from '../../helpers/mediaSize';
|
||||||
|
import RLottiePlayer from '../rlottie/rlottiePlayer';
|
||||||
|
import type {MyDocument} from './appDocsManager';
|
||||||
|
import deferredPromise from '../../helpers/cancellablePromise';
|
||||||
|
import {STICKER_EFFECT_MULTIPLIER} from '../../components/wrappers/sticker';
|
||||||
|
|
||||||
export const CHAT_ANIMATION_GROUP: AnimationItemGroup = 'chat';
|
export const CHAT_ANIMATION_GROUP: AnimationItemGroup = 'chat';
|
||||||
|
|
||||||
@ -207,15 +216,17 @@ export class AppImManager extends EventListenerBase<{
|
|||||||
this.setSettings();
|
this.setSettings();
|
||||||
rootScope.addEventListener('settings_updated', this.setSettings);
|
rootScope.addEventListener('settings_updated', this.setSettings);
|
||||||
|
|
||||||
rootScope.addEventListener('premium_toggle', (isPremium) => {
|
const onPremiumToggle = (isPremium: boolean) => {
|
||||||
document.body.classList.toggle('is-premium', isPremium);
|
document.body.classList.toggle('is-premium', isPremium);
|
||||||
});
|
};
|
||||||
|
rootScope.addEventListener('premium_toggle', onPremiumToggle);
|
||||||
|
onPremiumToggle(rootScope.premium);
|
||||||
|
|
||||||
useHeavyAnimationCheck(() => {
|
useHeavyAnimationCheck(() => {
|
||||||
animationIntersector.setOnlyOnePlayableGroup('lock');
|
animationIntersector.setOnlyOnePlayableGroup('lock');
|
||||||
animationIntersector.checkAnimations(true);
|
animationIntersector.checkAnimations(true);
|
||||||
}, () => {
|
}, () => {
|
||||||
animationIntersector.setOnlyOnePlayableGroup('');
|
animationIntersector.setOnlyOnePlayableGroup();
|
||||||
animationIntersector.checkAnimations(false);
|
animationIntersector.checkAnimations(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -404,6 +415,251 @@ export class AppImManager extends EventListenerBase<{
|
|||||||
}, useRafs);
|
}, useRafs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let hasViewer = false;
|
||||||
|
!IS_TOUCH_SUPPORTED && document.addEventListener('mousedown', (e) => {
|
||||||
|
if(hasViewer || e.buttons > 1 || e.button !== 0) return;
|
||||||
|
let mediaContainer = findUpClassName(e.target, 'media-sticker-wrapper');
|
||||||
|
if(!mediaContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const img: HTMLImageElement = mediaContainer.querySelector('img.media-sticker');
|
||||||
|
|
||||||
|
const docId = mediaContainer.dataset.docId;
|
||||||
|
if(!docId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const className = 'sticker-viewer';
|
||||||
|
const group: AnimationItemGroup = 'STICKER-VIEWER';
|
||||||
|
const openDuration = 200;
|
||||||
|
const switchDuration = 200;
|
||||||
|
const previousGroup = animationIntersector.getOnlyOnePlayableGroup();
|
||||||
|
const _middleware = getMiddleware();
|
||||||
|
let container: HTMLElement, previousTransformer: HTMLElement;
|
||||||
|
|
||||||
|
const doThatSticker = async({mediaContainer, doc, middleware, lockGroups, isSwitching}: {
|
||||||
|
mediaContainer: HTMLElement,
|
||||||
|
doc: MyDocument,
|
||||||
|
middleware: () => boolean,
|
||||||
|
lockGroups?: boolean,
|
||||||
|
isSwitching?: boolean
|
||||||
|
}) => {
|
||||||
|
const effectThumb = getStickerEffectThumb(doc);
|
||||||
|
const mediaRect: DOMRect = mediaContainer.getBoundingClientRect();
|
||||||
|
const s = makeMediaSize(doc.w, doc.h);
|
||||||
|
const size = effectThumb ? 280 : 360;
|
||||||
|
const boxSize = makeMediaSize(size, size);
|
||||||
|
const fitted = mediaRect.width === mediaRect.height ? boxSize : s.aspectFitted(boxSize);
|
||||||
|
|
||||||
|
const bubble = findUpClassName(mediaContainer, 'bubble');
|
||||||
|
const isOut = bubble ? bubble.classList.contains('is-out') : true;
|
||||||
|
|
||||||
|
const transformer = document.createElement('div');
|
||||||
|
transformer.classList.add(className + '-transformer');
|
||||||
|
|
||||||
|
const stickerContainer = document.createElement('div');
|
||||||
|
stickerContainer.classList.add(className + '-sticker');
|
||||||
|
/* transformer.style.width = */stickerContainer.style.width = fitted.width + 'px';
|
||||||
|
/* transformer.style.height = */stickerContainer.style.height = fitted.height + 'px';
|
||||||
|
|
||||||
|
const stickerEmoji = document.createElement('div');
|
||||||
|
stickerEmoji.classList.add(className + '-emoji');
|
||||||
|
stickerEmoji.append(wrapEmojiText(doc.stickerEmojiRaw));
|
||||||
|
|
||||||
|
if(effectThumb) {
|
||||||
|
const margin = (size * STICKER_EFFECT_MULTIPLIER - size) / 3 * (isOut ? 1 : -1);
|
||||||
|
transformer.classList.add('has-effect');
|
||||||
|
// const property = `--margin-${isOut ? 'right' : 'left'}`;
|
||||||
|
// stickerContainer.style.setProperty(property, `${margin * 2}px`);
|
||||||
|
transformer.style.setProperty('--translateX', `${margin}px`);
|
||||||
|
stickerEmoji.style.setProperty('--translateX', `${-margin}px`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const overflowElement = findUpClassName(mediaContainer, 'scrollable');
|
||||||
|
const visibleRect = getVisibleRect(mediaContainer, overflowElement, true, mediaRect);
|
||||||
|
if(visibleRect.overflow.vertical || visibleRect.overflow.horizontal) {
|
||||||
|
stickerContainer.classList.add('is-overflow');
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(img) {
|
||||||
|
// const ratio = img.naturalWidth / img.naturalHeight;
|
||||||
|
// if((mediaRect.width / mediaRect.height).toFixed(1) !== ratio.toFixed(1)) {
|
||||||
|
|
||||||
|
// mediaRect = mediaRect.toJSON();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const rect = mediaContainer.getBoundingClientRect();
|
||||||
|
const scaleX = rect.width / fitted.width;
|
||||||
|
const scaleY = rect.height / fitted.height;
|
||||||
|
const transformX = rect.left - (windowSize.width - rect.width) / 2;
|
||||||
|
const transformY = rect.top - (windowSize.height - rect.height) / 2;
|
||||||
|
transformer.style.transform = `translate(${transformX}px, ${transformY}px) scale(${scaleX}, ${scaleY})`;
|
||||||
|
if(isSwitching) transformer.classList.add('is-switching');
|
||||||
|
transformer.append(stickerContainer, stickerEmoji);
|
||||||
|
container.append(transformer);
|
||||||
|
|
||||||
|
const player = await wrapSticker({
|
||||||
|
doc,
|
||||||
|
div: stickerContainer,
|
||||||
|
group,
|
||||||
|
width: fitted.width,
|
||||||
|
height: fitted.height,
|
||||||
|
play: false,
|
||||||
|
loop: true,
|
||||||
|
middleware,
|
||||||
|
managers: this.managers,
|
||||||
|
needFadeIn: false,
|
||||||
|
isOut,
|
||||||
|
withThumb: false,
|
||||||
|
relativeEffect: true,
|
||||||
|
loopEffect: true
|
||||||
|
}).then(({render}) => render);
|
||||||
|
if(!middleware()) return;
|
||||||
|
|
||||||
|
if(!container.parentElement) {
|
||||||
|
document.body.append(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstFramePromise = player instanceof RLottiePlayer ?
|
||||||
|
new Promise<void>((resolve) => player.addEventListener('firstFrame', resolve, {once: true})) :
|
||||||
|
Promise.resolve();
|
||||||
|
await Promise.all([firstFramePromise, doubleRaf()]);
|
||||||
|
await pause(0); // ! need it because firstFrame will be called just from the loop
|
||||||
|
if(!middleware()) return;
|
||||||
|
|
||||||
|
if(lockGroups) {
|
||||||
|
animationIntersector.setOnlyOnePlayableGroup(group);
|
||||||
|
animationIntersector.checkAnimations(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(player instanceof RLottiePlayer) {
|
||||||
|
const prevPlayer = lottieLoader.getAnimation(mediaContainer);
|
||||||
|
player.curFrame = prevPlayer.curFrame;
|
||||||
|
player.play();
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
let i = 0;
|
||||||
|
const c = () => {
|
||||||
|
if(++i === 2) {
|
||||||
|
resolve();
|
||||||
|
player.removeEventListener('enterFrame', c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
player.addEventListener('enterFrame', c);
|
||||||
|
});
|
||||||
|
player.pause();
|
||||||
|
} else if(player instanceof HTMLVideoElement) {
|
||||||
|
player.currentTime = (mediaContainer.querySelector('video') as HTMLVideoElement).currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ready: () => {
|
||||||
|
if(player instanceof RLottiePlayer || player instanceof HTMLVideoElement) {
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(effectThumb) {
|
||||||
|
simulateClickEvent(stickerContainer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transformer
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeout = window.setTimeout(async() => {
|
||||||
|
document.removeEventListener('mousemove', onMousePreMove);
|
||||||
|
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.classList.add(className);
|
||||||
|
hasViewer = true;
|
||||||
|
|
||||||
|
const middleware = _middleware.get();
|
||||||
|
const doc = await this.managers.appDocsManager.getDoc(docId);
|
||||||
|
if(!middleware()) return;
|
||||||
|
|
||||||
|
const {ready, transformer} = await doThatSticker({
|
||||||
|
doc,
|
||||||
|
mediaContainer,
|
||||||
|
middleware,
|
||||||
|
lockGroups: true
|
||||||
|
});
|
||||||
|
|
||||||
|
previousTransformer = transformer;
|
||||||
|
|
||||||
|
SetTransition(container, 'is-visible', true, openDuration, () => {
|
||||||
|
if(!middleware()) return;
|
||||||
|
ready();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
const onMouseMove = async(e: MouseEvent) => {
|
||||||
|
const newMediaContainer = findUpClassName(e.target, 'media-sticker-wrapper');
|
||||||
|
if(!newMediaContainer || mediaContainer === newMediaContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docId = newMediaContainer.dataset.docId;
|
||||||
|
if(!docId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaContainer = newMediaContainer;
|
||||||
|
_middleware.clean();
|
||||||
|
const middleware = _middleware.get();
|
||||||
|
|
||||||
|
const doc = await this.managers.appDocsManager.getDoc(docId);
|
||||||
|
if(!middleware()) return;
|
||||||
|
|
||||||
|
const {ready, transformer} = await doThatSticker({
|
||||||
|
doc,
|
||||||
|
mediaContainer,
|
||||||
|
middleware,
|
||||||
|
isSwitching: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const _previousTransformer = previousTransformer;
|
||||||
|
SetTransition(_previousTransformer, 'is-switching', true, switchDuration, () => {
|
||||||
|
_previousTransformer.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
previousTransformer = transformer;
|
||||||
|
|
||||||
|
SetTransition(transformer, 'is-switching', false, switchDuration, () => {
|
||||||
|
if(!middleware()) return;
|
||||||
|
ready();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMousePreMove = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
_middleware.clean();
|
||||||
|
|
||||||
|
if(container) {
|
||||||
|
SetTransition(container, 'is-visible', false, openDuration, () => {
|
||||||
|
container.remove();
|
||||||
|
animationIntersector.setOnlyOnePlayableGroup(previousGroup);
|
||||||
|
animationIntersector.checkAnimations(false);
|
||||||
|
hasViewer = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMousePreMove, {once: true});
|
||||||
|
document.addEventListener('mouseup', onMouseUp, {once: true});
|
||||||
|
});
|
||||||
|
|
||||||
apiManagerProxy.addEventListener('notificationBuild', (options) => {
|
apiManagerProxy.addEventListener('notificationBuild', (options) => {
|
||||||
if(this.chat.peerId === options.message.peerId && !idleController.isIdle) {
|
if(this.chat.peerId === options.message.peerId && !idleController.isIdle) {
|
||||||
return;
|
return;
|
||||||
|
133
src/scss/partials/_stickerViewer.scss
Normal file
133
src/scss/partials/_stickerViewer.scss
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* https://github.com/morethanwords/tweb
|
||||||
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||||
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
.sticker-viewer {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 4;
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgba(0, 0, 0, .6);
|
||||||
|
opacity: 0;
|
||||||
|
content: " ";
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: opacity var(--sticker-viewer-open-transition-out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-visible {
|
||||||
|
&:not(.backwards) {
|
||||||
|
&:before {
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: opacity var(--sticker-viewer-open-transition-in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticker-viewer-transformer:not(.is-switching) {
|
||||||
|
transform: translateX(var(--translateX)) scale(1, 1) !important;
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: transform var(--sticker-viewer-open-transition-in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticker-viewer-emoji,
|
||||||
|
.sticker-viewer-sticker,
|
||||||
|
.emoji-animation {
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: opacity var(--sticker-viewer-open-transition-in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticker-viewer-sticker:not(.is-overflow) {
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: opacity 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-transformer {
|
||||||
|
--translateX: 0;
|
||||||
|
position: absolute;
|
||||||
|
// transform: translateX(0) scale(1);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 360px;
|
||||||
|
height: 360px;
|
||||||
|
|
||||||
|
&.has-effect {
|
||||||
|
width: 280px;
|
||||||
|
height: 280px;
|
||||||
|
|
||||||
|
.sticker-viewer-emoji {
|
||||||
|
top: -5.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: transform var(--sticker-viewer-open-transition-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-switching {
|
||||||
|
transform: translateX(var(--translateX)) scale(1) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: transform var(--sticker-viewer-switch-transition), opacity var(--sticker-viewer-switch-transition) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.backwards) {
|
||||||
|
transform: scale(0.4) translateX(var(--translateX)) !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-emoji {
|
||||||
|
position: absolute;
|
||||||
|
top: -3rem;
|
||||||
|
transform: translateX(var(--translateX)) scale(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-sticker {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// &-sticker,
|
||||||
|
// .emoji-animation {
|
||||||
|
// margin-left: var(--margin-left);
|
||||||
|
// margin-right: var(--margin-right);
|
||||||
|
// }
|
||||||
|
|
||||||
|
&-emoji,
|
||||||
|
&-sticker,
|
||||||
|
.emoji-animation {
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: opacity var(--sticker-viewer-open-transition-out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -68,6 +68,10 @@ $chat-input-inner-padding-handhelds: .25rem;
|
|||||||
--btn-menu-transition: .2s cubic-bezier(.4, 0, .2, 1);
|
--btn-menu-transition: .2s cubic-bezier(.4, 0, .2, 1);
|
||||||
--esg-transition: var(--btn-menu-transition);
|
--esg-transition: var(--btn-menu-transition);
|
||||||
--input-transition: .2s ease-out;
|
--input-transition: .2s ease-out;
|
||||||
|
--sticker-viewer-open-transition-in: .2s var(--transition-standard-easing);
|
||||||
|
--sticker-viewer-open-transition-out: .2s var(--transition-standard-easing);
|
||||||
|
// --sticker-viewer-switch-transition: .2s cubic-bezier(.07,1.21,.56,1.2);
|
||||||
|
--sticker-viewer-switch-transition: .2s cubic-bezier(.12,1.1,.56,1.2);
|
||||||
--popup-transition-function: cubic-bezier(.4, 0, .2, 1);
|
--popup-transition-function: cubic-bezier(.4, 0, .2, 1);
|
||||||
--popup-transition-time: .15s;
|
--popup-transition-time: .15s;
|
||||||
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
|
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
|
||||||
@ -357,6 +361,7 @@ $chat-input-inner-padding-handhelds: .25rem;
|
|||||||
@import "partials/reactions";
|
@import "partials/reactions";
|
||||||
@import "partials/reaction";
|
@import "partials/reaction";
|
||||||
@import "partials/stackedAvatars";
|
@import "partials/stackedAvatars";
|
||||||
|
@import "partials/stickerViewer";
|
||||||
|
|
||||||
@import "partials/popups/popup";
|
@import "partials/popups/popup";
|
||||||
@import "partials/popups/editAvatar";
|
@import "partials/popups/editAvatar";
|
||||||
|
4
src/types.d.ts
vendored
4
src/types.d.ts
vendored
@ -84,6 +84,10 @@ type ModifyFunctionsToAsync<T> = {
|
|||||||
[key in keyof T]: T[key] extends (...args: infer A) => infer R ? (R extends PromiseLike<infer O> ? T[key] : (...args: A) => Promise<Awaited<R>>) : T[key]
|
[key in keyof T]: T[key] extends (...args: infer A) => infer R ? (R extends PromiseLike<infer O> ? T[key] : (...args: A) => Promise<Awaited<R>>) : T[key]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Mutable<T> = {
|
||||||
|
-readonly [K in keyof T]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
|
export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
|
||||||
export namespace AuthState {
|
export namespace AuthState {
|
||||||
export type signIn = {
|
export type signIn = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user