commit
9e56988a49
src
components
appMediaPlaybackController.tsappMediaViewer.tsappSearchSuper..tsappSelectPeers.tsavatar.tsbuttonCorner.ts
chat
editPeer.tsemoticonsDropdown/tabs
gifsMasonry.tspopups
sidebarLeft
sidebarRight/tabs
sortedUserList.tswrappers.tshelpers
index.hbslang.tslayer.d.tslib
appManagers
apiUpdatesManager.tsappChatsManager.tsappDialogsManager.tsappDocsManager.tsappDownloadManager.tsappImManager.tsappMessagesManager.tsappPeersManager.tsappPhotosManager.tsappProfileManager.tsappStateManager.tsappStickersManager.ts
idb.tsmtproto
rootScope.tsstorage.tspages
scripts
scss/partials
@ -10,6 +10,7 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
|
|||||||
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
|
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
|
||||||
import { isSafari } from "../helpers/userAgent";
|
import { isSafari } from "../helpers/userAgent";
|
||||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||||
|
import appDownloadManager from "../lib/appManagers/appDownloadManager";
|
||||||
|
|
||||||
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда
|
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда
|
||||||
|
|
||||||
@ -125,7 +126,8 @@ class AppMediaPlaybackController {
|
|||||||
this.handleSafariStreamable(media);
|
this.handleSafariStreamable(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
media.src = doc.url;
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
media.src = cacheContext.url;
|
||||||
});
|
});
|
||||||
}, onError);
|
}, onError);
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ import findUpClassName from "../helpers/dom/findUpClassName";
|
|||||||
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
|
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
|
||||||
import findUpAsChild from "../helpers/dom/findUpAsChild";
|
import findUpAsChild from "../helpers/dom/findUpAsChild";
|
||||||
import getVisibleRect from "../helpers/dom/getVisibleRect";
|
import getVisibleRect from "../helpers/dom/getVisibleRect";
|
||||||
|
import appDownloadManager from "../lib/appManagers/appDownloadManager";
|
||||||
|
|
||||||
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
|
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
|
||||||
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
|
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
|
||||||
@ -952,8 +953,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
|
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
|
||||||
const maxHeight = appPhotosManager.windowH - 100;
|
const maxHeight = appPhotosManager.windowH - 100;
|
||||||
let thumbPromise: Promise<any> = Promise.resolve();
|
let thumbPromise: Promise<any> = Promise.resolve();
|
||||||
|
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true);
|
||||||
if(useContainerAsTarget) {
|
if(useContainerAsTarget) {
|
||||||
const cacheContext = appPhotosManager.getCacheContext(media);
|
const cacheContext = appDownloadManager.getCacheContext(media, size.type);
|
||||||
let img: HTMLImageElement;
|
let img: HTMLImageElement;
|
||||||
if(cacheContext.downloaded) {
|
if(cacheContext.downloaded) {
|
||||||
img = new Image();
|
img = new Image();
|
||||||
@ -971,7 +973,6 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
container.append(img);
|
container.append(img);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true);
|
|
||||||
|
|
||||||
// need after setAttachmentSize
|
// need after setAttachmentSize
|
||||||
/* if(useContainerAsTarget) {
|
/* if(useContainerAsTarget) {
|
||||||
@ -1091,11 +1092,12 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
|
|
||||||
//if(!video.src || media.url !== video.src) {
|
//if(!video.src || media.url !== video.src) {
|
||||||
const load = () => {
|
const load = () => {
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(media);
|
||||||
const promise = media.supportsStreaming ? Promise.resolve() : appDocsManager.downloadDoc(media);
|
const promise = media.supportsStreaming ? Promise.resolve() : appDocsManager.downloadDoc(media);
|
||||||
|
|
||||||
if(!media.supportsStreaming) {
|
if(!media.supportsStreaming) {
|
||||||
onAnimationEnd.then(() => {
|
onAnimationEnd.then(() => {
|
||||||
if(!media.url) {
|
if(!cacheContext.url) {
|
||||||
preloader.attach(mover, true, promise);
|
preloader.attach(mover, true, promise);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1107,7 +1109,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = media.url;
|
const url = cacheContext.url;
|
||||||
if(target instanceof SVGSVGElement/* && (video.parentElement || !isSafari) */) { // if video exists
|
if(target instanceof SVGSVGElement/* && (video.parentElement || !isSafari) */) { // if video exists
|
||||||
//if(!video.parentElement) {
|
//if(!video.parentElement) {
|
||||||
div.firstElementChild.lastElementChild.append(video);
|
div.firstElementChild.lastElementChild.append(video);
|
||||||
@ -1136,10 +1138,11 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
//return;
|
//return;
|
||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(media, size.type);
|
||||||
const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
|
const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
|
||||||
|
|
||||||
onAnimationEnd.then(() => {
|
onAnimationEnd.then(() => {
|
||||||
if(!media.url) {
|
if(!cacheContext.url) {
|
||||||
this.preloader.attachPromise(cancellablePromise);
|
this.preloader.attachPromise(cancellablePromise);
|
||||||
//this.preloader.attach(mover, true, cancellablePromise);
|
//this.preloader.attach(mover, true, cancellablePromise);
|
||||||
}
|
}
|
||||||
@ -1153,7 +1156,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
|
|
||||||
///////this.log('indochina', blob);
|
///////this.log('indochina', blob);
|
||||||
|
|
||||||
const url = media.url;
|
const url = cacheContext.url;
|
||||||
if(target instanceof SVGSVGElement) {
|
if(target instanceof SVGSVGElement) {
|
||||||
this.updateMediaSource(target, url, 'img');
|
this.updateMediaSource(target, url, 'img');
|
||||||
this.updateMediaSource(mover, url, 'img');
|
this.updateMediaSource(mover, url, 'img');
|
||||||
|
@ -477,6 +477,7 @@ export default class AppSearchSuper {
|
|||||||
//this.log(message, photo);
|
//this.log(message, photo);
|
||||||
|
|
||||||
let wrapped: ReturnType<typeof wrapPhoto>;
|
let wrapped: ReturnType<typeof wrapPhoto>;
|
||||||
|
const size = appPhotosManager.choosePhotoSize(media, 200, 200);
|
||||||
if(media._ !== 'photo') {
|
if(media._ !== 'photo') {
|
||||||
wrapped = wrapVideo({
|
wrapped = wrapVideo({
|
||||||
doc: media,
|
doc: media,
|
||||||
@ -488,7 +489,8 @@ export default class AppSearchSuper {
|
|||||||
middleware,
|
middleware,
|
||||||
onlyPreview: true,
|
onlyPreview: true,
|
||||||
withoutPreloader: true,
|
withoutPreloader: true,
|
||||||
noPlayButton: true
|
noPlayButton: true,
|
||||||
|
size
|
||||||
}).thumb;
|
}).thumb;
|
||||||
} else {
|
} else {
|
||||||
wrapped = wrapPhoto({
|
wrapped = wrapPhoto({
|
||||||
@ -500,7 +502,8 @@ export default class AppSearchSuper {
|
|||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware,
|
middleware,
|
||||||
withoutPreloader: true,
|
withoutPreloader: true,
|
||||||
noBlur: true
|
noBlur: true,
|
||||||
|
size
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,6 +886,7 @@ export default class AppSearchSuper {
|
|||||||
|
|
||||||
if(!this.membersList) {
|
if(!this.membersList) {
|
||||||
this.membersList = new SortedUserList();
|
this.membersList = new SortedUserList();
|
||||||
|
this.membersList.lazyLoadQueue = this.lazyLoadQueue;
|
||||||
this.membersList.list.addEventListener('click', (e) => {
|
this.membersList.list.addEventListener('click', (e) => {
|
||||||
const li = findUpTag(e.target, 'LI');
|
const li = findUpTag(e.target, 'LI');
|
||||||
if(!li) {
|
if(!li) {
|
||||||
@ -904,12 +908,17 @@ export default class AppSearchSuper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
participants.forEach(participant => {
|
participants.forEach(participant => {
|
||||||
const user = appUsersManager.getUser(participant.user_id);
|
const peerId = appChatsManager.getParticipantPeerId(participant);
|
||||||
|
if(peerId < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = appUsersManager.getUser(peerId);
|
||||||
if(user.pFlags.deleted) {
|
if(user.pFlags.deleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.membersList.add(participant.user_id);
|
this.membersList.add(peerId);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1161,6 +1170,10 @@ export default class AppSearchSuper {
|
|||||||
return !this.loaded[inputFilter] || (this.historyStorage[inputFilter] && this.usedFromHistory[inputFilter] < this.historyStorage[inputFilter].length);
|
return !this.loaded[inputFilter] || (this.historyStorage[inputFilter] && this.usedFromHistory[inputFilter] < this.historyStorage[inputFilter].length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(peerId > 0) {
|
||||||
|
toLoad.findAndSplice(mediaTab => mediaTab.type === 'members');
|
||||||
|
}
|
||||||
|
|
||||||
if(!toLoad.length) {
|
if(!toLoad.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -353,9 +353,11 @@ export default class AppSelectPeers {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userIds = participants.participants.map(participant => participant.user_id);
|
const peerIds = participants.participants.map(participant => {
|
||||||
userIds.findAndSplice(u => u === rootScope.myId);
|
return appChatsManager.getParticipantPeerId(participant);
|
||||||
this.renderResultsFunc(userIds);
|
});
|
||||||
|
peerIds.findAndSplice(u => u === rootScope.myId);
|
||||||
|
this.renderResultsFunc(peerIds);
|
||||||
|
|
||||||
if(this.list.childElementCount >= participants.count || participants.participants.length < pageCount) {
|
if(this.list.childElementCount >= participants.count || participants.participants.length < pageCount) {
|
||||||
this.loadedWhat.channelParticipants = true;
|
this.loadedWhat.channelParticipants = true;
|
||||||
|
@ -9,10 +9,10 @@ import appProfileManager from "../lib/appManagers/appProfileManager";
|
|||||||
import rootScope from "../lib/rootScope";
|
import rootScope from "../lib/rootScope";
|
||||||
import { attachClickEvent, cancelEvent } from "../helpers/dom";
|
import { attachClickEvent, cancelEvent } from "../helpers/dom";
|
||||||
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
|
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
|
||||||
import { Message, Photo } from "../layer";
|
import { Message } from "../layer";
|
||||||
import appPeersManager from "../lib/appManagers/appPeersManager";
|
import appPeersManager from "../lib/appManagers/appPeersManager";
|
||||||
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
||||||
//import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||||
|
|
||||||
const onAvatarUpdate = (peerId: number) => {
|
const onAvatarUpdate = (peerId: number) => {
|
||||||
appProfileManager.removeFromAvatarsCache(peerId);
|
appProfileManager.removeFromAvatarsCache(peerId);
|
||||||
@ -98,24 +98,22 @@ export async function openAvatarViewer(target: HTMLElement, peerId: number, midd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const believeMe: Map<number, Set<AvatarElement>> = new Map();
|
||||||
|
const seen: Set<number> = new Set();
|
||||||
|
|
||||||
export default class AvatarElement extends HTMLElement {
|
export default class AvatarElement extends HTMLElement {
|
||||||
private peerId: number;
|
private peerId: number;
|
||||||
private isDialog = false;
|
private isDialog = false;
|
||||||
public peerTitle: string;
|
private peerTitle: string;
|
||||||
public loadPromises: Promise<any>[];
|
public loadPromises: Promise<any>[];
|
||||||
//public lazyLoadQueue: LazyLoadQueueIntersector;
|
public lazyLoadQueue: LazyLoadQueueIntersector;
|
||||||
//private addedToQueue = false;
|
private addedToQueue = false;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
// элемент создан
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
// браузер вызывает этот метод при добавлении элемента в документ
|
// браузер вызывает этот метод при добавлении элемента в документ
|
||||||
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
||||||
|
|
||||||
this.isDialog = !!this.getAttribute('dialog');
|
this.isDialog = this.getAttribute('dialog') === '1';
|
||||||
if(this.getAttribute('clickable') === '') {
|
if(this.getAttribute('clickable') === '') {
|
||||||
this.setAttribute('clickable', 'set');
|
this.setAttribute('clickable', 'set');
|
||||||
let loading = false;
|
let loading = false;
|
||||||
@ -131,13 +129,21 @@ export default class AvatarElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
// браузер вызывает этот метод при удалении элемента из документа
|
// браузер вызывает этот метод при удалении элемента из документа
|
||||||
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
||||||
|
const set = believeMe.get(this.peerId);
|
||||||
|
if(set && set.has(this)) {
|
||||||
|
set.delete(this);
|
||||||
|
if(!set.size) {
|
||||||
|
believeMe.delete(this.peerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(this.lazyLoadQueue) {
|
if(this.lazyLoadQueue) {
|
||||||
this.lazyLoadQueue.unobserve(this);
|
this.lazyLoadQueue.unobserve(this);
|
||||||
}
|
}
|
||||||
} */
|
}
|
||||||
|
|
||||||
static get observedAttributes(): string[] {
|
static get observedAttributes(): string[] {
|
||||||
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
|
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
|
||||||
@ -152,36 +158,88 @@ export default class AvatarElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.peerId = appPeersManager.getPeerMigratedTo(+newValue) || +newValue;
|
this.peerId = appPeersManager.getPeerMigratedTo(+newValue) || +newValue;
|
||||||
|
|
||||||
|
const wasPeerId = +oldValue;
|
||||||
|
if(wasPeerId) {
|
||||||
|
const set = believeMe.get(wasPeerId);
|
||||||
|
if(set) {
|
||||||
|
set.delete(this);
|
||||||
|
if(!set.size) {
|
||||||
|
believeMe.delete(wasPeerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
} else if(name === 'peer-title') {
|
} else if(name === 'peer-title') {
|
||||||
this.peerTitle = newValue;
|
this.peerTitle = newValue;
|
||||||
} else if(name === 'dialog') {
|
} else if(name === 'dialog') {
|
||||||
this.isDialog = !!+newValue;
|
this.isDialog = newValue === '1';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public update() {
|
public update() {
|
||||||
/* if(this.lazyLoadQueue) {
|
if(this.lazyLoadQueue) {
|
||||||
if(this.addedToQueue) return;
|
if(!seen.has(this.peerId)) {
|
||||||
this.lazyLoadQueue.push({
|
if(this.addedToQueue) return;
|
||||||
div: this,
|
this.addedToQueue = true;
|
||||||
load: () => {
|
|
||||||
return appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle).finally(() => {
|
let set = believeMe.get(this.peerId);
|
||||||
this.addedToQueue = false;
|
if(!set) {
|
||||||
});
|
set = new Set();
|
||||||
|
believeMe.set(this.peerId, set);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
this.addedToQueue = true;
|
set.add(this);
|
||||||
} else { */
|
|
||||||
const res = appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
|
this.lazyLoadQueue.push({
|
||||||
if(this.loadPromises && res && res.cached) {
|
div: this,
|
||||||
this.loadPromises.push(res.loadPromise);
|
load: () => {
|
||||||
res.loadPromise.finally(() => {
|
seen.add(this.peerId);
|
||||||
this.loadPromises = undefined;
|
return this.update();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if(this.addedToQueue) {
|
||||||
|
this.lazyLoadQueue.unobserve(this);
|
||||||
}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
|
seen.add(this.peerId);
|
||||||
|
|
||||||
|
const res = appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
|
||||||
|
const promise = res ? res.loadPromise : Promise.resolve();
|
||||||
|
if(this.loadPromises) {
|
||||||
|
if(res && res.cached) {
|
||||||
|
this.loadPromises.push(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.finally(() => {
|
||||||
|
this.loadPromises = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.addedToQueue) {
|
||||||
|
promise.finally(() => {
|
||||||
|
this.addedToQueue = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = believeMe.get(this.peerId);
|
||||||
|
if(set) {
|
||||||
|
set.delete(this);
|
||||||
|
const arr = Array.from(set);
|
||||||
|
believeMe.delete(this.peerId);
|
||||||
|
|
||||||
|
|
||||||
|
for(let i = 0, length = arr.length; i < length; ++i) {
|
||||||
|
arr[i].update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("avatar-element", AvatarElement);
|
customElements.define('avatar-element', AvatarElement);
|
||||||
|
14
src/components/buttonCorner.ts
Normal file
14
src/components/buttonCorner.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* https://github.com/morethanwords/tweb
|
||||||
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||||
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Button from "./button";
|
||||||
|
|
||||||
|
const ButtonCorner = (options: Partial<{className: string, icon: string, noRipple: true, onlyMobile: true, asDiv: boolean}> = {}) => {
|
||||||
|
const button = Button('btn-circle btn-corner z-depth-1' + (options.className ? ' ' + options.className : ''), options);
|
||||||
|
return button;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ButtonCorner;
|
@ -1618,17 +1618,33 @@ export default class ChatBubbles {
|
|||||||
|
|
||||||
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
|
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
|
||||||
|
|
||||||
// warning
|
const isFetchIntervalNeeded = () => peerId < 0 && !this.appChatsManager.isInChat(peerId);
|
||||||
if((!lastMsgId && !savedPosition) || this.bubbles[topMessage] || lastMsgId === topMessage) {
|
const needFetchInterval = isFetchIntervalNeeded();
|
||||||
this.scrollable.loadedAll.bottom = true;
|
const needFetchNew = savedPosition || needFetchInterval;
|
||||||
}
|
if(!needFetchNew) {
|
||||||
|
// warning
|
||||||
if(savedPosition) {
|
if(!lastMsgId || this.bubbles[topMessage] || lastMsgId === topMessage) {
|
||||||
|
this.scrollable.loadedAll.bottom = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Promise.all([setPeerPromise, getHeavyAnimationPromise()]).then(() => {
|
Promise.all([setPeerPromise, getHeavyAnimationPromise()]).then(() => {
|
||||||
this.scrollable.checkForTriggers();
|
this.scrollable.checkForTriggers();
|
||||||
|
|
||||||
|
if(needFetchInterval) {
|
||||||
|
const middleware = this.getMiddleware();
|
||||||
|
const interval = window.setInterval(() => {
|
||||||
|
if(!middleware() || !isFetchIntervalNeeded()) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scrollable.loadedAll.bottom = false;
|
||||||
|
this.loadMoreHistory(false);
|
||||||
|
}, 30e3);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log('scrolledAllDown:', this.scrollable.loadedAll.bottom);
|
this.log('scrolledAllDown:', this.scrollable.loadedAll.bottom);
|
||||||
|
|
||||||
//if(!this.unreaded.length && dialog) { // lol
|
//if(!this.unreaded.length && dialog) { // lol
|
||||||
@ -2560,7 +2576,8 @@ export default class ChatBubbles {
|
|||||||
bubbleContainer,
|
bubbleContainer,
|
||||||
message: messageWithReplies,
|
message: messageWithReplies,
|
||||||
messageDiv,
|
messageDiv,
|
||||||
loadPromises
|
loadPromises,
|
||||||
|
lazyLoadQueue: this.lazyLoadQueue
|
||||||
});
|
});
|
||||||
|
|
||||||
if(isFooter) {
|
if(isFooter) {
|
||||||
|
@ -47,6 +47,7 @@ import { isMobile } from '../../helpers/userAgent';
|
|||||||
import { i18n } from '../../lib/langPack';
|
import { i18n } from '../../lib/langPack';
|
||||||
import { generateTail } from './bubbles';
|
import { generateTail } from './bubbles';
|
||||||
import findUpClassName from '../../helpers/dom/findUpClassName';
|
import findUpClassName from '../../helpers/dom/findUpClassName';
|
||||||
|
import ButtonCorner from '../buttonCorner';
|
||||||
|
|
||||||
const RECORD_MIN_TIME = 500;
|
const RECORD_MIN_TIME = 500;
|
||||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||||
@ -156,7 +157,7 @@ export default class ChatInput {
|
|||||||
this.inputContainer.append(this.rowsWrapper, fakeRowsWrapper, fakeSelectionWrapper);
|
this.inputContainer.append(this.rowsWrapper, fakeRowsWrapper, fakeSelectionWrapper);
|
||||||
this.chatInput.append(this.inputContainer);
|
this.chatInput.append(this.inputContainer);
|
||||||
|
|
||||||
this.goDownBtn = Button('bubbles-go-down btn-corner btn-circle z-depth-1 hide', {icon: 'arrow_down'});
|
this.goDownBtn = ButtonCorner({icon: 'arrow_down', className: 'bubbles-go-down hide'});
|
||||||
this.inputContainer.append(this.goDownBtn);
|
this.inputContainer.append(this.goDownBtn);
|
||||||
|
|
||||||
attachClickEvent(this.goDownBtn, (e) => {
|
attachClickEvent(this.goDownBtn, (e) => {
|
||||||
@ -747,6 +748,21 @@ export default class ChatInput {
|
|||||||
this.sendMessage();
|
this.sendMessage();
|
||||||
} else if(e.ctrlKey || e.metaKey) {
|
} else if(e.ctrlKey || e.metaKey) {
|
||||||
this.handleMarkdownShortcut(e);
|
this.handleMarkdownShortcut(e);
|
||||||
|
} else if((e.key === 'PageUp' || e.key === 'PageDown') && !e.shiftKey) { // * fix pushing page to left (Chrome Windows)
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if(e.key === 'PageUp') {
|
||||||
|
const range = document.createRange();
|
||||||
|
const sel = window.getSelection();
|
||||||
|
|
||||||
|
range.setStart(this.messageInput.childNodes[0] || this.messageInput, 0);
|
||||||
|
range.collapse(true);
|
||||||
|
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
} else {
|
||||||
|
placeCaretAtEnd(this.messageInput);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import { getFullDate } from "../../helpers/date";
|
import { getFullDate } from "../../helpers/date";
|
||||||
import { formatNumber } from "../../helpers/number";
|
import { formatNumber } from "../../helpers/number";
|
||||||
import RichTextProcessor from "../../lib/richtextprocessor";
|
import RichTextProcessor from "../../lib/richtextprocessor";
|
||||||
|
import { LazyLoadQueueIntersector } from "../lazyLoadQueue";
|
||||||
import { wrapReply } from "../wrappers";
|
import { wrapReply } from "../wrappers";
|
||||||
import Chat from "./chat";
|
import Chat from "./chat";
|
||||||
import RepliesElement from "./replies";
|
import RepliesElement from "./replies";
|
||||||
@ -65,18 +66,20 @@ export namespace MessageRender {
|
|||||||
return timeSpan;
|
return timeSpan;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv, loadPromises}: {
|
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv, loadPromises, lazyLoadQueue}: {
|
||||||
bubble: HTMLElement,
|
bubble: HTMLElement,
|
||||||
bubbleContainer: HTMLElement,
|
bubbleContainer: HTMLElement,
|
||||||
message: any,
|
message: any,
|
||||||
messageDiv: HTMLElement,
|
messageDiv: HTMLElement,
|
||||||
loadPromises?: Promise<any>[]
|
loadPromises?: Promise<any>[],
|
||||||
|
lazyLoadQueue?: LazyLoadQueueIntersector
|
||||||
}) => {
|
}) => {
|
||||||
const isFooter = !bubble.classList.contains('sticker') && !bubble.classList.contains('emoji-big') && !bubble.classList.contains('round');
|
const isFooter = !bubble.classList.contains('sticker') && !bubble.classList.contains('emoji-big') && !bubble.classList.contains('round');
|
||||||
const repliesFooter = new RepliesElement();
|
const repliesFooter = new RepliesElement();
|
||||||
repliesFooter.message = message;
|
repliesFooter.message = message;
|
||||||
repliesFooter.type = isFooter ? 'footer' : 'beside';
|
repliesFooter.type = isFooter ? 'footer' : 'beside';
|
||||||
repliesFooter.loadPromises = loadPromises;
|
repliesFooter.loadPromises = loadPromises;
|
||||||
|
repliesFooter.lazyLoadQueue = lazyLoadQueue;
|
||||||
repliesFooter.init();
|
repliesFooter.init();
|
||||||
bubbleContainer.prepend(repliesFooter);
|
bubbleContainer.prepend(repliesFooter);
|
||||||
return isFooter;
|
return isFooter;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { LazyLoadQueueIntersector } from "../lazyLoadQueue";
|
||||||
import { formatNumber } from "../../helpers/number";
|
import { formatNumber } from "../../helpers/number";
|
||||||
import { Message } from "../../layer";
|
import { Message } from "../../layer";
|
||||||
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
||||||
@ -26,6 +27,7 @@ export default class RepliesElement extends HTMLElement {
|
|||||||
public message: Message.message;
|
public message: Message.message;
|
||||||
public type: 'footer' | 'beside';
|
public type: 'footer' | 'beside';
|
||||||
public loadPromises: Promise<any>[];
|
public loadPromises: Promise<any>[];
|
||||||
|
public lazyLoadQueue: LazyLoadQueueIntersector;
|
||||||
|
|
||||||
private updated = false;
|
private updated = false;
|
||||||
|
|
||||||
@ -69,6 +71,7 @@ export default class RepliesElement extends HTMLElement {
|
|||||||
avatarElem = new AvatarElement();
|
avatarElem = new AvatarElement();
|
||||||
avatarElem.setAttribute('dialog', '0');
|
avatarElem.setAttribute('dialog', '0');
|
||||||
avatarElem.classList.add('avatar-30');
|
avatarElem.classList.add('avatar-30');
|
||||||
|
avatarElem.lazyLoadQueue = this.lazyLoadQueue;
|
||||||
|
|
||||||
if(this.loadPromises) {
|
if(this.loadPromises) {
|
||||||
avatarElem.loadPromises = this.loadPromises;
|
avatarElem.loadPromises = this.loadPromises;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
|
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
|
||||||
import { limitSymbols } from "../../helpers/string";
|
import { limitSymbols } from "../../helpers/string";
|
||||||
|
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||||
import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
||||||
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
||||||
import appPhotosManager from "../../lib/appManagers/appPhotosManager";
|
import appPhotosManager from "../../lib/appManagers/appPhotosManager";
|
||||||
@ -70,22 +71,22 @@ export function wrapReplyDivAndCaption(options: {
|
|||||||
|
|
||||||
const photo = media.photo || media.document;
|
const photo = media.photo || media.document;
|
||||||
|
|
||||||
const cacheContext = appPhotosManager.getCacheContext(photo);
|
const size = appPhotosManager.choosePhotoSize(photo, boxSize, boxSize/* mediaSizes.active.regular.width, mediaSizes.active.regular.height */);
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(photo, size.type);
|
||||||
|
|
||||||
if(!cacheContext.downloaded) {
|
if(!cacheContext.downloaded) {
|
||||||
const sizes = photo.sizes || photo.thumbs;
|
const sizes = photo.sizes || photo.thumbs;
|
||||||
if(sizes && sizes[0].bytes) {
|
if(sizes && sizes[0].bytes) {
|
||||||
setMedia = true;
|
setMedia = true;
|
||||||
renderImageFromUrl(mediaEl, appPhotosManager.getPreviewURLFromThumb(sizes[0]));
|
renderImageFromUrl(mediaEl, appPhotosManager.getPreviewURLFromThumb(photo, sizes[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const size = appPhotosManager.choosePhotoSize(photo, boxSize, boxSize/* mediaSizes.active.regular.width, mediaSizes.active.regular.height */);
|
|
||||||
if(size._ !== 'photoSizeEmpty') {
|
if(size._ !== 'photoSizeEmpty') {
|
||||||
setMedia = true;
|
setMedia = true;
|
||||||
appPhotosManager.preloadPhoto(photo, size)
|
appPhotosManager.preloadPhoto(photo, size)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
renderImageFromUrl(mediaEl, photo._ === 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url);
|
renderImageFromUrl(mediaEl, cacheContext.url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ import AvatarEdit from "./avatarEdit";
|
|||||||
import AvatarElement from "./avatar";
|
import AvatarElement from "./avatar";
|
||||||
import InputField from "./inputField";
|
import InputField from "./inputField";
|
||||||
import ListenerSetter from "../helpers/listenerSetter";
|
import ListenerSetter from "../helpers/listenerSetter";
|
||||||
import Button from "./button";
|
|
||||||
import { safeAssign } from "../helpers/object";
|
import { safeAssign } from "../helpers/object";
|
||||||
|
import ButtonCorner from "./buttonCorner";
|
||||||
|
|
||||||
export default class EditPeer {
|
export default class EditPeer {
|
||||||
public nextBtn: HTMLButtonElement;
|
public nextBtn: HTMLButtonElement;
|
||||||
@ -32,7 +32,7 @@ export default class EditPeer {
|
|||||||
}) {
|
}) {
|
||||||
safeAssign(this, options);
|
safeAssign(this, options);
|
||||||
|
|
||||||
this.nextBtn = Button('btn-circle btn-corner tgico-check');
|
this.nextBtn = ButtonCorner({icon: 'check'});
|
||||||
|
|
||||||
this.avatarElem = document.createElement('avatar-element') as AvatarElement;
|
this.avatarElem = document.createElement('avatar-element') as AvatarElement;
|
||||||
this.avatarElem.classList.add('avatar-placeholder', 'avatar-120');
|
this.avatarElem.classList.add('avatar-placeholder', 'avatar-120');
|
||||||
|
@ -12,6 +12,7 @@ import { MessagesAllStickers, StickerSet } from "../../../layer";
|
|||||||
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager";
|
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager";
|
||||||
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
|
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
|
||||||
import appStickersManager from "../../../lib/appManagers/appStickersManager";
|
import appStickersManager from "../../../lib/appManagers/appStickersManager";
|
||||||
|
import { i18n } from "../../../lib/langPack";
|
||||||
import lottieLoader from "../../../lib/lottieLoader";
|
import lottieLoader from "../../../lib/lottieLoader";
|
||||||
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
||||||
import rootScope from "../../../lib/rootScope";
|
import rootScope from "../../../lib/rootScope";
|
||||||
@ -143,7 +144,7 @@ export default class StickersTab implements EmoticonsTab {
|
|||||||
|
|
||||||
private superStickerRenderer: SuperStickerRenderer;
|
private superStickerRenderer: SuperStickerRenderer;
|
||||||
|
|
||||||
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MyDocument[]>, prepend?: boolean) {
|
categoryPush(categoryDiv: HTMLElement, categoryTitle: string = '', promise: Promise<MyDocument[]>, prepend?: boolean) {
|
||||||
//if((docs.length % 5) !== 0) categoryDiv.classList.add('not-full');
|
//if((docs.length % 5) !== 0) categoryDiv.classList.add('not-full');
|
||||||
|
|
||||||
const itemsDiv = document.createElement('div');
|
const itemsDiv = document.createElement('div');
|
||||||
@ -151,7 +152,10 @@ export default class StickersTab implements EmoticonsTab {
|
|||||||
|
|
||||||
const titleDiv = document.createElement('div');
|
const titleDiv = document.createElement('div');
|
||||||
titleDiv.classList.add('category-title');
|
titleDiv.classList.add('category-title');
|
||||||
titleDiv.innerHTML = categoryTitle;
|
|
||||||
|
if(categoryTitle) {
|
||||||
|
titleDiv.innerHTML = categoryTitle;
|
||||||
|
}
|
||||||
|
|
||||||
categoryDiv.append(titleDiv, itemsDiv);
|
categoryDiv.append(titleDiv, itemsDiv);
|
||||||
|
|
||||||
@ -180,6 +184,8 @@ export default class StickersTab implements EmoticonsTab {
|
|||||||
this.queueCategoryPush.length = 0;
|
this.queueCategoryPush.length = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {titleDiv};
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
|
async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
|
||||||
@ -324,7 +330,8 @@ export default class StickersTab implements EmoticonsTab {
|
|||||||
};
|
};
|
||||||
|
|
||||||
preloader.remove();
|
preloader.remove();
|
||||||
this.categoryPush(this.recentDiv, 'Recent', Promise.resolve(this.recentStickers), true);
|
const {titleDiv} = this.categoryPush(this.recentDiv, '', Promise.resolve(this.recentStickers), true);
|
||||||
|
titleDiv.append(i18n('Stickers.Recent'));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
appStickersManager.getAllStickers().then((res) => {
|
appStickersManager.getAllStickers().then((res) => {
|
||||||
|
@ -199,9 +199,9 @@ export default class GifsMasonry {
|
|||||||
if(willBeAPoster) {
|
if(willBeAPoster) {
|
||||||
img = new Image();
|
img = new Image();
|
||||||
|
|
||||||
if(!gotThumb.thumb.url) {
|
if(!gotThumb.cacheContext.url) {
|
||||||
gotThumb.promise.then(() => {
|
gotThumb.promise.then(() => {
|
||||||
img.src = gotThumb.thumb.url;
|
img.src = gotThumb.cacheContext.url;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,6 +213,6 @@ export default class GifsMasonry {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(gotThumb?.thumb?.url ? renderImageFromUrl(img, gotThumb.thumb.url, afterRender) : afterRender());
|
(gotThumb?.cacheContext?.url ? renderImageFromUrl(img, gotThumb.cacheContext.url, afterRender) : afterRender());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import SendContextMenu from "../chat/sendContextMenu";
|
|||||||
import { createPosterFromVideo, onVideoLoad } from "../../helpers/files";
|
import { createPosterFromVideo, onVideoLoad } from "../../helpers/files";
|
||||||
import { MyDocument } from "../../lib/appManagers/appDocsManager";
|
import { MyDocument } from "../../lib/appManagers/appDocsManager";
|
||||||
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
||||||
|
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||||
|
|
||||||
type SendFileParams = Partial<{
|
type SendFileParams = Partial<{
|
||||||
file: File,
|
file: File,
|
||||||
@ -288,6 +289,18 @@ export default class PopupNewMedia extends PopupElement {
|
|||||||
params.objectURL = URL.createObjectURL(file);
|
params.objectURL = URL.createObjectURL(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const doc = {
|
||||||
|
_: 'document',
|
||||||
|
file: file,
|
||||||
|
file_name: file.name || '',
|
||||||
|
size: file.size,
|
||||||
|
type: isPhoto ? 'photo' : 'doc'
|
||||||
|
} as MyDocument;
|
||||||
|
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
cacheContext.url = params.objectURL;
|
||||||
|
cacheContext.downloaded = file.size;
|
||||||
|
|
||||||
const docDiv = wrapDocument({
|
const docDiv = wrapDocument({
|
||||||
message: {
|
message: {
|
||||||
_: 'message',
|
_: 'message',
|
||||||
@ -298,15 +311,7 @@ export default class PopupNewMedia extends PopupElement {
|
|||||||
peerId: 0,
|
peerId: 0,
|
||||||
media: {
|
media: {
|
||||||
_: 'messageMediaDocument',
|
_: 'messageMediaDocument',
|
||||||
document: {
|
document: doc
|
||||||
_: 'document',
|
|
||||||
file: file,
|
|
||||||
file_name: file.name || '',
|
|
||||||
size: file.size,
|
|
||||||
type: isPhoto ? 'photo' : 'doc',
|
|
||||||
url: params.objectURL,
|
|
||||||
downloaded: true
|
|
||||||
} as MyDocument
|
|
||||||
}
|
}
|
||||||
} as any
|
} as any
|
||||||
});
|
});
|
||||||
|
@ -208,7 +208,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
|||||||
text: 'NewPrivateChat',
|
text: 'NewPrivateChat',
|
||||||
onClick: onContactsClick
|
onClick: onContactsClick
|
||||||
}]);
|
}]);
|
||||||
this.newBtnMenu.className = 'btn-circle rp btn-corner btn-menu-toggle animated-button-icon';
|
this.newBtnMenu.className = 'btn-circle rp btn-corner z-depth-1 btn-menu-toggle animated-button-icon';
|
||||||
this.newBtnMenu.insertAdjacentHTML('afterbegin', `
|
this.newBtnMenu.insertAdjacentHTML('afterbegin', `
|
||||||
<span class="tgico tgico-newchat_filled"></span>
|
<span class="tgico tgico-newchat_filled"></span>
|
||||||
<span class="tgico tgico-close"></span>
|
<span class="tgico tgico-close"></span>
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
import { SliderSuperTab } from "../../slider";
|
import { SliderSuperTab } from "../../slider";
|
||||||
import AppSelectPeers from "../../appSelectPeers";
|
import AppSelectPeers from "../../appSelectPeers";
|
||||||
import { putPreloader } from "../../misc";
|
import { putPreloader } from "../../misc";
|
||||||
import Button from "../../button";
|
|
||||||
import { LangPackKey, _i18n } from "../../../lib/langPack";
|
import { LangPackKey, _i18n } from "../../../lib/langPack";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
export default class AppAddMembersTab extends SliderSuperTab {
|
export default class AppAddMembersTab extends SliderSuperTab {
|
||||||
private nextBtn: HTMLButtonElement;
|
private nextBtn: HTMLButtonElement;
|
||||||
@ -18,7 +18,7 @@ export default class AppAddMembersTab extends SliderSuperTab {
|
|||||||
private skippable: boolean;
|
private skippable: boolean;
|
||||||
|
|
||||||
protected init() {
|
protected init() {
|
||||||
this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'});
|
this.nextBtn = ButtonCorner({icon: 'arrow_next'});
|
||||||
this.content.append(this.nextBtn);
|
this.content.append(this.nextBtn);
|
||||||
|
|
||||||
this.nextBtn.addEventListener('click', () => {
|
this.nextBtn.addEventListener('click', () => {
|
||||||
|
@ -110,7 +110,6 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
location: {} as any,
|
location: {} as any,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
type: 'full',
|
type: 'full',
|
||||||
url: URL.createObjectURL(file)
|
|
||||||
} as PhotoSize.photoSize;
|
} as PhotoSize.photoSize;
|
||||||
let document: MyDocument = {
|
let document: MyDocument = {
|
||||||
_: 'document',
|
_: 'document',
|
||||||
@ -121,9 +120,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
id,
|
id,
|
||||||
mime_type: file.type,
|
mime_type: file.type,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
downloaded: true,
|
|
||||||
date: Date.now() / 1000,
|
date: Date.now() / 1000,
|
||||||
url: thumb.url,
|
|
||||||
pFlags: {},
|
pFlags: {},
|
||||||
thumbs: [thumb],
|
thumbs: [thumb],
|
||||||
file_name: file.name
|
file_name: file.name
|
||||||
@ -131,9 +128,9 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
|
|
||||||
document = appDocsManager.saveDoc(document);
|
document = appDocsManager.saveDoc(document);
|
||||||
|
|
||||||
const docThumb = appPhotosManager.getDocumentCachedThumb(document.id);
|
const cacheContext = appDownloadManager.getCacheContext(document);
|
||||||
docThumb.downloaded = thumb.size;
|
cacheContext.downloaded = file.size;
|
||||||
docThumb.url = thumb.url;
|
cacheContext.url = URL.createObjectURL(file);
|
||||||
|
|
||||||
let wallpaper: WallPaper.wallPaper = {
|
let wallpaper: WallPaper.wallPaper = {
|
||||||
_: 'wallPaper',
|
_: 'wallPaper',
|
||||||
@ -159,8 +156,8 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
}
|
}
|
||||||
}).then(_wallpaper => {
|
}).then(_wallpaper => {
|
||||||
const newDoc = (_wallpaper as WallPaper.wallPaper).document as MyDocument;
|
const newDoc = (_wallpaper as WallPaper.wallPaper).document as MyDocument;
|
||||||
newDoc.downloaded = document.downloaded;
|
const newCacheContext = appDownloadManager.getCacheContext(newDoc);
|
||||||
newDoc.url = document.url;
|
Object.assign(newCacheContext, cacheContext);
|
||||||
|
|
||||||
wallpaper = _wallpaper as WallPaper.wallPaper;
|
wallpaper = _wallpaper as WallPaper.wallPaper;
|
||||||
wallpaper.document = appDocsManager.saveDoc(wallpaper.document);
|
wallpaper.document = appDocsManager.saveDoc(wallpaper.document);
|
||||||
@ -220,9 +217,8 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
photo: wallpaper.document,
|
photo: wallpaper.document,
|
||||||
message: null,
|
message: null,
|
||||||
container: media,
|
container: media,
|
||||||
boxWidth: 0,
|
withoutPreloader: true,
|
||||||
boxHeight: 0,
|
size: appPhotosManager.choosePhotoSize(wallpaper.document, 200, 200)
|
||||||
withoutPreloader: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
container.dataset.docId = wallpaper.document.id;
|
container.dataset.docId = wallpaper.document.id;
|
||||||
@ -260,7 +256,8 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
const promise = this.setBackgroundDocument(slug, doc);
|
const promise = this.setBackgroundDocument(slug, doc);
|
||||||
if(!doc.url || this.theme.background.blur) {
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
if(!cacheContext.url || this.theme.background.blur) {
|
||||||
preloader.attach(target, true, promise);
|
preloader.attach(target, true, promise);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -325,9 +322,10 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
if(background.blur) {
|
if(background.blur) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
blur(doc.url, 12, 4)
|
blur(cacheContext.url, 12, 4)
|
||||||
.then(url => {
|
.then(url => {
|
||||||
if(!middleware()) {
|
if(!middleware()) {
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
@ -338,7 +336,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
} else {
|
} else {
|
||||||
onReady(doc.url);
|
onReady(cacheContext.url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ import { attachClickEvent } from "../../../helpers/dom";
|
|||||||
import ButtonMenu from "../../buttonMenu";
|
import ButtonMenu from "../../buttonMenu";
|
||||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||||
import Button from "../../button";
|
|
||||||
import PopupPickUser from "../../popups/pickUser";
|
import PopupPickUser from "../../popups/pickUser";
|
||||||
import rootScope from "../../../lib/rootScope";
|
import rootScope from "../../../lib/rootScope";
|
||||||
import findUpTag from "../../../helpers/dom/findUpTag";
|
import findUpTag from "../../../helpers/dom/findUpTag";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
export default class AppBlockedUsersTab extends SliderSuperTab {
|
export default class AppBlockedUsersTab extends SliderSuperTab {
|
||||||
public peerIds: number[];
|
public peerIds: number[];
|
||||||
@ -32,7 +32,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
|
|||||||
this.scrollable.append(section.container);
|
this.scrollable.append(section.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
const btnAdd = Button('btn-circle btn-corner tgico-add is-visible');
|
const btnAdd = ButtonCorner({icon: 'add', className: 'is-visible'});
|
||||||
this.content.append(btnAdd);
|
this.content.append(btnAdd);
|
||||||
|
|
||||||
attachClickEvent(btnAdd, (e) => {
|
attachClickEvent(btnAdd, (e) => {
|
||||||
|
@ -121,7 +121,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||||||
caption.classList.add('caption');
|
caption.classList.add('caption');
|
||||||
i18n_({element: caption, key: 'ChatList.Filter.Header'});
|
i18n_({element: caption, key: 'ChatList.Filter.Header'});
|
||||||
|
|
||||||
this.createFolderBtn = Button('btn-primary btn-color-primary btn-create-folder', {
|
this.createFolderBtn = Button('btn-primary btn-color-primary btn-control tgico', {
|
||||||
text: 'ChatList.Filter.NewTitle',
|
text: 'ChatList.Filter.NewTitle',
|
||||||
icon: 'add'
|
icon: 'add'
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ import { SliderSuperTab } from "../../slider";
|
|||||||
import AvatarEdit from "../../avatarEdit";
|
import AvatarEdit from "../../avatarEdit";
|
||||||
import AppAddMembersTab from "./addMembers";
|
import AppAddMembersTab from "./addMembers";
|
||||||
import { _i18n } from "../../../lib/langPack";
|
import { _i18n } from "../../../lib/langPack";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
export default class AppNewChannelTab extends SliderSuperTab {
|
export default class AppNewChannelTab extends SliderSuperTab {
|
||||||
private uploadAvatar: () => Promise<InputFile> = null;
|
private uploadAvatar: () => Promise<InputFile> = null;
|
||||||
@ -58,7 +59,7 @@ export default class AppNewChannelTab extends SliderSuperTab {
|
|||||||
caption.classList.add('caption');
|
caption.classList.add('caption');
|
||||||
_i18n(caption, 'Channel.DescriptionHolderDescrpiton');
|
_i18n(caption, 'Channel.DescriptionHolderDescrpiton');
|
||||||
|
|
||||||
this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'});
|
this.nextBtn = ButtonCorner({icon: 'arrow_next'});
|
||||||
|
|
||||||
this.nextBtn.addEventListener('click', () => {
|
this.nextBtn.addEventListener('click', () => {
|
||||||
const title = this.channelNameInputField.value;
|
const title = this.channelNameInputField.value;
|
||||||
|
@ -15,6 +15,7 @@ import InputField from "../../inputField";
|
|||||||
import { SliderSuperTab } from "../../slider";
|
import { SliderSuperTab } from "../../slider";
|
||||||
import AvatarEdit from "../../avatarEdit";
|
import AvatarEdit from "../../avatarEdit";
|
||||||
import { i18n } from "../../../lib/langPack";
|
import { i18n } from "../../../lib/langPack";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
export default class AppNewGroupTab extends SliderSuperTab {
|
export default class AppNewGroupTab extends SliderSuperTab {
|
||||||
private searchGroup = new SearchGroup(' ', 'contacts', true, 'new-group-members disable-hover', false);
|
private searchGroup = new SearchGroup(' ', 'contacts', true, 'new-group-members disable-hover', false);
|
||||||
@ -47,7 +48,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
|
|||||||
this.nextBtn.classList.toggle('is-visible', !!value.length && !this.groupNameInputField.input.classList.contains('error'));
|
this.nextBtn.classList.toggle('is-visible', !!value.length && !this.groupNameInputField.input.classList.contains('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'});
|
this.nextBtn = ButtonCorner({icon: 'arrow_next'});
|
||||||
|
|
||||||
this.nextBtn.addEventListener('click', () => {
|
this.nextBtn.addEventListener('click', () => {
|
||||||
const title = this.groupNameInputField.value;
|
const title = this.groupNameInputField.value;
|
||||||
|
@ -11,6 +11,7 @@ import ScrollableLoader from "../../../helpers/listLoader";
|
|||||||
import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../layer";
|
import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../layer";
|
||||||
import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager";
|
import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager";
|
||||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||||
|
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
||||||
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
||||||
import I18n, { i18n, join, LangPackKey } from "../../../lib/langPack";
|
import I18n, { i18n, join, LangPackKey } from "../../../lib/langPack";
|
||||||
import rootScope from "../../../lib/rootScope";
|
import rootScope from "../../../lib/rootScope";
|
||||||
@ -241,7 +242,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
|
|||||||
|
|
||||||
const add = (participant: ChannelParticipant.channelParticipantBanned, append: boolean) => {
|
const add = (participant: ChannelParticipant.channelParticipantBanned, append: boolean) => {
|
||||||
const {dom} = appDialogsManager.addDialogNew({
|
const {dom} = appDialogsManager.addDialogNew({
|
||||||
dialog: participant.user_id,
|
dialog: appPeersManager.getPeerId(participant.peer),
|
||||||
container: list,
|
container: list,
|
||||||
drawStatus: false,
|
drawStatus: false,
|
||||||
rippleEnabled: true,
|
rippleEnabled: true,
|
||||||
|
@ -20,6 +20,7 @@ import { UsernameInputField } from "../../usernameInputField";
|
|||||||
import { SliderSuperTabEventable } from "../../sliderTab";
|
import { SliderSuperTabEventable } from "../../sliderTab";
|
||||||
import I18n from "../../../lib/langPack";
|
import I18n from "../../../lib/langPack";
|
||||||
import PopupPeer from "../../popups/peer";
|
import PopupPeer from "../../popups/peer";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
||||||
public peerId: number;
|
public peerId: number;
|
||||||
@ -134,7 +135,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
inputWrapper.append(linkInputField.container)
|
inputWrapper.append(linkInputField.container)
|
||||||
publicSection.content.append(inputWrapper);
|
publicSection.content.append(inputWrapper);
|
||||||
|
|
||||||
const applyBtn = Button('btn-circle btn-corner tgico-check is-visible');
|
const applyBtn = ButtonCorner({icon: 'check', className: 'is-visible'});
|
||||||
this.content.append(applyBtn);
|
this.content.append(applyBtn);
|
||||||
|
|
||||||
attachClickEvent(applyBtn, () => {
|
attachClickEvent(applyBtn, () => {
|
||||||
|
@ -45,6 +45,8 @@ import PopupPeer from "../../popups/peer";
|
|||||||
import Scrollable from "../../scrollable";
|
import Scrollable from "../../scrollable";
|
||||||
import { isTouchSupported } from "../../../helpers/touchSupport";
|
import { isTouchSupported } from "../../../helpers/touchSupport";
|
||||||
import { isFirefox } from "../../../helpers/userAgent";
|
import { isFirefox } from "../../../helpers/userAgent";
|
||||||
|
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
let setText = (text: string, row: Row) => {
|
let setText = (text: string, row: Row) => {
|
||||||
//fastRaf(() => {
|
//fastRaf(() => {
|
||||||
@ -425,8 +427,10 @@ class PeerProfileAvatars {
|
|||||||
img.draggable = false;
|
img.draggable = false;
|
||||||
|
|
||||||
if(photo) {
|
if(photo) {
|
||||||
appPhotosManager.preloadPhoto(photo, appPhotosManager.choosePhotoSize(photo, 420, 420, false)).then(() => {
|
const size = appPhotosManager.choosePhotoSize(photo, 420, 420, false);
|
||||||
renderImageFromUrl(img, photo.url, () => {
|
appPhotosManager.preloadPhoto(photo, size).then(() => {
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(photo, size.type);
|
||||||
|
renderImageFromUrl(img, cacheContext.url, () => {
|
||||||
avatar.append(img);
|
avatar.append(img);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -909,7 +913,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
|||||||
|
|
||||||
this.profile.element.append(this.searchSuper.container);
|
this.profile.element.append(this.searchSuper.container);
|
||||||
|
|
||||||
const btnAddMembers = Button('btn-corner btn-circle', {icon: 'addmember_filled'});
|
const btnAddMembers = ButtonCorner({icon: 'addmember_filled'});
|
||||||
this.content.append(btnAddMembers);
|
this.content.append(btnAddMembers);
|
||||||
|
|
||||||
btnAddMembers.addEventListener('click', () => {
|
btnAddMembers.addEventListener('click', () => {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||||
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
|
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
|
||||||
import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom";
|
import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom";
|
||||||
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
||||||
@ -20,6 +21,7 @@ export default class SortedUserList {
|
|||||||
public list: HTMLUListElement;
|
public list: HTMLUListElement;
|
||||||
public users: Map<number, SortedUser>;
|
public users: Map<number, SortedUser>;
|
||||||
public sorted: Array<SortedUser>;
|
public sorted: Array<SortedUser>;
|
||||||
|
public lazyLoadQueue: LazyLoadQueueIntersector;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.list = appDialogsManager.createChatList();
|
this.list = appDialogsManager.createChatList();
|
||||||
@ -75,7 +77,8 @@ export default class SortedUserList {
|
|||||||
avatarSize: 48,
|
avatarSize: 48,
|
||||||
autonomous: true,
|
autonomous: true,
|
||||||
meAsSaved: false,
|
meAsSaved: false,
|
||||||
rippleEnabled: false
|
rippleEnabled: false,
|
||||||
|
lazyLoadQueue: this.lazyLoadQueue
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedUser: SortedUser = {
|
const sortedUser: SortedUser = {
|
||||||
|
@ -38,10 +38,12 @@ import { animateSingle } from '../helpers/animation';
|
|||||||
import renderImageFromUrl from '../helpers/dom/renderImageFromUrl';
|
import renderImageFromUrl from '../helpers/dom/renderImageFromUrl';
|
||||||
import sequentialDom from '../helpers/sequentialDom';
|
import sequentialDom from '../helpers/sequentialDom';
|
||||||
import { fastRaf } from '../helpers/schedulers';
|
import { fastRaf } from '../helpers/schedulers';
|
||||||
|
import appDownloadManager from '../lib/appManagers/appDownloadManager';
|
||||||
|
import appStickersManager from '../lib/appManagers/appStickersManager';
|
||||||
|
|
||||||
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
|
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
|
||||||
|
|
||||||
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, noAutoDownload}: {
|
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, noAutoDownload, size}: {
|
||||||
doc: MyDocument,
|
doc: MyDocument,
|
||||||
container?: HTMLElement,
|
container?: HTMLElement,
|
||||||
message?: any,
|
message?: any,
|
||||||
@ -58,6 +60,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
withoutPreloader?: boolean,
|
withoutPreloader?: boolean,
|
||||||
loadPromises?: Promise<any>[],
|
loadPromises?: Promise<any>[],
|
||||||
noAutoDownload?: boolean,
|
noAutoDownload?: boolean,
|
||||||
|
size?: PhotoSize
|
||||||
}) {
|
}) {
|
||||||
const isAlbumItem = !(boxWidth && boxHeight);
|
const isAlbumItem = !(boxWidth && boxHeight);
|
||||||
const canAutoplay = (doc.type !== 'video' || (doc.size <= MAX_VIDEO_AUTOPLAY_SIZE && !isAlbumItem))
|
const canAutoplay = (doc.type !== 'video' || (doc.size <= MAX_VIDEO_AUTOPLAY_SIZE && !isAlbumItem))
|
||||||
@ -114,7 +117,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
middleware,
|
middleware,
|
||||||
withoutPreloader,
|
withoutPreloader,
|
||||||
loadPromises,
|
loadPromises,
|
||||||
noAutoDownload
|
noAutoDownload,
|
||||||
|
size
|
||||||
});
|
});
|
||||||
|
|
||||||
res.thumb = photoRes;
|
res.thumb = photoRes;
|
||||||
@ -251,7 +255,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
middleware,
|
middleware,
|
||||||
withoutPreloader: true,
|
withoutPreloader: true,
|
||||||
loadPromises,
|
loadPromises,
|
||||||
noAutoDownload
|
noAutoDownload,
|
||||||
|
size
|
||||||
});
|
});
|
||||||
|
|
||||||
res.thumb = photoRes;
|
res.thumb = photoRes;
|
||||||
@ -271,7 +276,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
const gotThumb = appDocsManager.getThumb(doc, false);
|
const gotThumb = appDocsManager.getThumb(doc, false);
|
||||||
if(gotThumb) {
|
if(gotThumb) {
|
||||||
gotThumb.promise.then(() => {
|
gotThumb.promise.then(() => {
|
||||||
video.poster = gotThumb.thumb.url;
|
video.poster = gotThumb.cacheContext.url;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,12 +285,14 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
container.append(video);
|
container.append(video);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
|
||||||
let preloader: ProgressivePreloader;
|
let preloader: ProgressivePreloader;
|
||||||
if(message?.media?.preloader) { // means upload
|
if(message?.media?.preloader) { // means upload
|
||||||
preloader = message.media.preloader as ProgressivePreloader;
|
preloader = message.media.preloader as ProgressivePreloader;
|
||||||
preloader.attach(container, false);
|
preloader.attach(container, false);
|
||||||
noAutoDownload = undefined;
|
noAutoDownload = undefined;
|
||||||
} else if(!doc.downloaded && !doc.supportsStreaming) {
|
} else if(!cacheContext.downloaded && !doc.supportsStreaming) {
|
||||||
preloader = new ProgressivePreloader({
|
preloader = new ProgressivePreloader({
|
||||||
attachMethod: 'prepend'
|
attachMethod: 'prepend'
|
||||||
});
|
});
|
||||||
@ -305,13 +312,13 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
|
|
||||||
let loadPromise: Promise<any> = Promise.resolve();
|
let loadPromise: Promise<any> = Promise.resolve();
|
||||||
if(preloader) {
|
if(preloader) {
|
||||||
if(!doc.downloaded && !doc.supportsStreaming) {
|
if(!cacheContext.downloaded && !doc.supportsStreaming) {
|
||||||
const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId, noAutoDownload);
|
const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId, noAutoDownload);
|
||||||
preloader.attach(container, false, promise);
|
preloader.attach(container, false, promise);
|
||||||
} else if(doc.supportsStreaming) {
|
} else if(doc.supportsStreaming) {
|
||||||
if(noAutoDownload) {
|
if(noAutoDownload) {
|
||||||
loadPromise = Promise.reject();
|
loadPromise = Promise.reject();
|
||||||
} else if(!doc.downloaded) { // * check for uploading video
|
} else if(!cacheContext.downloaded) { // * check for uploading video
|
||||||
preloader.attach(container, false, null);
|
preloader.attach(container, false, null);
|
||||||
video.addEventListener(isSafari ? 'timeupdate' : 'canplay', () => {
|
video.addEventListener(isSafari ? 'timeupdate' : 'canplay', () => {
|
||||||
preloader.detach();
|
preloader.detach();
|
||||||
@ -361,7 +368,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
//video.play();
|
//video.play();
|
||||||
video.autoplay = true;
|
video.autoplay = true;
|
||||||
|
|
||||||
renderImageFromUrl(video, doc.url);
|
renderImageFromUrl(video, cacheContext.url);
|
||||||
}, () => {});
|
}, () => {});
|
||||||
|
|
||||||
return {download: loadPromise, render: deferred};
|
return {download: loadPromise, render: deferred};
|
||||||
@ -462,12 +469,13 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
|||||||
const icoDiv = document.createElement('div');
|
const icoDiv = document.createElement('div');
|
||||||
icoDiv.classList.add('document-ico');
|
icoDiv.classList.add('document-ico');
|
||||||
|
|
||||||
if(doc.thumbs?.length || (message.pFlags.is_outgoing && doc.url && doc.type === 'photo')) {
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
if(doc.thumbs?.length || (message.pFlags.is_outgoing && cacheContext.url && doc.type === 'photo')) {
|
||||||
docDiv.classList.add('document-with-thumb');
|
docDiv.classList.add('document-with-thumb');
|
||||||
|
|
||||||
let imgs: HTMLImageElement[] = [];
|
let imgs: HTMLImageElement[] = [];
|
||||||
if(message.pFlags.is_outgoing) {
|
if(message.pFlags.is_outgoing) {
|
||||||
icoDiv.innerHTML = `<img src="${doc.url}">`;
|
icoDiv.innerHTML = `<img src="${cacheContext.url}">`;
|
||||||
imgs.push(icoDiv.firstElementChild as HTMLImageElement);
|
imgs.push(icoDiv.firstElementChild as HTMLImageElement);
|
||||||
} else {
|
} else {
|
||||||
const wrapped = wrapPhoto({
|
const wrapped = wrapPhoto({
|
||||||
@ -507,7 +515,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
|||||||
}
|
}
|
||||||
|
|
||||||
docDiv.innerHTML = `
|
docDiv.innerHTML = `
|
||||||
${doc.downloaded && !uploading ? '' : `<div class="document-download"></div>`}
|
${cacheContext.downloaded && !uploading ? '' : `<div class="document-download"></div>`}
|
||||||
<div class="document-name"><middle-ellipsis-element data-font-weight="${fontWeight}">${fileName}</middle-ellipsis-element>${titleAdditionHTML}</div>
|
<div class="document-name"><middle-ellipsis-element data-font-weight="${fontWeight}">${fileName}</middle-ellipsis-element>${titleAdditionHTML}</div>
|
||||||
<div class="document-size">${size}</div>
|
<div class="document-size">${size}</div>
|
||||||
`;
|
`;
|
||||||
@ -546,7 +554,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
|||||||
return {download};
|
return {download};
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!(doc.downloaded && !uploading)) {
|
if(!(cacheContext.downloaded && !uploading)) {
|
||||||
downloadDiv = docDiv.querySelector('.document-download');
|
downloadDiv = docDiv.querySelector('.document-download');
|
||||||
preloader = message.media.preloader as ProgressivePreloader;
|
preloader = message.media.preloader as ProgressivePreloader;
|
||||||
|
|
||||||
@ -650,7 +658,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
noBlur?: boolean,
|
noBlur?: boolean,
|
||||||
}) {
|
}) {
|
||||||
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
|
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
|
||||||
if(boxWidth && boxHeight && photo._ === 'document') {
|
if(boxWidth && boxHeight && !size && photo._ === 'document') {
|
||||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
|
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,8 +675,10 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(boxWidth === undefined) boxWidth = mediaSizes.active.regular.width;
|
if(!size) {
|
||||||
if(boxHeight === undefined) boxHeight = mediaSizes.active.regular.height;
|
if(boxWidth === undefined) boxWidth = mediaSizes.active.regular.width;
|
||||||
|
if(boxHeight === undefined) boxHeight = mediaSizes.active.regular.height;
|
||||||
|
}
|
||||||
|
|
||||||
let loadThumbPromise: Promise<any>;
|
let loadThumbPromise: Promise<any>;
|
||||||
let thumbImage: HTMLImageElement;
|
let thumbImage: HTMLImageElement;
|
||||||
@ -678,7 +688,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
} else {
|
} else {
|
||||||
image = new Image();
|
image = new Image();
|
||||||
|
|
||||||
if(boxWidth && boxHeight) { // !album
|
if(boxWidth && boxHeight && !size) { // !album
|
||||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
|
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,7 +705,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
|
|
||||||
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||||
|
|
||||||
const cacheContext = appPhotosManager.getCacheContext(photo);
|
const cacheContext = appDownloadManager.getCacheContext(photo, size?.type/* photo._ === 'photo' ? size?.type : undefined */);
|
||||||
|
|
||||||
const needFadeIn = (thumbImage || !cacheContext.downloaded) && rootScope.settings.animationsEnabled;
|
const needFadeIn = (thumbImage || !cacheContext.downloaded) && rootScope.settings.animationsEnabled;
|
||||||
if(needFadeIn) {
|
if(needFadeIn) {
|
||||||
@ -727,7 +737,12 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
if(middleware && !middleware()) return Promise.resolve();
|
if(middleware && !middleware()) return Promise.resolve();
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
renderImageFromUrl(image, cacheContext.url || photo.url, () => {
|
/* if(photo._ === 'document') {
|
||||||
|
console.error('wrapPhoto: will render document', photo, size, cacheContext);
|
||||||
|
return resolve();
|
||||||
|
} */
|
||||||
|
|
||||||
|
renderImageFromUrl(image, cacheContext.url, () => {
|
||||||
sequentialDom.mutateElement(container, () => {
|
sequentialDom.mutateElement(container, () => {
|
||||||
container.append(image);
|
container.append(image);
|
||||||
|
|
||||||
@ -840,8 +855,10 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
|
|
||||||
//console.log('wrap sticker', doc, div, onlyThumb);
|
//console.log('wrap sticker', doc, div, onlyThumb);
|
||||||
|
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
|
||||||
const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1;
|
const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1;
|
||||||
const downloaded = doc.downloaded && !needFadeIn;
|
const downloaded = cacheContext.downloaded && !needFadeIn;
|
||||||
|
|
||||||
let loadThumbPromise = deferredPromise<void>();
|
let loadThumbPromise = deferredPromise<void>();
|
||||||
let haveThumbCached = false;
|
let haveThumbCached = false;
|
||||||
@ -881,8 +898,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
if(thumb && thumb._ !== 'photoPathSize' && toneIndex <= 0) {
|
if(thumb && thumb._ !== 'photoPathSize' && toneIndex <= 0) {
|
||||||
thumbImage = new Image();
|
thumbImage = new Image();
|
||||||
|
|
||||||
if((webpWorkerController.isWebpSupported() || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) {
|
if((webpWorkerController.isWebpSupported() || doc.pFlags.stickerThumbConverted || cacheContext.url)/* && false */) {
|
||||||
renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(thumb as PhotoSize.photoStrippedSize, true), afterRender);
|
renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(doc, thumb as PhotoSize.photoStrippedSize, true), afterRender);
|
||||||
haveThumbCached = true;
|
haveThumbCached = true;
|
||||||
} else {
|
} else {
|
||||||
webpWorkerController.convert(doc.id, (thumb as PhotoSize.photoStrippedSize).bytes as Uint8Array).then(bytes => {
|
webpWorkerController.convert(doc.id, (thumb as PhotoSize.photoStrippedSize).bytes as Uint8Array).then(bytes => {
|
||||||
@ -892,7 +909,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
|
|
||||||
if(!div.childElementCount) {
|
if(!div.childElementCount) {
|
||||||
renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(thumb as PhotoSize.photoStrippedSize, true), afterRender);
|
renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(doc, thumb as PhotoSize.photoStrippedSize, true), afterRender);
|
||||||
}
|
}
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
@ -905,10 +922,10 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
|
|
||||||
const r = () => {
|
const r = () => {
|
||||||
if(div.childElementCount || (middleware && !middleware())) return;
|
if(div.childElementCount || (middleware && !middleware())) return;
|
||||||
renderImageFromUrl(thumbImage, (thumb as PhotoSize.photoStrippedSize).url, afterRender);
|
renderImageFromUrl(thumbImage, cacheContext.url, afterRender);
|
||||||
};
|
};
|
||||||
|
|
||||||
if((thumb as PhotoSize.photoStrippedSize).url) {
|
if(cacheContext.url) {
|
||||||
r();
|
r();
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
@ -1038,7 +1055,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
const r = () => {
|
const r = () => {
|
||||||
if(middleware && !middleware()) return resolve();
|
if(middleware && !middleware()) return resolve();
|
||||||
|
|
||||||
renderImageFromUrl(image, doc.url, () => {
|
renderImageFromUrl(image, cacheContext.url, () => {
|
||||||
sequentialDom.mutateElement(div, () => {
|
sequentialDom.mutateElement(div, () => {
|
||||||
div.append(image);
|
div.append(image);
|
||||||
if(thumbImage) {
|
if(thumbImage) {
|
||||||
@ -1059,7 +1076,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if(doc.url) r();
|
if(cacheContext.url) r();
|
||||||
else {
|
else {
|
||||||
appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId).then(r, resolve);
|
appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId).then(r, resolve);
|
||||||
}
|
}
|
||||||
@ -1081,6 +1098,35 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
return loadPromise;
|
return loadPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function wrapLocalSticker({emoji, width, height}: {
|
||||||
|
doc?: MyDocument,
|
||||||
|
url?: string,
|
||||||
|
emoji?: string,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
}) {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const doc = appStickersManager.getAnimatedEmojiSticker(emoji);
|
||||||
|
if(doc) {
|
||||||
|
wrapSticker({
|
||||||
|
doc,
|
||||||
|
div: container,
|
||||||
|
loop: false,
|
||||||
|
play: true,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
emoji
|
||||||
|
}).then(() => {
|
||||||
|
// this.animation = player;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
container.classList.add('media-sticker-wrapper');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {container};
|
||||||
|
}
|
||||||
|
|
||||||
export function wrapReply(title: string, subtitle: string, message?: any) {
|
export function wrapReply(title: string, subtitle: string, message?: any) {
|
||||||
const replyContainer = new ReplyContainer('reply');
|
const replyContainer = new ReplyContainer('reply');
|
||||||
replyContainer.fill(title, subtitle, message);
|
replyContainer.fill(title, subtitle, message);
|
||||||
|
@ -11,7 +11,7 @@ import pushHeavyTask from './heavyQueue';
|
|||||||
const RADIUS = 2;
|
const RADIUS = 2;
|
||||||
const ITERATIONS = 2;
|
const ITERATIONS = 2;
|
||||||
|
|
||||||
const DEBUG = _DEBUG && false;
|
const DEBUG = _DEBUG && true;
|
||||||
|
|
||||||
function processBlur(dataUri: string, radius: number, iterations: number) {
|
function processBlur(dataUri: string, radius: number, iterations: number) {
|
||||||
return new Promise<string>((resolve) => {
|
return new Promise<string>((resolve) => {
|
||||||
@ -49,6 +49,11 @@ function processBlur(dataUri: string, radius: number, iterations: number) {
|
|||||||
const blurPromises: {[dataUri: string]: Promise<string>} = {};
|
const blurPromises: {[dataUri: string]: Promise<string>} = {};
|
||||||
|
|
||||||
export default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) {
|
export default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) {
|
||||||
|
if(!dataUri) {
|
||||||
|
console.error('no dataUri for blur', dataUri);
|
||||||
|
return Promise.resolve(dataUri);
|
||||||
|
}
|
||||||
|
|
||||||
if(blurPromises[dataUri]) return blurPromises[dataUri];
|
if(blurPromises[dataUri]) return blurPromises[dataUri];
|
||||||
return blurPromises[dataUri] = new Promise<string>((resolve) => {
|
return blurPromises[dataUri] = new Promise<string>((resolve) => {
|
||||||
//return resolve(dataUri);
|
//return resolve(dataUri);
|
||||||
|
@ -13,6 +13,12 @@ const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoE
|
|||||||
|
|
||||||
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
|
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
|
||||||
export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void, useCache = true): boolean {
|
export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void, useCache = true): boolean {
|
||||||
|
if(!url) {
|
||||||
|
console.error('renderImageFromUrl: no url?', elem, url);
|
||||||
|
//callback && callback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(((loadedURLs[url]/* && false */) && useCache) || elem instanceof HTMLVideoElement) {
|
if(((loadedURLs[url]/* && false */) && useCache) || elem instanceof HTMLVideoElement) {
|
||||||
if(elem) {
|
if(elem) {
|
||||||
set(elem, url);
|
set(elem, url);
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { InputFileLocation, FileLocation } from "../layer";
|
import type { InputFileLocation, InputStickerSet } from "../layer";
|
||||||
import type { DownloadOptions } from "../lib/mtproto/apiFileManager";
|
import type { DownloadOptions } from "../lib/mtproto/apiFileManager";
|
||||||
|
|
||||||
export function getFileNameByLocation(location: InputFileLocation | FileLocation, options?: Partial<{
|
export function getFileNameByLocation(location: InputFileLocation, options?: Partial<{
|
||||||
fileName: string
|
fileName: string
|
||||||
}>) {
|
}>) {
|
||||||
const fileName = '';//(options?.fileName || '').split('.');
|
const fileName = '';//(options?.fileName || '').split('.');
|
||||||
@ -20,9 +20,17 @@ export function getFileNameByLocation(location: InputFileLocation | FileLocation
|
|||||||
return (fileName[0] ? fileName[0] + '_' : '') + location.id + thumbPart + (ext ? '.' + ext : ext);
|
return (fileName[0] ? fileName[0] + '_' : '') + location.id + thumbPart + (ext ? '.' + ext : ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'fileLocationToBeDeprecated':
|
|
||||||
case 'inputPeerPhotoFileLocation':
|
case 'inputPeerPhotoFileLocation':
|
||||||
case 'inputStickerSetThumb':
|
return ['peerPhoto', location.photo_id, location.pFlags.big ? 'big' : 'small'].join('_');
|
||||||
|
|
||||||
|
case 'inputStickerSetThumb': {
|
||||||
|
const id = (location.stickerset as InputStickerSet.inputStickerSetID).id ||
|
||||||
|
(location.stickerset as InputStickerSet.inputStickerSetShortName).short_name ||
|
||||||
|
(location.stickerset as InputStickerSet.inputStickerSetDice).emoticon ||
|
||||||
|
location.stickerset._;
|
||||||
|
return ['stickerSetThumb', id, location.thumb_version].join('_');
|
||||||
|
}
|
||||||
|
|
||||||
case 'inputFileLocation': {
|
case 'inputFileLocation': {
|
||||||
return location.volume_id + '_' + location.local_id + (ext ? '.' + ext : ext);
|
return location.volume_id + '_' + location.local_id + (ext ? '.' + ext : ext);
|
||||||
}
|
}
|
||||||
@ -42,4 +50,4 @@ export function getFileURL(type: FileURLType, options: DownloadOptions) {
|
|||||||
//console.log('getFileURL encode:', performance.now() - perf, encoded);
|
//console.log('getFileURL encode:', performance.now() - perf, encoded);
|
||||||
|
|
||||||
return '/' + type + '/' + encoded;
|
return '/' + type + '/' + encoded;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<meta name="msapplication-TileImage" content="/assets/img/mstile-144x144.png?v=jw3mK7G9Ry">
|
<meta name="msapplication-TileImage" content="/assets/img/mstile-144x144.png?v=jw3mK7G9Ry">
|
||||||
<meta name="msapplication-config" content="/assets/img/browserconfig.xml?v=jw3mK7G9Ry">
|
<meta name="msapplication-config" content="/assets/img/browserconfig.xml?v=jw3mK7G9Ry">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
<meta name="color-scheme" content="light dark">
|
<meta name="color-scheme" content="light">
|
||||||
{{# each htmlWebpackPlugin.files.css }}
|
{{# each htmlWebpackPlugin.files.css }}
|
||||||
<link rel="stylesheet" href="{{ this }}">
|
<link rel="stylesheet" href="{{ this }}">
|
||||||
{{/ each }}
|
{{/ each }}
|
||||||
|
@ -406,6 +406,8 @@ const lang = {
|
|||||||
"Open": "Open",
|
"Open": "Open",
|
||||||
"OpenUrlTitle": "Open Link",
|
"OpenUrlTitle": "Open Link",
|
||||||
"OpenUrlAlert2": "Do you want to open %1$s?",
|
"OpenUrlAlert2": "Do you want to open %1$s?",
|
||||||
|
"FilterNoChatsToDisplay": "Folder is empty",
|
||||||
|
"FilterNoChatsToDisplayInfo": "No chats currently belong to this folder.",
|
||||||
|
|
||||||
// * macos
|
// * macos
|
||||||
"AccountSettings.Filters": "Chat Folders",
|
"AccountSettings.Filters": "Chat Folders",
|
||||||
@ -488,7 +490,8 @@ const lang = {
|
|||||||
"ChatList.Filter.List.Title": "Chat Folders",
|
"ChatList.Filter.List.Title": "Chat Folders",
|
||||||
"ChatList.Filter.Include.AddChat": "Add Chats",
|
"ChatList.Filter.Include.AddChat": "Add Chats",
|
||||||
"ChatList.Filter.Exclude.AddChat": "Add Chats",
|
"ChatList.Filter.Exclude.AddChat": "Add Chats",
|
||||||
"ChatList.Filter.All": "All",
|
//"ChatList.Filter.All": "All",
|
||||||
|
"ChatList.Filter.AllChats": "All Chats",
|
||||||
"ChatList.Filter.Contacts": "Contacts",
|
"ChatList.Filter.Contacts": "Contacts",
|
||||||
"ChatList.Filter.NonContacts": "Non-Contacts",
|
"ChatList.Filter.NonContacts": "Non-Contacts",
|
||||||
"ChatList.Filter.Groups": "Groups",
|
"ChatList.Filter.Groups": "Groups",
|
||||||
@ -650,6 +653,8 @@ const lang = {
|
|||||||
"Schedule.SendToday": "Send today at %@",
|
"Schedule.SendToday": "Send today at %@",
|
||||||
"Schedule.SendDate": "Send on %@ at %@",
|
"Schedule.SendDate": "Send on %@ at %@",
|
||||||
//"Schedule.SendWhenOnline": "Send When Online",
|
//"Schedule.SendWhenOnline": "Send When Online",
|
||||||
|
"Stickers.Recent": "Recent",
|
||||||
|
//"Stickers.Favorite": "Favorite",
|
||||||
"TwoStepAuth.SetPasswordHelp": "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.",
|
"TwoStepAuth.SetPasswordHelp": "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.",
|
||||||
"TwoStepAuth.GenericHelp": "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.",
|
"TwoStepAuth.GenericHelp": "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.",
|
||||||
"TwoStepAuth.ChangePassword": "Change Password",
|
"TwoStepAuth.ChangePassword": "Change Password",
|
||||||
|
3421
src/layer.d.ts
vendored
3421
src/layer.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@ -596,7 +596,7 @@ export class ApiUpdatesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveUpdate(update: Update) {
|
public saveUpdate(update: Update) {
|
||||||
this.debug && this.log('saveUpdate', update);
|
//this.debug && this.log('saveUpdate', update);
|
||||||
rootScope.dispatchEvent(update._, update as any);
|
rootScope.dispatchEvent(update._, update as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||||
import { numberThousandSplitter } from "../../helpers/number";
|
import { numberThousandSplitter } from "../../helpers/number";
|
||||||
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object";
|
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object";
|
||||||
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatFull, ChatParticipants, InputChannel, InputChatPhoto, InputFile, InputPeer, SendMessageAction, Update, Updates } from "../../layer";
|
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatFull, ChatParticipant, ChatParticipants, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, SendMessageAction, Update, Updates } from "../../layer";
|
||||||
import { i18n, LangPackKey } from "../langPack";
|
import { i18n, LangPackKey } from "../langPack";
|
||||||
import apiManagerProxy from "../mtproto/mtprotoworker";
|
import apiManagerProxy from "../mtproto/mtprotoworker";
|
||||||
import apiManager from '../mtproto/mtprotoworker';
|
import apiManager from '../mtproto/mtprotoworker';
|
||||||
@ -178,7 +178,8 @@ export class AppChatsManager {
|
|||||||
apiChats.forEach(chat => this.saveApiChat(chat, override));
|
apiChats.forEach(chat => this.saveApiChat(chat, override));
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveApiChat(chat: any, override?: boolean) {
|
public saveApiChat(chat: Chat, override?: boolean) {
|
||||||
|
if(chat._ === 'chatEmpty') return;
|
||||||
/* if(chat._ !== 'chat' && chat._ !== 'channel') {
|
/* if(chat._ !== 'chat' && chat._ !== 'channel') {
|
||||||
return;
|
return;
|
||||||
} */
|
} */
|
||||||
@ -186,17 +187,17 @@ export class AppChatsManager {
|
|||||||
// * exclude from state
|
// * exclude from state
|
||||||
// defineNotNumerableProperties(chat, ['rTitle', 'initials']);
|
// defineNotNumerableProperties(chat, ['rTitle', 'initials']);
|
||||||
|
|
||||||
const oldChat = this.chats[chat.id];
|
const oldChat: Exclude<Chat, Chat.chatEmpty> = this.chats[chat.id];
|
||||||
|
|
||||||
/* if(oldChat && !override) {
|
/* if(oldChat && !override) {
|
||||||
return;
|
return;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
if(chat.pFlags === undefined) {
|
if((chat as Chat.chat).pFlags === undefined) {
|
||||||
chat.pFlags = {};
|
(chat as Chat.chat).pFlags = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chat.pFlags.min && oldChat !== undefined) {
|
if((chat as Chat.channel).pFlags.min && oldChat !== undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +206,8 @@ export class AppChatsManager {
|
|||||||
if(chat._ === 'channel' &&
|
if(chat._ === 'channel' &&
|
||||||
chat.participants_count === undefined &&
|
chat.participants_count === undefined &&
|
||||||
oldChat !== undefined &&
|
oldChat !== undefined &&
|
||||||
oldChat.participants_count) {
|
(oldChat as Chat.channel).participants_count) {
|
||||||
chat.participants_count = oldChat.participants_count;
|
chat.participants_count = (oldChat as Chat.channel).participants_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if(chat.username) {
|
/* if(chat.username) {
|
||||||
@ -218,9 +219,9 @@ export class AppChatsManager {
|
|||||||
if(oldChat === undefined) {
|
if(oldChat === undefined) {
|
||||||
this.chats[chat.id] = chat;
|
this.chats[chat.id] = chat;
|
||||||
} else {
|
} else {
|
||||||
const oldPhoto = oldChat.photo?.photo_small;
|
const oldPhotoId = ((oldChat as Chat.chat).photo as ChatPhoto.chatPhoto)?.photo_id;
|
||||||
const newPhoto = chat.photo?.photo_small;
|
const newPhotoId = ((chat as Chat.chat).photo as ChatPhoto.chatPhoto)?.photo_id;
|
||||||
if(JSON.stringify(oldPhoto) !== JSON.stringify(newPhoto)) {
|
if(oldPhotoId !== newPhotoId) {
|
||||||
changedPhoto = true;
|
changedPhoto = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +393,21 @@ export class AppChatsManager {
|
|||||||
return this.isChannel(id) && !this.isMegagroup(id);
|
return this.isChannel(id) && !this.isMegagroup(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isInChat(id: number) {
|
||||||
|
let good = true;
|
||||||
|
const chat: Chat = this.getChat(id);
|
||||||
|
if(chat._ === 'channelForbidden'
|
||||||
|
|| chat._ === 'chatForbidden'
|
||||||
|
|| chat._ === 'chatEmpty'
|
||||||
|
|| (chat as Chat.chat).pFlags.left
|
||||||
|
|| (chat as Chat.chat).pFlags.kicked
|
||||||
|
|| (chat as Chat.chat).pFlags.deactivated) {
|
||||||
|
good = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return good;
|
||||||
|
}
|
||||||
|
|
||||||
public getChannelInput(id: number): InputChannel {
|
public getChannelInput(id: number): InputChannel {
|
||||||
if(id < 0) id = -id;
|
if(id < 0) id = -id;
|
||||||
const chat: Chat = this.getChat(id);
|
const chat: Chat = this.getChat(id);
|
||||||
@ -742,11 +758,18 @@ export class AppChatsManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getParticipantPeerId(participant: ChannelParticipant | ChatParticipant) {
|
||||||
|
const peerId = (participant as ChannelParticipant.channelParticipantBanned).peer ?
|
||||||
|
appPeersManager.getPeerId((participant as ChannelParticipant.channelParticipantBanned).peer) :
|
||||||
|
(participant as ChatParticipant.chatParticipant).user_id;
|
||||||
|
return peerId;
|
||||||
|
}
|
||||||
|
|
||||||
public editBanned(id: number, participant: number | ChannelParticipant, banned_rights: ChatBannedRights) {
|
public editBanned(id: number, participant: number | ChannelParticipant, banned_rights: ChatBannedRights) {
|
||||||
const userId = typeof(participant) === 'number' ? participant : participant.user_id;
|
const peerId = typeof(participant) === 'number' ? participant : this.getParticipantPeerId(participant);
|
||||||
return apiManager.invokeApi('channels.editBanned', {
|
return apiManager.invokeApi('channels.editBanned', {
|
||||||
channel: this.getChannelInput(id),
|
channel: this.getChannelInput(id),
|
||||||
user_id: appUsersManager.getUserInput(userId),
|
participant: appPeersManager.getInputPeerById(peerId),
|
||||||
banned_rights
|
banned_rights
|
||||||
}).then((updates) => {
|
}).then((updates) => {
|
||||||
this.onChatUpdated(id, updates);
|
this.onChatUpdated(id, updates);
|
||||||
@ -759,15 +782,16 @@ export class AppChatsManager {
|
|||||||
_: 'updateChannelParticipant',
|
_: 'updateChannelParticipant',
|
||||||
channel_id: id,
|
channel_id: id,
|
||||||
date: timestamp,
|
date: timestamp,
|
||||||
//qts: 0,
|
actor_id: undefined,
|
||||||
user_id: userId,
|
qts: undefined,
|
||||||
|
user_id: peerId,
|
||||||
prev_participant: participant,
|
prev_participant: participant,
|
||||||
new_participant: Object.keys(banned_rights.pFlags).length ? {
|
new_participant: Object.keys(banned_rights.pFlags).length ? {
|
||||||
_: 'channelParticipantBanned',
|
_: 'channelParticipantBanned',
|
||||||
date: timestamp,
|
date: timestamp,
|
||||||
banned_rights,
|
banned_rights,
|
||||||
kicked_by: appUsersManager.getSelf().id,
|
kicked_by: appUsersManager.getSelf().id,
|
||||||
user_id: userId,
|
peer: appPeersManager.getOutputPeer(peerId),
|
||||||
pFlags: {}
|
pFlags: {}
|
||||||
} : undefined
|
} : undefined
|
||||||
} as Update.updateChannelParticipant
|
} as Update.updateChannelParticipant
|
||||||
|
@ -16,7 +16,7 @@ import { isSafari } from "../../helpers/userAgent";
|
|||||||
import { logger, LogTypes } from "../logger";
|
import { logger, LogTypes } from "../logger";
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import rootScope from "../rootScope";
|
import rootScope from "../rootScope";
|
||||||
import { positionElementByIndex, replaceContent } from "../../helpers/dom";
|
import { attachClickEvent, positionElementByIndex, replaceContent } from "../../helpers/dom";
|
||||||
import apiUpdatesManager from "./apiUpdatesManager";
|
import apiUpdatesManager from "./apiUpdatesManager";
|
||||||
import appPeersManager from './appPeersManager';
|
import appPeersManager from './appPeersManager';
|
||||||
import appImManager from "./appImManager";
|
import appImManager from "./appImManager";
|
||||||
@ -33,8 +33,13 @@ import App from "../../config/app";
|
|||||||
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
|
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
|
||||||
import appNotificationsManager from "./appNotificationsManager";
|
import appNotificationsManager from "./appNotificationsManager";
|
||||||
import PeerTitle from "../../components/peerTitle";
|
import PeerTitle from "../../components/peerTitle";
|
||||||
import { i18n } from "../langPack";
|
import { i18n, _i18n } from "../langPack";
|
||||||
import findUpTag from "../../helpers/dom/findUpTag";
|
import findUpTag from "../../helpers/dom/findUpTag";
|
||||||
|
import { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
|
||||||
|
import lottieLoader from "../lottieLoader";
|
||||||
|
import { wrapLocalSticker } from "../../components/wrappers";
|
||||||
|
import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder";
|
||||||
|
import appSidebarLeft from "../../components/sidebarLeft";
|
||||||
|
|
||||||
export type DialogDom = {
|
export type DialogDom = {
|
||||||
avatarEl: AvatarElement,
|
avatarEl: AvatarElement,
|
||||||
@ -276,7 +281,7 @@ export class AppDialogsManager {
|
|||||||
this.addFilter({
|
this.addFilter({
|
||||||
id: this.filterId,
|
id: this.filterId,
|
||||||
title: '',
|
title: '',
|
||||||
titleEl: i18n('ChatList.Filter.All'),
|
titleEl: i18n('ChatList.Filter.AllChats'),
|
||||||
orderIndex: 0
|
orderIndex: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -342,14 +347,9 @@ export class AppDialogsManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rootScope.on('dialog_drop', (e) => {
|
rootScope.on('dialog_drop', (e) => {
|
||||||
const {peerId, dialog} = e;
|
const {peerId} = e;
|
||||||
|
|
||||||
const dom = this.getDialogDom(peerId);
|
|
||||||
if(dom) {
|
|
||||||
dom.listEl.remove();
|
|
||||||
delete this.doms[peerId];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.deleteDialog(peerId);
|
||||||
this.setFiltersUnreadCount();
|
this.setFiltersUnreadCount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -536,6 +536,8 @@ export class AppDialogsManager {
|
|||||||
|
|
||||||
new ConnectionStatusComponent(this.chatsContainer);
|
new ConnectionStatusComponent(this.chatsContainer);
|
||||||
this.chatsContainer.append(bottomPart);
|
this.chatsContainer.append(bottomPart);
|
||||||
|
|
||||||
|
lottieLoader.loadLottieWorkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOffset(side: 'top' | 'bottom'): {index: number, pos: number} {
|
private getOffset(side: 'top' | 'bottom'): {index: number, pos: number} {
|
||||||
@ -564,6 +566,20 @@ export class AppDialogsManager {
|
|||||||
return (!topOffset.index || index <= topOffset.index) && (!bottomOffset.index || index >= bottomOffset.index);
|
return (!topOffset.index || index <= topOffset.index) && (!bottomOffset.index || index >= bottomOffset.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private deleteDialog(peerId: number) {
|
||||||
|
const dom = this.getDialogDom(peerId);
|
||||||
|
if(dom) {
|
||||||
|
dom.listEl.remove();
|
||||||
|
delete this.doms[peerId];
|
||||||
|
|
||||||
|
this.onListLengthChange();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private updateDialog(dialog: Dialog) {
|
private updateDialog(dialog: Dialog) {
|
||||||
if(!dialog) {
|
if(!dialog) {
|
||||||
return;
|
return;
|
||||||
@ -575,17 +591,13 @@ export class AppDialogsManager {
|
|||||||
if(ret) {
|
if(ret) {
|
||||||
const idx = appMessagesManager.getDialogByPeerId(dialog.peerId)[1];
|
const idx = appMessagesManager.getDialogByPeerId(dialog.peerId)[1];
|
||||||
positionElementByIndex(ret.dom.listEl, this.chatList, idx);
|
positionElementByIndex(ret.dom.listEl, this.chatList, idx);
|
||||||
|
this.onListLengthChange();
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const dom = this.getDialogDom(dialog.peerId);
|
this.deleteDialog(dialog.peerId);
|
||||||
if(dom) {
|
|
||||||
dom.listEl.remove();
|
|
||||||
delete this.doms[dialog.peerId];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,9 +679,7 @@ export class AppDialogsManager {
|
|||||||
|
|
||||||
// если больше не подходит по фильтру, удаляем
|
// если больше не подходит по фильтру, удаляем
|
||||||
if(folder.findIndex((dialog) => dialog.peerId === peerId) === -1) {
|
if(folder.findIndex((dialog) => dialog.peerId === peerId) === -1) {
|
||||||
const listEl = this.doms[peerId].listEl;
|
this.deleteDialog(peerId);
|
||||||
listEl.remove();
|
|
||||||
delete this.doms[peerId];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,6 +815,8 @@ export class AppDialogsManager {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onListLengthChange();
|
||||||
|
|
||||||
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.chatList.childElementCount);
|
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.chatList.childElementCount);
|
||||||
|
|
||||||
@ -824,6 +836,50 @@ export class AppDialogsManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onListLengthChange = () => {
|
||||||
|
return;
|
||||||
|
|
||||||
|
const emptyFolder = this.chatList.parentElement.querySelector('.empty-folder');
|
||||||
|
if(this.scroll.loadedAll.bottom && !this.chatList.childElementCount) {
|
||||||
|
if(emptyFolder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE_CLASS = 'empty-folder';
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.classList.add(BASE_CLASS);
|
||||||
|
|
||||||
|
div.append(wrapLocalSticker({
|
||||||
|
emoji: '📂',
|
||||||
|
width: 128,
|
||||||
|
height: 128
|
||||||
|
}).container);
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.classList.add(BASE_CLASS + '-header');
|
||||||
|
_i18n(header, 'FilterNoChatsToDisplay');
|
||||||
|
|
||||||
|
const subtitle = document.createElement('div');
|
||||||
|
subtitle.classList.add(BASE_CLASS + '-subtitle');
|
||||||
|
_i18n(subtitle, 'FilterNoChatsToDisplayInfo');
|
||||||
|
|
||||||
|
const button = Button('btn-primary btn-color-primary btn-control tgico', {
|
||||||
|
text: 'FilterHeaderEdit',
|
||||||
|
icon: 'settings'
|
||||||
|
});
|
||||||
|
|
||||||
|
attachClickEvent(button, () => {
|
||||||
|
new AppEditFolderTab(appSidebarLeft).open(appMessagesManager.filtersStorage.filters[this.filterId]);
|
||||||
|
});
|
||||||
|
|
||||||
|
div.append(header, subtitle, button);
|
||||||
|
|
||||||
|
this.chatList.parentElement.append(div);
|
||||||
|
} else if(emptyFolder) {
|
||||||
|
emptyFolder.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public onChatsRegularScroll = () => {
|
public onChatsRegularScroll = () => {
|
||||||
if(this.sliceTimeout) clearTimeout(this.sliceTimeout);
|
if(this.sliceTimeout) clearTimeout(this.sliceTimeout);
|
||||||
this.sliceTimeout = window.setTimeout(() => {
|
this.sliceTimeout = window.setTimeout(() => {
|
||||||
@ -899,9 +955,8 @@ export class AppDialogsManager {
|
|||||||
sliced.push(...sliceFromEnd);
|
sliced.push(...sliceFromEnd);
|
||||||
|
|
||||||
sliced.forEach(el => {
|
sliced.forEach(el => {
|
||||||
el.remove();
|
|
||||||
const peerId = +el.dataset.peerId;
|
const peerId = +el.dataset.peerId;
|
||||||
delete this.doms[peerId];
|
this.deleteDialog(peerId);
|
||||||
});
|
});
|
||||||
|
|
||||||
//this.log('[slicer] elements', firstElement, lastElement, rect, sliced, sliceFromStart.length, sliceFromEnd.length);
|
//this.log('[slicer] elements', firstElement, lastElement, rect, sliced, sliceFromStart.length, sliceFromEnd.length);
|
||||||
@ -1023,8 +1078,7 @@ export class AppDialogsManager {
|
|||||||
|
|
||||||
const needIndex = index - offset;
|
const needIndex = index - offset;
|
||||||
if(needIndex > currentOrder.length) {
|
if(needIndex > currentOrder.length) {
|
||||||
dom.listEl.remove();
|
this.deleteDialog(dialog.peerId);
|
||||||
delete this.doms[dialog.peerId];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1213,12 +1267,13 @@ export class AppDialogsManager {
|
|||||||
meAsSaved?: boolean,
|
meAsSaved?: boolean,
|
||||||
append?: boolean,
|
append?: boolean,
|
||||||
avatarSize?: number,
|
avatarSize?: number,
|
||||||
autonomous?: boolean
|
autonomous?: boolean,
|
||||||
|
lazyLoadQueue?: LazyLoadQueueIntersector,
|
||||||
}) {
|
}) {
|
||||||
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous);
|
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable | false, drawStatus = true, rippleEnabled = true, onlyFirstName = false, meAsSaved = true, append = true, avatarSize = 54, autonomous = !!container) {
|
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable | false, drawStatus = true, rippleEnabled = true, onlyFirstName = false, meAsSaved = true, append = true, avatarSize = 54, autonomous = !!container, lazyLoadQueue?: LazyLoadQueueIntersector) {
|
||||||
let dialog: Dialog;
|
let dialog: Dialog;
|
||||||
|
|
||||||
if(typeof(_dialog) === 'number') {
|
if(typeof(_dialog) === 'number') {
|
||||||
@ -1248,6 +1303,7 @@ export class AppDialogsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const avatarEl = new AvatarElement();
|
const avatarEl = new AvatarElement();
|
||||||
|
avatarEl.lazyLoadQueue = lazyLoadQueue;
|
||||||
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
|
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
|
||||||
avatarEl.setAttribute('peer', '' + peerId);
|
avatarEl.setAttribute('peer', '' + peerId);
|
||||||
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);
|
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);
|
||||||
|
@ -37,8 +37,12 @@ export class AppDocsManager {
|
|||||||
public onServiceWorkerFail = () => {
|
public onServiceWorkerFail = () => {
|
||||||
for(const id in this.docs) {
|
for(const id in this.docs) {
|
||||||
const doc = this.docs[id];
|
const doc = this.docs[id];
|
||||||
delete doc.supportsStreaming;
|
|
||||||
delete doc.url;
|
if(doc.supportsStreaming) {
|
||||||
|
delete doc.supportsStreaming;
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
delete cacheContext.url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,8 +85,7 @@ export class AppDocsManager {
|
|||||||
// 'file', 'duration', 'downloaded', 'url', 'audioTitle',
|
// 'file', 'duration', 'downloaded', 'url', 'audioTitle',
|
||||||
// 'audioPerformer', 'sticker', 'stickerEmoji', 'stickerEmojiRaw',
|
// 'audioPerformer', 'sticker', 'stickerEmoji', 'stickerEmojiRaw',
|
||||||
// 'stickerSetInput', 'stickerThumbConverted', 'animated', 'supportsStreaming']);
|
// 'stickerSetInput', 'stickerThumbConverted', 'animated', 'supportsStreaming']);
|
||||||
defineNotNumerableProperties(doc, ['downloaded', 'url']);
|
|
||||||
|
|
||||||
doc.attributes.forEach(attribute => {
|
doc.attributes.forEach(attribute => {
|
||||||
switch(attribute._) {
|
switch(attribute._) {
|
||||||
case 'documentAttributeFilename':
|
case 'documentAttributeFilename':
|
||||||
@ -175,8 +178,9 @@ export class AppDocsManager {
|
|||||||
if((doc.type === 'gif' && doc.size > 8e6) || doc.type === 'audio' || doc.type === 'video') {
|
if((doc.type === 'gif' && doc.size > 8e6) || doc.type === 'audio' || doc.type === 'video') {
|
||||||
doc.supportsStreaming = true;
|
doc.supportsStreaming = true;
|
||||||
|
|
||||||
if(!doc.url) {
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
doc.url = this.getFileURL(doc);
|
if(!cacheContext.url) {
|
||||||
|
cacheContext.url = this.getFileURL(doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +193,7 @@ export class AppDocsManager {
|
|||||||
doc.file_name = '';
|
doc.file_name = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(doc.mime_type === 'application/x-tgsticker' && doc.file_name === "AnimatedSticker.tgs") {
|
if(doc.mime_type === 'application/x-tgsticker' && doc.file_name === 'AnimatedSticker.tgs') {
|
||||||
doc.type = 'sticker';
|
doc.type = 'sticker';
|
||||||
doc.animated = true;
|
doc.animated = true;
|
||||||
doc.sticker = 2;
|
doc.sticker = 2;
|
||||||
@ -268,12 +272,11 @@ export class AppDocsManager {
|
|||||||
public getThumbURL(doc: MyDocument, thumb: PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) {
|
public getThumbURL(doc: MyDocument, thumb: PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) {
|
||||||
let promise: Promise<any> = Promise.resolve();
|
let promise: Promise<any> = Promise.resolve();
|
||||||
|
|
||||||
if(!thumb.url) {
|
const cacheContext = appDownloadManager.getCacheContext(doc, thumb.type);
|
||||||
|
if(!cacheContext.url) {
|
||||||
if('bytes' in thumb) {
|
if('bytes' in thumb) {
|
||||||
promise = blur(appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker)).then(url => {
|
promise = blur(appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker)).then(url => {
|
||||||
defineNotNumerableProperties(thumb, ['url']); // * exclude from state
|
cacheContext.url = url;
|
||||||
const cacheContext = appPhotosManager.getCacheContext(doc);
|
|
||||||
cacheContext.url = thumb.url = url;
|
|
||||||
}) as any;
|
}) as any;
|
||||||
} else {
|
} else {
|
||||||
//return this.getFileURL(doc, false, thumb);
|
//return this.getFileURL(doc, false, thumb);
|
||||||
@ -281,7 +284,7 @@ export class AppDocsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {thumb, promise};
|
return {thumb, cacheContext, promise};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getThumb(doc: MyDocument, tryNotToUseBytes = true) {
|
public getThumb(doc: MyDocument, tryNotToUseBytes = true) {
|
||||||
@ -305,10 +308,11 @@ export class AppDocsManager {
|
|||||||
const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId, onlyCache);
|
const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId, onlyCache);
|
||||||
download = appDownloadManager.download(downloadOptions);
|
download = appDownloadManager.download(downloadOptions);
|
||||||
|
|
||||||
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
const originalPromise = download;
|
const originalPromise = download;
|
||||||
originalPromise.then((blob) => {
|
originalPromise.then((blob) => {
|
||||||
doc.url = URL.createObjectURL(blob);
|
cacheContext.url = URL.createObjectURL(blob);
|
||||||
doc.downloaded = true;
|
cacheContext.downloaded = blob.size;
|
||||||
}, () => {});
|
}, () => {});
|
||||||
|
|
||||||
if(doc.type === 'voice' && !opusDecodeController.isPlaySupported()) {
|
if(doc.type === 'voice' && !opusDecodeController.isPlaySupported()) {
|
||||||
@ -320,10 +324,10 @@ export class AppDocsManager {
|
|||||||
const uint8 = new Uint8Array(e.target.result as ArrayBuffer);
|
const uint8 = new Uint8Array(e.target.result as ArrayBuffer);
|
||||||
//console.log('sending uint8 to decoder:', uint8);
|
//console.log('sending uint8 to decoder:', uint8);
|
||||||
opusDecodeController.decode(uint8).then(result => {
|
opusDecodeController.decode(uint8).then(result => {
|
||||||
doc.url = result.url;
|
cacheContext.url = result.url;
|
||||||
resolve();
|
resolve();
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
delete doc.downloaded;
|
delete cacheContext.downloaded;
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -403,7 +407,8 @@ export class AppDocsManager {
|
|||||||
return appDownloadManager.downloadToDisc(options, doc.file_name); */
|
return appDownloadManager.downloadToDisc(options, doc.file_name); */
|
||||||
const promise = this.downloadDoc(doc, queueId);
|
const promise = this.downloadDoc(doc, queueId);
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
appDownloadManager.createDownloadAnchor(doc.url, doc.file_name);
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
appDownloadManager.createDownloadAnchor(cacheContext.url, doc.file_name);
|
||||||
});
|
});
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,18 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { DownloadOptions } from "../mtproto/apiFileManager";
|
||||||
|
import type { ApiError } from "../mtproto/apiManager";
|
||||||
|
import type { MyDocument } from "./appDocsManager";
|
||||||
|
import type { MyPhoto } from "./appPhotosManager";
|
||||||
import rootScope from "../rootScope";
|
import rootScope from "../rootScope";
|
||||||
import apiManager from "../mtproto/mtprotoworker";
|
import apiManager from "../mtproto/mtprotoworker";
|
||||||
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
|
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
|
||||||
import type { DownloadOptions } from "../mtproto/apiFileManager";
|
|
||||||
import { InputFile } from "../../layer";
|
import { InputFile } from "../../layer";
|
||||||
import referenceDatabase, {ReferenceBytes} from "../mtproto/referenceDatabase";
|
import referenceDatabase, {ReferenceBytes} from "../mtproto/referenceDatabase";
|
||||||
import type { ApiError } from "../mtproto/apiManager";
|
|
||||||
import { getFileNameByLocation } from "../../helpers/fileName";
|
import { getFileNameByLocation } from "../../helpers/fileName";
|
||||||
import CacheStorageController from "../cacheStorage";
|
import CacheStorageController from "../cacheStorage";
|
||||||
|
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||||
|
|
||||||
export type ResponseMethodBlob = 'blob';
|
export type ResponseMethodBlob = 'blob';
|
||||||
export type ResponseMethodJson = 'json';
|
export type ResponseMethodJson = 'json';
|
||||||
@ -28,6 +31,17 @@ export type Download = DownloadBlob/* | DownloadJson */;
|
|||||||
export type Progress = {done: number, fileName: string, total: number, offset: number};
|
export type Progress = {done: number, fileName: string, total: number, offset: number};
|
||||||
export type ProgressCallback = (details: Progress) => void;
|
export type ProgressCallback = (details: Progress) => void;
|
||||||
|
|
||||||
|
export type ThumbCache = {
|
||||||
|
downloaded: number,
|
||||||
|
url: string
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ThumbsCache = {
|
||||||
|
[id: string]: {
|
||||||
|
[size: string]: ThumbCache
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export class AppDownloadManager {
|
export class AppDownloadManager {
|
||||||
public cacheStorage = new CacheStorageController('cachedFiles');
|
public cacheStorage = new CacheStorageController('cachedFiles');
|
||||||
private downloads: {[fileName: string]: Download} = {};
|
private downloads: {[fileName: string]: Download} = {};
|
||||||
@ -36,6 +50,14 @@ export class AppDownloadManager {
|
|||||||
|
|
||||||
private uploadId = 0;
|
private uploadId = 0;
|
||||||
|
|
||||||
|
private thumbsCache: {
|
||||||
|
photo: ThumbsCache,
|
||||||
|
document: ThumbsCache
|
||||||
|
} = {
|
||||||
|
photo: {},
|
||||||
|
document: {}
|
||||||
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
rootScope.on('download_progress', (e) => {
|
rootScope.on('download_progress', (e) => {
|
||||||
const details = e as {done: number, fileName: string, total: number, offset: number};
|
const details = e as {done: number, fileName: string, total: number, offset: number};
|
||||||
@ -247,6 +269,17 @@ export class AppDownloadManager {
|
|||||||
|
|
||||||
return download;
|
return download;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCacheContext(media: MyPhoto | MyDocument, thumbSize: string = 'full'): ThumbCache {
|
||||||
|
if(media._ === 'photo' && thumbSize !== 'i') {
|
||||||
|
thumbSize = 'full';
|
||||||
|
}
|
||||||
|
|
||||||
|
const cache = this.thumbsCache[media._][media.id] ?? (this.thumbsCache[media._][media.id] = {});
|
||||||
|
return cache[thumbSize] ?? (cache[thumbSize] = {downloaded: 0, url: ''});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new AppDownloadManager();
|
const appDownloadManager = new AppDownloadManager();
|
||||||
|
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appDownloadManager = appDownloadManager);
|
||||||
|
export default appDownloadManager;
|
||||||
|
@ -429,8 +429,9 @@ export class AppImManager {
|
|||||||
private init() {
|
private init() {
|
||||||
document.addEventListener('paste', this.onDocumentPaste, true);
|
document.addEventListener('paste', this.onDocumentPaste, true);
|
||||||
|
|
||||||
|
const IGNORE_KEYS = new Set(['PageUp', 'PageDown', 'Meta', 'Control']);
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
if(rootScope.overlayIsActive) return;
|
if(rootScope.overlayIsActive || IGNORE_KEYS.has(e.key)) return;
|
||||||
|
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
|
|
||||||
@ -440,9 +441,7 @@ export class AppImManager {
|
|||||||
|
|
||||||
const chat = this.chat;
|
const chat = this.chat;
|
||||||
|
|
||||||
if(e.key === 'Meta' || e.key === 'Control') {
|
if(e.code === 'KeyC' && (e.ctrlKey || e.metaKey) && target.tagName !== 'INPUT') {
|
||||||
return;
|
|
||||||
} else if(e.code === "KeyC" && (e.ctrlKey || e.metaKey) && target.tagName !== 'INPUT') {
|
|
||||||
return;
|
return;
|
||||||
} else if(e.code === 'ArrowUp') {
|
} else if(e.code === 'ArrowUp') {
|
||||||
if(!chat.input.editMsgId && chat.input.isInputEmpty()) {
|
if(!chat.input.editMsgId && chat.input.isInputEmpty()) {
|
||||||
@ -469,10 +468,18 @@ export class AppImManager {
|
|||||||
cancelEvent(e); // * prevent from scrolling
|
cancelEvent(e); // * prevent from scrolling
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
} else if(e.code === 'ArrowDown') {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chat.input.messageInput && e.target !== chat.input.messageInput && target.tagName !== 'INPUT' && !target.hasAttribute('contenteditable') && !isTouchSupported) {
|
if(chat.input.messageInput &&
|
||||||
|
e.target !== chat.input.messageInput &&
|
||||||
|
target.tagName !== 'INPUT' &&
|
||||||
|
!target.hasAttribute('contenteditable') &&
|
||||||
|
!isTouchSupported) {
|
||||||
chat.input.messageInput.focus();
|
chat.input.messageInput.focus();
|
||||||
placeCaretAtEnd(chat.input.messageInput);
|
placeCaretAtEnd(chat.input.messageInput);
|
||||||
}
|
}
|
||||||
|
@ -628,26 +628,28 @@ export class AppMessagesManager {
|
|||||||
apiFileName = 'photo.' + fileType.split('/')[1];
|
apiFileName = 'photo.' + fileType.split('/')[1];
|
||||||
actionName = 'sendMessageUploadPhotoAction';
|
actionName = 'sendMessageUploadPhotoAction';
|
||||||
|
|
||||||
|
const photoSize = {
|
||||||
|
_: 'photoSize',
|
||||||
|
w: options.width,
|
||||||
|
h: options.height,
|
||||||
|
type: 'full',
|
||||||
|
location: null,
|
||||||
|
size: file.size
|
||||||
|
} as PhotoSize.photoSize;
|
||||||
|
|
||||||
photo = {
|
photo = {
|
||||||
_: 'photo',
|
_: 'photo',
|
||||||
id: '' + message.id,
|
id: '' + message.id,
|
||||||
sizes: [{
|
sizes: [photoSize],
|
||||||
_: 'photoSize',
|
|
||||||
w: options.width,
|
|
||||||
h: options.height,
|
|
||||||
type: 'full',
|
|
||||||
location: null,
|
|
||||||
size: file.size
|
|
||||||
}],
|
|
||||||
w: options.width,
|
w: options.width,
|
||||||
h: options.height
|
h: options.height
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
defineNotNumerableProperties(photo, ['downloaded', 'url']);
|
const cacheContext = appDownloadManager.getCacheContext(photo, photoSize.type);
|
||||||
photo.downloaded = file.size;
|
cacheContext.downloaded = file.size;
|
||||||
photo.url = options.objectURL || '';
|
cacheContext.url = options.objectURL || '';
|
||||||
|
|
||||||
appPhotosManager.savePhoto(photo);
|
photo = appPhotosManager.savePhoto(photo);
|
||||||
} else if(fileType.indexOf('video/') === 0) {
|
} else if(fileType.indexOf('video/') === 0) {
|
||||||
attachType = 'video';
|
attachType = 'video';
|
||||||
apiFileName = 'video.mp4';
|
apiFileName = 'video.mp4';
|
||||||
@ -686,11 +688,11 @@ export class AppMessagesManager {
|
|||||||
size: file.size
|
size: file.size
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
defineNotNumerableProperties(document, ['downloaded', 'url']);
|
const cacheContext = appDownloadManager.getCacheContext(document);
|
||||||
// @ts-ignore
|
cacheContext.downloaded = file.size;
|
||||||
document.downloaded = file.size;
|
cacheContext.url = options.objectURL || '';
|
||||||
document.url = options.objectURL || '';
|
|
||||||
|
|
||||||
|
let thumb: PhotoSize.photoSize;
|
||||||
if(isPhoto) {
|
if(isPhoto) {
|
||||||
attributes.push({
|
attributes.push({
|
||||||
_: 'documentAttributeImageSize',
|
_: 'documentAttributeImageSize',
|
||||||
@ -698,32 +700,31 @@ export class AppMessagesManager {
|
|||||||
h: options.height
|
h: options.height
|
||||||
});
|
});
|
||||||
|
|
||||||
thumbs.push({
|
thumb = {
|
||||||
_: 'photoSize',
|
_: 'photoSize',
|
||||||
w: options.width,
|
w: options.width,
|
||||||
h: options.height,
|
h: options.height,
|
||||||
type: 'full',
|
type: 'full',
|
||||||
location: null,
|
size: file.size
|
||||||
size: file.size,
|
};
|
||||||
url: options.objectURL
|
|
||||||
});
|
|
||||||
} else if(attachType === 'video') {
|
} else if(attachType === 'video') {
|
||||||
if(options.thumbURL) {
|
if(options.thumbURL) {
|
||||||
thumbs.push({
|
thumb = {
|
||||||
_: 'photoSize',
|
_: 'photoSize',
|
||||||
w: options.width,
|
w: options.width,
|
||||||
h: options.height,
|
h: options.height,
|
||||||
type: 'full',
|
type: 'full',
|
||||||
location: null,
|
size: options.thumbBlob.size
|
||||||
size: options.thumbBlob.size,
|
};
|
||||||
url: options.thumbURL
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const thumb = thumbs[0] as PhotoSize.photoSize;
|
const thumbCacheContext = appDownloadManager.getCacheContext(document, thumb.type);
|
||||||
const docThumb = appPhotosManager.getDocumentCachedThumb(document.id);
|
thumbCacheContext.downloaded = thumb.size;
|
||||||
docThumb.downloaded = thumb.size;
|
thumbCacheContext.url = options.thumbURL;
|
||||||
docThumb.url = thumb.url;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(thumb) {
|
||||||
|
thumbs.push(thumb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if(thumbs.length) {
|
/* if(thumbs.length) {
|
||||||
@ -733,7 +734,7 @@ export class AppMessagesManager {
|
|||||||
docThumb.url = thumb.url;
|
docThumb.url = thumb.url;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
appDocsManager.saveDoc(document);
|
document = appDocsManager.saveDoc(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log('sendFile', attachType, apiFileName, file.type, options);
|
this.log('sendFile', attachType, apiFileName, file.type, options);
|
||||||
@ -1537,7 +1538,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refreshConversations() {
|
public async refreshConversations() {
|
||||||
const limit = 100, outDialogs: Dialog[] = [];
|
const limit = 200, outDialogs: Dialog[] = [];
|
||||||
for(let folderId = 0; folderId < 2; ++folderId) {
|
for(let folderId = 0; folderId < 2; ++folderId) {
|
||||||
let offsetDate = 0;
|
let offsetDate = 0;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
@ -1572,7 +1573,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getConversationsAll(query = '', folderId = 0) {
|
public async getConversationsAll(query = '', folderId = 0) {
|
||||||
const limit = 100, outDialogs: Dialog[] = [];
|
const limit = 200, outDialogs: Dialog[] = [];
|
||||||
for(; folderId < 2; ++folderId) {
|
for(; folderId < 2; ++folderId) {
|
||||||
let offsetIndex = 0;
|
let offsetIndex = 0;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
@ -3651,14 +3652,7 @@ export class AppMessagesManager {
|
|||||||
if(!dialog && !isLocalThreadUpdate) {
|
if(!dialog && !isLocalThreadUpdate) {
|
||||||
let good = true;
|
let good = true;
|
||||||
if(peerId < 0) {
|
if(peerId < 0) {
|
||||||
const chat = appChatsManager.getChat(-peerId);
|
good = appChatsManager.isInChat(-peerId);
|
||||||
if(chat._ === 'channelForbidden'
|
|
||||||
|| chat._ === 'chatForbidden'
|
|
||||||
|| (chat as Chat.chat).pFlags.left
|
|
||||||
|| (chat as Chat.chat).pFlags.kicked
|
|
||||||
|| (chat as Chat.chat).pFlags.deactivated) {
|
|
||||||
good = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(good) {
|
if(good) {
|
||||||
@ -4033,7 +4027,7 @@ export class AppMessagesManager {
|
|||||||
const peerId = -channelId;
|
const peerId = -channelId;
|
||||||
const channel = appChatsManager.getChat(channelId);
|
const channel = appChatsManager.getChat(channelId);
|
||||||
|
|
||||||
const needDialog = channel._ === 'channel' && (!channel.pFlags.left && !channel.pFlags.kicked);
|
const needDialog = channel._ === 'channel' && appChatsManager.isInChat(channelId);
|
||||||
const dialog = this.getDialogOnly(peerId);
|
const dialog = this.getDialogOnly(peerId);
|
||||||
|
|
||||||
const canViewHistory = channel._ === 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked);
|
const canViewHistory = channel._ === 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked);
|
||||||
@ -4367,29 +4361,27 @@ export class AppMessagesManager {
|
|||||||
const photo = appPhotosManager.getPhoto('' + tempId);
|
const photo = appPhotosManager.getPhoto('' + tempId);
|
||||||
if(/* photo._ !== 'photoEmpty' */photo) {
|
if(/* photo._ !== 'photoEmpty' */photo) {
|
||||||
const newPhoto = message.media.photo as MyPhoto;
|
const newPhoto = message.media.photo as MyPhoto;
|
||||||
// костыль
|
const cacheContext = appDownloadManager.getCacheContext(newPhoto);
|
||||||
defineNotNumerableProperties(newPhoto, ['downloaded', 'url']);
|
const oldCacheContext = appDownloadManager.getCacheContext(photo, 'full');
|
||||||
newPhoto.downloaded = photo.downloaded;
|
Object.assign(cacheContext, oldCacheContext);
|
||||||
newPhoto.url = photo.url;
|
|
||||||
|
|
||||||
const photoSize = newPhoto.sizes[newPhoto.sizes.length - 1] as PhotoSize.photoSize;
|
const photoSize = newPhoto.sizes[newPhoto.sizes.length - 1] as PhotoSize.photoSize;
|
||||||
defineNotNumerableProperties(photoSize, ['url']);
|
|
||||||
photoSize.url = photo.url;
|
|
||||||
|
|
||||||
const downloadOptions = appPhotosManager.getPhotoDownloadOptions(newPhoto, photoSize);
|
const downloadOptions = appPhotosManager.getPhotoDownloadOptions(newPhoto, photoSize);
|
||||||
const fileName = getFileNameByLocation(downloadOptions.location);
|
const fileName = getFileNameByLocation(downloadOptions.location);
|
||||||
appDownloadManager.fakeDownload(fileName, photo.url);
|
appDownloadManager.fakeDownload(fileName, oldCacheContext.url);
|
||||||
}
|
}
|
||||||
} else if(message.media.document) {
|
} else if(message.media.document) {
|
||||||
const doc = appDocsManager.getDoc('' + tempId);
|
const doc = appDocsManager.getDoc('' + tempId);
|
||||||
if(doc) {
|
if(doc) {
|
||||||
if(/* doc._ !== 'documentEmpty' && */doc.type && doc.type !== 'sticker') {
|
if(/* doc._ !== 'documentEmpty' && */doc.type && doc.type !== 'sticker') {
|
||||||
const newDoc = message.media.document;
|
const newDoc = message.media.document;
|
||||||
newDoc.downloaded = doc.downloaded;
|
const cacheContext = appDownloadManager.getCacheContext(newDoc);
|
||||||
newDoc.url = doc.url;
|
const oldCacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
|
Object.assign(cacheContext, oldCacheContext);
|
||||||
|
|
||||||
const fileName = appDocsManager.getInputFileName(newDoc);
|
const fileName = appDocsManager.getInputFileName(newDoc);
|
||||||
appDownloadManager.fakeDownload(fileName, doc.url);
|
appDownloadManager.fakeDownload(fileName, oldCacheContext.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(message.media.poll) {
|
} else if(message.media.poll) {
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { ChatPhoto, DialogPeer, InputDialogPeer, InputNotifyPeer, InputPeer, Peer, Update, UserProfilePhoto } from "../../layer";
|
||||||
|
import type { LangPackKey } from "../langPack";
|
||||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||||
import { isObject } from "../../helpers/object";
|
import { isObject } from "../../helpers/object";
|
||||||
import { ChatPhoto, DialogPeer, InputDialogPeer, InputNotifyPeer, InputPeer, Peer, Update, UserProfilePhoto } from "../../layer";
|
|
||||||
import { LangPackKey } from "../langPack";
|
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import rootScope from "../rootScope";
|
import rootScope from "../rootScope";
|
||||||
import appChatsManager from "./appChatsManager";
|
import appChatsManager from "./appChatsManager";
|
||||||
|
@ -9,12 +9,13 @@
|
|||||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { DownloadOptions } from "../mtproto/apiFileManager";
|
||||||
import { bytesFromHex } from "../../helpers/bytes";
|
import { bytesFromHex } from "../../helpers/bytes";
|
||||||
import { CancellablePromise } from "../../helpers/cancellablePromise";
|
import { CancellablePromise } from "../../helpers/cancellablePromise";
|
||||||
import { getFileNameByLocation } from "../../helpers/fileName";
|
import { getFileNameByLocation } from "../../helpers/fileName";
|
||||||
import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from "../../helpers/object";
|
import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from "../../helpers/object";
|
||||||
import { isSafari } from "../../helpers/userAgent";
|
import { isSafari } from "../../helpers/userAgent";
|
||||||
import { FileLocation, InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from "../../layer";
|
import { InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from "../../layer";
|
||||||
import apiManager from "../mtproto/mtprotoworker";
|
import apiManager from "../mtproto/mtprotoworker";
|
||||||
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
|
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
|
||||||
import { calcImageInBox } from "../../helpers/dom";
|
import { calcImageInBox } from "../../helpers/dom";
|
||||||
@ -30,23 +31,16 @@ export type MyPhoto = Photo.photo;
|
|||||||
// TIMES = 2 DUE TO SIDEBAR AND CHAT
|
// TIMES = 2 DUE TO SIDEBAR AND CHAT
|
||||||
//let TEST_FILE_REFERENCE = "5440692274120994569", TEST_FILE_REFERENCE_TIMES = 2;
|
//let TEST_FILE_REFERENCE = "5440692274120994569", TEST_FILE_REFERENCE_TIMES = 2;
|
||||||
|
|
||||||
type DocumentCacheThumb = {
|
|
||||||
downloaded: number,
|
|
||||||
url: string
|
|
||||||
};
|
|
||||||
|
|
||||||
export class AppPhotosManager {
|
export class AppPhotosManager {
|
||||||
private photos: {
|
private photos: {
|
||||||
[id: string]: MyPhoto
|
[id: string]: MyPhoto
|
||||||
} = {};
|
} = {};
|
||||||
private documentThumbsCache: {
|
|
||||||
[docId: string]: DocumentCacheThumb
|
|
||||||
} = {};
|
|
||||||
public windowW = 0;
|
public windowW = 0;
|
||||||
public windowH = 0;
|
public windowH = 0;
|
||||||
|
|
||||||
public static jf = new Uint8Array(bytesFromHex('ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00'));
|
private static jpegHeader = bytesFromHex('ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00');
|
||||||
public static Df = bytesFromHex('ffd9');
|
private static jpegTail = bytesFromHex('ffd9');
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -155,7 +149,7 @@ export class AppPhotosManager {
|
|||||||
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
|
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
|
||||||
let arr: Uint8Array;
|
let arr: Uint8Array;
|
||||||
if(!isSticker) {
|
if(!isSticker) {
|
||||||
arr = AppPhotosManager.jf.concat(bytes.slice(3), AppPhotosManager.Df);
|
arr = new Uint8Array(AppPhotosManager.jpegHeader.concat(Array.from(bytes.slice(3)), AppPhotosManager.jpegTail));
|
||||||
arr[164] = bytes[1];
|
arr[164] = bytes[1];
|
||||||
arr[166] = bytes[2];
|
arr[166] = bytes[2];
|
||||||
} else {
|
} else {
|
||||||
@ -200,12 +194,13 @@ export class AppPhotosManager {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPreviewURLFromThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) {
|
public getPreviewURLFromThumb(photo: MyPhoto | MyDocument, thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) {
|
||||||
return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
|
const cacheContext = appDownloadManager.getCacheContext(photo, thumb.type);
|
||||||
|
return cacheContext.url || (cacheContext.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getImageFromStrippedThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, useBlur: boolean) {
|
public getImageFromStrippedThumb(photo: MyPhoto | MyDocument, thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, useBlur: boolean) {
|
||||||
const url = this.getPreviewURLFromThumb(thumb, false);
|
const url = this.getPreviewURLFromThumb(photo, thumb, false);
|
||||||
|
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.classList.add('thumbnail');
|
image.classList.add('thumbnail');
|
||||||
@ -253,26 +248,24 @@ export class AppPhotosManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, useBlur: boolean): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> {
|
public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, useBlur: boolean): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> {
|
||||||
if(!photo.downloaded || (photo as MyDocument).type === 'video' || (photo as MyDocument).type === 'gif') {
|
const cacheContext = appDownloadManager.getCacheContext(photo);
|
||||||
if(photo._ === 'document') {
|
if(!cacheContext.downloaded || (photo as MyDocument).type === 'video' || (photo as MyDocument).type === 'gif') {
|
||||||
const cacheContext = this.getCacheContext(photo);
|
if(photo._ === 'document' && cacheContext.downloaded) {
|
||||||
if(cacheContext.downloaded) {
|
return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
||||||
const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null;
|
const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null;
|
||||||
if(thumb && ('bytes' in thumb)) {
|
if(thumb && ('bytes' in thumb)) {
|
||||||
return appPhotosManager.getImageFromStrippedThumb(thumb as any, useBlur);
|
return appPhotosManager.getImageFromStrippedThumb(photo, thumb as any, useBlur);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number, onlyCache?: boolean) {
|
public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number, onlyCache?: boolean): DownloadOptions {
|
||||||
const isMyDocument = photo._ === 'document';
|
const isDocument = photo._ === 'document';
|
||||||
|
|
||||||
if(!photoSize || photoSize._ === 'photoSizeEmpty') {
|
if(!photoSize || photoSize._ === 'photoSizeEmpty') {
|
||||||
//console.error('no photoSize by photo:', photo);
|
//console.error('no photoSize by photo:', photo);
|
||||||
@ -281,15 +274,21 @@ export class AppPhotosManager {
|
|||||||
|
|
||||||
// maybe it's a thumb
|
// maybe it's a thumb
|
||||||
const isPhoto = (photoSize._ === 'photoSize' || photoSize._ === 'photoSizeProgressive') && photo.access_hash && photo.file_reference;
|
const isPhoto = (photoSize._ === 'photoSize' || photoSize._ === 'photoSizeProgressive') && photo.access_hash && photo.file_reference;
|
||||||
const location: InputFileLocation.inputPhotoFileLocation | InputFileLocation.inputDocumentFileLocation | FileLocation = isPhoto ? {
|
const location: InputFileLocation.inputPhotoFileLocation | InputFileLocation.inputDocumentFileLocation = {
|
||||||
_: isMyDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
_: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
||||||
id: photo.id,
|
id: photo.id,
|
||||||
access_hash: photo.access_hash,
|
access_hash: photo.access_hash,
|
||||||
file_reference: photo.file_reference,
|
file_reference: photo.file_reference,
|
||||||
thumb_size: photoSize.type
|
thumb_size: photoSize.type
|
||||||
} : (photoSize as PhotoSize.photoSize).location;
|
};
|
||||||
|
|
||||||
return {dcId: photo.dc_id, location, size: isPhoto ? (photoSize as PhotoSize.photoSize).size : undefined, queueId, onlyCache};
|
return {
|
||||||
|
dcId: photo.dc_id,
|
||||||
|
location,
|
||||||
|
size: isPhoto ? (photoSize as PhotoSize.photoSize).size : undefined,
|
||||||
|
queueId,
|
||||||
|
onlyCache
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* public getPhotoURL(photo: MTPhoto | MTMyDocument, photoSize: MTPhotoSize) {
|
/* public getPhotoURL(photo: MTPhoto | MTMyDocument, photoSize: MTPhotoSize) {
|
||||||
@ -298,7 +297,7 @@ export class AppPhotosManager {
|
|||||||
return {url: getFileURL('photo', downloadOptions), location: downloadOptions.location};
|
return {url: getFileURL('photo', downloadOptions), location: downloadOptions.location};
|
||||||
} */
|
} */
|
||||||
|
|
||||||
public isDownloaded(media: any) {
|
/* public isDownloaded(media: any) {
|
||||||
const isPhoto = media._ === 'photo';
|
const isPhoto = media._ === 'photo';
|
||||||
const photo = isPhoto ? this.getPhoto(media.id) : null;
|
const photo = isPhoto ? this.getPhoto(media.id) : null;
|
||||||
let isDownloaded: boolean;
|
let isDownloaded: boolean;
|
||||||
@ -310,9 +309,9 @@ export class AppPhotosManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return isDownloaded;
|
return isDownloaded;
|
||||||
}
|
} */
|
||||||
|
|
||||||
public preloadPhoto(photoId: any, photoSize?: PhotoSize, queueId?: number, onlyCache?: boolean): CancellablePromise<Blob> {
|
public preloadPhoto(photoId: MyPhoto | MyDocument | string, photoSize?: PhotoSize, queueId?: number, onlyCache?: boolean): CancellablePromise<Blob> {
|
||||||
const photo = this.getPhoto(photoId);
|
const photo = this.getPhoto(photoId);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -327,13 +326,12 @@ export class AppPhotosManager {
|
|||||||
photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
|
photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheContext = this.getCacheContext(photo);
|
const cacheContext = appDownloadManager.getCacheContext(photo, photoSize.type);
|
||||||
if(cacheContext.downloaded >= ('size' in photoSize ? photoSize.size : 0) && cacheContext.url) {
|
if(cacheContext.downloaded >= ('size' in photoSize ? photoSize.size : 0) && cacheContext.url) {
|
||||||
return Promise.resolve() as any;
|
return Promise.resolve() as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize, queueId, onlyCache);
|
const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize, queueId, onlyCache);
|
||||||
|
|
||||||
const fileName = getFileNameByLocation(downloadOptions.location);
|
const fileName = getFileNameByLocation(downloadOptions.location);
|
||||||
|
|
||||||
let download = appDownloadManager.getDownload(fileName);
|
let download = appDownloadManager.getDownload(fileName);
|
||||||
@ -343,35 +341,22 @@ export class AppPhotosManager {
|
|||||||
|
|
||||||
download = appDownloadManager.download(downloadOptions);
|
download = appDownloadManager.download(downloadOptions);
|
||||||
download.then(blob => {
|
download.then(blob => {
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {
|
if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {
|
||||||
defineNotNumerableProperties(cacheContext, ['downloaded', 'url']);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
cacheContext.downloaded = blob.size;
|
cacheContext.downloaded = blob.size;
|
||||||
cacheContext.url = url;
|
cacheContext.url = url;
|
||||||
|
|
||||||
//console.log('wrote photo:', photo, photoSize, cacheContext, blob);
|
//console.log('wrote photo:', photo, photoSize, cacheContext, blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineNotNumerableProperties(photoSize, ['url']);
|
|
||||||
(photoSize as any).url = url;
|
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
|
||||||
return download;
|
return download;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCacheContext(photo: any): DocumentCacheThumb {
|
|
||||||
return photo._ === 'document' ? this.getDocumentCachedThumb(photo.id) : photo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDocumentCachedThumb(docId: string) {
|
|
||||||
return this.documentThumbsCache[docId] ?? (this.documentThumbsCache[docId] = {downloaded: 0, url: ''});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getPhoto(photoId: any): MyPhoto {
|
public getPhoto(photoId: any/* MyPhoto | string */): MyPhoto {
|
||||||
return isObject(photoId) ? photoId : this.photos[photoId];
|
return isObject(photoId) ? photoId as MyPhoto : this.photos[photoId as any as string];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getInput(photo: MyPhoto): InputMedia.inputMediaPhoto {
|
public getInput(photo: MyPhoto): InputMedia.inputMediaPhoto {
|
||||||
@ -393,21 +378,9 @@ export class AppPhotosManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const location: InputFileLocation.inputDocumentFileLocation | InputFileLocation.inputPhotoFileLocation = {
|
const downloadOptions = this.getPhotoDownloadOptions(photo, fullPhotoSize, queueId);
|
||||||
_: photo._ === 'document' ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
downloadOptions.fileName = 'photo' + photo.id + '.jpg';
|
||||||
id: photo.id,
|
appDownloadManager.downloadToDisc(downloadOptions, downloadOptions.fileName);
|
||||||
access_hash: photo.access_hash,
|
|
||||||
file_reference: photo.file_reference,
|
|
||||||
thumb_size: fullPhotoSize.type
|
|
||||||
};
|
|
||||||
|
|
||||||
appDownloadManager.downloadToDisc({
|
|
||||||
dcId: photo.dc_id,
|
|
||||||
location,
|
|
||||||
size: fullPhotoSize.size,
|
|
||||||
fileName: 'photo' + photo.id + '.jpg',
|
|
||||||
queueId
|
|
||||||
}, 'photo' + photo.id + '.jpg');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import { tsNow } from "../../helpers/date";
|
|||||||
import { replaceContent } from "../../helpers/dom";
|
import { replaceContent } from "../../helpers/dom";
|
||||||
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
|
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
|
||||||
import sequentialDom from "../../helpers/sequentialDom";
|
import sequentialDom from "../../helpers/sequentialDom";
|
||||||
import { ChannelParticipantsFilter, ChannelsChannelParticipants, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, UserFull, UserProfilePhoto } from "../../layer";
|
import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, Update, UserFull, UserProfilePhoto } from "../../layer";
|
||||||
//import apiManager from '../mtproto/apiManager';
|
//import apiManager from '../mtproto/apiManager';
|
||||||
import apiManager from '../mtproto/mtprotoworker';
|
import apiManager from '../mtproto/mtprotoworker';
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
@ -97,10 +97,11 @@ export class AppProfileManager {
|
|||||||
|
|
||||||
rootScope.on('chat_update', (chatId) => {
|
rootScope.on('chat_update', (chatId) => {
|
||||||
const fullChat = this.chatsFull[chatId];
|
const fullChat = this.chatsFull[chatId];
|
||||||
const chat = appChatsManager.getChat(chatId);
|
const chat: Chat.chat = appChatsManager.getChat(chatId);
|
||||||
if(!chat.photo || !fullChat) {
|
if(!chat.photo || !fullChat) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyPhoto = chat.photo._ === 'chatPhotoEmpty';
|
const emptyPhoto = chat.photo._ === 'chatPhotoEmpty';
|
||||||
//////console.log('chat_update:', fullChat);
|
//////console.log('chat_update:', fullChat);
|
||||||
if(fullChat.chat_photo && emptyPhoto !== (fullChat.chat_photo._ === 'photoEmpty')) {
|
if(fullChat.chat_photo && emptyPhoto !== (fullChat.chat_photo._ === 'photoEmpty')) {
|
||||||
@ -112,9 +113,9 @@ export class AppProfileManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const smallUserpic = chat.photo.photo_small;
|
const photoId = (chat.photo as ChatPhoto.chatPhoto).photo_id;
|
||||||
const smallPhotoSize = fullChat.chat_photo ? appPhotosManager.choosePhotoSize(fullChat.chat_photo as MyPhoto, 0, 0) : undefined;
|
const chatFullPhotoId = fullChat.chat_photo?.id;
|
||||||
if(!smallPhotoSize || JSON.stringify(smallUserpic) !== JSON.stringify((smallPhotoSize as PhotoSize.photoSize).location)) {
|
if(chatFullPhotoId !== photoId) {
|
||||||
delete this.chatsFull[chatId];
|
delete this.chatsFull[chatId];
|
||||||
rootScope.broadcast('chat_full_update', chatId);
|
rootScope.broadcast('chat_full_update', chatId);
|
||||||
}
|
}
|
||||||
@ -328,10 +329,10 @@ export class AppProfileManager {
|
|||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
public getChannelParticipant(id: number, userId: number) {
|
public getChannelParticipant(id: number, peerId: number) {
|
||||||
return apiManager.invokeApiSingle('channels.getParticipant', {
|
return apiManager.invokeApiSingle('channels.getParticipant', {
|
||||||
channel: appChatsManager.getChannelInput(id),
|
channel: appChatsManager.getChannelInput(id),
|
||||||
user_id: appUsersManager.getUserInput(userId)
|
participant: appPeersManager.getInputPeerById(peerId),
|
||||||
}).then(channelParticipant => {
|
}).then(channelParticipant => {
|
||||||
appUsersManager.saveApiUsers(channelParticipant.users);
|
appUsersManager.saveApiUsers(channelParticipant.users);
|
||||||
return channelParticipant.participant;
|
return channelParticipant.participant;
|
||||||
@ -375,7 +376,7 @@ export class AppProfileManager {
|
|||||||
updates: [{
|
updates: [{
|
||||||
_: 'updateChannel',
|
_: 'updateChannel',
|
||||||
channel_id: id
|
channel_id: id
|
||||||
}],
|
} as Update.updateChannel],
|
||||||
chats: [channel],
|
chats: [channel],
|
||||||
users: []
|
users: []
|
||||||
});
|
});
|
||||||
@ -425,7 +426,7 @@ export class AppProfileManager {
|
|||||||
date: tsNow(true),
|
date: tsNow(true),
|
||||||
photo: appUsersManager.getUser(myId).photo,
|
photo: appUsersManager.getUser(myId).photo,
|
||||||
previous: true
|
previous: true
|
||||||
}
|
} as Update.updateUserPhoto
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -452,8 +453,7 @@ export class AppProfileManager {
|
|||||||
_: 'inputPeerPhotoFileLocation',
|
_: 'inputPeerPhotoFileLocation',
|
||||||
pFlags: {},
|
pFlags: {},
|
||||||
peer: inputPeer,
|
peer: inputPeer,
|
||||||
volume_id: photo[size].volume_id,
|
photo_id: photo.photo_id
|
||||||
local_id: photo[size].local_id
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if(size === 'photo_big') {
|
if(size === 'photo_big') {
|
||||||
@ -520,6 +520,10 @@ export class AppProfileManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const renderPromise = loadPromise.then((url) => {
|
const renderPromise = loadPromise.then((url) => {
|
||||||
|
/* if(photo.stripped_thumb) {
|
||||||
|
url = appPhotosManager.getPreviewURLFromBytes(photo.stripped_thumb);
|
||||||
|
} */
|
||||||
|
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
renderImageFromUrl(img, url, () => {
|
renderImageFromUrl(img, url, () => {
|
||||||
callback();
|
callback();
|
||||||
@ -536,8 +540,8 @@ export class AppProfileManager {
|
|||||||
const photo = appPeersManager.getPeerPhoto(peerId);
|
const photo = appPeersManager.getPeerPhoto(peerId);
|
||||||
|
|
||||||
const size: PeerPhotoSize = 'photo_small';
|
const size: PeerPhotoSize = 'photo_small';
|
||||||
const avatarAvailable = photo && photo[size];
|
const avatarAvailable = !!photo;
|
||||||
const avatarRendered = !!div.firstElementChild;
|
const avatarRendered = div.firstElementChild && !(div.firstElementChild as HTMLElement).classList.contains('emoji');
|
||||||
|
|
||||||
const myId = rootScope.myId;
|
const myId = rootScope.myId;
|
||||||
|
|
||||||
@ -573,7 +577,8 @@ export class AppProfileManager {
|
|||||||
|
|
||||||
let abbr: string;
|
let abbr: string;
|
||||||
if(!title) {
|
if(!title) {
|
||||||
abbr = appPeersManager.getPeer(peerId).initials ?? '';
|
const peer = appPeersManager.getPeer(peerId);
|
||||||
|
abbr = peer.initials ?? '';
|
||||||
} else {
|
} else {
|
||||||
abbr = RichTextProcessor.getAbbreviation(title);
|
abbr = RichTextProcessor.getAbbreviation(title);
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,8 @@ export class AppStateManager extends EventListenerBase<{
|
|||||||
|
|
||||||
public storagesResults: {[key in keyof AppStateManager['storages']]: any[]} = {} as any;
|
public storagesResults: {[key in keyof AppStateManager['storages']]: any[]} = {} as any;
|
||||||
|
|
||||||
|
public storage = sessionStorage;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.loadSavedState();
|
this.loadSavedState();
|
||||||
|
@ -132,11 +132,10 @@ export class AppStickersManager {
|
|||||||
const input: InputFileLocation.inputStickerSetThumb = {
|
const input: InputFileLocation.inputStickerSetThumb = {
|
||||||
_: 'inputStickerSetThumb',
|
_: 'inputStickerSetThumb',
|
||||||
stickerset: this.getStickerSetInput(stickerSet),
|
stickerset: this.getStickerSetInput(stickerSet),
|
||||||
volume_id: thumb.location.volume_id,
|
thumb_version: stickerSet.thumb_version
|
||||||
local_id: thumb.location.local_id
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {dcId, location: input, size: thumb.size, mimeType: isAnimated ? "application/x-tgsticker" : 'image/webp'};
|
return {dcId, location: input, size: thumb.size, mimeType: isAnimated ? 'application/x-tgsticker' : 'image/webp'};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* public getStickerSetThumbURL(stickerSet: MTStickerSet) {
|
/* public getStickerSetThumbURL(stickerSet: MTStickerSet) {
|
||||||
|
@ -110,7 +110,7 @@ export default class IDBStorage {
|
|||||||
var request = indexedDB.open(this.name, this.version);
|
var request = indexedDB.open(this.name, this.version);
|
||||||
|
|
||||||
if(!request) {
|
if(!request) {
|
||||||
throw new Error();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
this.log.error('error opening db', error.message)
|
this.log.error('error opening db', error.message)
|
||||||
|
@ -15,7 +15,7 @@ import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePr
|
|||||||
import { notifyAll, notifySomeone } from "../../helpers/context";
|
import { notifyAll, notifySomeone } from "../../helpers/context";
|
||||||
import { getFileNameByLocation } from "../../helpers/fileName";
|
import { getFileNameByLocation } from "../../helpers/fileName";
|
||||||
import { nextRandomInt } from "../../helpers/random";
|
import { nextRandomInt } from "../../helpers/random";
|
||||||
import { FileLocation, InputFile, InputFileLocation, UploadFile } from "../../layer";
|
import { InputFile, InputFileLocation, UploadFile } from "../../layer";
|
||||||
import CacheStorageController from "../cacheStorage";
|
import CacheStorageController from "../cacheStorage";
|
||||||
import cryptoWorker from "../crypto/cryptoworker";
|
import cryptoWorker from "../crypto/cryptoworker";
|
||||||
import FileManager from "../filemanager";
|
import FileManager from "../filemanager";
|
||||||
@ -31,7 +31,7 @@ type Delayed = {
|
|||||||
|
|
||||||
export type DownloadOptions = {
|
export type DownloadOptions = {
|
||||||
dcId: number,
|
dcId: number,
|
||||||
location: InputFileLocation | FileLocation,
|
location: InputFileLocation,
|
||||||
size?: number,
|
size?: number,
|
||||||
fileName?: string,
|
fileName?: string,
|
||||||
mimeType?: string,
|
mimeType?: string,
|
||||||
@ -150,7 +150,7 @@ export class ApiFileManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public requestFilePart(dcId: number, location: InputFileLocation | FileLocation, offset: number, limit: number, id = 0, queueId = 0, checkCancel?: () => void) {
|
public requestFilePart(dcId: number, location: InputFileLocation, offset: number, limit: number, id = 0, queueId = 0, checkCancel?: () => void) {
|
||||||
return this.downloadRequest(dcId, id, async() => {
|
return this.downloadRequest(dcId, id, async() => {
|
||||||
checkCancel && checkCancel();
|
checkCancel && checkCancel();
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import { logger, LogTypes } from '../logger';
|
|||||||
import type { DownloadOptions } from './apiFileManager';
|
import type { DownloadOptions } from './apiFileManager';
|
||||||
import type { WorkerTaskTemplate } from '../../types';
|
import type { WorkerTaskTemplate } from '../../types';
|
||||||
import { notifySomeone } from '../../helpers/context';
|
import { notifySomeone } from '../../helpers/context';
|
||||||
import type { InputFileLocation, FileLocation, UploadFile } from '../../layer';
|
import type { InputFileLocation, UploadFile } from '../../layer';
|
||||||
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
|
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
|
||||||
|
|
||||||
const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);
|
const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);
|
||||||
@ -39,7 +39,7 @@ let taskId = 0;
|
|||||||
|
|
||||||
export interface ServiceWorkerTask extends WorkerTaskTemplate {
|
export interface ServiceWorkerTask extends WorkerTaskTemplate {
|
||||||
type: 'requestFilePart',
|
type: 'requestFilePart',
|
||||||
payload: [number, InputFileLocation | FileLocation, number, number]
|
payload: [number, InputFileLocation, number, number]
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ServiceWorkerTaskResponse extends WorkerTaskTemplate {
|
export interface ServiceWorkerTaskResponse extends WorkerTaskTemplate {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -163,7 +163,13 @@ export class RootScope extends EventListenerBase<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setTheme() {
|
public setTheme() {
|
||||||
document.documentElement.classList.toggle('night', this.getTheme().name === 'night');
|
const isNight = this.getTheme().name === 'night';
|
||||||
|
const colorScheme = document.head.querySelector('[name="color-scheme"]');
|
||||||
|
if(colorScheme) {
|
||||||
|
colorScheme.setAttribute('content', isNight ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.classList.toggle('night', isNight);
|
||||||
}
|
}
|
||||||
|
|
||||||
get overlayIsActive() {
|
get overlayIsActive() {
|
||||||
|
@ -14,6 +14,8 @@ import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromi
|
|||||||
import { throttle } from "../helpers/schedulers";
|
import { throttle } from "../helpers/schedulers";
|
||||||
import IDBStorage, { IDBOptions } from "./idb";
|
import IDBStorage, { IDBOptions } from "./idb";
|
||||||
|
|
||||||
|
function noop() {}
|
||||||
|
|
||||||
export default class AppStorage<Storage extends Record<string, any>/* Storage extends {[name: string]: any} *//* Storage extends Record<string, any> */> {
|
export default class AppStorage<Storage extends Record<string, any>/* Storage extends {[name: string]: any} *//* Storage extends Record<string, any> */> {
|
||||||
private static STORAGES: AppStorage<any>[] = [];
|
private static STORAGES: AppStorage<any>[] = [];
|
||||||
private storage: IDBStorage;//new CacheStorageController('session');
|
private storage: IDBStorage;//new CacheStorageController('session');
|
||||||
@ -55,6 +57,10 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
|
||||||
|
if(this.keysToSet.size) {
|
||||||
|
this.saveThrottled();
|
||||||
|
}
|
||||||
}, 16, false);
|
}, 16, false);
|
||||||
|
|
||||||
this.getThrottled = throttle(async() => {
|
this.getThrottled = throttle(async() => {
|
||||||
@ -73,21 +79,30 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
|
|||||||
}, (error) => {
|
}, (error) => {
|
||||||
if(!['NO_ENTRY_FOUND', 'STORAGE_OFFLINE'].includes(error)) {
|
if(!['NO_ENTRY_FOUND', 'STORAGE_OFFLINE'].includes(error)) {
|
||||||
this.useStorage = false;
|
this.useStorage = false;
|
||||||
console.error('[AS]: get error:', error, keys);
|
console.error('[AS]: get error:', error, keys, storageOptions.storeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let i = 0, length = keys.length; i < length; ++i) {
|
for(let i = 0, length = keys.length; i < length; ++i) {
|
||||||
const key = keys[i];
|
const key = keys[i];
|
||||||
const deferred = this.getPromises.get(key);
|
const deferred = this.getPromises.get(key);
|
||||||
if(deferred) {
|
if(deferred) {
|
||||||
deferred.reject(error);
|
//deferred.reject(error);
|
||||||
|
deferred.resolve();
|
||||||
this.getPromises.delete(key);
|
this.getPromises.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
if(this.getPromises.size) {
|
||||||
|
this.getThrottled();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, 16, false);
|
}, 16, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isAvailable() {
|
||||||
|
return this.useStorage;
|
||||||
|
}
|
||||||
|
|
||||||
public getCache() {
|
public getCache() {
|
||||||
return this.cache;
|
return this.cache;
|
||||||
}
|
}
|
||||||
@ -119,7 +134,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getAll() {
|
public getAll() {
|
||||||
return this.storage.getAll();
|
return this.storage.getAll().catch(() => []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(obj: Partial<Storage>, onlyLocal = false) {
|
public set(obj: Partial<Storage>, onlyLocal = false) {
|
||||||
@ -151,7 +166,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.saveDeferred;
|
return this.useStorage ? this.saveDeferred : Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async delete(key: keyof Storage, saveLocal = false) {
|
public async delete(key: keyof Storage, saveLocal = false) {
|
||||||
@ -179,7 +194,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
public clear() {
|
public clear() {
|
||||||
return this.storage.deleteAll();
|
return this.storage.deleteAll().catch(noop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static toggleStorage(enabled: boolean) {
|
public static toggleStorage(enabled: boolean) {
|
||||||
@ -194,10 +209,10 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
|
|||||||
} else {
|
} else {
|
||||||
return storage.set(storage.cache);
|
return storage.set(storage.cache);
|
||||||
}
|
}
|
||||||
}));
|
})).catch(noop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteDatabase() {
|
public deleteDatabase() {
|
||||||
return IDBStorage.deleteDatabase();
|
return IDBStorage.deleteDatabase().catch(noop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,12 @@ let onFirstMount = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
appStateManager.getState().then(state => {
|
appStateManager.getState().then(state => {
|
||||||
signedCheckboxField.checked = state.keepSigned;
|
if(!appStateManager.storage.isAvailable()) {
|
||||||
|
signedCheckboxField.checked = false;
|
||||||
|
signedCheckboxField.label.classList.add('checkbox-disabled');
|
||||||
|
} else {
|
||||||
|
signedCheckboxField.checked = state.keepSigned;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
btnNext = Button('btn-primary btn-color-primary', {text: 'Login.Next'});
|
btnNext = Button('btn-primary btn-color-primary', {text: 'Login.Next'});
|
||||||
|
File diff suppressed because one or more lines are too long
@ -8,8 +8,6 @@
|
|||||||
{"name": "file_name", "type": "string"},
|
{"name": "file_name", "type": "string"},
|
||||||
{"name": "file", "type": "File"},
|
{"name": "file", "type": "File"},
|
||||||
{"name": "duration", "type": "number"},
|
{"name": "duration", "type": "number"},
|
||||||
{"name": "downloaded", "type": "boolean"},
|
|
||||||
{"name": "url", "type": "string"},
|
|
||||||
{"name": "audioTitle", "type": "string"},
|
{"name": "audioTitle", "type": "string"},
|
||||||
{"name": "audioPerformer", "type": "string"},
|
{"name": "audioPerformer", "type": "string"},
|
||||||
{"name": "sticker", "type": "number"},
|
{"name": "sticker", "type": "number"},
|
||||||
@ -21,31 +19,9 @@
|
|||||||
{"name": "animated", "type": "boolean"},
|
{"name": "animated", "type": "boolean"},
|
||||||
{"name": "supportsStreaming", "type": "boolean"}
|
{"name": "supportsStreaming", "type": "boolean"}
|
||||||
]
|
]
|
||||||
}, {
|
|
||||||
"predicate": "photo",
|
|
||||||
"params": [
|
|
||||||
{"name": "downloaded", "type": "boolean | number"},
|
|
||||||
{"name": "url", "type": "string"}
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
"predicate": "photoSize",
|
|
||||||
"params": [
|
|
||||||
{"name": "url", "type": "string"}
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
"predicate": "photoCachedSize",
|
|
||||||
"params": [
|
|
||||||
{"name": "url", "type": "string"}
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
"predicate": "photoStrippedSize",
|
|
||||||
"params": [
|
|
||||||
{"name": "url", "type": "string"}
|
|
||||||
]
|
|
||||||
}, {
|
}, {
|
||||||
"predicate": "photoSizeProgressive",
|
"predicate": "photoSizeProgressive",
|
||||||
"params": [
|
"params": [
|
||||||
{"name": "url", "type": "string"},
|
|
||||||
{"name": "size", "type": "number"}
|
{"name": "size", "type": "number"}
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
@ -138,7 +114,22 @@
|
|||||||
}, {
|
}, {
|
||||||
"predicate": "chat",
|
"predicate": "chat",
|
||||||
"params": [
|
"params": [
|
||||||
{"name": "rTitle", "type": "string"}
|
{"name": "initials", "type": "string"}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"predicate": "chatForbidden",
|
||||||
|
"params": [
|
||||||
|
{"name": "initials", "type": "string"}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"predicate": "channel",
|
||||||
|
"params": [
|
||||||
|
{"name": "initials", "type": "string"}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"predicate": "channelForbidden",
|
||||||
|
"params": [
|
||||||
|
{"name": "initials", "type": "string"}
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
"predicate": "messageActionChatLeave",
|
"predicate": "messageActionChatLeave",
|
||||||
|
File diff suppressed because one or more lines are too long
@ -300,6 +300,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-control {
|
||||||
|
width: auto;
|
||||||
|
height: 40px;
|
||||||
|
align-items: center;
|
||||||
|
margin: 15px auto 1rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 0 24px 0 12px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&.tgico:before {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-right: .375rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ! example: multiselect input, button in pinned messages chat, settings, chat background tab
|
// ! example: multiselect input, button in pinned messages chat, settings, chat background tab
|
||||||
.btn-transparent {
|
.btn-transparent {
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
|
@ -1625,6 +1625,7 @@ $bubble-margin: .25rem;
|
|||||||
|
|
||||||
.bubble-content {
|
.bubble-content {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
border-radius: .875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-msg {
|
.service-msg {
|
||||||
@ -1632,9 +1633,9 @@ $bubble-margin: .25rem;
|
|||||||
//background-color: rgba(0, 0, 0, .24);
|
//background-color: rgba(0, 0, 0, .24);
|
||||||
background-color: var(--message-highlightning-color);
|
background-color: var(--message-highlightning-color);
|
||||||
font-size: .9375rem;
|
font-size: .9375rem;
|
||||||
padding: .28125rem .5rem;
|
padding: .28125rem .625rem;
|
||||||
line-height: 1.3125;
|
line-height: var(--line-height);
|
||||||
border-radius: .75rem;
|
border-radius: inherit;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
min-height: 3.5rem;
|
min-height: 3.5rem;
|
||||||
max-height: 3.5rem;
|
max-height: 3.5rem;
|
||||||
border-left: 1px solid var(--border-color);
|
|
||||||
|
|
||||||
&.is-pinned-floating {
|
&.is-pinned-floating {
|
||||||
&.is-pinned-audio-shown, &.is-pinned-message-shown:not(.hide-pinned) {
|
&.is-pinned-audio-shown, &.is-pinned-message-shown:not(.hide-pinned) {
|
||||||
@ -57,10 +56,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to(handhelds) {
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-close-button {
|
.sidebar-close-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
@ -44,13 +44,7 @@
|
|||||||
width: calc(100% + 1rem);
|
width: calc(100% + 1rem);
|
||||||
margin-left: -.5rem;
|
margin-left: -.5rem;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
> .menu-horizontal-div {
|
|
||||||
//font-weight: 500;
|
|
||||||
margin-top: 2px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.emoji-container {
|
.emoji-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@ -60,6 +54,9 @@
|
|||||||
|
|
||||||
.emoji-tabs {
|
.emoji-tabs {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
//font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
|
||||||
&-search {
|
&-search {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -150,10 +147,8 @@
|
|||||||
.emoji-padding, .stickers-padding {
|
.emoji-padding, .stickers-padding {
|
||||||
.menu-horizontal-div {
|
.menu-horizontal-div {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-bottom: none;
|
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, 0.21);
|
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
background-color: var(--surface-color);
|
background-color: var(--surface-color);
|
||||||
|
|
||||||
@ -270,7 +265,6 @@
|
|||||||
.menu-horizontal-div {
|
.menu-horizontal-div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
box-shadow: 0px -2px 5px -1px rgba(0, 0, 0, .21);
|
|
||||||
|
|
||||||
.menu-horizontal-div-item {
|
.menu-horizontal-div-item {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
@ -288,12 +282,10 @@
|
|||||||
&.active {
|
&.active {
|
||||||
.scrollable {
|
.scrollable {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21);
|
//box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-horizontal-div {
|
.menu-horizontal-div {
|
||||||
box-shadow: none;
|
|
||||||
|
|
||||||
.menu-horizontal-div-item {
|
.menu-horizontal-div-item {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#column-left {
|
#column-left {
|
||||||
//display: flex;
|
//display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1 1 auto;
|
||||||
min-width: 18rem;
|
min-width: 18rem;
|
||||||
max-width: calc(#{$large-screen} / 4);
|
max-width: calc(#{$large-screen} / 4);
|
||||||
|
|
||||||
@ -20,6 +20,22 @@
|
|||||||
filter: brightness(80%);
|
filter: brightness(80%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include respond-to(not-handhelds) {
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
height: 100vh;
|
||||||
|
width: 1px;
|
||||||
|
background-color: var(--border-color);
|
||||||
|
right: -1px;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include respond-to(floating-left-sidebar) {
|
@include respond-to(floating-left-sidebar) {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -57,41 +73,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-main {
|
||||||
|
--menu-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-horizontal-scrollable {
|
.menu-horizontal-scrollable {
|
||||||
|
--scrollable-size: var(--menu-size);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background-color: var(--surface-color);
|
background-color: var(--surface-color);
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16);
|
|
||||||
top: unset;
|
top: unset;
|
||||||
height: 43px;
|
|
||||||
|
@include respond-to(handhelds) {
|
||||||
|
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16);
|
||||||
|
}
|
||||||
|
|
||||||
.scrollable {
|
.scrollable {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-horizontal-div {
|
.menu-horizontal-div {
|
||||||
height: 43px;
|
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
|
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
height: 43px;
|
|
||||||
padding: 0 1rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
min-width: 3rem;
|
min-width: 3rem;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
bottom: calc(-.6875rem);
|
bottom: calc(-.6875rem - 2px);
|
||||||
padding-right: 1rem !important;
|
/* padding-right: 1rem !important;
|
||||||
margin-left: -.5rem !important;
|
margin-left: -.5rem !important; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,8 +125,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:not(.hide) + .scrollable {
|
&:not(.hide) + .scrollable {
|
||||||
top: 43px;
|
top: var(--menu-size);
|
||||||
height: calc(100% - 43px);
|
height: calc(100% - var(--menu-size));
|
||||||
|
|
||||||
#folders-container {
|
#folders-container {
|
||||||
margin-top: .5rem;
|
margin-top: .5rem;
|
||||||
@ -606,21 +624,6 @@
|
|||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
width: auto;
|
|
||||||
height: 40px;
|
|
||||||
align-items: center;
|
|
||||||
margin: 15px auto 1rem;
|
|
||||||
border-radius: 30px;
|
|
||||||
padding: 0 24px 0 12px;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tgico-add:before {
|
|
||||||
font-size: 24px;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
@ -633,6 +636,7 @@
|
|||||||
right: 1rem;
|
right: 1rem;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
|
border-radius: 15px;
|
||||||
|
|
||||||
body.animation-level-0 & {
|
body.animation-level-0 & {
|
||||||
transition: none;
|
transition: none;
|
||||||
@ -1159,3 +1163,38 @@
|
|||||||
padding-bottom: .5rem;
|
padding-bottom: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-folder {
|
||||||
|
top: 40%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
text-align: center;
|
||||||
|
line-height: var(--line-height);
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.media-sticker-wrapper {
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
margin: 0 auto 1.9375rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-subtitle {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: .875rem;
|
||||||
|
margin-top: .3125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-control {
|
||||||
|
margin-top: 1.75rem;
|
||||||
|
padding: 0 1.0625rem 0 .8125rem;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
margin-right: .625rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -117,14 +117,14 @@
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 0 .125rem;
|
margin: 0 .125rem;
|
||||||
background-color: var(--secondary-color);
|
background-color: #fff;
|
||||||
opacity: .3;
|
opacity: .2;
|
||||||
transition: opacity .2s ease-in-out;
|
transition: opacity .2s ease-in-out;
|
||||||
//min-width: 1.6875rem;
|
//min-width: 1.6875rem;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
opacity: 1;
|
opacity: .6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to(not-handhelds) {
|
@include respond-to(not-handhelds) {
|
||||||
width: var(--right-column-width);
|
width: calc(var(--right-column-width));
|
||||||
transition: transform var(--transition-standard-out);
|
transition: transform var(--transition-standard-out);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
transform: translate3d(calc(var(--right-column-width) + 1px), 0, 0);
|
transform: translate3d(var(--right-column-width), 0, 0);
|
||||||
|
//transform: translate3d(calc(var(--right-column-width) + 1px), 0, 0);
|
||||||
|
|
||||||
.sidebar-content {
|
.sidebar-content {
|
||||||
min-width: var(--right-column-width);
|
min-width: var(--right-column-width);
|
||||||
@ -43,16 +44,23 @@
|
|||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
/* &:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: " ";
|
content: " ";
|
||||||
display: block;
|
display: block;
|
||||||
height: 56px;
|
//height: 56px;
|
||||||
|
height: 100vh;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background-color: var(--border-color);
|
background-color: var(--border-color);
|
||||||
left: -1px;
|
left: 0px;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
@include respond-to(medium-screens) {
|
||||||
|
width: calc(var(--right-column-width) + 1px);
|
||||||
|
transform: translate3d(calc(var(--right-column-width) + 1px), 0, 0);
|
||||||
|
border-left: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @include respond-to(medium-screens) {
|
/* @include respond-to(medium-screens) {
|
||||||
@ -162,24 +170,6 @@
|
|||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-super {
|
|
||||||
.menu-horizontal-div-item {
|
|
||||||
height: 3.5rem;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: var(--line-height);
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-horizontal-div i {
|
|
||||||
bottom: calc(-.625rem - 7px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-super {
|
.search-super {
|
||||||
@ -235,7 +225,6 @@
|
|||||||
top: 0px;
|
top: 0px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background-color: var(--surface-color);
|
background-color: var(--surface-color);
|
||||||
height: 3.5rem;
|
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.menu-horizontal-scrollable {
|
.menu-horizontal-scrollable {
|
||||||
|
--scrollable-size: 3.5rem;
|
||||||
|
height: var(--scrollable-size);
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: " ";
|
content: " ";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -22,34 +25,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-horizontal-div {
|
.menu-horizontal-div {
|
||||||
|
--size: var(--scrollable-size, 3.5rem);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
flex-direction: row;
|
user-select: none;
|
||||||
|
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
display: inline-block;
|
height: var(--size);
|
||||||
padding: .75rem 1rem;
|
padding: 0 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
//flex: 0 0 auto;
|
font-size: 1rem;
|
||||||
//overflow: hidden;
|
|
||||||
user-select: none;
|
|
||||||
// font-size: 1rem;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-top-left-radius: 6px;
|
|
||||||
border-top-right-radius: 6px;
|
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: var(--line-height);
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
@include hover-background-effect();
|
@include hover-background-effect();
|
||||||
|
|
||||||
@ -81,7 +84,7 @@
|
|||||||
|
|
||||||
i {
|
i {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: calc(-.625rem - 3px);
|
bottom: calc(-.625rem - 7px);
|
||||||
left: 0;
|
left: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
|
@ -103,9 +103,9 @@
|
|||||||
|
|
||||||
@include respond-to(large-screens) {
|
@include respond-to(large-screens) {
|
||||||
border-top-width: 0;
|
border-top-width: 0;
|
||||||
|
border-right-width: 1px;
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
border-left-width: 1px;
|
border-left-width: 1px;
|
||||||
border-right-width: 1px;
|
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: var(--border-color);
|
border-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ const fs = require('fs');
|
|||||||
const allowedIPs = ['127.0.0.1'];
|
const allowedIPs = ['127.0.0.1'];
|
||||||
const devMode = process.env.NODE_ENV !== 'production';
|
const devMode = process.env.NODE_ENV !== 'production';
|
||||||
const useLocal = true;
|
const useLocal = true;
|
||||||
const useLocalNotLocal = false;
|
const useLocalNotLocal = true;
|
||||||
|
|
||||||
if(devMode) {
|
if(devMode) {
|
||||||
console.log('DEVMODE IS ON!');
|
console.log('DEVMODE IS ON!');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user