Browse Source

Chat and sidebar load onload & media viewer supports aspects & reply markup buttons (only single answer)

master
morethanwords 5 years ago
parent
commit
fee0aab1d7
  1. 9
      src/components/appSelectPeers.ts
  2. 10
      src/components/bubbleGroups.ts
  3. 2
      src/components/lazyLoadQueue.ts
  4. 23
      src/components/misc.ts
  5. 57
      src/components/scrollable_new.ts
  6. 95
      src/components/wrappers.ts
  7. 421
      src/lib/appManagers/AppInlineBotsManager.ts
  8. 52
      src/lib/appManagers/apiUpdatesManager.ts
  9. 2
      src/lib/appManagers/appChatsManager.ts
  10. 68
      src/lib/appManagers/appDialogsManager.ts
  11. 750
      src/lib/appManagers/appImManager.ts
  12. 173
      src/lib/appManagers/appMediaViewer.ts
  13. 389
      src/lib/appManagers/appMessagesManager.ts
  14. 29
      src/lib/appManagers/appPhotosManager.ts
  15. 231
      src/lib/appManagers/appSidebarRight.ts
  16. 10
      src/lib/appManagers/appStickersManager.ts
  17. 2
      src/lib/appManagers/appUsersManager.ts
  18. 7
      src/lib/polyfill.ts
  19. 7
      src/lib/storage.ts
  20. 5
      src/lib/utils.js
  21. 107
      src/scss/partials/_chatBubble.scss
  22. 2
      src/scss/partials/_fonts.scss
  23. 1
      src/scss/partials/_ico.scss
  24. 67
      src/scss/partials/_mediaViewer.scss
  25. 19
      src/scss/partials/_rightSIdebar.scss
  26. 4
      src/scss/partials/_selector.scss
  27. 31
      src/scss/style.scss
  28. 1
      webpack.common.js

9
src/components/appSelectPeers.ts

@ -5,7 +5,6 @@ import appDialogsManager from "../lib/appManagers/appDialogsManager"; @@ -5,7 +5,6 @@ import appDialogsManager from "../lib/appManagers/appDialogsManager";
import appChatsManager from "../lib/appManagers/appChatsManager";
import appUsersManager from "../lib/appManagers/appUsersManager";
import { appPeersManager } from "../lib/services";
import appProfileManager from "../lib/appManagers/appProfileManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager";
export class AppSelectPeers {
@ -116,16 +115,18 @@ export class AppSelectPeers { @@ -116,16 +115,18 @@ export class AppSelectPeers {
appMessagesManager.getConversations(this.offsetIndex, 50, 0).then(value => {
let dialogs = value.dialogs;
this.offsetIndex = dialogs[value.dialogs.length - 1].index || 0;
let newOffsetIndex = dialogs[value.dialogs.length - 1].index || 0;
if(dialogs[0].peerID != this.myID) {
dialogs.findAndSplice(d => d.peerID == this.myID);
dialogs = dialogs.filter(d => d.peerID != this.myID);
if(!this.offsetIndex) {
dialogs.unshift({
peerID: this.myID,
pFlags: {}
} as any);
}
this.offsetIndex = newOffsetIndex;
this.renderResults(dialogs.map(dialog => dialog.peerID));
});
}

10
src/components/bubbleGroups.ts

@ -12,6 +12,8 @@ export default class BubbleGroups { @@ -12,6 +12,8 @@ export default class BubbleGroups {
details.group.findAndSplice(d => d == bubble);
if(!details.group.length) {
this.groups.findAndSplice(g => g == details.group);
} else {
this.updateGroup(details.group);
}
}
}
@ -49,7 +51,7 @@ export default class BubbleGroups { @@ -49,7 +51,7 @@ export default class BubbleGroups {
//console.log('addBubble', bubble, message.mid, fromID, reverse, group);
this.bubblesByGroups[reverse ? 'unshift' : 'push']({timestamp, fromID, mid: message.mid, group});
this.updateGroup(group, reverse);
this.updateGroup(group);
}
setClipIfNeeded(bubble: HTMLDivElement, remove = false) {
@ -100,7 +102,7 @@ export default class BubbleGroups { @@ -100,7 +102,7 @@ export default class BubbleGroups {
}
}
updateGroup(group: HTMLDivElement[], reverse = false) {
updateGroup(group: HTMLDivElement[]) {
/* if(this.updateRAFs.has(group)) {
window.cancelAnimationFrame(this.updateRAFs.get(group));
this.updateRAFs.delete(group);
@ -115,8 +117,6 @@ export default class BubbleGroups { @@ -115,8 +117,6 @@ export default class BubbleGroups {
let first = group[0];
//appImManager.scrollPosition.prepareFor(reverse ? 'up' : 'down');
//console.log('updateGroup', group, first);
if(group.length == 1) {
@ -140,8 +140,6 @@ export default class BubbleGroups { @@ -140,8 +140,6 @@ export default class BubbleGroups {
last.classList.remove('is-group-first');
last.classList.add('is-group-last');
this.setClipIfNeeded(last);
//appImManager.scrollPosition.restore();
//}));
}

2
src/components/lazyLoadQueue.ts

@ -73,7 +73,7 @@ export default class LazyLoadQueue { @@ -73,7 +73,7 @@ export default class LazyLoadQueue {
this.debug && this.log('will load media', this.lockPromise, item);
try {
if(this.lockPromise) {
if(this.lockPromise && false) {
let perf = performance.now();
await this.lockPromise;

23
src/components/misc.ts

@ -3,6 +3,8 @@ import Config from "../lib/config"; @@ -3,6 +3,8 @@ import Config from "../lib/config";
let rippleClickID = 0;
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
if(elem.querySelector('.c-ripple')) return;
let r = document.createElement('div');
r.classList.add('c-ripple');
@ -99,6 +101,19 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -99,6 +101,19 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
});
}
const toastEl = document.createElement('div');
toastEl.classList.add('toast');
export function toast(html: string) {
toastEl.innerHTML = html;
document.body.append(toastEl);
if(toastEl.dataset.timeout) clearTimeout(+toastEl.dataset.timeout);
toastEl.dataset.timeout = '' + setTimeout(() => {
toastEl.remove();
delete toastEl.dataset.timeout;
}, 3000);
}
let loadedURLs: {[url: string]: boolean} = {};
let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceElement, url: string) => {
if(elem instanceof HTMLImageElement || elem instanceof HTMLSourceElement) elem.src = url;
@ -117,10 +132,12 @@ export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGIma @@ -117,10 +132,12 @@ export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGIma
} else {
let loader = new Image();
loader.src = url;
loader.onload = () => {
//let perf = performance.now();
loader.addEventListener('load', () => {
set(elem, url);
loadedURLs[url] = true;
};
//console.log('onload:', url, performance.now() - perf);
});
}
return false;
@ -336,7 +353,7 @@ let onMouseMove = (e: MouseEvent) => { @@ -336,7 +353,7 @@ let onMouseMove = (e: MouseEvent) => {
};
let onClick = (e: MouseEvent) => {
e.preventDefault();
//e.preventDefault();
closeBtnMenu();
};

57
src/components/scrollable_new.ts

@ -367,48 +367,39 @@ export default class Scrollable { @@ -367,48 +367,39 @@ export default class Scrollable {
return !!element.parentElement;
}
public scrollIntoView(element: HTMLElement, smooth = true, fromUp = false) {
public scrollIntoView(element: HTMLElement, smooth = true) {
if(element.parentElement && !this.scrollLocked) {
let scrollTop = this.scrollTop;
let isFirstUnread = element.classList.contains('is-first-unread');
let offsetTop = element.offsetTop;
let clientHeight = this.container.clientHeight;
if(!smooth && isFirstUnread) {
this.scrollTo(offsetTop, false);
return;
}
let clientHeight = this.container.clientHeight;
let height = element.scrollHeight;
let diff = (clientHeight - height) / 2;
offsetTop -= (clientHeight - height) / 2;
this.scrollTo(offsetTop, smooth);
}
}
/* if(scrollTop < offsetTop) {
offsetTop += diff;
} else { */
offsetTop -= diff;
//}
public scrollTo(top: number, smooth = true) {
if(this.scrollLocked) return;
if(element.dataset.timeout) {
clearTimeout(+element.dataset.timeout);
element.classList.remove('is-selected');
void element.offsetWidth; // reflow
}
element.classList.add('is-selected');
element.dataset.timeout = '' + setTimeout(() => {
element.classList.remove('is-selected');
delete element.dataset.timeout;
}, 2000);
let scrollTop = this.scrollTop;
if(scrollTop == Math.floor(top)) {
return;
}
if(scrollTop == Math.floor(offsetTop)) {
return;
}
if(this.scrollLocked) clearTimeout(this.scrollLocked);
this.scrollLocked = setTimeout(() => {
this.scrollLocked = 0;
this.onScroll();
}, 468);
if(this.scrollLocked) clearTimeout(this.scrollLocked);
this.scrollLocked = setTimeout(() => {
this.scrollLocked = 0;
this.onScroll();
}, 468);
if(fromUp) {
this.container.scrollTo({behavior: 'auto', top: 0});
}
this.container.scrollTo({behavior: smooth ? 'smooth' : 'auto', top: offsetTop});
//element.scrollIntoView({behavior: 'smooth', block: 'center'});
}
this.container.scrollTo({behavior: smooth ? 'smooth' : 'auto', top});
}
public removeElement(element: Element) {

95
src/components/wrappers.ts

@ -15,7 +15,6 @@ import { CancellablePromise } from '../lib/polyfill'; @@ -15,7 +15,6 @@ import { CancellablePromise } from '../lib/polyfill';
import { renderImageFromUrl } from './misc';
import appMessagesManager from '../lib/appManagers/appMessagesManager';
import { Layouter, RectPart } from './groupedLayout';
import { Poll, PollResults } from '../lib/appManagers/appPollsManager';
import PollElement from './poll';
export type MTDocument = {
@ -135,47 +134,43 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -135,47 +134,43 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
}
}
let loadVideo = () => {
let promise = appDocsManager.downloadDoc(doc);
let loadVideo = async() => {
if(message.media.preloader) { // means upload
message.media.preloader.attach(container);
} else if(!doc.downloaded) {
let preloader = new ProgressivePreloader(container, true);
let promise = appDocsManager.downloadDoc(doc);
preloader.attach(container, true, promise);
await promise;
}
return promise.then(blob => {
if(middleware && !middleware()) {
return;
}
//return;
//console.log('loaded doc:', doc, doc.url, blob, container);
renderImageFromUrl(source, doc.url);
source.type = doc.mime_type;
video.append(source);
if(middleware && !middleware()) {
return;
}
if(!withTail) {
if(img && container.contains(img)) {
container.removeChild(img);
}
console.log('loaded doc:', doc, doc.url, container);
renderImageFromUrl(source, doc.url);
source.type = doc.mime_type;
video.append(source);
container.append(video);
}
if(doc.type == 'gif') {
video.autoplay = true;
video.loop = true;
} else if(doc.type == 'round') {
//video.dataset.ckin = doc.type == 'round' ? 'circle' : 'default';
video.dataset.ckin = 'circle';
video.dataset.overlay = '1';
let player = new VideoPlayer(video/* , doc.type != 'round' */);
if(!withTail) {
if(img && container.contains(img)) {
container.removeChild(img);
}
});
container.append(video);
}
if(doc.type == 'gif') {
video.autoplay = true;
video.loop = true;
} else if(doc.type == 'round') {
//video.dataset.ckin = doc.type == 'round' ? 'circle' : 'default';
video.dataset.ckin = 'circle';
video.dataset.overlay = '1';
let player = new VideoPlayer(video/* , doc.type != 'round' */);
}
};
if(doc.size >= 20e6 && !doc.downloaded) {
@ -826,11 +821,11 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -826,11 +821,11 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
if(!downloaded && (!div.firstElementChild || div.firstElementChild.tagName != 'IMG')) {
img.style.opacity = '' + 0;
img.onload = () => {
img.addEventListener('load', () => {
window.requestAnimationFrame(() => {
img.style.opacity = '';
});
};
});
}
if(!doc.url) {
@ -889,8 +884,8 @@ export function wrapReply(title: string, subtitle: string, message?: any) { @@ -889,8 +884,8 @@ export function wrapReply(title: string, subtitle: string, message?: any) {
}
appPhotosManager.preloadPhoto(photo, appPhotosManager.choosePhotoSize(photo, 32, 32))
.then(blob => {
renderImageFromUrl(replyMedia, photo._ == 'photo' ? photo.url : URL.createObjectURL(blob));
.then(() => {
renderImageFromUrl(replyMedia, photo._ == 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url);
});
replyContent.append(replyMedia);
@ -1004,34 +999,6 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo @@ -1004,34 +999,6 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo
});
}
/* let load = () => appPhotosManager.preloadPhoto(media._ == 'photo' ? media.id : media, size)
.then((blob) => {
if(middleware && !middleware()) {
console.warn('peer changed');
return;
}
if(!uploading) {
preloader.detach();
}
if(media && media.url) {
renderImageFromUrl(div, media.url);
} else {
let url = URL.createObjectURL(blob);
let img = new Image();
img.src = url;
img.onload = () => {
div.style.backgroundImage = 'url(' + url + ')';
};
}
//div.style.backgroundImage = 'url(' + url + ')';
});
load(); */
// @ts-ignore
//div.style.backgroundColor = '#' + Math.floor(Math.random() * (2 ** 24 - 1)).toString(16).padStart(6, '0');

421
src/lib/appManagers/AppInlineBotsManager.ts

@ -0,0 +1,421 @@ @@ -0,0 +1,421 @@
import appMessagesManager from "./appMessagesManager";
import apiManagerProxy from "../mtproto/mtprotoworker";
import { appPeersManager } from "../services";
import appMessagesIDsManager from "./appMessagesIDsManager";
import { toast } from "../../components/misc";
import { RichTextProcessor } from "../richtextprocessor";
export class AppInlineBotsManager {
/* private inlineResults: any = {};
function getPopularBots () {
return Storage.get('inline_bots_popular').then(function (bots) {
var result = []
var i, len
var userID
if (bots && bots.length) {
var now = tsNow(true)
for (i = 0, len = bots.length; i < len; i++) {
if ((now - bots[i][3]) > 14 * 86400) {
continue
}
userID = bots[i][0]
if (!AppUsersManager.hasUser(userID)) {
AppUsersManager.saveApiUser(bots[i][1])
}
result.push({id: userID, rate: bots[i][2], date: bots[i][3]})
}
}
return result
})
}
function pushPopularBot (id) {
getPopularBots().then(function (bots) {
var exists = false
var count = bots.length
var result = []
for (var i = 0; i < count; i++) {
if (bots[i].id == id) {
exists = true
bots[i].rate++
bots[i].date = tsNow(true)
}
var user = AppUsersManager.getUser(bots[i].id)
result.push([bots[i].id, user, bots[i].rate, bots[i].date])
}
if (exists) {
result.sort(function (a, b) {
return b[2] - a[2]
})
} else {
if (result.length > 15) {
result = result.slice(0, 15)
}
result.push([id, AppUsersManager.getUser(id), 1, tsNow(true)])
}
ConfigStorage.set({inline_bots_popular: result})
$rootScope.$broadcast('inline_bots_popular')
})
}
function resolveInlineMention (username) {
return AppPeersManager.resolveUsername(username).then(function (peerID) {
if (peerID > 0) {
var bot = AppUsersManager.getUser(peerID)
if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) {
var resolvedBot = {
username: username,
id: peerID,
placeholder: bot.bot_inline_placeholder
}
if (bot.pFlags.bot_inline_geo &&
GeoLocationManager.isAvailable()) {
return checkGeoLocationAccess(peerID).then(function () {
return GeoLocationManager.getPosition().then(function (coords) {
resolvedBot.geo = coords
return qSync.when(resolvedBot)
})
})['catch'](function () {
return qSync.when(resolvedBot)
})
}
return qSync.when(resolvedBot)
}
}
return $q.reject()
}, function (error) {
error.handled = true
return $q.reject(error)
})
}
function getInlineResults (peerID, botID, query, geo, offset) {
return MtpApiManager.invokeApi('messages.getInlineBotResults', {
flags: 0 | (geo ? 1 : 0),
bot: AppUsersManager.getUserInput(botID),
peer: AppPeersManager.getInputPeerByID(peerID),
query: query,
geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']},
offset: offset
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function (botResults) {
var queryID = botResults.query_id
delete botResults._
delete botResults.flags
delete botResults.query_id
if (botResults.switch_pm) {
botResults.switch_pm.rText = RichTextProcessor.wrapRichText(botResults.switch_pm.text, {noLinebreaks: true, noLinks: true})
}
angular.forEach(botResults.results, function (result) {
var qID = queryID + '_' + result.id
result.qID = qID
result.botID = botID
result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true})
result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true})
result.initials = (result.url || result.title || result.type || '').substr(0, 1)
if (result.document) {
AppDocsManager.saveDoc(result.document)
}
if (result.photo) {
AppPhotosManager.savePhoto(result.photo)
}
inlineResults[qID] = result
})
return botResults
})
}
function regroupWrappedResults (results, rowW, rowH) {
if (!results ||
!results[0] ||
['photo', 'gif', 'sticker'].indexOf(results[0].type) == -1) {
return
}
var ratios = []
angular.forEach(results, function (result) {
var w
var h, doc
var photo
if (result._ == 'botInlineMediaResult') {
if (doc = result.document) {
w = result.document.w
h = result.document.h
}
else if (photo = result.photo) {
var photoSize = (photo.sizes || [])[0]
w = photoSize && photoSize.w
h = photoSize && photoSize.h
}
}else {
w = result.w
h = result.h
}
if (!w || !h) {
w = h = 1
}
ratios.push(w / h)
})
var rows = []
var curCnt = 0
var curW = 0
angular.forEach(ratios, function (ratio) {
var w = ratio * rowH
curW += w
if (!curCnt || curCnt < 4 && curW < (rowW * 1.1)) {
curCnt++
} else {
rows.push(curCnt)
curCnt = 1
curW = w
}
})
if (curCnt) {
rows.push(curCnt)
}
var i = 0
var thumbs = []
var lastRowI = rows.length - 1
angular.forEach(rows, function (rowCnt, rowI) {
var lastRow = rowI == lastRowI
var curRatios = ratios.slice(i, i + rowCnt)
var sumRatios = 0
angular.forEach(curRatios, function (ratio) {
sumRatios += ratio
})
angular.forEach(curRatios, function (ratio, j) {
var thumbH = rowH
var thumbW = rowW * ratio / sumRatios
var realW = thumbH * ratio
if (lastRow && thumbW > realW) {
thumbW = realW
}
var result = results[i + j]
result.thumbW = Math.floor(thumbW) - 2
result.thumbH = Math.floor(thumbH) - 2
})
i += rowCnt
})
}
function switchToPM (fromPeerID, botID, startParam) {
var peerString = AppPeersManager.getPeerString(fromPeerID)
var setHash = {}
setHash['inline_switch_pm' + botID] = {peer: peerString, time: tsNow()}
Storage.set(setHash)
$rootScope.$broadcast('history_focus', {peerString: AppPeersManager.getPeerString(botID)})
AppMessagesManager.startBot(botID, 0, startParam)
}
function checkSwitchReturn (botID) {
var bot = AppUsersManager.getUser(botID)
if (!bot || !bot.pFlags.bot || !bot.bot_inline_placeholder) {
return qSync.when(false)
}
var key = 'inline_switch_pm' + botID
return Storage.get(key).then(function (peerData) {
if (peerData) {
Storage.remove(key)
if (tsNow() - peerData.time < 3600000) {
return peerData.peer
}
}
return false
})
}
function switchInlineQuery (botID, toPeerString, query) {
$rootScope.$broadcast('history_focus', {
peerString: toPeerString,
attachment: {
_: 'inline_query',
mention: '@' + AppUsersManager.getUser(botID).username,
query: query
}
})
}
function switchInlineButtonClick (id, button) {
var message = AppMessagesManager.getMessage(id)
var botID = message.viaBotID || message.fromID
if (button.pFlags && button.pFlags.same_peer) {
var peerID = AppMessagesManager.getMessagePeer(message)
var toPeerString = AppPeersManager.getPeerString(peerID)
switchInlineQuery(botID, toPeerString, button.query)
return
}
return checkSwitchReturn(botID).then(function (retPeerString) {
if (retPeerString) {
return switchInlineQuery(botID, retPeerString, button.query)
}
PeersSelectService.selectPeer({
canSend: true
}).then(function (toPeerString) {
return switchInlineQuery(botID, toPeerString, button.query)
})
})
} */
public callbackButtonClick(mid: number, button: any) {
let message = appMessagesManager.getMessage(mid);
let peerID = appMessagesManager.getMessagePeer(message);
return apiManagerProxy.invokeApi('messages.getBotCallbackAnswer', {
flags: 1,
peer: appPeersManager.getInputPeerByID(peerID),
msg_id: appMessagesIDsManager.getMessageLocalID(mid),
data: button.data
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then((callbackAnswer: any) => {
if(typeof callbackAnswer.message === 'string' && callbackAnswer.message.length) {
toast(RichTextProcessor.wrapRichText(callbackAnswer.message, {noLinks: true, noLinebreaks: true}));
}
console.log('callbackButtonClick callbackAnswer:', callbackAnswer);
});
}
/* function gameButtonClick (id) {
var message = AppMessagesManager.getMessage(id)
var peerID = AppMessagesManager.getMessagePeer(message)
return MtpApiManager.invokeApi('messages.getBotCallbackAnswer', {
flags: 2,
peer: AppPeersManager.getInputPeerByID(peerID),
msg_id: AppMessagesIDsManager.getMessageLocalID(id)
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function (callbackAnswer) {
if (typeof callbackAnswer.message === 'string' &&
callbackAnswer.message.length) {
showCallbackMessage(callbackAnswer.message, callbackAnswer.pFlags.alert)
}
else if (typeof callbackAnswer.url === 'string') {
AppGamesManager.openGame(message.media.game.id, id, callbackAnswer.url)
}
})
}
function sendInlineResult (peerID, qID, options) {
var inlineResult = inlineResults[qID]
if (inlineResult === undefined) {
return false
}
pushPopularBot(inlineResult.botID)
var splitted = qID.split('_')
var queryID = splitted.shift()
var resultID = splitted.join('_')
options = options || {}
options.viaBotID = inlineResult.botID
options.queryID = queryID
options.resultID = resultID
if (inlineResult.send_message.reply_markup) {
options.reply_markup = inlineResult.send_message.reply_markup
}
if (inlineResult.send_message._ == 'botInlineMessageText') {
options.entities = inlineResult.send_message.entities
AppMessagesManager.sendText(peerID, inlineResult.send_message.message, options)
} else {
var caption = ''
var inputMedia = false
switch (inlineResult.send_message._) {
case 'botInlineMessageMediaAuto':
caption = inlineResult.send_message.caption
if (inlineResult._ == 'botInlineMediaResult') {
var doc = inlineResult.document
var photo = inlineResult.photo
if (doc) {
inputMedia = {
_: 'inputMediaDocument',
id: {_: 'inputDocument', id: doc.id, access_hash: doc.access_hash},
caption: caption
}
} else {
inputMedia = {
_: 'inputMediaPhoto',
id: {_: 'inputPhoto', id: photo.id, access_hash: photo.access_hash},
caption: caption
}
}
}
break
case 'botInlineMessageMediaGeo':
inputMedia = {
_: 'inputMediaGeoPoint',
geo_point: {
_: 'inputGeoPoint',
'lat': inlineResult.send_message.geo['lat'],
'long': inlineResult.send_message.geo['long']
}
}
break
case 'botInlineMessageMediaVenue':
inputMedia = {
_: 'inputMediaVenue',
geo_point: {
_: 'inputGeoPoint',
'lat': inlineResult.send_message.geo['lat'],
'long': inlineResult.send_message.geo['long']
},
title: inlineResult.send_message.title,
address: inlineResult.send_message.address,
provider: inlineResult.send_message.provider,
venue_id: inlineResult.send_message.venue_id
}
break
case 'botInlineMessageMediaContact':
inputMedia = {
_: 'inputMediaContact',
phone_number: inlineResult.send_message.phone_number,
first_name: inlineResult.send_message.first_name,
last_name: inlineResult.send_message.last_name
}
break
}
if (!inputMedia) {
inputMedia = {
_: 'messageMediaPending',
type: inlineResult.type,
file_name: inlineResult.title || inlineResult.content_url || inlineResult.url,
size: 0,
progress: {percent: 30, total: 0}
}
}
AppMessagesManager.sendOther(peerID, inputMedia, options)
}
}
function checkGeoLocationAccess (botID) {
var key = 'bot_access_geo' + botID
return Storage.get(key).then(function (geoAccess) {
if (geoAccess && geoAccess.granted) {
return true
}
return ErrorService.confirm({
type: 'BOT_ACCESS_GEO_INLINE'
}).then(function () {
var setHash = {}
setHash[key] = {granted: true, time: tsNow()}
Storage.set(setHash)
return true
}, function () {
var setHash = {}
setHash[key] = {denied: true, time: tsNow()}
Storage.set(setHash)
return $q.reject()
})
})
} */
}
const appInlineBotsManager = new AppInlineBotsManager();
export default appInlineBotsManager;

52
src/lib/appManagers/apiUpdatesManager.ts

@ -13,9 +13,9 @@ export class ApiUpdatesManager { @@ -13,9 +13,9 @@ export class ApiUpdatesManager {
syncPending: any,
syncLoading: any,
seq?: any,
pts?: any,
date?: any
seq?: number,
pts?: number,
date?: number
} = {
pendingPtsUpdates: [],
pendingSeqUpdates: {},
@ -24,14 +24,7 @@ export class ApiUpdatesManager { @@ -24,14 +24,7 @@ export class ApiUpdatesManager {
};
public channelStates: any = {};
public myID = 0;
private attached = false;
constructor() {
apiManager.getUserID().then((id) => {
this.myID = id;
});
}
public popPendingSeqUpdate() {
var nextSeq = this.updatesState.seq + 1;
@ -141,10 +134,10 @@ export class ApiUpdatesManager { @@ -141,10 +134,10 @@ export class ApiUpdatesManager {
case 'updateShortMessage':
case 'updateShortChatMessage':
var isOut = updateMessage.flags & 2;
var fromID = updateMessage.from_id || (isOut ? this.myID : updateMessage.user_id);
var fromID = updateMessage.from_id || (isOut ? $rootScope.myID : updateMessage.user_id);
var toID = updateMessage.chat_id
? -updateMessage.chat_id
: (isOut ? updateMessage.user_id : this.myID);
: (isOut ? updateMessage.user_id : $rootScope.myID);
this.processUpdate({
_: 'updateNewMessage',
@ -500,24 +493,31 @@ export class ApiUpdatesManager { @@ -500,24 +493,31 @@ export class ApiUpdatesManager {
$rootScope.$broadcast('apiUpdate', update);
}
public attach() {
public attach(state: Pick<ApiUpdatesManager['updatesState'], 'seq' | 'pts' | 'date'>) {
if(this.attached) return;
this.attached = true;
apiManager.setUpdatesProcessor(this.processUpdateMessage.bind(this));
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => {
this.updatesState.seq = stateResult.seq;
this.updatesState.pts = stateResult.pts;
this.updatesState.date = stateResult.date;
setTimeout(() => {
this.updatesState.syncLoading = false;
}, 1000);
// updatesState.seq = 1
// updatesState.pts = stateResult.pts - 5000
// updatesState.date = 1
// getDifference()
});
if(!state) {
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => {
this.updatesState.seq = stateResult.seq;
this.updatesState.pts = stateResult.pts;
this.updatesState.date = stateResult.date;
setTimeout(() => {
this.updatesState.syncLoading = false;
}, 1000);
// updatesState.seq = 1
// updatesState.pts = stateResult.pts - 5000
// updatesState.date = 1
// getDifference()
});
} else {
Object.assign(this.updatesState, state);
this.updatesState.syncLoading = false;
this.getDifference();
}
}
}

2
src/lib/appManagers/appChatsManager.ts

@ -72,7 +72,7 @@ export class AppChatsManager { @@ -72,7 +72,7 @@ export class AppChatsManager {
let titleWords = SearchIndexManager.cleanSearchText(apiChat.title || '', false).split(' ');
let firstWord = titleWords.shift();
let lastWord = titleWords.pop();
apiChat.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : firstWord.charAt(1));
apiChat.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : '');
if(apiChat.pFlags === undefined) {
apiChat.pFlags = {};

68
src/lib/appManagers/appDialogsManager.ts

@ -384,8 +384,6 @@ export class AppDialogsManager { @@ -384,8 +384,6 @@ export class AppDialogsManager {
public chatsArchivedContainer = document.getElementById('chats-archived-container') as HTMLDivElement;
public chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
private chatsArchivedOffsetIndex = 0;
private chatsOffsetIndex = 0;
private chatsPreloader: HTMLDivElement;
//private chatsLoadCount = 0;
//private loadDialogsPromise: Promise<any>;
@ -544,10 +542,30 @@ export class AppDialogsManager { @@ -544,10 +542,30 @@ export class AppDialogsManager {
}
});
this.loadDialogs().then(result => {
this.setPinnedDelimiter();
//appSidebarLeft.onChatsScroll();
this.loadDialogs(true);
$rootScope.$on('peer_changed', (e: CustomEvent) => {
let peerID = e.detail;
let lastPeerID = this.lastActiveListElement && +this.lastActiveListElement.getAttribute('data-peerID');
if(this.lastActiveListElement && lastPeerID != peerID) {
this.lastActiveListElement.classList.remove('active');
this.lastActiveListElement = null;
}
if(lastPeerID != peerID) {
let dom = this.getDialogDom(peerID);
if(dom) {
this.lastActiveListElement = dom.listEl;
dom.listEl.classList.add('active');
}
}
});
appMessagesManager.loaded.then(() => {
this.loadDialogs().then(result => {
this.setPinnedDelimiter();
//appSidebarLeft.onChatsScroll();
this.loadDialogs(true);
});
});
}
@ -559,30 +577,30 @@ export class AppDialogsManager { @@ -559,30 +577,30 @@ export class AppDialogsManager {
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
(archived ? this.chatsArchivedContainer : this.chatsContainer).append(this.chatsPreloader);
//let offset = appMessagesManager.generateDialogIndex();/* appMessagesManager.dialogsNum */;
let offset = archived ? this.chatsArchivedOffsetIndex : this.chatsOffsetIndex;
//let offset = 0;
let scroll = archived ? this.scrollArchived : this.scroll;
let storage = appMessagesManager.dialogsStorage[+archived] || [];
let offsetIndex = 0;
for(let i = storage.length - 1; i >= 0; --i) {
let dialog = storage[i];
if(this.getDialogDom(dialog.peerID)) {
offsetIndex = dialog.index;
break;
}
}
//let offset = storage[storage.length - 1]?.index || 0;
try {
console.time('getDialogs time');
let loadCount = 50/*this.chatsLoadCount */;
this.loadDialogsPromise = appMessagesManager.getConversations(offset, loadCount, +archived);
this.loadDialogsPromise = appMessagesManager.getConversations(offsetIndex, loadCount, +archived);
let result = await this.loadDialogsPromise;
console.timeEnd('getDialogs time');
if(result && result.dialogs && result.dialogs.length) {
let index = result.dialogs[result.dialogs.length - 1].index;
if(archived) this.chatsArchivedOffsetIndex = index;
else this.chatsOffsetIndex = index;
result.dialogs.forEach((dialog: any) => {
this.addDialog(dialog);
});
@ -598,7 +616,7 @@ export class AppDialogsManager { @@ -598,7 +616,7 @@ export class AppDialogsManager {
this.archivedCount.innerText = '' + count;
} */
this.log('getDialogs ' + loadCount + ' dialogs by offset:', offset, result, this.scroll.length);
this.log('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.scroll.length, archived);
this.scroll.onScroll();
} catch(err) {
this.log.error(err);
@ -650,14 +668,14 @@ export class AppDialogsManager { @@ -650,14 +668,14 @@ export class AppDialogsManager {
if(onFound) onFound();
let peerID = +elem.getAttribute('data-peerID');
let lastMsgID = +elem.dataset.mid;
let lastMsgID = +elem.dataset.mid || 0;
if(!samePeer) {
elem.classList.add('active');
this.lastActiveListElement = elem;
}
result = appImManager.setPeer(peerID, lastMsgID, true);
result = appImManager.setPeer(peerID, lastMsgID);
if(result instanceof Promise) {
this.lastGoodClickID = this.lastClickID;
@ -721,7 +739,7 @@ export class AppDialogsManager { @@ -721,7 +739,7 @@ export class AppDialogsManager {
public setPinnedDelimiter() {
let index = -1;
let dialogs = appMessagesManager.dialogsStorage.dialogs[0];
let dialogs = appMessagesManager.dialogsStorage[0];
for(let dialog of dialogs) {
if(dialog.pFlags?.pinned) {
index++;
@ -766,7 +784,7 @@ export class AppDialogsManager { @@ -766,7 +784,7 @@ export class AppDialogsManager {
if(lastMessage._ == 'messageEmpty') {
dom.lastMessageSpan.innerHTML = '';
dom.lastTimeSpan.innerHTML = '';
dom.listEl.removeAttribute('data-mid');
delete dom.listEl.dataset.mid;
return;
}
@ -853,10 +871,10 @@ export class AppDialogsManager { @@ -853,10 +871,10 @@ export class AppDialogsManager {
dom.lastTimeSpan.innerHTML = timeStr;
} else dom.lastTimeSpan.innerHTML = '';
dom.listEl.setAttribute('data-mid', lastMessage.mid);
if(this.doms[peerID] || this.domsArchived[peerID]) {
this.setUnreadMessages(dialog);
} else { // means search
dom.listEl.dataset.mid = lastMessage.mid;
}
}

750
src/lib/appManagers/appImManager.ts

File diff suppressed because it is too large Load Diff

173
src/lib/appManagers/appMediaViewer.ts

@ -171,7 +171,7 @@ export class AppMediaViewer { @@ -171,7 +171,7 @@ export class AppMediaViewer {
mover = this.setNewMover();
} */
///////this.log('setMoverToTarget', target, closing, wasActive, fromRight);
this.log('setMoverToTarget', target, closing, wasActive, fromRight);
let realParent: HTMLDivElement;
@ -200,13 +200,38 @@ export class AppMediaViewer { @@ -200,13 +200,38 @@ export class AppMediaViewer {
top = rect.top;
}
let aspecter: HTMLDivElement;
if(target instanceof HTMLImageElement || target instanceof HTMLVideoElement) {
if(mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter')) {
aspecter = mover.firstElementChild as HTMLDivElement;
let player = aspecter.querySelector('.ckin__player');
if(player) {
let video = player.firstElementChild as HTMLVideoElement;
aspecter.append(video);
player.remove();
}
if(!aspecter.style.cssText) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
mover.classList.remove('active');
this.setFullAspect(aspecter, containerRect, rect);
void mover.offsetLeft; // reflow
mover.classList.add('active');
}
} else {
aspecter = document.createElement('div');
aspecter.classList.add('media-viewer-aspecter');
mover.prepend(aspecter);
}
aspecter.style.cssText = `width: ${rect.width}px; height: ${rect.height}px; transform: scale(${containerRect.width / rect.width}, ${containerRect.height / rect.height});`;
}
transform += `translate(${left}px,${top}px) `;
mover.style.width = containerRect.width + 'px';
mover.style.height = containerRect.height + 'px';
mover.classList.remove('cover');
let scaleX = rect.width / containerRect.width;
let scaleY = rect.height / containerRect.height;
if(!wasActive) {
@ -234,19 +259,15 @@ export class AppMediaViewer { @@ -234,19 +259,15 @@ export class AppMediaViewer {
let video: HTMLVideoElement;
if(target.tagName == 'DIV') { // means backgrounded with cover
//img.style.objectFit = 'cover';
img = new Image();
img.src = target.style.backgroundImage.slice(5, -2);
//mover.classList.add('cover');
//mover.style.backgroundImage = target.style.backgroundImage;
} else if(target.tagName == 'IMG' || target.tagName == 'image') {
} else if(target instanceof HTMLImageElement) {
img = new Image();
img.src = target instanceof SVGImageElement ? target.getAttributeNS(null, 'href') : (target as HTMLImageElement).src;
img.style.objectFit = 'contain';
} else if(target.tagName == 'VIDEO') {
img.src = target.src;
} else if(target instanceof HTMLVideoElement) {
video = document.createElement('video');
let source = document.createElement('source');
source.src = target.querySelector('source').src;
source.src = target.querySelector('source')?.src;
video.append(source);
} else if(target instanceof SVGSVGElement) {
let clipID = target.dataset.clipID;
@ -298,12 +319,9 @@ export class AppMediaViewer { @@ -298,12 +319,9 @@ export class AppMediaViewer {
mover.prepend(newSvg);
}
if(img) {
img.style.borderRadius = borderRadius;
mover.prepend(img);
} else if(video) {
video.style.borderRadius = borderRadius;
mover.prepend(video);
if(aspecter) {
aspecter.style.borderRadius = borderRadius;
aspecter.append(img || video);
}
mover.style.display = '';
@ -330,15 +348,11 @@ export class AppMediaViewer { @@ -330,15 +348,11 @@ export class AppMediaViewer {
if(mover.firstElementChild) {
(mover.firstElementChild as HTMLElement).style.borderRadius = borderRadius;
}
if(target.tagName == 'DIV') {
mover.classList.add('cover');
}
}, delay / 2);
setTimeout(() => {
mover.innerHTML = '';
mover.classList.remove('moving', 'active', 'cover');
mover.classList.remove('moving', 'active');
mover.style.display = 'none';
}, delay);
}
@ -346,22 +360,60 @@ export class AppMediaViewer { @@ -346,22 +360,60 @@ export class AppMediaViewer {
return () => {
mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`;
if(aspecter) {
this.setFullAspect(aspecter, containerRect, rect);
}
setTimeout(() => {
mover.style.borderRadius = '';
if(mover.firstElementChild) {
(mover.firstElementChild as HTMLElement).style.borderRadius = '';
}
mover.classList.remove('cover');
}, delay / 2);
mover.dataset.timeout = '' + setTimeout(() => {
mover.classList.remove('moving');
if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
mover.classList.remove('active');
aspecter.style.cssText = '';
void mover.offsetLeft; // reflow
}
mover.classList.add('active');
delete mover.dataset.timeout;
}, delay);
if(path) {
this.sizeTailPath(path, containerRect, scaleX, delay, true, isOut, borderRadius);
}
};
}
private setFullAspect(aspecter: HTMLDivElement, containerRect: DOMRect, rect: DOMRect) {
let media = aspecter.firstElementChild;
let proportion: number;
if(media instanceof HTMLImageElement) {
proportion = media.naturalWidth / media.naturalHeight;
} else if(media instanceof HTMLVideoElement) {
proportion = media.videoWidth / media.videoHeight;
}
let {width, height} = rect;
if(proportion == 1) {
aspecter.style.cssText = '';
} else {
if(proportion > 0) {
width = height * proportion;
} else {
height = width * proportion;
}
aspecter.style.cssText = `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`;
}
}
public sizeTailPath(path: SVGPathElement, rect: DOMRect, scaleX: number, delay: number, upscale: boolean, isOut: boolean, borderRadius: string) {
let start = Date.now();
let {width, height} = rect;
@ -393,8 +445,13 @@ export class AppMediaViewer { @@ -393,8 +445,13 @@ export class AppMediaViewer {
public moveTheMover(mover: HTMLDivElement, toLeft = true) {
let windowW = appPhotosManager.windowW;
//mover.classList.remove('active');
mover.classList.add('moving');
if(mover.dataset.timeout) { // и это тоже всё из-за скейла видео, так бы это не нужно было
clearTimeout(+mover.dataset.timeout);
}
let rect = mover.getBoundingClientRect();
let newTransform = mover.style.transform.replace(/translate\((.+?),/, (match, p1) => {
@ -617,30 +674,35 @@ export class AppMediaViewer { @@ -617,30 +674,35 @@ export class AppMediaViewer {
if(useContainerAsTarget) target = target.querySelector('img, video') || target;
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
//return; // set and don't move
//if(wasActive) return;
//return;
setTimeout(() => {
afterTimeout();
//return;
let video = mover.querySelector('video') || document.createElement('video');
let source: HTMLSourceElement;
if(video.firstElementChild) {
source = video.firstElementChild as HTMLSourceElement;
}
let source = video.firstElementChild as HTMLSourceElement || document.createElement('source');
if(media.type == 'gif') {
video.autoplay = true;
}
video.dataset.ckin = 'default';
video.dataset.overlay = '1';
let createPlayer = () => {
if(media.type != 'gif') {
video.dataset.ckin = 'default';
video.dataset.overlay = '1';
let player = new VideoPlayer(video, true);
/* player.wrapper.parentElement.append(video);
mover.append(player.wrapper); */
}
};
if(!source || !source.src) {
let promise = appDocsManager.downloadDoc(media);
this.preloader.attach(mover, true, promise);
promise.then(blob => {
promise.then(() => {
if(this.currentMessageID != message.mid) {
this.log.warn('media viewer changed video');
return;
@ -651,36 +713,27 @@ export class AppMediaViewer { @@ -651,36 +713,27 @@ export class AppMediaViewer {
this.updateMediaSource(mover, url, 'source');
this.updateMediaSource(target, url, 'source');
} else {
let img = mover.firstElementChild;
if(img instanceof Image) {
mover.removeChild(img);
let aspecter = mover.firstElementChild;
let img = aspecter.firstElementChild;
if(img instanceof HTMLImageElement) {
img.remove();
}
source = document.createElement('source');
renderImageFromUrl(source, url);
source.type = media.mime_type;
mover.prepend(video);
video.append(source);
}
if(!source.parentElement) {
video.append(source);
}
if(media.type != 'gif') {
let player = new VideoPlayer(video, true);
if(!video.parentElement) {
aspecter.prepend(video);
}
}
});
} else if(media.type != 'gif') {
let player = new VideoPlayer(video, true);
}
/* wrapVideo.call(this, media, mover, message, false, this.preloader).then(() => {
if(this.currentMessageID != message.mid) {
this.log.warn('media viewer changed video');
return;
}
}); */
createPlayer();
});
} else createPlayer();
}, 0);
} else {
let size = appPhotosManager.setAttachmentSize(media.id, container, maxWidth, maxHeight);
@ -688,7 +741,7 @@ export class AppMediaViewer { @@ -688,7 +741,7 @@ export class AppMediaViewer {
if(useContainerAsTarget) target = target.querySelector('img, video') || target;
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
//return;
//return; // set and don't move
//if(wasActive) return;
setTimeout(() => {
afterTimeout();
@ -709,10 +762,14 @@ export class AppMediaViewer { @@ -709,10 +762,14 @@ export class AppMediaViewer {
this.updateMediaSource(target, url, 'image');
this.updateMediaSource(mover, url, 'image');
} else {
let image = mover.firstElementChild as HTMLImageElement || new Image();
//image.src = url;
let aspecter = mover.firstElementChild;
let image = aspecter.firstElementChild as HTMLImageElement;
if(!image) {
image = new Image();
aspecter.append(image);
}
renderImageFromUrl(image, url);
mover.prepend(image);
}
this.preloader.detach();

389
src/lib/appManagers/appMessagesManager.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { SearchIndexManager, $rootScope, copy, tsNow, safeReplaceObject, dT, _, listMergeSorted, deepEqual, langPack } from "../utils";
import { $rootScope, copy, tsNow, safeReplaceObject, dT, _, listMergeSorted, deepEqual, langPack } from "../utils";
import appMessagesIDsManager from "./appMessagesIDsManager";
import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager";
@ -9,7 +9,7 @@ import apiUpdatesManager from "./apiUpdatesManager"; @@ -9,7 +9,7 @@ import apiUpdatesManager from "./apiUpdatesManager";
import appPhotosManager from "./appPhotosManager";
import AppStorage from '../storage';
import AppPeersManager from "./appPeersManager";
import appPeersManager from "./appPeersManager";
import ServerTimeManager from "../mtproto/serverTimeManager";
import apiFileManager from "../mtproto/apiFileManager";
import appDocsManager from "./appDocsManager";
@ -64,20 +64,13 @@ export type Dialog = { @@ -64,20 +64,13 @@ export type Dialog = {
export class AppMessagesManager {
public messagesStorage: any = {};
public messagesForDialogs: any = {};
public groupedMessagesStorage: {[groupID: string]: any} = {}; // will be used for albums
public historiesStorage: {
[peerID: string]: HistoryStorage
} = {};
public dialogsStorage: {
count: number,
dialogs: {
[folderID: number]: Dialog[]
}
} = {
count: null,
dialogs: {}
};
[folderID: number]: Dialog[]
} = {};
public pendingByRandomID: {[randomID: string]: [number, number]} = {};
public pendingByMessageID: any = {};
public pendingAfterMsgs: any = {};
@ -86,9 +79,6 @@ export class AppMessagesManager { @@ -86,9 +79,6 @@ export class AppMessagesManager {
public tempID = -1;
public tempFinalizeCallbacks: any = {};
public dialogsIndex: any = SearchIndexManager.createIndex();
public cachedResults: any = {query: false};
public lastSearchFilter: any = {};
public lastSearchResults: any = [];
@ -96,10 +86,6 @@ export class AppMessagesManager { @@ -96,10 +86,6 @@ export class AppMessagesManager {
public fetchSingleMessagesTimeout = 0;
private fetchSingleMessagesPromise: Promise<void> = null;
public incrementedMessageViews: any = {};
public needIncrementMessageViews: any = [];
public incrementMessageViewsTimeout: any = false;
public maxSeenID = 0;
public allDialogsLoaded: {[folder_id: number]: boolean} = {};
@ -116,16 +102,9 @@ export class AppMessagesManager { @@ -116,16 +102,9 @@ export class AppMessagesManager {
public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {};
public newUpdatesAfterReloadToHandle: any = {};
public fwdMessagesPluralize = _('conversation_forwarded_X_messages');
public gameScorePluralize = _('conversation_scored_X');
public loaded: Promise<any> = null;
constructor() {
AppStorage.get<number>('max_seen_msg').then((maxID) => {
if(maxID && !appMessagesIDsManager.getMessageIDInfo(maxID)[1]) {
this.maxSeenID = maxID;
}
});
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update: any = e.detail;
// if(update._ != 'updateUserStatus') {
@ -159,7 +138,7 @@ export class AppMessagesManager { @@ -159,7 +138,7 @@ export class AppMessagesManager {
if(draft && draft.date) {
topDate = draft.date;
} else {
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0
var channelID = appPeersManager.isChannel(peerID) ? -peerID : 0
var topDate = this.getMessage(dialog.top_message).date;
if(channelID) {
@ -183,6 +162,94 @@ export class AppMessagesManager { @@ -183,6 +162,94 @@ export class AppMessagesManager {
});
}
});
this.loaded = new Promise((resolve, reject) => {
AppStorage.get<{
dialogs: Dialog[],
allDialogsLoaded: AppMessagesManager['allDialogsLoaded'],
peers: any[],
messages: any[],
updates: any,
maxSeenMsgID: number
}>('state').then(({dialogs, allDialogsLoaded, peers, messages, maxSeenMsgID, updates}) => {
console.log('state res', dialogs, messages);
if(maxSeenMsgID && !appMessagesIDsManager.getMessageIDInfo(maxSeenMsgID)[1]) {
this.maxSeenID = maxSeenMsgID;
}
//return resolve();
if(peers) {
for(let peerID in peers) {
let peer = peers[peerID];
if(+peerID < 0) appChatsManager.saveApiChat(peer);
else appUsersManager.saveApiUser(peer);
}
}
if(messages) {
this.saveMessages(messages);
}
if(allDialogsLoaded) {
this.allDialogsLoaded = allDialogsLoaded;
}
if(dialogs) {
dialogs.forEachReverse(dialog => {
this.saveConversation(dialog);
});
}
apiUpdatesManager.attach(updates ?? null);
resolve();
}).catch(resolve);
});
setInterval(() => this.saveState(), 10000);
}
public saveState() {
let messages: any[] = [];
let dialogs: Dialog[] = [];
let peers: {[peerID: number]: any} = {};
for(let folderID in this.dialogsStorage) {
for(let dialog of this.dialogsStorage[folderID]) {
let message = this.getMessage(dialog.top_message);
if(message._ != 'messageEmpty' && message.id > 0) {
messages.push(message);
if(message.fromID != dialog.peerID) {
peers[message.fromID] = appPeersManager.getPeer(message.fromID);
}
}
dialogs.push(dialog);
peers[dialog.peerID] = appPeersManager.getPeer(dialog.peerID);
}
}
let us = apiUpdatesManager.updatesState;
let updates = {
seq: us.seq,
pts: us.pts,
date: us.date
};
AppStorage.set({
state: {
dialogs,
messages,
allDialogsLoaded: this.allDialogsLoaded,
peers,
updates,
maxSeenMsgID: this.maxSeenID
}
});
}
public getInputEntities(entities: any) {
@ -240,7 +307,7 @@ export class AppMessagesManager { @@ -240,7 +307,7 @@ export class AppMessagesManager {
return apiManager.invokeApi('messages.editMessage', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
id: appMessagesIDsManager.getMessageLocalID(messageID),
message: text,
media: message.media,
@ -260,22 +327,22 @@ export class AppMessagesManager { @@ -260,22 +327,22 @@ export class AppMessagesManager {
});
}
public sendText(peerID: number, text: string, options: {
entities?: any[],
replyToMsgID?: number,
viaBotID?: number,
queryID?: number,
resultID?: number,
noWebPage?: boolean,
reply_markup?: any,
clearDraft?: boolean,
webPage?: any
} = {}) {
public sendText(peerID: number, text: string, options: Partial<{
entities: any[],
replyToMsgID: number,
viaBotID: number,
queryID: number,
resultID: number,
noWebPage: boolean,
reply_markup: any,
clearDraft: boolean,
webPage: any
}> = {}) {
if(typeof(text) != 'string') {
return;
}
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
var entities = options.entities || [];
if(!options.viaBotID) {
@ -293,8 +360,8 @@ export class AppMessagesManager { @@ -293,8 +360,8 @@ export class AppMessagesManager {
var flags = 0;
var pFlags: any = {};
var replyToMsgID = options.replyToMsgID;
var isChannel = AppPeersManager.isChannel(peerID);
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID);
var isChannel = appPeersManager.isChannel(peerID);
var isMegagroup = isChannel && appPeersManager.isMegagroup(peerID);
var asChannel = isChannel && !isMegagroup ? true : false;
var message: any;
let noWebPage = options.noWebPage || false;
@ -329,7 +396,7 @@ export class AppMessagesManager { @@ -329,7 +396,7 @@ export class AppMessagesManager {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
to_id: appPeersManager.getOutputPeer(peerID),
flags: flags,
pFlags: pFlags,
date: tsNow(true) + serverTimeManager.serverTimeOffset,
@ -387,7 +454,7 @@ export class AppMessagesManager { @@ -387,7 +454,7 @@ export class AppMessagesManager {
if(options.viaBotID) {
apiPromise = apiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
random_id: randomID,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
query_id: options.queryID,
@ -401,7 +468,7 @@ export class AppMessagesManager { @@ -401,7 +468,7 @@ export class AppMessagesManager {
apiPromise = apiManager.invokeApi('messages.sendMessage', {
flags: flags,
no_webpage: noWebPage,
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
@ -490,7 +557,7 @@ export class AppMessagesManager { @@ -490,7 +557,7 @@ export class AppMessagesManager {
duration: number,
background: boolean
}> = {}) {
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
var messageID = this.tempID--;
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString();
@ -498,8 +565,8 @@ export class AppMessagesManager { @@ -498,8 +565,8 @@ export class AppMessagesManager {
var flags = 0;
var pFlags: any = {};
var replyToMsgID = options.replyToMsgID;
var isChannel = AppPeersManager.isChannel(peerID);
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID);
var isChannel = appPeersManager.isChannel(peerID);
var isMegagroup = isChannel && appPeersManager.isMegagroup(peerID);
var asChannel = isChannel && !isMegagroup ? true : false;
var attachType: string, apiFileName: string;
@ -651,7 +718,7 @@ export class AppMessagesManager { @@ -651,7 +718,7 @@ export class AppMessagesManager {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
to_id: appPeersManager.getOutputPeer(peerID),
flags: flags,
pFlags: pFlags,
date: date,
@ -688,7 +755,7 @@ export class AppMessagesManager { @@ -688,7 +755,7 @@ export class AppMessagesManager {
flags: flags,
background: options.background,
clear_draft: true,
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
media: inputMedia,
message: caption,
random_id: randomID,
@ -821,14 +888,14 @@ export class AppMessagesManager { @@ -821,14 +888,14 @@ export class AppMessagesManager {
objectURL: string,
}>[]
}> = {}) {
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
let groupID: number;
let historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {count: null, history: [], pending: []});
let flags = 0;
let pFlags: any = {};
let replyToMsgID = options.replyToMsgID;
let isChannel = AppPeersManager.isChannel(peerID);
let isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID);
let isChannel = appPeersManager.isChannel(peerID);
let isMegagroup = isChannel && appPeersManager.isMegagroup(peerID);
let asChannel = isChannel && !isMegagroup ? true : false;
let caption = options.caption || '';
@ -949,7 +1016,7 @@ export class AppMessagesManager { @@ -949,7 +1016,7 @@ export class AppMessagesManager {
id: messageID,
from_id: fromID,
grouped_id: groupID,
to_id: AppPeersManager.getOutputPeer(peerID),
to_id: appPeersManager.getOutputPeer(peerID),
flags: flags,
pFlags: pFlags,
date: date,
@ -987,7 +1054,7 @@ export class AppMessagesManager { @@ -987,7 +1054,7 @@ export class AppMessagesManager {
let uploaded = false,
uploadPromise: ReturnType<typeof apiFileManager.uploadFile> = null;
let inputPeer = AppPeersManager.getInputPeerByID(peerID);
let inputPeer = appPeersManager.getInputPeerByID(peerID);
let invoke = (multiMedia: any[]) => {
appImManager.setTyping('sendMessageCancelAction');
@ -1129,9 +1196,7 @@ export class AppMessagesManager { @@ -1129,9 +1196,7 @@ export class AppMessagesManager {
}
public getConversations(offsetIndex?: number, limit = 20, folderID = 0) {
let curDialogStorage = this.dialogsStorage.dialogs[folderID] ?? (this.dialogsStorage.dialogs[folderID] = []);
this.cachedResults.query = false;
let curDialogStorage = this.dialogsStorage[folderID] ?? (this.dialogsStorage[folderID] = []);
let offset = 0;
if(offsetIndex > 0) {
@ -1150,7 +1215,7 @@ export class AppMessagesManager { @@ -1150,7 +1215,7 @@ export class AppMessagesManager {
}
return this.getTopMessages(limit, folderID).then(count => {
let curDialogStorage = this.dialogsStorage.dialogs[folderID];
let curDialogStorage = this.dialogsStorage[folderID];
offset = 0;
if(offsetIndex > 0) {
@ -1171,12 +1236,12 @@ export class AppMessagesManager { @@ -1171,12 +1236,12 @@ export class AppMessagesManager {
}
public getTopMessages(limit: number, folderID: number): Promise<number> {
var dialogs = this.dialogsStorage.dialogs[folderID];
var offsetDate = 0;
var offsetID = 0;
var offsetPeerID = 0;
var offsetIndex = 0;
var flags = 0;
const dialogs = this.dialogsStorage[folderID];
let offsetID = 0;
let offsetDate = 0;
let offsetPeerID = 0;
let offsetIndex = 0;
let flags = 0;
if(this.dialogsOffsetDate[folderID]) {
offsetDate = this.dialogsOffsetDate[folderID] + serverTimeManager.serverTimeOffset;
@ -1196,7 +1261,7 @@ export class AppMessagesManager { @@ -1196,7 +1261,7 @@ export class AppMessagesManager {
folder_id: folderID,
offset_date: offsetDate,
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
limit: limit,
hash: hash
}, {
@ -1230,7 +1295,7 @@ export class AppMessagesManager { @@ -1230,7 +1295,7 @@ export class AppMessagesManager {
}
if(!maxSeenIdIncremented &&
!AppPeersManager.isChannel(AppPeersManager.getPeerID(dialog.peer))) {
!appPeersManager.isChannel(appPeersManager.getPeerID(dialog.peer))) {
this.incrementMaxSeenID(dialog.top_message);
maxSeenIdIncremented = true;
}
@ -1267,7 +1332,7 @@ export class AppMessagesManager { @@ -1267,7 +1332,7 @@ export class AppMessagesManager {
public forwardMessages(peerID: number, mids: number[], options: Partial<{
withMyScore: boolean
}> = {}) {
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
mids = mids.sort((a, b) => a - b);
var flags = 0;
@ -1293,10 +1358,10 @@ export class AppMessagesManager { @@ -1293,10 +1358,10 @@ export class AppMessagesManager {
let promise = apiManager.invokeApi('messages.forwardMessages', {
flags: flags,
from_peer: AppPeersManager.getInputPeerByID(-channelID),
from_peer: appPeersManager.getInputPeerByID(-channelID),
id: msgIDs,
random_id: randomIDs,
to_peer: AppPeersManager.getInputPeerByID(peerID)
to_peer: appPeersManager.getInputPeerByID(peerID)
}, sentRequestOptions).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, () => {}).then(() => {
@ -1329,7 +1394,7 @@ export class AppMessagesManager { @@ -1329,7 +1394,7 @@ export class AppMessagesManager {
}
public generateIndexForDialog(dialog: Dialog) {
let channelID = AppPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
let channelID = appPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
let mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
let message = this.getMessage(mid);
@ -1354,15 +1419,15 @@ export class AppMessagesManager { @@ -1354,15 +1419,15 @@ export class AppMessagesManager {
}
public pushDialogToStorage(dialog: Dialog, offsetDate?: number) {
let dialogs = this.dialogsStorage.dialogs[dialog.folder_id] ?? (this.dialogsStorage.dialogs[dialog.folder_id] = []);
let dialogs = this.dialogsStorage[dialog.folder_id] ?? (this.dialogsStorage[dialog.folder_id] = []);
let pos = dialogs.findIndex(d => d.peerID == dialog.peerID);
if(pos !== -1) {
dialogs.splice(pos, 1);
}
if(offsetDate &&
!dialog.pFlags.pinned &&
(!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {
!dialog.pFlags.pinned &&
(!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {
if(pos !== -1) {
// So the dialog jumped to the last position
return false;
@ -1395,7 +1460,7 @@ export class AppMessagesManager { @@ -1395,7 +1460,7 @@ export class AppMessagesManager {
}
public getMessagePeer(message: any): number {
var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0;
var toID = message.to_id && appPeersManager.getPeerID(message.to_id) || 0;
if(toID < 0) {
return toID;
@ -1406,7 +1471,7 @@ export class AppMessagesManager { @@ -1406,7 +1471,7 @@ export class AppMessagesManager {
}
public getDialogByPeerID(peerID: number): [Dialog, number] | [] {
let dialogs = this.dialogsStorage.dialogs;
let dialogs = this.dialogsStorage;
for(let folderID in dialogs) {
let index = dialogs[folderID].findIndex(dialog => dialog.peerID == peerID);
if(index !== -1) {
@ -1418,7 +1483,7 @@ export class AppMessagesManager { @@ -1418,7 +1483,7 @@ export class AppMessagesManager {
}
public reloadConversation(peerID: number | number[]) {
let peers = [].concat(peerID).map(peerID => AppPeersManager.getInputPeerByID(peerID));
let peers = [].concat(peerID).map(peerID => appPeersManager.getInputPeerByID(peerID));
console.log('will reloadConversation', peerID);
@ -1456,7 +1521,7 @@ export class AppMessagesManager { @@ -1456,7 +1521,7 @@ export class AppMessagesManager {
}
public async flushHistory(peerID: number, justClear: boolean) {
if(AppPeersManager.isChannel(peerID)) {
if(appPeersManager.isChannel(peerID)) {
let promise = this.getHistory(peerID, 0, 1);
let historyResult = promise instanceof Promise ? await promise : promise;
@ -1480,7 +1545,7 @@ export class AppMessagesManager { @@ -1480,7 +1545,7 @@ export class AppMessagesManager {
});
}
return this.doFlushHistory(AppPeersManager.getInputPeerByID(peerID), justClear).then(() => {
return this.doFlushHistory(appPeersManager.getInputPeerByID(peerID), justClear).then(() => {
delete this.historiesStorage[peerID];
for(let mid in this.messagesStorage) {
let message = this.messagesStorage[mid];
@ -1494,7 +1559,7 @@ export class AppMessagesManager { @@ -1494,7 +1559,7 @@ export class AppMessagesManager {
} else {
let foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog[0]) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
this.dialogsStorage[foundDialog[0].folder_id].splice(foundDialog[1], 1);
}
$rootScope.$broadcast('dialog_drop', {peerID: peerID});
@ -1559,9 +1624,9 @@ export class AppMessagesManager { @@ -1559,9 +1624,9 @@ export class AppMessagesManager {
if(fwdHeader) {
if(peerID == appUsersManager.getSelf().id) {
if(fwdHeader.saved_from_peer && fwdHeader.saved_from_msg_id) {
var savedFromPeerID = AppPeersManager.getPeerID(fwdHeader.saved_from_peer);
var savedFromPeerID = appPeersManager.getPeerID(fwdHeader.saved_from_peer);
var savedFromMid = appMessagesIDsManager.getFullMessageID(fwdHeader.saved_from_msg_id,
AppPeersManager.isChannel(savedFromPeerID) ? -savedFromPeerID : 0);
appPeersManager.isChannel(savedFromPeerID) ? -savedFromPeerID : 0);
apiMessage.savedFrom = savedFromPeerID + '_' + savedFromMid;
}
@ -1834,7 +1899,7 @@ export class AppMessagesManager { @@ -1834,7 +1899,7 @@ export class AppMessagesManager {
folder_peers: peerIDs.map(peerID => {
return {
_: 'inputFolderPeer',
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
folder_id: folderID
};
})
@ -1850,7 +1915,7 @@ export class AppMessagesManager { @@ -1850,7 +1915,7 @@ export class AppMessagesManager {
let peer = {
_: 'inputDialogPeer',
peer: AppPeersManager.getInputPeerByID(peerID)
peer: appPeersManager.getInputPeerByID(peerID)
};
let flags = dialog.pFlags?.pinned ? 0 : 1;
@ -1874,7 +1939,7 @@ export class AppMessagesManager { @@ -1874,7 +1939,7 @@ export class AppMessagesManager {
let peer = {
_: 'inputDialogPeer',
peer: AppPeersManager.getInputPeerByID(peerID)
peer: appPeersManager.getInputPeerByID(peerID)
};
let flags = dialog.pFlags?.unread_mark ? 0 : 1;
@ -1906,7 +1971,7 @@ export class AppMessagesManager { @@ -1906,7 +1971,7 @@ export class AppMessagesManager {
setTimeout(() => {
var foundDialog = this.getDialogByPeerID(migrateFrom);
if(foundDialog.length) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
this.dialogsStorage[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom, dialog: foundDialog[0]});
}
@ -1969,7 +2034,7 @@ export class AppMessagesManager { @@ -1969,7 +2034,7 @@ export class AppMessagesManager {
var updatedDialogs: {[peerID: number]: Dialog} = {};
var hasUpdated = false;
dialogsResult.dialogs.forEach((dialog: any) => {
var peerID = AppPeersManager.getPeerID(dialog.peer);
var peerID = appPeersManager.getPeerID(dialog.peer);
var topMessage = dialog.top_message;
var topPendingMesage = this.pendingTopMsgs[peerID];
if(topPendingMesage) {
@ -1991,7 +2056,6 @@ export class AppMessagesManager { @@ -1991,7 +2056,6 @@ export class AppMessagesManager {
this.saveConversation(dialog);
if(wasDialogBefore) {
this.clearDialogCache(topMessage);
$rootScope.$broadcast('dialog_top', dialog);
} else {
updatedDialogs[peerID] = dialog;
@ -2000,7 +2064,7 @@ export class AppMessagesManager { @@ -2000,7 +2064,7 @@ export class AppMessagesManager {
} else {
var foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog.length) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
this.dialogsStorage[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: foundDialog[0]});
}
}
@ -2020,20 +2084,13 @@ export class AppMessagesManager { @@ -2020,20 +2084,13 @@ export class AppMessagesManager {
}
}
public clearDialogCache(msgID: number) {
delete this.messagesForDialogs[msgID];
}
public saveConversation(dialog: Dialog) {
var peerID = AppPeersManager.getPeerID(dialog.peer);
var peerID = appPeersManager.getPeerID(dialog.peer);
if(!peerID) {
return false;
}
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0;
var peerText = AppPeersManager.getPeerSearchText(peerID);
SearchIndexManager.indexObject(peerID, peerText, this.dialogsIndex);
var channelID = appPeersManager.isChannel(peerID) ? -peerID : 0;
//var isMegagroup = AppPeersManager.isMegagroup(channelID);
if(dialog.top_message) {
var mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
var message = this.getMessage(mid);
@ -2044,7 +2101,7 @@ export class AppMessagesManager { @@ -2044,7 +2101,7 @@ export class AppMessagesManager {
id: mid,
mid: mid,
from_id: appUsersManager.getSelf().id,
to_id: AppPeersManager.getOutputPeer(peerID),
to_id: appPeersManager.getOutputPeer(peerID),
deleted: true,
flags: 0,
pFlags: {unread: false, out: true},
@ -2053,12 +2110,11 @@ export class AppMessagesManager { @@ -2053,12 +2110,11 @@ export class AppMessagesManager {
}
this.saveMessages([message]);
}
var offsetDate = message.date;
if(!channelID && peerID < 0) {
var chat = appChatsManager.getChat(-peerID)
if(chat && chat.migrated_to && chat.pFlags.deactivated) {
var migratedToPeer = AppPeersManager.getPeerID(chat.migrated_to)
var migratedToPeer = appPeersManager.getPeerID(chat.migrated_to)
this.migratedFromTo[peerID] = migratedToPeer;
this.migratedToFrom[migratedToPeer] = peerID;
return;
@ -2073,7 +2129,7 @@ export class AppMessagesManager { @@ -2073,7 +2129,7 @@ export class AppMessagesManager {
dialog.peerID = peerID;
this.generateIndexForDialog(dialog);
this.pushDialogToStorage(dialog, offsetDate);
this.pushDialogToStorage(dialog, message.date);
// Because we saved message without dialog present
if(message.mid > 0) {
@ -2093,12 +2149,6 @@ export class AppMessagesManager { @@ -2093,12 +2149,6 @@ export class AppMessagesManager {
}
}
//NotificationsManager.savePeerSettings(peerID, dialog.notify_settings); // warning
/* if(dialog.pts || dialog.pFlags.pts) {
console.warn('dialog pts!', dialog, dialog.pts);
} */
if(channelID && dialog.pts) {
apiUpdatesManager.addChannelState(channelID, dialog.pts);
}
@ -2190,8 +2240,7 @@ export class AppMessagesManager { @@ -2190,8 +2240,7 @@ export class AppMessagesManager {
var foundMsgs: number[] = [];
var useSearchCache = !query;
var newSearchFilter = {peer: peerID, filter: inputFilter};
var sameSearchCache = useSearchCache
&& deepEqual(this.lastSearchFilter, newSearchFilter); //angular.equals(this.lastSearchFilter, newSearchFilter);
var sameSearchCache = useSearchCache && deepEqual(this.lastSearchFilter, newSearchFilter);
if(useSearchCache && !sameSearchCache) {
// console.warn(dT(), 'new search filter', lastSearchFilter, newSearchFilter)
@ -2313,7 +2362,7 @@ export class AppMessagesManager { @@ -2313,7 +2362,7 @@ export class AppMessagesManager {
if(peerID || !query) {
apiPromise = apiManager.invokeApi('messages.search', {
flags: 0,
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
q: query || '',
filter: inputFilter || {_: 'inputMessagesFilterEmpty'},
min_date: 0,
@ -2342,7 +2391,7 @@ export class AppMessagesManager { @@ -2342,7 +2391,7 @@ export class AppMessagesManager {
apiPromise = apiManager.invokeApi('messages.searchGlobal', {
q: query,
offset_rate: offsetRate,
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
limit: limit || 20
}, {
@ -2396,7 +2445,7 @@ export class AppMessagesManager { @@ -2396,7 +2445,7 @@ export class AppMessagesManager {
let pinnedIndex: number;
if(dialog) {
if(dialog.pinnedIndex) {
if(dialog.hasOwnProperty('pinnedIndex')) {
pinnedIndex = dialog.pinnedIndex;
} else {
dialog.pinnedIndex = pinnedIndex = this.pinnedIndex++;
@ -2405,6 +2454,10 @@ export class AppMessagesManager { @@ -2405,6 +2454,10 @@ export class AppMessagesManager {
pinnedIndex = this.pinnedIndex++;
}
if(pinnedIndex > this.pinnedIndex) {
this.pinnedIndex = pinnedIndex;
}
return 0x7fffff00 + (pinnedIndex & 0xff);
}
@ -2428,13 +2481,13 @@ export class AppMessagesManager { @@ -2428,13 +2481,13 @@ export class AppMessagesManager {
delete this.newDialogsToHandle[peerID];
} else {
this.pushDialogToStorage(dialog);
if(!AppPeersManager.isChannel(+peerID)) {
if(!appPeersManager.isChannel(+peerID)) {
newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0);
}
}
}
console.log('after order:', this.dialogsStorage.dialogs[0].map(d => d.peerID));
//console.log('after order:', this.dialogsStorage[0].map(d => d.peerID));
if(newMaxSeenID != 0) {
this.incrementMaxSeenID(newMaxSeenID);
@ -2452,7 +2505,7 @@ export class AppMessagesManager { @@ -2452,7 +2505,7 @@ export class AppMessagesManager {
public readHistory(peerID: number, maxID = 0, minID = 0): Promise<boolean> {
// console.trace('start read')
var isChannel = AppPeersManager.isChannel(peerID);
var isChannel = appPeersManager.isChannel(peerID);
var historyStorage = this.historiesStorage[peerID];
var foundDialog = this.getDialogByPeerID(peerID)[0];
@ -2489,7 +2542,7 @@ export class AppMessagesManager { @@ -2489,7 +2542,7 @@ export class AppMessagesManager {
});
} else {
apiPromise = apiManager.invokeApi('messages.readHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
max_id: maxID
}).then((affectedMessages: any) => {
apiUpdatesManager.processUpdateMessage({
@ -2539,9 +2592,6 @@ export class AppMessagesManager { @@ -2539,9 +2592,6 @@ export class AppMessagesManager {
if(message && !message.pFlags.out) {
message.pFlags.unread = false;
if(this.messagesForDialogs[messageID]) {
this.messagesForDialogs[messageID].pFlags.unread = false;
}
//NotificationsManager.cancel('msg' + messageID); // warning
}
@ -2549,7 +2599,7 @@ export class AppMessagesManager { @@ -2549,7 +2599,7 @@ export class AppMessagesManager {
}
}
// NotificationsManager.soundReset(AppPeersManager.getPeerString(peerID)) // warning
// NotificationsManager.soundReset(appPeersManager.getPeerString(peerID)) // warning
return historyStorage.readPromise;
}
@ -2602,7 +2652,7 @@ export class AppMessagesManager { @@ -2602,7 +2652,7 @@ export class AppMessagesManager {
if(pendingData) {
var peerID: number = pendingData[0];
var tempID = pendingData[1];
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0;
var channelID = appPeersManager.isChannel(peerID) ? -peerID : 0;
var mid = appMessagesIDsManager.getFullMessageID(update.id, channelID);
var message = this.messagesStorage[mid];
if(message) {
@ -2721,7 +2771,7 @@ export class AppMessagesManager { @@ -2721,7 +2771,7 @@ export class AppMessagesManager {
case 'updateDialogUnreadMark': {
console.log('updateDialogUnreadMark', update);
let peerID = AppPeersManager.getPeerID(update.peer.peer);
let peerID = appPeersManager.getPeerID(update.peer.peer);
let foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length) {
@ -2750,7 +2800,7 @@ export class AppMessagesManager { @@ -2750,7 +2800,7 @@ export class AppMessagesManager {
peers.forEach((folderPeer: any) => {
let {folder_id, peer} = folderPeer;
let peerID = AppPeersManager.getPeerID(peer);
let peerID = appPeersManager.getPeerID(peer);
let foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length) {
this.newDialogsToHandle[peerID] = {reload: true};
@ -2758,7 +2808,7 @@ export class AppMessagesManager { @@ -2758,7 +2808,7 @@ export class AppMessagesManager {
let dialog = foundDialog[0];
this.newDialogsToHandle[peerID] = dialog;
this.dialogsStorage.dialogs[dialog.folder_id].splice(foundDialog[1], 1);
this.dialogsStorage[dialog.folder_id].splice(foundDialog[1], 1);
dialog.folder_id = folder_id;
this.generateIndexForDialog(dialog);
@ -2770,7 +2820,7 @@ export class AppMessagesManager { @@ -2770,7 +2820,7 @@ export class AppMessagesManager {
case 'updateDialogPinned': {
console.log('updateDialogPinned', update);
let peerID = AppPeersManager.getPeerID(update.peer.peer);
let peerID = appPeersManager.getPeerID(update.peer.peer);
let foundDialog = this.getDialogByPeerID(peerID);
this.scheduleHandleNewDialogs();
@ -2806,7 +2856,7 @@ export class AppMessagesManager { @@ -2806,7 +2856,7 @@ export class AppMessagesManager {
newPinned[dialog.peerID] = true;
});
this.dialogsStorage.dialogs[0].forEach((dialog: any) => {
this.dialogsStorage[0].forEach((dialog: any) => {
let peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true};
@ -2817,13 +2867,13 @@ export class AppMessagesManager { @@ -2817,13 +2867,13 @@ export class AppMessagesManager {
break;
}
console.log('before order:', this.dialogsStorage.dialogs[0].map(d => d.peerID));
//console.log('before order:', this.dialogsStorage[0].map(d => d.peerID));
this.pinnedIndex = 0;
let willHandle = false;
update.order.reverse(); // index must be higher
update.order.forEach((peer: any) => {
let peerID = AppPeersManager.getPeerID(peer.peer);
let peerID = appPeersManager.getPeerID(peer.peer);
newPinned[peerID] = true;
let foundDialog = this.getDialogByPeerID(peerID);
@ -2842,7 +2892,7 @@ export class AppMessagesManager { @@ -2842,7 +2892,7 @@ export class AppMessagesManager {
willHandle = true;
});
this.dialogsStorage.dialogs[0].forEach(dialog => {
this.dialogsStorage[0].forEach(dialog => {
let peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true};
@ -2901,7 +2951,7 @@ export class AppMessagesManager { @@ -2901,7 +2951,7 @@ export class AppMessagesManager {
var isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox';
var channelID: number = update.channel_id;
var maxID = appMessagesIDsManager.getFullMessageID(update.max_id, channelID);
var peerID = channelID ? -channelID : AppPeersManager.getPeerID(update.peer);
var peerID = channelID ? -channelID : appPeersManager.getPeerID(update.peer);
var foundDialog = this.getDialogByPeerID(peerID);
var history = (this.historiesStorage[peerID] || {}).history || [];
var newUnreadCount = 0;
@ -2935,9 +2985,7 @@ export class AppMessagesManager { @@ -2935,9 +2985,7 @@ export class AppMessagesManager {
if(!foundAffected) {
foundAffected = true;
}
if(this.messagesForDialogs[messageID]) {
this.messagesForDialogs[messageID].pFlags.unread = false;
}
if(!message.pFlags.out) {
if(foundDialog[0]) {
newUnreadCount = --foundDialog[0].unread_count;
@ -3007,33 +3055,22 @@ export class AppMessagesManager { @@ -3007,33 +3055,22 @@ export class AppMessagesManager {
case 'updateDeleteMessages':
case 'updateDeleteChannelMessages': {
var historiesUpdated: any = {};
var channelID: number = update.channel_id;
var messageID: number;
var message, i;
var peerID: number, foundDialog: ReturnType<AppMessagesManager['getDialogByPeerID']>;
let history: any;
var peerMessagesToHandle;
var peerMessagesHandlePos;
for (i = 0; i < update.messages.length; i++) {
messageID = appMessagesIDsManager.getFullMessageID(update.messages[i], channelID);
message = this.messagesStorage[messageID];
let historiesUpdated: {[peerID: number]: {count: number, unread: number, msgs: {[mid: number]: true}}} = {};
let channelID: number = update.channel_id;
for(let i = 0; i < update.messages.length; i++) {
let messageID = appMessagesIDsManager.getFullMessageID(update.messages[i], channelID);
let message = this.messagesStorage[messageID];
if(message) {
peerID = this.getMessagePeer(message);
history = historiesUpdated[peerID] || (historiesUpdated[peerID] = {count: 0, unread: 0, msgs: {}});
let peerID = this.getMessagePeer(message);
let history = historiesUpdated[peerID] || (historiesUpdated[peerID] = {count: 0, unread: 0, msgs: {}});
if(!message.pFlags.out && message.pFlags.unread) {
history.unread++;
// NotificationsManager.cancel('msg' + messageID); // warning
}
history.count++;
history.msgs[messageID] = true;
if(this.messagesForDialogs[messageID]) {
this.messagesForDialogs[messageID].deleted = true;
delete this.messagesForDialogs[messageID];
}
message.deleted = true
this.messagesStorage[messageID] = {
deleted: true,
@ -3045,9 +3082,9 @@ export class AppMessagesManager { @@ -3045,9 +3082,9 @@ export class AppMessagesManager {
date: message.date
};
peerMessagesToHandle = this.newMessagesToHandle[peerID];
let peerMessagesToHandle = this.newMessagesToHandle[peerID];
if(peerMessagesToHandle && peerMessagesToHandle.length) {
peerMessagesHandlePos = peerMessagesToHandle.indexOf(messageID);
let peerMessagesHandlePos = peerMessagesToHandle.indexOf(messageID);
if(peerMessagesHandlePos != -1) {
peerMessagesToHandle.splice(peerMessagesHandlePos);
}
@ -3055,13 +3092,13 @@ export class AppMessagesManager { @@ -3055,13 +3092,13 @@ export class AppMessagesManager {
}
}
Object.keys(historiesUpdated).forEach((peerID: string) => {
let updatedData = historiesUpdated[peerID];
var historyStorage = this.historiesStorage[peerID];
Object.keys(historiesUpdated).forEach(peerID => {
let updatedData = historiesUpdated[+peerID];
let historyStorage = this.historiesStorage[peerID];
if(historyStorage !== undefined) {
var newHistory = []
var newPending = []
for(var i = 0; i < historyStorage.history.length; i++) {
let newHistory: number[] = [];
let newPending: number[] = [];
for(let i = 0; i < historyStorage.history.length; i++) {
if(!updatedData.msgs[historyStorage.history[i]]) {
newHistory.push(historyStorage.history[i]);
}
@ -3070,13 +3107,13 @@ export class AppMessagesManager { @@ -3070,13 +3107,13 @@ export class AppMessagesManager {
if(updatedData.count &&
historyStorage.count !== null &&
historyStorage.count > 0) {
historyStorage.count -= updatedData.count
historyStorage.count -= updatedData.count;
if(historyStorage.count < 0) {
historyStorage.count = 0;
}
}
for(var i = 0; i < historyStorage.pending.length; i++) {
for(let i = 0; i < historyStorage.pending.length; i++) {
if(!updatedData.msgs[historyStorage.pending[i]]) {
newPending.push(historyStorage.pending[i]);
}
@ -3086,7 +3123,7 @@ export class AppMessagesManager { @@ -3086,7 +3123,7 @@ export class AppMessagesManager {
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: updatedData.msgs});
}
var foundDialog = this.getDialogByPeerID(+peerID)[0];
let foundDialog = this.getDialogByPeerID(+peerID)[0];
if(foundDialog) {
if(updatedData.unread) {
foundDialog.unread_count -= updatedData.unread;
@ -3101,7 +3138,7 @@ export class AppMessagesManager { @@ -3101,7 +3138,7 @@ export class AppMessagesManager {
this.reloadConversation(+peerID);
}
}
})
});
break;
}
@ -3127,7 +3164,7 @@ export class AppMessagesManager { @@ -3127,7 +3164,7 @@ export class AppMessagesManager {
this.reloadConversation(-channelID);
} else {
if(foundDialog[0]) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
this.dialogsStorage[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: foundDialog[0]});
}
}
@ -3141,7 +3178,7 @@ export class AppMessagesManager { @@ -3141,7 +3178,7 @@ export class AppMessagesManager {
let peerID = -channelID;
let foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog[0]) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
this.dialogsStorage[foundDialog[0].folder_id].splice(foundDialog[1], 1);
}
delete this.historiesStorage[peerID];
@ -3167,8 +3204,6 @@ export class AppMessagesManager { @@ -3167,8 +3204,6 @@ export class AppMessagesManager {
}
case 'updateServiceNotification': {
// update.inbox_date = tsNow(true)
// update.pFlags = {popup: true}
var fromID = 777000;
var peerID = fromID;
var messageID = this.tempID--;
@ -3176,7 +3211,7 @@ export class AppMessagesManager { @@ -3176,7 +3211,7 @@ export class AppMessagesManager {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
to_id: appPeersManager.getOutputPeer(peerID),
flags: 0,
pFlags: {unread: true},
date: (update.inbox_date || tsNow(true)) + serverTimeManager.serverTimeOffset,
@ -3203,9 +3238,7 @@ export class AppMessagesManager { @@ -3203,9 +3238,7 @@ export class AppMessagesManager {
message: message
});
}
if(update.pFlags.popup && update.message) {
//ErrorService.show({error: {code: 400, type: 'UPDATE_SERVICE_NOTIFICATION'}, historyMessage: historyMessage}); // warning
}
break;
}
}
@ -3464,14 +3497,14 @@ export class AppMessagesManager { @@ -3464,14 +3497,14 @@ export class AppMessagesManager {
}
public requestHistory(peerID: number, maxID: number, limit: number, offset = 0): Promise<any> {
var isChannel = AppPeersManager.isChannel(peerID);
var isChannel = appPeersManager.isChannel(peerID);
//console.trace('requestHistory', peerID, maxID, limit, offset);
$rootScope.$broadcast('history_request');
return apiManager.invokeApi('messages.getHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
peer: appPeersManager.getInputPeerByID(peerID),
offset_id: maxID ? appMessagesIDsManager.getMessageLocalID(maxID) : 0,
offset_date: 0,
add_offset: offset || 0,
@ -3523,7 +3556,7 @@ export class AppMessagesManager { @@ -3523,7 +3556,7 @@ export class AppMessagesManager {
_: 'messageService',
id: messageID,
from_id: peerID,
to_id: AppPeersManager.getOutputPeer(peerID),
to_id: appPeersManager.getOutputPeer(peerID),
flags: 0,
pFlags: {},
date: tsNow(true) + serverTimeManager.serverTimeOffset,
@ -3617,16 +3650,14 @@ export class AppMessagesManager { @@ -3617,16 +3650,14 @@ export class AppMessagesManager {
public wrapSingleMessage(msgID: number) {
if(this.messagesStorage[msgID]) {
//let ret = this.wrapForDialog(msgID); // hm
$rootScope.$broadcast('messages_downloaded', [msgID]);
//return ret;
return {mid: msgID, loading: false};
}
if(this.needSingleMessages.indexOf(msgID) == -1) {
this.needSingleMessages.push(msgID);
if(this.fetchSingleMessagesTimeout == 0) {
this.fetchSingleMessagesTimeout = window.setTimeout(this.fetchSingleMessages.bind(this), 25);
this.fetchSingleMessagesTimeout = window.setTimeout(this.fetchSingleMessages.bind(this), 10);
}
return {mid: msgID, loading: true};

29
src/lib/appManagers/appPhotosManager.ts

@ -184,34 +184,47 @@ export class AppPhotosManager { @@ -184,34 +184,47 @@ export class AppPhotosManager {
arr[164] = bytes[1];
arr[166] = bytes[2];
} else {
arr = bytes;
arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
}
//console.log('setAttachmentPreview', bytes, arr, div, isSticker);
let blob = new Blob([arr], {type: "image/jpeg"});
/* let reader = new FileReader();
reader.onloadend = () => {
let src = reader.result;
};
reader.readAsDataURL(blob); */
if(background) {
let url = URL.createObjectURL(blob);
let img = new Image();
img.src = url;
img.onload = () => {
img.addEventListener('load', () => {
element.style.backgroundImage = 'url(' + url + ')';
};
});
return element;
//element.style.backgroundImage = 'url(' + url + ')';
} else {
if(element instanceof SVGSVGElement) {
let image = element.firstElementChild as SVGImageElement || document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttributeNS(null, 'href', URL.createObjectURL(blob));
element.append(image);
return image;
} else if(element instanceof HTMLImageElement) {
element.src = URL.createObjectURL(blob);
return element;
} else {
let image = new Image();
image.src = URL.createObjectURL(blob);
let img = new Image();
img.style.width = '100%';
img.style.height = '100%';
image.style.width = '100%';
image.style.height = '100%';
element.append(image);
img.src = URL.createObjectURL(blob);
element.append(img);
return img;
}
}
}

231
src/lib/appManagers/appSidebarRight.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { horizontalMenu, formatPhoneNumber, putPreloader, renderImageFromUrl } from "../../components/misc";
import { horizontalMenu, putPreloader, renderImageFromUrl } from "../../components/misc";
//import Scrollable from '../../components/scrollable';
import Scrollable from '../../components/scrollable_new';
import { $rootScope } from "../utils";
@ -18,6 +18,22 @@ import AvatarElement from "../../components/avatar"; @@ -18,6 +18,22 @@ import AvatarElement from "../../components/avatar";
const testScroll = false;
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
//this.scroll.getScrollTopOffset();
});
};
class AppSidebarRight {
public sidebarEl = document.getElementById('column-right') as HTMLDivElement;
public profileContainer = this.sidebarEl.querySelector('.profile-container') as HTMLDivElement;
@ -56,7 +72,7 @@ class AppSidebarRight { @@ -56,7 +72,7 @@ class AppSidebarRight {
'inputMessagesFilterUrl',
'inputMessagesFilterMusic'
];
public sharedMediaType: string = '';
public sharedMediaType: AppSidebarRight['sharedMediaTypes'][number] = '';
private sharedMediaSelected: HTMLDivElement = null;
private lazyLoadQueueSidebar = new LazyLoadQueue(5);
@ -91,6 +107,8 @@ class AppSidebarRight { @@ -91,6 +107,8 @@ class AppSidebarRight {
public privateSearch = new AppSearch(this.searchContainer.querySelector('.chats-container') as HTMLDivElement, this.searchInput, {
messages: new SearchGroup('Private Search', 'messages')
});
private loadMutex: Promise<any> = Promise.resolve();
constructor() {
let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement;
@ -156,7 +174,7 @@ class AppSidebarRight { @@ -156,7 +174,7 @@ class AppSidebarRight {
let ids = Object.keys(this.mediaDivsByIDs).map(k => +k).sort((a, b) => a - b);
let idx = ids.findIndex(i => i == messageID);
let targets = ids.map(id => ({element: this.mediaDivsByIDs[id], mid: id}));
let targets = ids.map(id => ({element: this.mediaDivsByIDs[id].firstElementChild as HTMLElement, mid: id}));
appMediaViewer.openMedia(message, target, false, this.sidebarEl, targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), true);
});
@ -289,12 +307,11 @@ class AppSidebarRight { @@ -289,12 +307,11 @@ class AppSidebarRight {
return filtered;
}
public performSearchResult(messages: any[], type: string) {
public async performSearchResult(messages: any[], type: string) {
let peerID = this.peerID;
let sharedMediaDiv: HTMLDivElement;
let elemsToAppend: HTMLElement[] = [];
let promises: Promise<any>[] = [];
// https://core.telegram.org/type/MessagesFilter
switch(type) {
@ -305,22 +322,23 @@ class AppSidebarRight { @@ -305,22 +322,23 @@ class AppSidebarRight {
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
let div = document.createElement('div');
div.classList.add('media-item');
//console.log(message, photo);
let isPhoto = media._ == 'photo';
let photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
let isDownloaded = (photo && photo.downloaded) || appPhotosManager.getDocumentCachedThumb(media.id);
if(!isDownloaded) {
//this.log('inputMessagesFilterPhotoVideo', message, media, photo, div);
let sizes = media.sizes || media.thumbs;
if(sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
} /* else {
this.log('no stripped size', message, media);
} */
let isDownloaded: boolean;
if(photo) {
isDownloaded = photo.downloaded > 0;
} else {
let cachedThumb = appPhotosManager.getDocumentCachedThumb(media.id);
isDownloaded = cachedThumb?.downloaded > 0;
}
let img = new Image();
img.classList.add('media-image');
div.append(img);
//this.log('inputMessagesFilterPhotoVideo', message, media);
@ -349,14 +367,37 @@ class AppSidebarRight { @@ -349,14 +367,37 @@ class AppSidebarRight {
let url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
if(url) {
renderImageFromUrl(div, url);
renderImageFromUrl(img, url);
}
});
div.dataset.mid = '' + message.mid;
img.dataset.mid = '' + message.mid;
let sizes = media.sizes || media.thumbs;
if(isDownloaded || (sizes && sizes[0].bytes)) {
let promise = new Promise((resolve, reject) => {
img.addEventListener('load', () => {
clearTimeout(timeout);
resolve();
});
let timeout = setTimeout(() => {
this.log('did not loaded', img, media, isDownloaded, sizes);
reject();
}, 1e3);
});
promises.push(promise);
}
if(isDownloaded) load();
else this.lazyLoadQueueSidebar.push({div, load});
else {
if(sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, img, false, false);
}
this.lazyLoadQueueSidebar.push({div, load});
}
this.lastSharedMediaDiv.append(div);
if(this.lastSharedMediaDiv.childElementCount == 3) {
@ -365,6 +406,7 @@ class AppSidebarRight { @@ -365,6 +406,7 @@ class AppSidebarRight {
}
this.lastSharedMediaDiv = document.createElement('div');
this.lastSharedMediaDiv.classList.add('media-row');
}
this.mediaDivsByIDs[message.mid] = div;
@ -397,6 +439,8 @@ class AppSidebarRight { @@ -397,6 +439,8 @@ class AppSidebarRight {
//this.log('wrapping webpage', webpage);
previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1);
previewDiv.classList.add('empty');
if(webpage.photo) {
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60))
.then(() => {
@ -405,13 +449,12 @@ class AppSidebarRight { @@ -405,13 +449,12 @@ class AppSidebarRight {
return;
}
previewDiv.classList.remove('empty');
renderImageFromUrl(previewDiv, webpage.photo.url);
});
this.lazyLoadQueueSidebar.push({div: previewDiv, load});
} else {
previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1);
previewDiv.classList.add('empty');
}
let title = webpage.rTitle || '';
@ -457,11 +500,19 @@ class AppSidebarRight { @@ -457,11 +500,19 @@ class AppSidebarRight {
if(this.lastSharedMediaDiv.childElementCount && !this.scroll.contains(this.lastSharedMediaDiv)) {
elemsToAppend.push(this.lastSharedMediaDiv);
}
if(this.loadMutex) {
promises.push(this.loadMutex);
}
if(elemsToAppend.length) {
//window.requestAnimationFrame(() => {
//elemsToAppend.forEach(el => this.scroll.append(el, false));
//});
if(promises.length) {
await Promise.all(promises);
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
}
sharedMediaDiv.append(...elemsToAppend);
}
@ -521,7 +572,7 @@ class AppSidebarRight { @@ -521,7 +572,7 @@ class AppSidebarRight {
this.usedFromHistory[type] = used;
if(messages.length) {
this.performSearchResult(messages, type);
return this.performSearchResult(messages, type);
}
return Promise.resolve();
@ -556,7 +607,7 @@ class AppSidebarRight { @@ -556,7 +607,7 @@ class AppSidebarRight {
this.usedFromHistory[type] = history.length;
if(ids.length) {
this.performSearchResult(this.filterMessagesByType(ids, type), type);
return this.performSearchResult(this.filterMessagesByType(ids, type), type);
}
}, (err) => {
this.log.error('load error:', err);
@ -567,77 +618,77 @@ class AppSidebarRight { @@ -567,77 +618,77 @@ class AppSidebarRight {
return Promise.all(promises);
}
public fillProfileElements() {
let peerID = this.peerID = $rootScope.selectedPeerID;
public cleanup() {
this.loadSidebarMediaPromises = {};
this.loadedAllMedia = {};
this.lastSharedMediaDiv = document.createElement('div');
this.lastSharedMediaDiv.classList.add('media-row');
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
this.mediaDivsByIDs = {};
//this.log('fillProfileElements');
this.lazyLoadQueueSidebar.clear();
this.sharedMediaTypes.forEach(type => {
this.usedFromHistory[type] = 0;
});
this.sharedMediaType = 'inputMessagesFilterPhotoVideo';
}
public cleanupHTML() {
this.contentContainer.classList.remove('loaded');
this.profileElements.avatar.setAttribute('peer', '' + peerID);
window.requestAnimationFrame(() => {
this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
if(this.urlsToRevoke.length) {
this.urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
});
this.urlsToRevoke.length = 0;
}
Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = '';
Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = '';
if(!this.historiesStorage[this.peerID] || !this.historiesStorage[this.peerID][key]) {
let parent = this.sharedMedia[key].parentElement;
if(!parent.querySelector('.preloader')) {
putPreloader(parent, true);
}
});
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
//this.scroll.getScrollTopOffset();
this.loadSidebarMedia(true);
});
this.mediaDivsByIDs = {};
this.lazyLoadQueueSidebar.clear();
this.urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
});
this.urlsToRevoke.length = 0;
this.sharedMediaTypes.forEach(type => {
this.usedFromHistory[type] = 0;
}
});
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
//this.scroll.getScrollTopOffset();
});
};
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
}
public setLoadMutex(promise: Promise<any>) {
this.loadMutex = promise;
}
public setPeer(peerID: number) {
this.peerID = peerID;
this.cleanup();
}
public fillProfileElements() {
let peerID = this.peerID = $rootScope.selectedPeerID;
this.cleanupHTML();
this.profileElements.avatar.setAttribute('peer', '' + peerID);
// username
if(peerID != appImManager.myID) {
if(peerID != $rootScope.myID) {
let username = appPeersManager.getPeerUsername(peerID);
if(username) {
setText(appPeersManager.getPeerUsername(peerID), this.profileElements.username);
@ -656,13 +707,12 @@ class AppSidebarRight { @@ -656,13 +707,12 @@ class AppSidebarRight {
} else {
window.requestAnimationFrame(() => {
this.profileElements.notificationsRow.style.display = 'none';
//this.scroll.getScrollTopOffset();
})
});
}
if(peerID > 0) {
let user = appUsersManager.getUser(peerID);
if(user.phone && peerID != appImManager.myID) {
if(user.phone && peerID != $rootScope.myID) {
setText(user.rPhone, this.profileElements.phone);
}
@ -672,7 +722,7 @@ class AppSidebarRight { @@ -672,7 +722,7 @@ class AppSidebarRight {
return;
}
if(userFull.rAbout && peerID != appImManager.myID) {
if(userFull.rAbout && peerID != $rootScope.myID) {
setText(userFull.rAbout, this.profileElements.bio);
}
@ -682,8 +732,6 @@ class AppSidebarRight { @@ -682,8 +732,6 @@ class AppSidebarRight {
appImManager.pinnedMsgID = userFull.pinned_msg_id;
appMessagesManager.wrapSingleMessage(userFull.pinned_msg_id);
}
//this.scroll.getScrollTopOffset();
});
} else {
let chat = appPeersManager.getPeer(peerID);
@ -699,13 +747,8 @@ class AppSidebarRight { @@ -699,13 +747,8 @@ class AppSidebarRight {
if(chatFull.about) {
setText(RichTextProcessor.wrapRichText(chatFull.about), this.profileElements.bio);
}
//this.scroll.getScrollTopOffset();
});
}
//this.scroll.getScrollTopOffset();
//this.loadSidebarMedia();
}
}

10
src/lib/appManagers/appStickersManager.ts

@ -60,9 +60,6 @@ class AppStickersManager { @@ -60,9 +60,6 @@ class AppStickersManager {
for(let id in sets) {
let set = sets[id];
set.documents.forEach(doc => {
delete doc.downloaded;
delete doc.url;
this.saveSticker(doc);
});
}
@ -78,13 +75,6 @@ class AppStickersManager { @@ -78,13 +75,6 @@ class AppStickersManager {
public saveSticker(doc: MTDocument) {
if(this.documents[doc.id]) return this.documents[doc.id];
/* Object.keys(doc).forEach(key => {
if(doc[key] instanceof Uint8Array) {
doc[key] = Array.from(doc[key]);
}
}); */
doc.file_reference = Array.from(doc.file_reference);
doc = appDocsManager.saveDoc(doc);
this.documents[doc.id] = doc;

2
src/lib/appManagers/appUsersManager.ts

@ -228,7 +228,7 @@ export class AppUsersManager { @@ -228,7 +228,7 @@ export class AppUsersManager {
var nameWords = apiUser.sortName.split(' ');
var firstWord = nameWords.shift();
var lastWord = nameWords.pop();
apiUser.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : firstWord.charAt(1));
apiUser.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : '');
if(apiUser.status) {
if(apiUser.status.expires) {

7
src/lib/polyfill.ts

@ -84,6 +84,10 @@ Uint8Array.prototype.toString = function() { @@ -84,6 +84,10 @@ Uint8Array.prototype.toString = function() {
return String.fromCharCode.apply(null, [...this]);
};
Uint8Array.prototype.toJSON = function() {
return [...this];
};
Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number, array?: Array<T>) => void) {
let length = this.length;
for(var i = length - 1; i >= 0; --i) {
@ -113,7 +117,8 @@ declare global { @@ -113,7 +117,8 @@ declare global {
hex: string;
randomize: () => Uint8Array,
concat: (...args: Array<Uint8Array | ArrayBuffer | number[]>) => Uint8Array,
toString: () => string
toString: () => string,
toJSON: () => number[],
}
interface Array<T> {

7
src/lib/storage.ts

@ -67,8 +67,11 @@ class ConfigStorage { @@ -67,8 +67,11 @@ class ConfigStorage {
value = obj[key];
key = prefix + key;
this.cache[key] = value;
//value = value instanceof Uint8Array ? Array.from(value) : JSON.stringify(value);
value = JSON.stringify(value);
value = JSON.stringify(value, (key, value) => {
if(key == 'downloaded' || (key == 'url' && value.indexOf('blob:') === 0)) return undefined;
return value;
});
if(this.useLs) {
try {
//console.log('setItem', key, value);

5
src/lib/utils.js

@ -754,7 +754,8 @@ function versionCompare (ver1, ver2) { @@ -754,7 +754,8 @@ function versionCompare (ver1, ver2) {
}
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
//var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g,
trimRe = /^\s+|\s$/g
function createIndex () {
@ -766,7 +767,7 @@ function versionCompare (ver1, ver2) { @@ -766,7 +767,7 @@ function versionCompare (ver1, ver2) {
function cleanSearchText(text, latinize = true) {
var hasTag = text.charAt(0) == '%';
text = text.replace(badCharsRe, ' ').replace(trimRe, '');
text = text.replace(badCharsRe, '').replace(trimRe, '');
if(latinize) {
text = text.replace(/[^A-Za-z0-9]/g, (ch) => {
var latinizeCh = Config.LatinizeMap[ch];

107
src/scss/partials/_chatBubble.scss

@ -14,36 +14,52 @@ @@ -14,36 +14,52 @@
.bubble {
padding-top: 5px;
/* display: grid;
grid-template-columns: 1fr $chat-max-width 1fr;
grid-row-gap: 0px; */
max-width: $chat-max-width;
margin: 0 auto;
position: relative;
&.is-selected {
&:before {
&:after {
position: absolute;
width: 200%;
height: 100%;
background-color: rgba(0, 132, 255, .3);
content: " ";
display: block;
left: -50%;
top: 0;
height: 100%;
content: " ";
background-color: rgba(0, 132, 255, .3);
animation: bubbleSelected 2s linear;
z-index: 1;
}
&:not(.is-group-last):before {
&:not(.is-group-last):after {
height: calc(100% + 5px);
}
}
&.is-first-unread {
&:before {
content: "Unread messages";
height: 30px;
margin-bottom: 5px;
margin-left: -50%;
text-align: center;
color: #538BCC;
line-height: 2.1;
font-weight: 500;
font-size: 15px;
background-color: rgba(255, 255, 255, 0.95);
}
}
&.is-selected:after, &.is-first-unread:before {
width: 200%;
display: block;
}
&.is-date {
position: -webkit-sticky;
position: sticky;
top: 5px;
z-index: 2;
z-index: 3;
pointer-events: none;
&.is-sticky {
@ -53,11 +69,6 @@ @@ -53,11 +69,6 @@
}
}
/* &:before, &:after {
content: " ";
width: 100%;
} */
&__container {
//min-width: 60px;
min-width: 56px;
@ -70,6 +81,7 @@ @@ -70,6 +81,7 @@
/* font-size: 0; */
width: max-content;
height: fit-content;
z-index: 2;
> .user-avatar {
position: absolute;
@ -560,6 +572,14 @@ @@ -560,6 +572,14 @@
.emoji {
font-size: 1.2rem;
}
pre, code {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
}
// all for audio (not voice)
@ -1204,7 +1224,62 @@ @@ -1204,7 +1224,62 @@
}
}
.reply-markup {
position: absolute;
width: 100%;
&-row {
margin-top: 5px;
overflow: hidden;
height: 40px;
display: flex;
&:last-child {
border-bottom-left-radius: $border-radius-big;
border-bottom-right-radius: $border-radius-big;
}
}
&-button {
display: flex;
justify-content: center;
align-items: center;
border-radius: 6px;
background-color: rgba(0, 0, 0, 0.23);
z-index: 2;
font-size: 14px;
user-select: none;
text-align: center;
color: white !important;
outline: none;
border: none;
width: 100%;
cursor: pointer;
position: relative;
transition: background-color 0.35s ease;
&:hover {
background-color: rgba(0, 0, 0, 0.06);
}
& + & {
margin-left: 5px;
}
&.is-link:before {
content: $tgico-next;
position: absolute;
right: 2px;
top: 2px;
display: block;
transform: rotate(-45deg);
}
/* img.emoji {
vertical-align: middle !important;
} */
}
}
poll-element {
margin-top: -1px;

2
src/scss/partials/_fonts.scss

@ -129,7 +129,7 @@ @@ -129,7 +129,7 @@
content: "\e91f";
}
.tgico-next:before {
content: "\e920";
content: $tgico-next;
}
.tgico-nosound:before {
content: "\e921";

1
src/scss/partials/_ico.scss

@ -5,3 +5,4 @@ $tgico-check: "\e900"; @@ -5,3 +5,4 @@ $tgico-check: "\e900";
$tgico-checks: "\e95a";
$tgico-sending: "\e919";
$tgico-close: "\e943";
$tgico-next: "\e920";

67
src/scss/partials/_mediaViewer.scss

@ -1,3 +1,7 @@ @@ -1,3 +1,7 @@
$open-duration: .2s;
//$open-duration: 10s;
$move-duration: .35s;
.media-viewer {
position: fixed;
top: 0;
@ -20,7 +24,7 @@ @@ -20,7 +24,7 @@
flex-direction: column;
justify-content: center;
color: #8b8b8b;
transition: .2s;
transition: $open-duration;
&:hover {
color: #fff;
@ -53,7 +57,7 @@ @@ -53,7 +57,7 @@
.btn-icon {
margin: 0 .25rem;
transition: .2s;
transition: $open-duration;
&:hover {
color: #fff;
@ -91,14 +95,6 @@ @@ -91,14 +95,6 @@
align-items: center;
justify-content: center;
visibility: hidden;
&.loading {
img, video {
object-fit: cover;
width: 100%;
height: 100%;
}
}
}
img, video {
@ -108,22 +104,11 @@ @@ -108,22 +104,11 @@
max-width: 1280px; */
}
img {
object-fit: contain;
}
video {
width: 100%;
height: 100%;
/* object-fit: cover; */
object-fit: contain;
}
.media-viewer-caption {
flex: 1;
text-align: center;
color: $color-gray;
transition: .2s;
transition: $open-duration;
max-width: 50vw;
word-break: break-word;
overflow: hidden;
@ -165,7 +150,7 @@ @@ -165,7 +150,7 @@
top: 50%;
transform: translateY(-50%) rotate(90deg);
opacity: 0;
transition: .2s opacity;
transition: $open-duration opacity;
z-index: 5;
/* box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.07); */
}
@ -189,40 +174,40 @@ @@ -189,40 +174,40 @@
transform-origin: top left;
overflow: hidden;
//border-radius: 0;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
.ckin__player {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
img, video {
width: 100%;
height: 100%;
opacity: 1;
transition: .2s opacity;
user-select: none;
}
&.cover {
align-items: center;
img, video {
width: auto;
height: auto;
overflow: hidden;
opacity: 0;
}
object-fit: cover;
}
&.active {
transition: .2s transform;
transition: $open-duration transform;
}
&.moving {
transition: 350ms transform ease;
transition: $move-duration transform ease;
}
}
// возможно тут это вообще не нужно
&-aspecter {
width: 100%;
height: 100%;
transform: scale(1);
overflow: hidden;
}
&-mover.active &-aspecter {
transition: $open-duration all;
}
}

19
src/scss/partials/_rightSIdebar.scss

@ -211,7 +211,7 @@ @@ -211,7 +211,7 @@
flex-direction: column;
padding: 4px 7.5px 7.5px;
> div {
.media-row {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: max-content;
@ -219,14 +219,9 @@ @@ -219,14 +219,9 @@
place-items: start;
padding-top: 3.5px;
> div {
.media-item {
width: 100%;
cursor: pointer;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
display: flex;
background-color: #000;
position: relative;
@ -241,8 +236,8 @@ @@ -241,8 +236,8 @@
}
}
span.video-time {
position: relative;
.video-time {
position: absolute;
left: 5px;
top: 4px;
height: 18px;
@ -254,6 +249,12 @@ @@ -254,6 +249,12 @@
color: white;
}
.media-image {
width: 100%;
max-height: 100%;
object-fit: cover;
}
/* span.video-play {
background-color: $time-background;
color: #fff;

4
src/scss/partials/_selector.scss

@ -14,9 +14,11 @@ @@ -14,9 +14,11 @@
flex-direction: column;
&-search-container {
flex: 1 1 auto;
flex: 0 0 auto;
//flex: 1 1 auto;
position: relative;
max-height: 132px;
overflow: hidden;
.scrollable {
position: relative;

31
src/scss/style.scss

@ -276,6 +276,37 @@ input { @@ -276,6 +276,37 @@ input {
}
}
@keyframes fadeInFadeOut {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
50% {
opacity: 1;
}
to {
opacity: 0;
}
}
.toast {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: .5rem 1rem;
background-color: rgba(0, 0, 0, .66);
color: #fff;
font-size: 1rem;
border-radius: $border-radius-medium;
animation: fadeInFadeOut 3s linear forwards;
}
hr {
width: 100%;
border: none;

1
webpack.common.js

@ -115,6 +115,7 @@ module.exports = { @@ -115,6 +115,7 @@ module.exports = {
filename: `index.html`,
template: 'public/index_template.html',
inject: true,
//inject: 'head',
/* minify: {
removeComments: true,
collapseWhitespace: true,

Loading…
Cancel
Save