Emoji animation sound
This commit is contained in:
parent
f6f35f4b70
commit
ffd3e231d4
4
.env
4
.env
@ -1,5 +1,5 @@
|
||||
API_ID=1025907
|
||||
API_HASH=452b0359b988148995f22ff0f4229750
|
||||
VERSION=1.0.4
|
||||
VERSION_FULL=1.0.4 (73)
|
||||
BUILD=73
|
||||
VERSION_FULL=1.0.4 (74)
|
||||
BUILD=74
|
||||
|
@ -1338,15 +1338,39 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
|
||||
let sendInteractionThrottled: () => void;
|
||||
|
||||
attachClickEvent(div, (e) => {
|
||||
cancelEvent(e);
|
||||
let animation = LottieLoader.getAnimation(div);
|
||||
appStickersManager.preloadAnimatedEmojiStickerAnimation(emoji);
|
||||
|
||||
attachClickEvent(div, async(e) => {
|
||||
const animation = LottieLoader.getAnimation(div);
|
||||
|
||||
if(animation.paused) {
|
||||
const doc = appStickersManager.getAnimatedEmojiSoundDocument(emoji);
|
||||
if(doc) {
|
||||
const audio = document.createElement('audio');
|
||||
|
||||
try {
|
||||
await appDocsManager.downloadDoc(doc);
|
||||
|
||||
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||
audio.src = cacheContext.url;
|
||||
await onMediaLoad(audio);
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
audio.src = '';
|
||||
}, {once: true});
|
||||
|
||||
audio.play();
|
||||
} catch(err) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
animation.autoplay = true;
|
||||
animation.restart();
|
||||
}
|
||||
|
||||
cancelEvent(e);
|
||||
|
||||
const doc = appStickersManager.getAnimatedEmojiSticker(emoji, true);
|
||||
if(!doc) {
|
||||
return;
|
||||
@ -1375,7 +1399,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
animation.addEventListener('enterFrame', (frameNo) => {
|
||||
if(frameNo === animation.maxFrame) {
|
||||
animation.remove();
|
||||
// animationDiv.remove();
|
||||
animationDiv.remove();
|
||||
appImManager.chat.bubbles.scrollable.container.removeEventListener('scroll', onScroll);
|
||||
}
|
||||
});
|
||||
|
7
src/helpers/fixBase64String.ts
Normal file
7
src/helpers/fixBase64String.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export default function fixBase64String(str: string, toUrl: boolean) {
|
||||
if(toUrl) {
|
||||
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
|
||||
} else {
|
||||
return str.replace(/-/g, '+').replace(/_/g, '/');
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import mediaSizes from '../../helpers/mediaSizes';
|
||||
import { getEmojiToneIndex } from '../../vendor/emoji';
|
||||
import RichTextProcessor from '../richtextprocessor';
|
||||
import assumeType from '../../helpers/assumeType';
|
||||
import fixBase64String from '../../helpers/fixBase64String';
|
||||
|
||||
const CACHE_TIME = 3600e3;
|
||||
|
||||
@ -29,6 +30,8 @@ const LOCAL_IDS_SET = new Set([
|
||||
EMOJI_ANIMATIONS_SET_LOCAL_ID
|
||||
]);
|
||||
|
||||
// let TEST_FILE_REFERENCE_REFRESH = true;
|
||||
|
||||
export type MyStickerSetInput = {
|
||||
id: StickerSet.stickerSet['id'],
|
||||
access_hash?: StickerSet.stickerSet['access_hash']
|
||||
@ -39,14 +42,21 @@ export type MyMessagesStickerSet = MessagesStickerSet.messagesStickerSet;
|
||||
export class AppStickersManager {
|
||||
private storage = new AppStorage<Record<Long, MyMessagesStickerSet>, typeof DATABASE_STATE>(DATABASE_STATE, 'stickerSets');
|
||||
|
||||
private getStickerSetPromises: {[setId: Long]: Promise<MyMessagesStickerSet>} = {};
|
||||
private getStickersByEmoticonsPromises: {[emoticon: string]: Promise<Document[]>} = {};
|
||||
private getStickerSetPromises: {[setId: Long]: Promise<MyMessagesStickerSet>};
|
||||
private getStickersByEmoticonsPromises: {[emoticon: string]: Promise<Document[]>};
|
||||
|
||||
private greetingStickers: Document.document[];
|
||||
private getGreetingStickersTimeout: number;
|
||||
private getGreetingStickersPromise: Promise<void>;
|
||||
|
||||
private sounds: Record<string, MyDocument>;
|
||||
getAnimatedEmojiSoundsPromise: Promise<void>;
|
||||
|
||||
constructor() {
|
||||
this.getStickerSetPromises = {};
|
||||
this.getStickersByEmoticonsPromises = {};
|
||||
this.sounds = {};
|
||||
|
||||
this.getAnimatedEmojiStickerSet();
|
||||
|
||||
rootScope.addMultipleEventsListeners({
|
||||
@ -143,12 +153,59 @@ export class AppStickersManager {
|
||||
public getAnimatedEmojiStickerSet() {
|
||||
return Promise.all([
|
||||
this.getStickerSet({id: EMOJI_SET_LOCAL_ID}, {saveById: true}),
|
||||
this.getStickerSet({id: EMOJI_ANIMATIONS_SET_LOCAL_ID}, {saveById: true})
|
||||
this.getStickerSet({id: EMOJI_ANIMATIONS_SET_LOCAL_ID}, {saveById: true}),
|
||||
this.getAnimatedEmojiSounds()
|
||||
]).then(([emoji, animations]) => {
|
||||
return {emoji, animations};
|
||||
});
|
||||
}
|
||||
|
||||
public getAnimatedEmojiSounds(overwrite?: boolean) {
|
||||
if(this.getAnimatedEmojiSoundsPromise && !overwrite) return this.getAnimatedEmojiSoundsPromise;
|
||||
return this.getAnimatedEmojiSoundsPromise = apiManager.getAppConfig(overwrite).then(appConfig => {
|
||||
for(const emoji in appConfig.emojies_sounds) {
|
||||
const sound = appConfig.emojies_sounds[emoji];
|
||||
const bytesStr = atob(fixBase64String(sound.file_reference_base64, false));
|
||||
const bytes = new Uint8Array(bytesStr.length);
|
||||
for(let i = 0, length = bytes.length; i < length; ++i) {
|
||||
bytes[i] = bytesStr[i].charCodeAt(0);
|
||||
}
|
||||
|
||||
// if(TEST_FILE_REFERENCE_REFRESH) {
|
||||
// bytes[0] = bytes[1] = bytes[2] = bytes[3] = bytes[4] = 0;
|
||||
// sound.access_hash += '999';
|
||||
// }
|
||||
|
||||
const doc = appDocsManager.saveDoc({
|
||||
_: 'document',
|
||||
pFlags: {},
|
||||
flags: 0,
|
||||
id: sound.id,
|
||||
access_hash: sound.access_hash,
|
||||
attributes: [/* {
|
||||
_: 'documentAttributeAudio',
|
||||
duration: 1,
|
||||
pFlags: {}
|
||||
} */],
|
||||
date: 0,
|
||||
dc_id: rootScope.config.this_dc,
|
||||
file_reference: bytes,
|
||||
mime_type: 'audio/mp3',
|
||||
size: 1
|
||||
// size: 101010 // test loading everytime
|
||||
}, {
|
||||
type: 'emojiesSounds'
|
||||
});
|
||||
|
||||
this.sounds[emoji] = doc;
|
||||
}
|
||||
|
||||
// if(TEST_FILE_REFERENCE_REFRESH) {
|
||||
// TEST_FILE_REFERENCE_REFRESH = false;
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
public async getRecentStickers(): Promise<Modify<MessagesRecentStickers.messagesRecentStickers, {
|
||||
stickers: Document[]
|
||||
}>> {
|
||||
@ -164,17 +221,25 @@ export class AppStickersManager {
|
||||
return res;
|
||||
}
|
||||
|
||||
private cleanEmoji(emoji: string) {
|
||||
return emoji.replace(/\ufe0f/g, '').replace(/🏻|🏼|🏽|🏾|🏿/g, '');
|
||||
}
|
||||
|
||||
public getAnimatedEmojiSticker(emoji: string, isAnimation?: boolean) {
|
||||
const stickerSet = this.storage.getFromCache(isAnimation ? EMOJI_ANIMATIONS_SET_LOCAL_ID : EMOJI_SET_LOCAL_ID);
|
||||
if(!stickerSet || !stickerSet.documents) return undefined;
|
||||
|
||||
emoji = emoji.replace(/\ufe0f/g, '').replace(/🏻|🏼|🏽|🏾|🏿/g, '');
|
||||
emoji = this.cleanEmoji(emoji);
|
||||
const pack = stickerSet.packs.find(p => p.emoticon === emoji);
|
||||
return pack ? appDocsManager.getDoc(pack.documents[0]) : undefined;
|
||||
}
|
||||
|
||||
public getAnimatedEmojiSoundDocument(emoji: string) {
|
||||
return this.sounds[this.cleanEmoji(emoji)];
|
||||
}
|
||||
|
||||
public preloadAnimatedEmojiSticker(emoji: string, width?: number, height?: number) {
|
||||
return this.getAnimatedEmojiStickerSet().then(() => {
|
||||
const preloadEmojiPromise = this.getAnimatedEmojiStickerSet().then(() => {
|
||||
const doc = this.getAnimatedEmojiSticker(emoji);
|
||||
if(doc) {
|
||||
return appDocsManager.downloadDoc(doc)
|
||||
@ -199,6 +264,24 @@ export class AppStickersManager {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
preloadEmojiPromise,
|
||||
this.preloadAnimatedEmojiStickerAnimation(emoji)
|
||||
]);
|
||||
}
|
||||
|
||||
public preloadAnimatedEmojiStickerAnimation(emoji: string) {
|
||||
return this.getAnimatedEmojiStickerSet().then(() => {
|
||||
const doc = this.getAnimatedEmojiSticker(emoji, true);
|
||||
if(doc) {
|
||||
const soundDoc = this.getAnimatedEmojiSoundDocument(emoji);
|
||||
return Promise.all([
|
||||
appDocsManager.downloadDoc(doc),
|
||||
soundDoc ? appDocsManager.downloadDoc(soundDoc) : undefined
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: DocId) {
|
||||
|
51
src/lib/mtproto/appConfig.d.ts
vendored
Normal file
51
src/lib/mtproto/appConfig.d.ts
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
export interface MTAppConfig {
|
||||
test?: number;
|
||||
emojies_animated_zoom?: number;
|
||||
emojies_send_dice?: string[];
|
||||
emojies_send_dice_success?: EmojiesSendDiceSuccess;
|
||||
emojies_sounds?: EmojiesSounds;
|
||||
gif_search_branding?: string;
|
||||
gif_search_emojies?: string[];
|
||||
stickers_emoji_suggest_only_api?: boolean;
|
||||
stickers_emoji_cache_time?: number;
|
||||
groupcall_video_participants_max?: number;
|
||||
youtube_pip?: string;
|
||||
qr_login_camera?: boolean;
|
||||
qr_login_code?: string;
|
||||
dialog_filters_enabled?: boolean;
|
||||
dialog_filters_tooltip?: boolean;
|
||||
ignore_restriction_reasons?: string[];
|
||||
autoarchive_setting_available?: boolean;
|
||||
pending_suggestions?: any[];
|
||||
autologin_token?: string;
|
||||
autologin_domains?: string[];
|
||||
round_video_encoding?: RoundVideoEncoding;
|
||||
chat_read_mark_expire_period?: number;
|
||||
chat_read_mark_size_threshold?: number;
|
||||
reactions_default?: string;
|
||||
reactions_uniq_max?: number;
|
||||
}
|
||||
|
||||
export interface EmojiesSendDiceSuccess {
|
||||
[k]: EmojiesSendDiceSuccessDetails
|
||||
}
|
||||
|
||||
export interface EmojiesSendDiceSuccessDetails {
|
||||
value?: number;
|
||||
frame_start?: number;
|
||||
}
|
||||
|
||||
export type EmojiesSounds = Record<string, EmojiSound>;
|
||||
|
||||
export interface EmojiSound {
|
||||
id?: string;
|
||||
access_hash?: string;
|
||||
file_reference_base64?: string;
|
||||
}
|
||||
|
||||
export interface RoundVideoEncoding {
|
||||
diameter?: number;
|
||||
video_bitrate?: number;
|
||||
audio_bitrate?: number;
|
||||
max_size?: number;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
import type { LocalStorageProxyTask, LocalStorageProxyTaskResponse } from '../localStorage';
|
||||
//import type { LocalStorageProxyDeleteTask, LocalStorageProxySetTask } from '../storage';
|
||||
import type { Awaited, InvokeApiOptions, WorkerTaskVoidTemplate } from '../../types';
|
||||
import type { Config, InputFile, MethodDeclMap, User } from '../../layer';
|
||||
import type { Config, InputFile, JSONValue, MethodDeclMap, User } from '../../layer';
|
||||
import MTProtoWorker from 'worker-loader!./mtproto.worker';
|
||||
//import './mtproto.worker';
|
||||
import { isObject } from '../../helpers/object';
|
||||
@ -32,6 +32,7 @@ import { CacheStorageDbName } from '../cacheStorage';
|
||||
import { pause } from '../../helpers/schedulers/pause';
|
||||
import IS_WEBP_SUPPORTED from '../../environment/webpSupport';
|
||||
import type { ApiError } from './apiManager';
|
||||
import { MTAppConfig } from './appConfig';
|
||||
|
||||
type Task = {
|
||||
taskId: number,
|
||||
@ -105,6 +106,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
|
||||
private postMessagesWaiting: any[][] = [];
|
||||
|
||||
private getConfigPromise: Promise<Config.config>;
|
||||
private getAppConfigPromise: Promise<MTAppConfig>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -693,6 +695,14 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
public getAppConfig(overwrite?: boolean): Promise<MTAppConfig> {
|
||||
if(this.getAppConfigPromise && !overwrite) return this.getAppConfigPromise;
|
||||
return this.getAppConfigPromise = this.invokeApi('help.getAppConfig').then(config => {
|
||||
rootScope.appConfig = config;
|
||||
return config;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const apiManagerProxy = new ApiManagerProxy();
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
import { RefreshReferenceTask, RefreshReferenceTaskResponse } from "./apiFileManager";
|
||||
import appMessagesManager from "../appManagers/appMessagesManager";
|
||||
import appStickersManager from "../appManagers/appStickersManager";
|
||||
import { Photo } from "../../layer";
|
||||
import { bytesToHex } from "../../helpers/bytes";
|
||||
import { deepEqual } from "../../helpers/object";
|
||||
@ -14,7 +15,7 @@ import apiManager from "./mtprotoworker";
|
||||
import assumeType from "../../helpers/assumeType";
|
||||
import { logger } from "../logger";
|
||||
|
||||
export type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage;
|
||||
export type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage | ReferenceContext.referenceContextEmojiesSounds;
|
||||
export namespace ReferenceContext {
|
||||
export type referenceContextProfilePhoto = {
|
||||
type: 'profilePhoto',
|
||||
@ -26,6 +27,10 @@ export namespace ReferenceContext {
|
||||
peerId: PeerId,
|
||||
messageId: number
|
||||
};
|
||||
|
||||
export type referenceContextEmojiesSounds = {
|
||||
type: 'emojiesSounds'
|
||||
};
|
||||
}
|
||||
|
||||
export type ReferenceBytes = Photo.photo['file_reference'];
|
||||
@ -38,6 +43,7 @@ class ReferenceDatabase {
|
||||
//private references: Map<ReferenceBytes, number[]> = new Map();
|
||||
private links: {[hex: string]: ReferenceBytes} = {};
|
||||
private log = logger('RD', undefined, true);
|
||||
private refreshEmojiesSoundsPromise: Promise<any>;
|
||||
|
||||
constructor() {
|
||||
apiManager.addTaskListener('refreshReference', (task: RefreshReferenceTask) => {
|
||||
@ -125,6 +131,13 @@ class ReferenceDatabase {
|
||||
// });
|
||||
}
|
||||
|
||||
case 'emojiesSounds': {
|
||||
promise = this.refreshEmojiesSoundsPromise || appStickersManager.getAnimatedEmojiSounds(true).then(() => {
|
||||
this.refreshEmojiesSoundsPromise = undefined;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
this.log.warn('refreshReference: not implemented context', context);
|
||||
return Promise.reject();
|
||||
|
@ -4,7 +4,7 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import type { Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, ConstructorDeclMap, Config, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, PhoneCall } from "../layer";
|
||||
import type { Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, ConstructorDeclMap, Config, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, PhoneCall, MethodDeclMap } from "../layer";
|
||||
import type { MyDocument } from "./appManagers/appDocsManager";
|
||||
import type { AppMessagesManager, Dialog, MessagesStorage, MyMessage } from "./appManagers/appMessagesManager";
|
||||
import type { MyDialogFilter } from "./storages/filters";
|
||||
@ -23,6 +23,7 @@ import type Chat from "../components/chat/chat";
|
||||
import { NULL_PEER_ID, UserAuth } from "./mtproto/mtproto_config";
|
||||
import EventListenerBase from "../helpers/eventListenerBase";
|
||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
import { MTAppConfig } from "./mtproto/appConfig";
|
||||
|
||||
export type BroadcastEvents = {
|
||||
'chat_full_update': ChatId,
|
||||
@ -183,6 +184,7 @@ export class RootScope extends EventListenerBase<{
|
||||
message_length_max: 4096,
|
||||
caption_length_max: 1024,
|
||||
};
|
||||
public appConfig: MTAppConfig;
|
||||
|
||||
public themeColor: string;
|
||||
private _themeColorElem: Element;
|
||||
|
@ -19,6 +19,7 @@ import rootScope from '../lib/rootScope';
|
||||
import { putPreloader } from '../components/misc';
|
||||
import getLanguageChangeButton from '../components/languageChangeButton';
|
||||
import { pause } from '../helpers/schedulers/pause';
|
||||
import fixBase64String from '../helpers/fixBase64String';
|
||||
|
||||
const FETCH_INTERVAL = 3;
|
||||
|
||||
@ -105,7 +106,7 @@ let onFirstMount = async() => {
|
||||
prevToken = loginToken.token;
|
||||
|
||||
let encoded = bytesToBase64(loginToken.token);
|
||||
let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
|
||||
let url = "tg://login?token=" + fixBase64String(encoded, true);
|
||||
|
||||
const style = window.getComputedStyle(document.documentElement);
|
||||
const surfaceColor = style.getPropertyValue('--surface-color').trim();
|
||||
|
Loading…
x
Reference in New Issue
Block a user