Browse Source

Wrap pinned photo

Video render photo, no video
Fix iOS Safari scroll
Stickers sizes for devicePixelRatio
master
morethanwords 5 years ago
parent
commit
cd4b8db2d1
  1. 3
      src/components/chatInput.ts
  2. 2
      src/components/emoticonsDropdown.ts
  3. 2
      src/components/lazyLoadQueue.ts
  4. 4
      src/components/preloader.ts
  5. 49
      src/components/wrappers.ts
  6. 7
      src/index.hbs
  7. 37
      src/lib/appManagers/apiUpdatesManager.ts
  8. 5
      src/lib/appManagers/appDialogsManager.ts
  9. 90
      src/lib/appManagers/appImManager.ts
  10. 2
      src/lib/appManagers/appMediaViewer.ts
  11. 10
      src/lib/appManagers/appMessagesManager.ts
  12. 6
      src/lib/appManagers/appWebpManager.ts
  13. 6
      src/lib/cacheStorage.ts
  14. 5
      src/lib/config.ts
  15. 103
      src/lib/lottieLoader.ts
  16. 17
      src/lib/polyfill.ts
  17. 2
      src/scss/partials/_chat.scss
  18. 5
      src/scss/style.scss

3
src/components/chatInput.ts

@ -92,7 +92,8 @@ export class ChatInput { @@ -92,7 +92,8 @@ export class ChatInput {
encoderSampleRate: 48000,
monitorGain: 0,
numberOfChannels: 1,
recordingGain: 1
recordingGain: 1,
reuseWorker: true
});
} catch(err) {
this.btnSend.classList.remove('tgico-microphone2');

2
src/components/emoticonsDropdown.ts

@ -345,7 +345,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -345,7 +345,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let prevCategoryIndex = 0;
let stickersScroll = new Scrollable(contentStickersDiv, 'y', 'STICKERS', undefined, undefined, 2);
stickersScroll.container.addEventListener('scroll', (e) => {
lottieLoader.checkAnimations();
lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP);
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
});

2
src/components/lazyLoadQueue.ts

@ -13,7 +13,7 @@ export default class LazyLoadQueue { @@ -13,7 +13,7 @@ export default class LazyLoadQueue {
private unlockResolve: () => void = null;
private log = console.log.bind(console, '[LL]:');
private debug = true;
private debug = false;
private observer: IntersectionObserver;

4
src/components/preloader.ts

@ -61,7 +61,7 @@ export default class ProgressivePreloader { @@ -61,7 +61,7 @@ export default class ProgressivePreloader {
promise.notify = (details: {done: number, total: number}) => {
if(tempID != this.tempID) return;
console.log('preloader download', promise, details);
//console.log('preloader download', promise, details);
let percents = details.done / details.total * 100;
this.setProgress(percents);
};
@ -116,7 +116,7 @@ export default class ProgressivePreloader { @@ -116,7 +116,7 @@ export default class ProgressivePreloader {
}
let totalLength = this.circle.getTotalLength();
console.log('setProgress', (percents / 100 * totalLength));
//console.log('setProgress', (percents / 100 * totalLength));
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
}
}

49
src/components/wrappers.ts

@ -2,12 +2,10 @@ import appPhotosManager from '../lib/appManagers/appPhotosManager'; @@ -2,12 +2,10 @@ import appPhotosManager from '../lib/appManagers/appPhotosManager';
//import CryptoWorker from '../lib/crypto/cryptoworker';
import apiManager from '../lib/mtproto/mtprotoworker';
import LottieLoader from '../lib/lottieLoader';
import appStickersManager from "../lib/appManagers/appStickersManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import { formatBytes, getEmojiToneIndex } from "../lib/utils";
import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue';
import apiFileManager from '../lib/mtproto/apiFileManager';
import VideoPlayer, { MediaProgressLine } from '../lib/mediaPlayer';
import { RichTextProcessor } from '../lib/richtextprocessor';
import { CancellablePromise } from '../lib/polyfill';
@ -30,6 +28,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -30,6 +28,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
middleware: () => boolean,
lazyLoadQueue: LazyLoadQueue
}) {
if(doc.type == 'video') {
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
}
let img: HTMLImageElement;
if(withTail) {
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
@ -95,17 +97,19 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -95,17 +97,19 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
return;
}
console.log('loaded doc:', doc, doc.url, container);
//console.log('loaded doc:', doc, doc.url, container);
renderImageFromUrl(source, doc.url);
source.type = doc.mime_type;
video.append(source);
video.setAttribute('playsinline', '');
if(img && img.parentElement) {
img.remove();
}
if(doc.type == 'gif') {
video.muted = true;
video.autoplay = true;
video.loop = true;
video.play();
@ -687,7 +691,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, @@ -687,7 +691,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
return img;
}
export function wrapPhoto(photoID: string, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) {
export function wrapPhoto(photoID: any, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) {
let photo = appPhotosManager.getPhoto(photoID);
let image: HTMLImageElement;
@ -734,7 +738,7 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme @@ -734,7 +738,7 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme
return promise.then(() => {
if(middleware && !middleware()) return;
renderImageFromUrl(image || container, photo.url);
renderImageFromUrl(image || container, photo._ == 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url);
});
};
@ -865,8 +869,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -865,8 +869,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
loop: !emoji,
autoplay: true,
animationData: JSON.parse(json),
width: !emoji ? 200 : 140,
height: !emoji ? 200 : 140
width: !emoji ? 200 : undefined,
height: !emoji ? 200 : undefined
}, group, toneIndex);
animation.addListener('ready', () => {
@ -932,31 +936,34 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -932,31 +936,34 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load();
}
export function wrapReply(title: string, subtitle: string, message?: any) {
let div = document.createElement('div');
div.classList.add('reply');
export function wrapReply(title: string, subtitle: string, message?: any, isPinned?: boolean) {
const prefix = isPinned ? 'pinned-message' : 'reply';
const div = document.createElement('div');
div.classList.add(prefix);
let replyBorder = document.createElement('div');
replyBorder.classList.add('reply-border');
const replyBorder = document.createElement('div');
replyBorder.classList.add(prefix + '-border');
let replyContent = document.createElement('div');
replyContent.classList.add('reply-content');
const replyContent = document.createElement('div');
replyContent.classList.add(prefix + '-content');
let replyTitle = document.createElement('div');
replyTitle.classList.add('reply-title');
const replyTitle = document.createElement('div');
replyTitle.classList.add(prefix + '-title');
let replySubtitle = document.createElement('div');
replySubtitle.classList.add('reply-subtitle');
const replySubtitle = document.createElement('div');
replySubtitle.classList.add(prefix + '-subtitle');
replyTitle.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : '';
let media = message && message.media;
const media = message && message.media;
if(media) {
replySubtitle.innerHTML = message.rReply;
console.log('wrap reply', media);
if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) {
let replyMedia = document.createElement('div');
replyMedia.classList.add('reply-media');
replyMedia.classList.add(prefix + '-media');
let photo = media.photo || media.document;
@ -971,7 +978,7 @@ export function wrapReply(title: string, subtitle: string, message?: any) { @@ -971,7 +978,7 @@ export function wrapReply(title: string, subtitle: string, message?: any) {
});
replyContent.append(replyMedia);
div.classList.add('is-reply-media');
div.classList.add('is-media');
}
} else {
replySubtitle.innerHTML = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : '';

7
src/index.hbs

@ -394,13 +394,6 @@ @@ -394,13 +394,6 @@
</div>
</div>
</div>
<div class="pinned-message">
<div class="pinned-message-border"></div>
<div class="pinned-message-content">
<div class="pinned-message-title">Pinned Message</div>
<div class="pinned-message-subtitle"></div>
</div>
</div>
<div class="btn-icon rp chat-mute-button" style="display: none;"></div>
<div class="btn-icon rp tgico-search chat-search-button"></div>
<div class="btn-icon btn-menu-toggle rp tgico-more chat-more-button">

37
src/lib/appManagers/apiUpdatesManager.ts

@ -5,6 +5,7 @@ import { dT, $rootScope, tsNow } from "../utils"; @@ -5,6 +5,7 @@ import { dT, $rootScope, tsNow } from "../utils";
import appPeersManager from "./appPeersManager";
import appUsersManager from "./appUsersManager";
import appChatsManager from "./appChatsManager";
import { logger, LogLevels } from '../polyfill';
export class ApiUpdatesManager {
public updatesState: {
@ -26,6 +27,8 @@ export class ApiUpdatesManager { @@ -26,6 +27,8 @@ export class ApiUpdatesManager {
public channelStates: any = {};
private attached = false;
private log = logger('UPDATES', LogLevels.error);
public popPendingSeqUpdate() {
var nextSeq = this.updatesState.seq + 1;
var pendingUpdatesData = this.updatesState.pendingSeqUpdates[nextSeq];
@ -67,7 +70,7 @@ export class ApiUpdatesManager { @@ -67,7 +70,7 @@ export class ApiUpdatesManager {
curState.pendingPtsUpdates.sort((a: any, b: any) => {
return a.pts - b.pts;
});
// console.log(dT(), 'pop update', channelID, curState.pendingPtsUpdates)
// this.log(dT(), 'pop update', channelID, curState.pendingPtsUpdates)
var curPts = curState.pts;
var goodPts = false;
@ -86,7 +89,7 @@ export class ApiUpdatesManager { @@ -86,7 +89,7 @@ export class ApiUpdatesManager {
return false;
}
console.log(dT(), 'pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1));
this.log(dT(), 'pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1));
curState.pts = goodPts;
for(i = 0; i <= goodIndex; i++) {
@ -170,12 +173,12 @@ export class ApiUpdatesManager { @@ -170,12 +173,12 @@ export class ApiUpdatesManager {
break;
default:
console.warn(dT(), 'Unknown update message', updateMessage);
this.log.warn(dT(), 'Unknown update message', updateMessage);
}
}
public getDifference() {
// console.trace(dT(), 'Get full diff')
// this.trace(dT(), 'Get full diff')
const updatesState = this.updatesState;
if(!updatesState.syncLoading) {
updatesState.syncLoading = true;
@ -196,7 +199,7 @@ export class ApiUpdatesManager { @@ -196,7 +199,7 @@ export class ApiUpdatesManager {
timeout: 0x7fffffff
}).then((differenceResult: any) => {
if(differenceResult._ == 'updates.differenceEmpty') {
console.log(dT(), 'apply empty diff', differenceResult.seq);
this.log(dT(), 'apply empty diff', differenceResult.seq);
updatesState.date = differenceResult.date;
updatesState.seq = differenceResult.seq;
updatesState.syncLoading = false;
@ -208,7 +211,7 @@ export class ApiUpdatesManager { @@ -208,7 +211,7 @@ export class ApiUpdatesManager {
appChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
// console.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates')
// this.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates')
differenceResult.other_updates.forEach((update: any) => {
switch(update._) {
@ -222,7 +225,7 @@ export class ApiUpdatesManager { @@ -222,7 +225,7 @@ export class ApiUpdatesManager {
this.saveUpdate(update);
});
// console.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages')
// this.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages')
differenceResult.new_messages.forEach((apiMessage: any) => {
this.saveUpdate({
_: 'updateNewMessage',
@ -237,12 +240,12 @@ export class ApiUpdatesManager { @@ -237,12 +240,12 @@ export class ApiUpdatesManager {
updatesState.pts = nextState.pts;
updatesState.date = nextState.date;
// console.log(dT(), 'apply diff', updatesState.seq, updatesState.pts)
// this.log(dT(), 'apply diff', updatesState.seq, updatesState.pts)
if(differenceResult._ == 'updates.differenceSlice') {
this.getDifference();
} else {
// console.log(dT(), 'finished get diff')
// this.log(dT(), 'finished get diff')
$rootScope.$broadcast('stateSynchronized');
updatesState.syncLoading = false;
}
@ -261,25 +264,25 @@ export class ApiUpdatesManager { @@ -261,25 +264,25 @@ export class ApiUpdatesManager {
clearTimeout(channelState.syncPending.timeout);
channelState.syncPending = false;
}
// console.log(dT(), 'Get channel diff', appChatsManager.getChat(channelID), channelState.pts)
// this.log(dT(), 'Get channel diff', appChatsManager.getChat(channelID), channelState.pts)
apiManager.invokeApi('updates.getChannelDifference', {
channel: appChatsManager.getChannelInput(channelID),
filter: {_: 'channelMessagesFilterEmpty'},
pts: channelState.pts,
limit: 30
}, {timeout: 0x7fffffff}).then((differenceResult: any) => {
// console.log(dT(), 'channel diff result', differenceResult)
// this.log(dT(), 'channel diff result', differenceResult)
channelState.pts = differenceResult.pts;
if (differenceResult._ == 'updates.channelDifferenceEmpty') {
console.log(dT(), 'apply channel empty diff', differenceResult);
this.log(dT(), 'apply channel empty diff', differenceResult);
channelState.syncLoading = false;
$rootScope.$broadcast('stateSynchronized');
return false;
}
if(differenceResult._ == 'updates.channelDifferenceTooLong') {
console.log(dT(), 'channel diff too long', differenceResult);
this.log(dT(), 'channel diff too long', differenceResult);
channelState.syncLoading = false;
delete this.channelStates[channelID];
this.saveUpdate({_: 'updateChannelReload', channel_id: channelID});
@ -290,12 +293,12 @@ export class ApiUpdatesManager { @@ -290,12 +293,12 @@ export class ApiUpdatesManager {
appChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
console.log(dT(), 'applying', differenceResult.other_updates.length, 'channel other updates')
this.log(dT(), 'applying', differenceResult.other_updates.length, 'channel other updates')
differenceResult.other_updates.forEach((update: any) => {
this.saveUpdate(update);
});
console.log(dT(), 'applying', differenceResult.new_messages.length, 'channel new messages')
this.log(dT(), 'applying', differenceResult.new_messages.length, 'channel new messages')
differenceResult.new_messages.forEach((apiMessage: any) => {
this.saveUpdate({
_: 'updateNewChannelMessage',
@ -305,13 +308,13 @@ export class ApiUpdatesManager { @@ -305,13 +308,13 @@ export class ApiUpdatesManager {
});
});
console.log(dT(), 'apply channel diff', channelState.pts);
this.log(dT(), 'apply channel diff', channelState.pts);
if(differenceResult._ == 'updates.channelDifference' &&
!differenceResult.pFlags['final']) {
this.getChannelDifference(channelID);
} else {
console.log(dT(), 'finished channel get diff');
this.log(dT(), 'finished channel get diff');
$rootScope.$broadcast('stateSynchronized');
channelState.syncLoading = false;
}

5
src/lib/appManagers/appDialogsManager.ts

@ -741,6 +741,11 @@ export class AppDialogsManager { @@ -741,6 +741,11 @@ export class AppDialogsManager {
public setUnreadMessages(dialog: Dialog) {
let dom = this.getDialogDom(dialog.peerID);
if(!dom) {
this.log.error('setUnreadMessages no dom!', dialog);
return;
}
let lastMessage = appMessagesManager.getMessage(dialog.top_message);
if(lastMessage._ != 'messageEmpty' && !lastMessage.deleted &&
lastMessage.from_id == $rootScope.myID && lastMessage.peerID != $rootScope.myID &&

90
src/lib/appManagers/appImManager.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import { $rootScope, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, findUpTag, langPack, whichChild } from "../utils";
import { $rootScope, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, findUpTag, langPack, whichChild, cancelEvent } from "../utils";
import appUsersManager from "./appUsersManager";
import appMessagesManager, { Dialog } from "./appMessagesManager";
import appPeersManager from "./appPeersManager";
@ -36,7 +36,7 @@ console.log('appImManager included33!'); @@ -36,7 +36,7 @@ console.log('appImManager included33!');
appSidebarLeft; // just to include
const testScroll = true;
const testScroll = false;
const IGNOREACTIONS = ['messageActionChannelMigrateFrom'];
@ -237,8 +237,7 @@ export class AppImManager { @@ -237,8 +237,7 @@ export class AppImManager {
public updateStatusInterval = 0;
public pinnedMsgID = 0;
private pinnedMessageContainer = this.columnEl.querySelector('.pinned-message') as HTMLDivElement;
private pinnedMessageContent = this.pinnedMessageContainer.querySelector('.pinned-message-subtitle') as HTMLDivElement;
private pinnedMessageContainer: HTMLDivElement = null;
public lazyLoadQueue = new LazyLoadQueue();
@ -496,8 +495,17 @@ export class AppImManager { @@ -496,8 +495,17 @@ export class AppImManager {
}, {once: true});
});
(this.columnEl.querySelector('.person') as HTMLDivElement).addEventListener('click', () => {
appSidebarRight.toggleSidebar(true);
this.topbar.addEventListener('click', (e) => {
const pinned = findUpClassName(e.target, 'pinned-message');
if(pinned) {
e.preventDefault();
e.cancelBubble = true;
let mid = +pinned.dataset.mid;
this.setPeer(this.peerID, mid);
} else {
appSidebarRight.toggleSidebar(true);
}
});
this.bubblesContainer.addEventListener('click', (e) => {
@ -630,14 +638,6 @@ export class AppImManager { @@ -630,14 +638,6 @@ export class AppImManager {
}
});
this.pinnedMessageContainer.addEventListener('click', (e) => {
e.preventDefault();
e.cancelBubble = true;
let mid = +this.pinnedMessageContainer.getAttribute('data-mid');
this.setPeer(this.peerID, mid);
});
[this.btnMute, this.menuButtons.mute].forEach(el => {
el.addEventListener('click', () => this.mutePeer(this.peerID));
});
@ -757,10 +757,21 @@ export class AppImManager { @@ -757,10 +757,21 @@ export class AppImManager {
public setPinnedMessage(message: any) {
/////this.log('setting pinned message', message);
return;
this.pinnedMessageContainer.dataset.mid = '' + message.mid;
//return;
const scrollTop = this.scrollable.container.scrollTop;
const newPinned = wrapReply('Pinned Message', message.message, message, true);
newPinned.dataset.mid = '' + message.mid;
this.topbar.insertBefore(newPinned, this.btnMute);
this.topbar.classList.add('is-pinned-shown');
this.pinnedMessageContent.innerHTML = message.rReply;
if(this.pinnedMessageContainer) {
this.pinnedMessageContainer.remove();
}
this.pinnedMessageContainer = newPinned;
//this.pinnedMessageContent.innerHTML = message.rReply;
this.scrollable.scrollTop = scrollTop + 60;
}
public updateStatus() {
@ -784,7 +795,7 @@ export class AppImManager { @@ -784,7 +795,7 @@ export class AppImManager {
}
public loadMoreHistory(top: boolean) {
this.log('loadMoreHistory', top);
//this.log('loadMoreHistory', top);
if(!this.peerID || testScroll || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return;
// warning, если иды только отрицательные то вниз не попадёт (хотя мб и так не попадёт)
@ -811,13 +822,13 @@ export class AppImManager { @@ -811,13 +822,13 @@ export class AppImManager {
}
}
public onScroll() {
public onScroll(e: Event) {
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF);
//if(this.scrollable.scrollLocked) return;
this.onScrollRAF = window.requestAnimationFrame(() => {
lottieLoader.checkAnimations(false, 'chat');
//lottieLoader.checkAnimations(false, 'chat');
if(!touchSupport) {
if(this.isScrollingTimeout) {
@ -1076,7 +1087,11 @@ export class AppImManager { @@ -1076,7 +1087,11 @@ export class AppImManager {
if(!cached) {
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
!samePeer && this.finishPeerChange();
if(!samePeer) {
this.finishPeerChange();
}
this.preloader.attach(this.bubblesContainer);
if(mediaSizes.isMobile) {
@ -1093,11 +1108,9 @@ export class AppImManager { @@ -1093,11 +1108,9 @@ export class AppImManager {
if(cached) {
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
!samePeer && this.finishPeerChange();
const pinned = appMessagesManager.getPinnedMessage(peerID);
if(pinned && !pinned.deleted) {
this.setPinnedMessage(pinned);
if(!samePeer) {
this.finishPeerChange();
}
if(mediaSizes.isMobile) {
@ -1206,6 +1219,14 @@ export class AppImManager { @@ -1206,6 +1219,14 @@ export class AppImManager {
this.btnMute.style.display = appPeersManager.isBroadcast(peerID) ? '' : 'none';
this.menuButtons.mute.style.display = this.myID == this.peerID ? 'none' : '';
const pinned = appMessagesManager.getPinnedMessage(peerID);
if(pinned && !pinned.deleted) {
this.setPinnedMessage(pinned);
} else if(this.pinnedMessageContainer) {
this.pinnedMessageContainer.remove();
this.pinnedMessageContainer = null;
}
window.requestAnimationFrame(() => {
let title = '';
if(this.peerID == this.myID) title = 'Saved Messages';
@ -1258,7 +1279,7 @@ export class AppImManager { @@ -1258,7 +1279,7 @@ export class AppImManager {
//bubble.remove();
});
lottieLoader.checkAnimations();
lottieLoader.checkAnimations(false, 'chat');
this.deleteEmptyDateGroups();
}
@ -1366,7 +1387,7 @@ export class AppImManager { @@ -1366,7 +1387,7 @@ export class AppImManager {
if(el instanceof HTMLVideoElement) {
let source = el.firstElementChild as HTMLSourceElement;
if(!source || !source.src) {
this.log.warn('no source', el, source, 'src', source.src);
//this.log.warn('no source', el, source, 'src', source.src);
return;
} else if(el.readyState >= 4) return;
} else if(el.complete || !el.src) return;
@ -1381,8 +1402,8 @@ export class AppImManager { @@ -1381,8 +1402,8 @@ export class AppImManager {
};
if(el instanceof HTMLVideoElement) {
el.addEventListener('loadeddata', onLoad);
r = () => el.readyState >= 4;
el.addEventListener('canplay', onLoad);
r = () => el.readyState >= 1;
} else {
el.addEventListener('load', onLoad);
r = () => el.complete;
@ -1393,8 +1414,9 @@ export class AppImManager { @@ -1393,8 +1414,9 @@ export class AppImManager {
window.requestAnimationFrame(c);
let timeout = setTimeout(() => {
console.log('did not called', el, el.parentElement, el.complete, src);
reject();
// @ts-ignore
this.log.error('did not called', el, el.parentElement, el.complete, el.readyState, src);
resolve();
}, 1500);
});
@ -2242,6 +2264,8 @@ export class AppImManager { @@ -2242,6 +2264,8 @@ export class AppImManager {
let scrollTop = this.scrollable.scrollTop;
previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop;
//this.chatInner.style.height = '100%';
//previousScrollHeightMinusTop = 0;
/* if(reverse) {
previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop;
} else {
@ -2262,7 +2286,11 @@ export class AppImManager { @@ -2262,7 +2286,11 @@ export class AppImManager {
if(previousScrollHeightMinusTop !== undefined) {
const newScrollTop = reverse ? this.scrollable.scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
this.log('performHistoryResult: will set scrollTop', this.scrollable.scrollHeight, newScrollTop, this.scrollable.container.clientHeight);
// touchSupport for safari iOS
touchSupport && (this.scrollable.container.style.overflow = 'hidden');
this.scrollable.scrollTop = newScrollTop;
touchSupport && (this.scrollable.container.style.overflow = '');
}
resolve(true);

2
src/lib/appManagers/appMediaViewer.ts

@ -777,7 +777,9 @@ export class AppMediaViewer { @@ -777,7 +777,9 @@ export class AppMediaViewer {
let video = mover.querySelector('video') || document.createElement('video');
let source = video.firstElementChild as HTMLSourceElement || document.createElement('source');
video.setAttribute('playsinline', '');
if(media.type == 'gif') {
video.muted = true;
video.autoplay = true;
video.loop = true;
}

10
src/lib/appManagers/appMessagesManager.ts

@ -23,7 +23,7 @@ import appPollsManager from "./appPollsManager"; @@ -23,7 +23,7 @@ import appPollsManager from "./appPollsManager";
import searchIndexManager from '../searchIndexManager';
import { MTDocument, MTPhotoSize } from "../../types";
console.trace('include');
//console.trace('include');
const APITIMEOUT = 0;
@ -238,10 +238,10 @@ export class AppMessagesManager { @@ -238,10 +238,10 @@ export class AppMessagesManager {
apiUpdatesManager.attach(updates ?? null);
resolve();
}).catch(resolve);
}).catch(resolve).finally(() => {
setInterval(() => this.saveState(), 10000);
});
});
setInterval(() => this.saveState(), 10000);
}
public saveState() {
@ -1986,7 +1986,7 @@ export class AppMessagesManager { @@ -1986,7 +1986,7 @@ export class AppMessagesManager {
str = langPack[_] + suffix;
}
console.log('message action:', action);
//console.log('message action:', action);
messageText = '<i>' + str + '</i>';
}

6
src/lib/appManagers/appWebpManager.ts

@ -19,7 +19,7 @@ class AppWebpManager { @@ -19,7 +19,7 @@ class AppWebpManager {
if(!res) {
(window as any).webpLoaded = () => {
console.log('webpHero loaded');
//console.log('webpHero loaded');
this.webpMachine = new (window as any).WebpMachine();
resolve();
};
@ -56,7 +56,7 @@ class AppWebpManager { @@ -56,7 +56,7 @@ class AppWebpManager {
this.busyPromise = this.convert(bytes);
let res = await this.busyPromise;
console.log('converted webp', res);
//console.log('converted webp', res);
callback(res as Uint8Array);
@ -85,7 +85,7 @@ class AppWebpManager { @@ -85,7 +85,7 @@ class AppWebpManager {
}
public convertToPng(bytes: Uint8Array) {
console.warn('convertToPng!');
//console.warn('convertToPng!');
return new Promise<Uint8Array>((resolve, reject) => {
// @ts-ignore
this.queue.push({bytes, callback: resolve});

6
src/lib/cacheStorage.ts

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
import {blobConstruct, bytesToBase64, blobSafeMimeType, dataUrlToBlob} from './bin_utils';
import {blobConstruct} from './bin_utils';
import FileManager from './filemanager';
import { logger } from './polyfill';
//import { logger } from './polyfill';
class CacheStorageController {
public dbName = 'cachedFiles';
public openDbPromise: Promise<Cache>;
private log: ReturnType<typeof logger> = logger('CS');
//private log: ReturnType<typeof logger> = logger('CS');
constructor() {
this.openDatabase();

5
src/lib/config.ts

@ -92,13 +92,16 @@ export const mediaSizes = new MediaSizes(); @@ -92,13 +92,16 @@ export const mediaSizes = new MediaSizes();
// @ts-ignore
export const touchSupport = ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch);
export const isApple = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) != -1;
const Config = {
Emoji,
LatinizeMap,
TLD,
Countries,
MediaSizes: mediaSizes,
touchSupport
touchSupport,
isApple
};
(window as any).Config = Config;
export default Config;

103
src/lib/lottieLoader.ts

@ -1,10 +1,19 @@ @@ -1,10 +1,19 @@
import { isInDOM } from "./utils";
import { isApple } from "./config";
let convert = (value: number) => {
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
};
type RLottiePlayerListeners = 'enterFrame' | 'ready';
type RLottieOptions = {
container: HTMLElement,
autoplay?: boolean,
animationData: any,
loop?: boolean,
width?: number,
height?: number
};
export class RLottiePlayer {
public static reqId = 0;
@ -16,8 +25,8 @@ export class RLottiePlayer { @@ -16,8 +25,8 @@ export class RLottiePlayer {
public worker: QueryableWorker;
public width: number;
public height: number;
private width = 0;
private height = 0;
public listeners: Partial<{
[k in RLottiePlayerListeners]: (res: any) => void
@ -40,21 +49,38 @@ export class RLottiePlayer { @@ -40,21 +49,38 @@ export class RLottiePlayer {
private frThen: number;
private rafId: number;
private playedTimes = 0;
//private playedTimes = 0;
constructor({el, width, height, worker}: {
constructor({el, worker, options}: {
el: HTMLElement,
width: number,
height: number,
worker: QueryableWorker
worker: QueryableWorker,
options: RLottieOptions
}) {
this.reqId = ++RLottiePlayer['reqId'];
this.el = el;
this.width = width;
this.height = height;
this.worker = worker;
for(let i in options) {
if(this.hasOwnProperty(i)) {
// @ts-ignore
this[i] = options[i];
}
}
//console.log("RLottiePlayer width:", this.width, this.height, options);
if(window.devicePixelRatio > 1) {
if(isApple) {
this.width *= window.devicePixelRatio;
this.height *= window.devicePixelRatio;
} else {
this.width *= (window.devicePixelRatio - 1.5);
this.height *= (window.devicePixelRatio - 1.5);
}
}
this.canvas = document.createElement('canvas');
this.canvas.classList.add('rlottie');
this.canvas.width = this.width;
this.canvas.height = this.height;
this.context = this.canvas.getContext('2d');
@ -134,11 +160,11 @@ export class RLottiePlayer { @@ -134,11 +160,11 @@ export class RLottiePlayer {
public renderFrame(frame: Uint8ClampedArray, frameNo: number) {
try {
this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0);
this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0/* , 0, 0, this.canvas.width, this.canvas.height */);
} catch(err) {
console.error('RLottiePlayer renderFrame error:', err, frame, this.width, this.height);
this.autoplay = false;
this.stop();
this.pause();
}
this.setListenerResult('enterFrame', frameNo);
@ -170,7 +196,7 @@ export class RLottiePlayer { @@ -170,7 +196,7 @@ export class RLottiePlayer {
private mainLoopForwards() {
this.sendQuery('renderFrame', this.curFrame++);
if(this.curFrame >= this.frameCount) {
this.playedTimes++;
//this.playedTimes++;
if(!this.loop) return false;
@ -183,7 +209,7 @@ export class RLottiePlayer { @@ -183,7 +209,7 @@ export class RLottiePlayer {
private mainLoopBackwards() {
this.sendQuery('renderFrame', this.curFrame--);
if(this.curFrame < 0) {
this.playedTimes++;
//this.playedTimes++;
if(!this.loop) return false;
@ -385,14 +411,7 @@ class LottieLoader { @@ -385,14 +411,7 @@ class LottieLoader {
}
}
public async loadAnimationWorker(params: {
container: HTMLElement,
autoplay?: boolean,
animationData: any,
loop?: boolean,
width?: number,
height?: number
}, group = '', toneIndex = -1) {
public async loadAnimationWorker(params: RLottieOptions, group = '', toneIndex = -1) {
params.autoplay = true;
if(toneIndex >= 1 && toneIndex <= 5) {
@ -403,21 +422,16 @@ class LottieLoader { @@ -403,21 +422,16 @@ class LottieLoader {
await this.loadLottieWorkers();
}
const width = params.width || parseInt(params.container.style.width);
const height = params.height || parseInt(params.container.style.height);
if(!params.width || !params.height) {
params.width = parseInt(params.container.style.width);
params.height = parseInt(params.container.style.height);
}
if(!width || !height) {
if(!params.width || !params.height) {
throw new Error('No size for sticker!');
}
const player = this.initPlayer(params.container, params.animationData, width, height);
for(let i in params) {
// @ts-ignore
if(player.hasOwnProperty(i)) {
// @ts-ignore
player[i] = params[i];
}
}
const player = this.initPlayer(params.container, params);
(this.byGroups[group] ?? (this.byGroups[group] = [])).push(player);
@ -425,7 +439,7 @@ class LottieLoader { @@ -425,7 +439,7 @@ class LottieLoader {
}
public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
const groups = group && false ? [group] : Object.keys(this.byGroups);
const groups = group /* && false */ ? [group] : Object.keys(this.byGroups);
if(group && !this.byGroups[group]) {
console.warn('no animation group:', group);
@ -438,20 +452,6 @@ class LottieLoader { @@ -438,20 +452,6 @@ class LottieLoader {
animations.forEach(player => {
this.checkAnimation(player, blurred, destroy);
//if(!autoplay) continue;
/* if(blurred || !isElementInViewport(container)) {
if(!paused) {
this.debug && console.log('pause animation', isElementInViewport(container), container);
animation.pause();
animations[i].paused = true;
}
} else if(paused) {
this.debug && console.log('play animation', container);
animation.play();
animations[i].paused = false;
} */
});
}
}
@ -523,12 +523,11 @@ class LottieLoader { @@ -523,12 +523,11 @@ class LottieLoader {
this.workers.length = 0;
}
private initPlayer(el: HTMLElement, json: any, width: number, height: number) {
private initPlayer(el: HTMLElement, options: RLottieOptions) {
const rlPlayer = new RLottiePlayer({
el,
width,
height,
worker: this.workers[this.curWorkerNum++]
worker: this.workers[this.curWorkerNum++],
options
});
this.players[rlPlayer.reqId] = rlPlayer;
@ -536,7 +535,7 @@ class LottieLoader { @@ -536,7 +535,7 @@ class LottieLoader {
this.curWorkerNum = 0;
}
rlPlayer.loadFromData(json);
rlPlayer.loadFromData(options.animationData);
return rlPlayer;
}

17
src/lib/polyfill.ts

@ -4,25 +4,30 @@ import {SecureRandom} from 'jsbn'; @@ -4,25 +4,30 @@ import {SecureRandom} from 'jsbn';
export const secureRandom = new SecureRandom();
export function logger(prefix: string) {
export enum LogLevels {
log = 1,
warn = 2,
error = 4
};
export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | LogLevels.error) {
function Log(...args: any[]) {
return console.log(dT(), '[' + prefix + ']:', ...args);
return level & LogLevels.log && console.log(dT(), '[' + prefix + ']:', ...args);
}
Log.warn = function(...args: any[]) {
return console.warn(dT(), '[' + prefix + ']:', ...args);
return level & LogLevels.warn && console.warn(dT(), '[' + prefix + ']:', ...args);
};
Log.info = function(...args: any[]) {
return console.info(dT(), '[' + prefix + ']:', ...args);
return level & LogLevels.log && console.info(dT(), '[' + prefix + ']:', ...args);
};
Log.error = function(...args: any[]) {
return console.error(dT(), '[' + prefix + ']:', ...args);
return level & LogLevels.error && console.error(dT(), '[' + prefix + ']:', ...args);
};
Log.trace = function(...args: any[]) {
return console.trace(dT(), '[' + prefix + ']:', ...args);
return level & LogLevels.log && console.trace(dT(), '[' + prefix + ']:', ...args);
}
return Log;

2
src/scss/partials/_chat.scss

@ -437,7 +437,7 @@ $time-background: rgba(0, 0, 0, .35); @@ -437,7 +437,7 @@ $time-background: rgba(0, 0, 0, .35);
position: relative;
/* padding: .25rem; */
&.is-reply-media {
&.is-media {
.pinned-message-content, .reply-content {
padding-left: 40px;
}

5
src/scss/style.scss

@ -1354,6 +1354,11 @@ img.emoji { @@ -1354,6 +1354,11 @@ img.emoji {
pointer-events: none;
}
.rlottie {
max-width: 100%;
max-height: 100%;
}
/* #chats-container {
display: -webkit-box;
display: -webkit-flex;

Loading…
Cancel
Save