This commit is contained in:
morethanwords 2021-10-21 17:16:43 +04:00
parent 264fbdf7c9
commit 72345caa1e
164 changed files with 5788 additions and 4590 deletions

6
.env
View File

@ -1,5 +1,5 @@
API_ID=1025907 API_ID=1025907
API_HASH=452b0359b988148995f22ff0f4229750 API_HASH=452b0359b988148995f22ff0f4229750
VERSION=0.8.6 VERSION=0.9.0
VERSION_FULL=0.8.6 (6) VERSION_FULL=0.9.0 (10)
BUILD=6 BUILD=10

View File

@ -1,3 +1,13 @@
### 0.9.0 (10)
* Application can now work with new 64-bit identifiers
* Added Swipe-to-Reply on devices with touchscreen
* Refactored folders & dialogs
* Added confirmation popups for logging out, canceling message editing
* Fixed sending video as file
* Fixed loading document's thumbnail on sending and forwarding
* Fixed inability to open a media sent as file in media viewer
* Other bugfixes and improvements
### 0.8.6 ### 0.8.6
* Added changelogs. * Added changelogs.
* Audio player improvements: seek, next/previous buttons, volume controls. Changing volume in the video player will affect audio as well. * Audio player improvements: seek, next/previous buttons, volume controls. Changing volume in the video player will affect audio as well.

View File

@ -3,7 +3,7 @@
const { spawn } = require('child_process'); const { spawn } = require('child_process');
const version = process.argv[2] || 'same'; const version = process.argv[2] || 'same';
const changelog = ''; const changelog = process.argv[3] || '';
const child = spawn(`npm`, ['run', 'change-version', version, changelog].filter(Boolean)); const child = spawn(`npm`, ['run', 'change-version', version, changelog].filter(Boolean));
child.stdout.on('data', (chunk) => { child.stdout.on('data', (chunk) => {
console.log(chunk.toString()); console.log(chunk.toString());

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
<svg width="148" height="256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><filter x="-9.1%" y="-5%" width="118.2%" height="110%" filterUnits="objectBoundingBox" id="a"><feOffset in="SourceAlpha" result="so1"/><feGaussianBlur stdDeviation="4" in="so1" result="sb1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" in="sb1"/></filter><path d="M128.495 111.434L20.685 3.543c-4.73-4.724-12.394-4.724-17.137 0-4.73 4.724-4.73 12.396 0 17.12l99.258 99.331-99.246 99.33c-4.73 4.725-4.73 12.397 0 17.133 4.73 4.724 12.407 4.724 17.137 0l107.81-107.891c4.66-4.675 4.66-12.469-.012-17.132z" id="b"/></defs><g transform="translate(8 8)" fill="none"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#FFF" xlink:href="#b"/></g></svg>

Before

Width:  |  Height:  |  Size: 792 B

View File

@ -1 +0,0 @@
<svg width="148" height="256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><filter x="-9.1%" y="-5%" width="118.2%" height="110%" filterUnits="objectBoundingBox" id="a"><feOffset in="SourceAlpha" result="so1"/><feGaussianBlur stdDeviation="4" in="so1" result="sb1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" in="sb1"/></filter><path d="M3.504 128.567l107.81 107.891c4.731 4.724 12.395 4.724 17.138 0 4.73-4.724 4.73-12.396 0-17.12l-99.259-99.331 99.247-99.33c4.73-4.725 4.73-12.397 0-17.133-4.73-4.724-12.407-4.724-17.138 0L3.492 111.434c-4.658 4.675-4.658 12.469.012 17.133z" id="b"/></defs><g transform="translate(8 8)" fill="none"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#FFF" xlink:href="#b"/></g></svg>

Before

Width:  |  Height:  |  Size: 793 B

View File

@ -0,0 +1,8 @@
• Application can now work with new 64-bit identifiers
• Added Swipe-to-Reply on devices with touchscreen
• Refactored folders & dialogs
• Added confirmation popups for logging out, canceling message editing
• Fixed sending video as file
• Fixed loading document's thumbnail on sending and forwarding
• Fixed inability to open a media sent as file in media viewer
• Other bugfixes and improvements

View File

@ -9,6 +9,7 @@ import rootScope from "../lib/rootScope";
import { IS_SAFARI } from "../environment/userAgent"; import { IS_SAFARI } from "../environment/userAgent";
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
import isInDOM from "../helpers/dom/isInDOM"; import isInDOM from "../helpers/dom/isInDOM";
import { indexOfAndSplice } from "../helpers/array";
export interface AnimationItem { export interface AnimationItem {
el: HTMLElement, el: HTMLElement,
@ -101,7 +102,7 @@ export class AnimationIntersector {
} }
for(const group in this.byGroups) { for(const group in this.byGroups) {
this.byGroups[group].findAndSplice(p => p === player); indexOfAndSplice(this.byGroups[group], player);
} }
this.observer.unobserve(el); this.observer.unobserve(el);

View File

@ -27,7 +27,7 @@ import { onMediaLoad } from "../helpers/files";
// TODO: Safari: попробовать замаскировать подгрузку последнего чанка // TODO: Safari: попробовать замаскировать подгрузку последнего чанка
// TODO: Safari: пофиксить момент, когда заканчивается песня и пытаешься включить её заново - прогресс сразу в конце // TODO: Safari: пофиксить момент, когда заканчивается песня и пытаешься включить её заново - прогресс сразу в конце
export type MediaItem = {mid: number, peerId: number}; export type MediaItem = {mid: number, peerId: PeerId};
type HTMLMediaElement = HTMLAudioElement | HTMLVideoElement; type HTMLMediaElement = HTMLAudioElement | HTMLVideoElement;
@ -47,9 +47,9 @@ export type MediaSearchContext = SearchSuperContext & Partial<{
}>; }>;
type MediaDetails = { type MediaDetails = {
peerId: number, peerId: PeerId,
mid: number, mid: number,
docId: string, docId: DocId,
clean?: boolean, clean?: boolean,
isScheduled?: boolean, isScheduled?: boolean,
isSingle?: boolean isSingle?: boolean
@ -57,12 +57,12 @@ type MediaDetails = {
class AppMediaPlaybackController { class AppMediaPlaybackController {
private container: HTMLElement; private container: HTMLElement;
private media: Map<number, Map<number, HTMLMediaElement>> = new Map(); private media: Map<PeerId, Map<number, HTMLMediaElement>> = new Map();
private scheduled: AppMediaPlaybackController['media'] = new Map(); private scheduled: AppMediaPlaybackController['media'] = new Map();
private mediaDetails: Map<HTMLMediaElement, MediaDetails> = new Map(); private mediaDetails: Map<HTMLMediaElement, MediaDetails> = new Map();
private playingMedia: HTMLMediaElement; private playingMedia: HTMLMediaElement;
private waitingMediaForLoad: Map<number, Map<number, CancellablePromise<void>>> = new Map(); private waitingMediaForLoad: Map<PeerId, Map<number, CancellablePromise<void>>> = new Map();
private waitingScheduledMediaForLoad: AppMediaPlaybackController['waitingMediaForLoad'] = new Map(); private waitingScheduledMediaForLoad: AppMediaPlaybackController['waitingMediaForLoad'] = new Map();
private waitingDocumentsForLoad: {[docId: string]: Set<HTMLMediaElement>} = {}; private waitingDocumentsForLoad: {[docId: string]: Set<HTMLMediaElement>} = {};
@ -271,7 +271,7 @@ class AppMediaPlaybackController {
return media; return media;
} }
public getMedia(peerId: number, mid: number, isScheduled?: boolean) { public getMedia(peerId: PeerId, mid: number, isScheduled?: boolean) {
const s = (isScheduled ? this.scheduled : this.media).get(peerId); const s = (isScheduled ? this.scheduled : this.media).get(peerId);
return s?.get(mid); return s?.get(mid);
} }
@ -326,7 +326,7 @@ class AppMediaPlaybackController {
}/* , {once: true} */); }/* , {once: true} */);
} }
public resolveWaitingForLoadMedia(peerId: number, mid: number, isScheduled?: boolean) { public resolveWaitingForLoadMedia(peerId: PeerId, mid: number, isScheduled?: boolean) {
const w = isScheduled ? this.waitingScheduledMediaForLoad : this.waitingMediaForLoad; const w = isScheduled ? this.waitingScheduledMediaForLoad : this.waitingMediaForLoad;
const storage = w.get(peerId); const storage = w.get(peerId);
if(!storage) { if(!storage) {

View File

@ -28,7 +28,7 @@ import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
type AppMediaViewerTargetType = { type AppMediaViewerTargetType = {
element: HTMLElement, element: HTMLElement,
mid: number, mid: number,
peerId: number peerId: PeerId
}; };
export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> { export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> {
protected btnMenuDelete: HTMLElement; protected btnMenuDelete: HTMLElement;
@ -157,7 +157,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
return promise; return promise;
} */ } */
protected getMessageByPeer(peerId: number, mid: number) { protected getMessageByPeer(peerId: PeerId, mid: number) {
return this.searchContext.isScheduled ? appMessagesManager.getScheduledMessageByPeer(peerId, mid) : appMessagesManager.getMessageByPeer(peerId, mid); return this.searchContext.isScheduled ? appMessagesManager.getScheduledMessageByPeer(peerId, mid) : appMessagesManager.getMessageByPeer(peerId, mid);
} }
@ -193,6 +193,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
const {mid, peerId} = this.target; const {mid, peerId} = this.target;
if(mid && mid !== Number.MAX_SAFE_INTEGER) { if(mid && mid !== Number.MAX_SAFE_INTEGER) {
const threadId = this.searchContext.threadId; const threadId = this.searchContext.threadId;
const message = this.getMessageByPeer(peerId, mid);
this.close(e) this.close(e)
//.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve()) //.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve())
.then(() => { .then(() => {
@ -203,7 +204,6 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
} }
} }
const message = this.getMessageByPeer(peerId, mid);
appImManager.setInnerPeer(message.peerId, mid, threadId ? 'discussion' : undefined, threadId); appImManager.setInnerPeer(message.peerId, mid, threadId ? 'discussion' : undefined, threadId);
}); });
} }

View File

@ -5,15 +5,16 @@
*/ */
import AvatarListLoader from "../helpers/avatarListLoader"; import AvatarListLoader from "../helpers/avatarListLoader";
import { Photo } from "../layer";
import appImManager from "../lib/appManagers/appImManager"; import appImManager from "../lib/appManagers/appImManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager"; import appPhotosManager from "../lib/appManagers/appPhotosManager";
import AppMediaViewerBase from "./appMediaViewerBase"; import AppMediaViewerBase from "./appMediaViewerBase";
type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string}; type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: Photo.photo['id']};
export default class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> { export default class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> {
public peerId: number; public peerId: PeerId;
constructor(peerId: number) { constructor(peerId: PeerId) {
super(new AvatarListLoader({peerId}), [/* 'delete' */]); super(new AvatarListLoader({peerId}), [/* 'delete' */]);
this.peerId = peerId; this.peerId = peerId;
@ -45,7 +46,7 @@ export default class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete
appPhotosManager.savePhotoFile(appPhotosManager.getPhoto(this.target.photoId), appImManager.chat.bubbles.lazyLoadQueue.queueId); appPhotosManager.savePhotoFile(appPhotosManager.getPhoto(this.target.photoId), appImManager.chat.bubbles.lazyLoadQueue.queueId);
}; };
public async openMedia(photoId: string, target?: HTMLElement, fromRight = 0, prevTargets?: AppMediaViewerAvatarTargetType[], nextTargets?: AppMediaViewerAvatarTargetType[]) { public async openMedia(photoId: Photo.photo['id'], target?: HTMLElement, fromRight = 0, prevTargets?: AppMediaViewerAvatarTargetType[], nextTargets?: AppMediaViewerAvatarTargetType[]) {
if(this.setMoverPromise) return this.setMoverPromise; if(this.setMoverPromise) return this.setMoverPromise;
const photo = appPhotosManager.getPhoto(photoId); const photo = appPhotosManager.getPhoto(photoId);

View File

@ -1096,7 +1096,7 @@ export default class AppMediaViewerBase<
} */ } */
} }
protected setAuthorInfo(fromId: number, timestamp: number) { protected setAuthorInfo(fromId: PeerId, timestamp: number) {
replaceContent(this.author.date, formatFullSentTime(timestamp)); replaceContent(this.author.date, formatFullSentTime(timestamp));
replaceContent(this.author.nameEl, new PeerTitle({ replaceContent(this.author.nameEl, new PeerTitle({
@ -1115,7 +1115,7 @@ export default class AppMediaViewerBase<
protected async _openMedia( protected async _openMedia(
media: MyDocument | MyPhoto, media: MyDocument | MyPhoto,
timestamp: number, timestamp: number,
fromId: number, fromId: PeerId,
fromRight: number, fromRight: number,
target?: HTMLElement, target?: HTMLElement,
reverse = false, reverse = false,

View File

@ -1080,7 +1080,7 @@
// type AppMediaViewerTargetType = { // type AppMediaViewerTargetType = {
// element: HTMLElement, // element: HTMLElement,
// mid: number, // mid: number,
// peerId: number // peerId: PeerId
// }; // };
// export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> { // export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> {
// public currentMessageId = 0; // public currentMessageId = 0;
@ -1336,9 +1336,9 @@
// type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string}; // type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string};
// export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> { // export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> {
// public currentPhotoId: string; // public currentPhotoId: string;
// public peerId: number; // public peerId: PeerId;
// constructor(peerId: number) { // constructor(peerId: PeerId) {
// super(['delete']); // super(['delete']);
// this.peerId = peerId; // this.peerId = peerId;
@ -1373,7 +1373,7 @@
// }; // };
// protected loadMoreMedia = (older = true) => { // protected loadMoreMedia = (older = true) => {
// if(this.peerId < 0) return Promise.resolve(); // ! это значит, что открыло аватар чата, но следующих фотографий нет. // if(this.peerId.isAnyChat()) return Promise.resolve(); // ! это значит, что открыло аватар чата, но следующих фотографий нет.
// if(this.loadedAllMediaDown) return Promise.resolve(); // if(this.loadedAllMediaDown) return Promise.resolve();
// if(this.loadMediaPromiseDown) return this.loadMediaPromiseDown; // if(this.loadMediaPromiseDown) return this.loadMediaPromiseDown;

View File

@ -10,6 +10,8 @@ import { logger } from "../lib/logger";
import { doubleRaf } from "../helpers/schedulers"; import { doubleRaf } from "../helpers/schedulers";
import blurActiveElement from "../helpers/dom/blurActiveElement"; import blurActiveElement from "../helpers/dom/blurActiveElement";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import { indexOfAndSplice } from "../helpers/array";
import isSwipingBackSafari from "../helpers/dom/isSwipingBackSafari";
export type NavigationItem = { export type NavigationItem = {
type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' | type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' |
@ -73,7 +75,17 @@ export class AppNavigationController {
if(e.touches.length > 1) return; if(e.touches.length > 1) return;
this.debug && this.log('touchstart'); this.debug && this.log('touchstart');
const detach = () => { if(isSwipingBackSafari(e)) {
isPossibleSwipe = true;
window.addEventListener('touchend', () => {
setTimeout(() => {
isPossibleSwipe = false;
}, 100);
}, {passive: true, once: true});
}
/* const detach = () => {
window.removeEventListener('touchend', onTouchEnd); window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('touchmove', onTouchMove); window.removeEventListener('touchmove', onTouchMove);
}; };
@ -105,7 +117,7 @@ export class AppNavigationController {
}; };
window.addEventListener('touchend', onTouchEnd, options); window.addEventListener('touchend', onTouchEnd, options);
window.addEventListener('touchmove', onTouchMove, options); window.addEventListener('touchmove', onTouchMove, options); */
}, options); }, options);
} }
@ -171,7 +183,7 @@ export class AppNavigationController {
} }
public removeItem(item: NavigationItem) { public removeItem(item: NavigationItem) {
this.navigations.findAndSplice(i => i === item); indexOfAndSplice(this.navigations, item);
} }
public removeByType(type: NavigationItem['type'], single = false) { public removeByType(type: NavigationItem['type'], single = false) {

View File

@ -74,7 +74,7 @@ export default class AppSearch {
private listsContainer: HTMLDivElement = null; private listsContainer: HTMLDivElement = null;
private peerId = 0; // 0 - means global private peerId: PeerId; // 0 - means global
private threadId = 0; private threadId = 0;
private scrollable: Scrollable; private scrollable: Scrollable;
@ -117,7 +117,7 @@ export default class AppSearch {
if(all) { if(all) {
this.searchInput.value = ''; this.searchInput.value = '';
this.query = ''; this.query = '';
this.peerId = 0; this.peerId = undefined;
this.threadId = 0; this.threadId = 0;
} }
@ -132,7 +132,7 @@ export default class AppSearch {
this.searchPromise = null; this.searchPromise = null;
} }
public beginSearch(peerId = 0, threadId = 0, query = '') { public beginSearch(peerId?: PeerId, threadId = 0, query = '') {
this.peerId = peerId; this.peerId = peerId;
this.threadId = threadId; this.threadId = threadId;

View File

@ -50,12 +50,13 @@ import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent, simulateClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent, simulateClickEvent } from "../helpers/dom/clickEvent";
import { MyDocument } from "../lib/appManagers/appDocsManager"; import { MyDocument } from "../lib/appManagers/appDocsManager";
import AppMediaViewer from "./appMediaViewer"; import AppMediaViewer from "./appMediaViewer";
import lockTouchScroll from "../helpers/dom/lockTouchScroll";
//const testScroll = false; //const testScroll = false;
export type SearchSuperType = MyInputMessagesFilter/* | 'members' */; export type SearchSuperType = MyInputMessagesFilter/* | 'members' */;
export type SearchSuperContext = { export type SearchSuperContext = {
peerId: number, peerId: PeerId,
inputFilter: {_: MyInputMessagesFilter}, inputFilter: {_: MyInputMessagesFilter},
query?: string, query?: string,
maxId?: number, maxId?: number,
@ -81,7 +82,7 @@ class SearchContextMenu {
private buttons: (ButtonMenuItemOptions & {verify?: () => boolean, withSelection?: true})[]; private buttons: (ButtonMenuItemOptions & {verify?: () => boolean, withSelection?: true})[];
private element: HTMLElement; private element: HTMLElement;
private target: HTMLElement; private target: HTMLElement;
private peerId: number; private peerId: PeerId;
private mid: number; private mid: number;
private isSelected: boolean; private isSelected: boolean;
@ -109,7 +110,7 @@ class SearchContextMenu {
if(e instanceof MouseEvent) e.cancelBubble = true; if(e instanceof MouseEvent) e.cancelBubble = true;
this.target = item; this.target = item;
this.peerId = +item.dataset.peerId; this.peerId = item.dataset.peerId.toPeerId();
this.mid = +item.dataset.mid; this.mid = +item.dataset.mid;
this.isSelected = searchSuper.selection.isMidSelected(this.peerId, this.mid); this.isSelected = searchSuper.selection.isMidSelected(this.peerId, this.mid);
@ -236,7 +237,7 @@ export default class AppSearchSuper {
private lazyLoadQueue = new LazyLoadQueue(); private lazyLoadQueue = new LazyLoadQueue();
public middleware = getMiddleware(); public middleware = getMiddleware();
public historyStorage: Partial<{[type in SearchSuperType]: {mid: number, peerId: number}[]}> = {}; public historyStorage: Partial<{[type in SearchSuperType]: {mid: number, peerId: PeerId}[]}> = {};
public usedFromHistory: Partial<{[type in SearchSuperType]: number}> = {}; public usedFromHistory: Partial<{[type in SearchSuperType]: number}> = {};
public urlsToRevoke: string[] = []; public urlsToRevoke: string[] = [];
@ -326,10 +327,35 @@ export default class AppSearchSuper {
this.tabsContainer = document.createElement('div'); this.tabsContainer = document.createElement('div');
this.tabsContainer.classList.add('search-super-tabs-container', 'tabs-container'); this.tabsContainer.classList.add('search-super-tabs-container', 'tabs-container');
let unlockScroll: ReturnType<typeof lockTouchScroll>;
if(IS_TOUCH_SUPPORTED) { if(IS_TOUCH_SUPPORTED) {
handleTabSwipe(this.tabsContainer, (next) => { handleTabSwipe({
const prevId = this.selectTab.prevId(); element: this.tabsContainer,
this.selectTab(next ? prevId + 1 : prevId - 1); onSwipe: (xDiff, yDiff, e) => {
const prevId = this.selectTab.prevId();
const children = Array.from(this.tabsMenu.children) as HTMLElement[];
let idx: number;
if(xDiff > 0) {
for(let i = prevId + 1; i < children.length; ++i) {
if(!children[i].classList.contains('hide')) {
idx = i;
break;
}
}
} else {
for(let i = prevId - 1; i >= 0; --i) {
if(!children[i].classList.contains('hide')) {
idx = i;
break;
}
}
}
if(idx !== undefined) {
unlockScroll = lockTouchScroll(this.tabsContainer);
this.selectTab(idx);
}
}
}); });
} }
@ -441,6 +467,11 @@ export default class AppSearchSuper {
this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop; this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop;
} }
if(unlockScroll) {
unlockScroll();
unlockScroll = undefined;
}
this.onTransitionEnd(); this.onTransitionEnd();
}, undefined, navScrollable); }, undefined, navScrollable);
@ -461,14 +492,14 @@ export default class AppSearchSuper {
return; return;
} }
const peerId = +target.dataset.peerId; const peerId = target.dataset.peerId.toPeerId();
const targets = (Array.from(this.tabs[inputFilter].querySelectorAll('.' + targetClassName)) as HTMLElement[]).map(el => { const targets = (Array.from(this.tabs[inputFilter].querySelectorAll('.' + targetClassName)) as HTMLElement[]).map(el => {
const containerEl = findUpClassName(el, className); const containerEl = findUpClassName(el, className);
return { return {
element: el, element: el,
mid: +containerEl.dataset.mid, mid: +containerEl.dataset.mid,
peerId: +containerEl.dataset.peerId peerId: containerEl.dataset.peerId.toPeerId()
}; };
}); });
@ -923,7 +954,7 @@ export default class AppSearchSuper {
} }
private loadChats() { private loadChats() {
const renderedPeerIds: Set<number> = new Set(); const renderedPeerIds: Set<PeerId> = new Set();
const middleware = this.middleware.get(); const middleware = this.middleware.get();
for(let i in this.searchGroups) { for(let i in this.searchGroups) {
@ -934,7 +965,7 @@ export default class AppSearchSuper {
const query = this.searchContext.query; const query = this.searchContext.query;
if(query) { if(query) {
const setResults = (results: number[], group: SearchGroup, showMembersCount = false) => { const setResults = (results: PeerId[], group: SearchGroup, showMembersCount = false) => {
results.forEach((peerId) => { results.forEach((peerId) => {
if(renderedPeerIds.has(peerId)) { if(renderedPeerIds.has(peerId)) {
return; return;
@ -957,7 +988,7 @@ export default class AppSearchSuper {
if(showMembersCount && (peer.participants_count || peer.participants)) { if(showMembersCount && (peer.participants_count || peer.participants)) {
const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi'); const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi');
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>'); dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>');
dom.lastMessageSpan.append(appProfileManager.getChatMembersString(-peerId)); dom.lastMessageSpan.append(appProfileManager.getChatMembersString(peerId.toChatId()));
} else if(peerId === rootScope.myId) { } else if(peerId === rootScope.myId) {
dom.lastMessageSpan.append(i18n('Presence.YourChat')); dom.lastMessageSpan.append(i18n('Presence.YourChat'));
} else { } else {
@ -989,7 +1020,7 @@ export default class AppSearchSuper {
}; };
return Promise.all([ return Promise.all([
appUsersManager.getContacts(query, true) appUsersManager.getContactsPeerIds(query, true)
.then(onLoad) .then(onLoad)
.then((contacts) => { .then((contacts) => {
if(contacts) { if(contacts) {
@ -1050,7 +1081,7 @@ export default class AppSearchSuper {
autonomous: true autonomous: true
}); });
dom.lastMessageSpan.append(peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appProfileManager.getChatMembersString(-peerId)); dom.lastMessageSpan.append(peerId.isUser() ? appUsersManager.getUserStatusString(peerId) : appProfileManager.getChatMembersString(peerId.toChatId()));
}); });
if(!state.recentSearch.length) { if(!state.recentSearch.length) {
@ -1093,7 +1124,7 @@ export default class AppSearchSuper {
} }
private loadMembers(mediaTab: SearchSuperMediaTab) { private loadMembers(mediaTab: SearchSuperMediaTab) {
const id = -this.searchContext.peerId; const id = this.searchContext.peerId.toChatId();
const middleware = this.middleware.get(); const middleware = this.middleware.get();
let promise: Promise<void>; let promise: Promise<void>;
@ -1114,7 +1145,7 @@ export default class AppSearchSuper {
return; return;
} }
const peerId = +li.dataset.peerId; const peerId = li.dataset.peerId.toPeerId();
let promise: Promise<any> = Promise.resolve(); let promise: Promise<any> = Promise.resolve();
if(mediaSizes.isMobile) { if(mediaSizes.isMobile) {
promise = appSidebarRight.toggleSidebar(false); promise = appSidebarRight.toggleSidebar(false);
@ -1130,7 +1161,7 @@ export default class AppSearchSuper {
participants.forEach(participant => { participants.forEach(participant => {
const peerId = appChatsManager.getParticipantPeerId(participant); const peerId = appChatsManager.getParticipantPeerId(participant);
if(peerId < 0) { if(peerId.isAnyChat()) {
return; return;
} }
@ -1386,7 +1417,7 @@ export default class AppSearchSuper {
return !this.loaded[inputFilter] || (this.historyStorage[inputFilter] && this.usedFromHistory[inputFilter] < this.historyStorage[inputFilter].length); return !this.loaded[inputFilter] || (this.historyStorage[inputFilter] && this.usedFromHistory[inputFilter] < this.historyStorage[inputFilter].length);
}); });
if(peerId > 0) { if(peerId.isUser()) {
toLoad.findAndSplice(mediaTab => mediaTab.type === 'members'); toLoad.findAndSplice(mediaTab => mediaTab.type === 'members');
} }
@ -1456,7 +1487,7 @@ export default class AppSearchSuper {
} }
public canViewMembers() { public canViewMembers() {
return this.searchContext.peerId < 0 && !appChatsManager.isBroadcast(-this.searchContext.peerId) && appChatsManager.hasRights(-this.searchContext.peerId, 'view_participants'); return this.searchContext.peerId.isAnyChat() && !appChatsManager.isBroadcast(this.searchContext.peerId.toChatId()) && appChatsManager.hasRights(this.searchContext.peerId.toChatId(), 'view_participants');
} }
public cleanup() { public cleanup() {
@ -1566,7 +1597,7 @@ export default class AppSearchSuper {
} }
public setQuery({peerId, query, threadId, historyStorage, folderId, minDate, maxDate}: { public setQuery({peerId, query, threadId, historyStorage, folderId, minDate, maxDate}: {
peerId: number, peerId: PeerId,
query?: string, query?: string,
threadId?: number, threadId?: number,
historyStorage?: AppSearchSuper['historyStorage'], historyStorage?: AppSearchSuper['historyStorage'],
@ -1575,7 +1606,7 @@ export default class AppSearchSuper {
maxDate?: number maxDate?: number
}) { }) {
this.searchContext = { this.searchContext = {
peerId: peerId || 0, peerId,
query: query || '', query: query || '',
inputFilter: {_: this.mediaTab.inputFilter}, inputFilter: {_: this.mediaTab.inputFilter},
threadId, threadId,

View File

@ -20,7 +20,7 @@ import findUpClassName from "../helpers/dom/findUpClassName";
import PeerTitle from "./peerTitle"; import PeerTitle from "./peerTitle";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import replaceContent from "../helpers/dom/replaceContent"; import replaceContent from "../helpers/dom/replaceContent";
import { filterUnique } from "../helpers/array"; import { filterUnique, indexOfAndSplice } from "../helpers/array";
import debounce from "../helpers/schedulers/debounce"; import debounce from "../helpers/schedulers/debounce";
import windowSize from "../helpers/windowSize"; import windowSize from "../helpers/windowSize";
@ -28,7 +28,6 @@ type PeerType = 'contacts' | 'dialogs' | 'channelParticipants';
// TODO: правильная сортировка для addMembers, т.е. для peerType: 'contacts', потому что там идут сначала контакты - потом неконтакты, а должно всё сортироваться по имени // TODO: правильная сортировка для addMembers, т.е. для peerType: 'contacts', потому что там идут сначала контакты - потом неконтакты, а должно всё сортироваться по имени
let loadedAllDialogs = false, loadAllDialogsPromise: Promise<any>;
export default class AppSelectPeers { export default class AppSelectPeers {
public container = document.createElement('div'); public container = document.createElement('div');
public list = appDialogsManager.createChatList(/* { public list = appDialogsManager.createChatList(/* {
@ -42,8 +41,8 @@ export default class AppSelectPeers {
public selectedContainer: HTMLElement; public selectedContainer: HTMLElement;
public input: HTMLInputElement; public input: HTMLInputElement;
//public selected: {[peerId: number]: HTMLElement} = {}; //public selected: {[peerId: PeerId]: HTMLElement} = {};
public selected = new Set<any>(); public selected = new Set<PeerId | string>();
public freezed = false; public freezed = false;
@ -52,23 +51,23 @@ export default class AppSelectPeers {
private promise: Promise<any>; private promise: Promise<any>;
private query = ''; private query = '';
private cachedContacts: number[]; private cachedContacts: PeerId[];
private loadedWhat: Partial<{[k in 'dialogs' | 'archived' | 'contacts' | 'channelParticipants']: true}> = {}; private loadedWhat: Partial<{[k in 'dialogs' | 'archived' | 'contacts' | 'channelParticipants']: true}> = {};
private renderedPeerIds: Set<number> = new Set(); private renderedPeerIds: Set<PeerId> = new Set();
private appendTo: HTMLElement; private appendTo: HTMLElement;
private onChange: (length: number) => void; private onChange: (length: number) => void;
private peerType: PeerType[] = ['dialogs']; private peerType: PeerType[] = ['dialogs'];
private renderResultsFunc: (peerIds: number[]) => void; private renderResultsFunc: (peerIds: PeerId[]) => void;
private chatRightsAction: ChatRights; private chatRightsAction: ChatRights;
private multiSelect = true; private multiSelect = true;
private rippleEnabled = true; private rippleEnabled = true;
private avatarSize = 48; private avatarSize = 48;
private tempIds: {[k in keyof AppSelectPeers['loadedWhat']]: number} = {}; private tempIds: {[k in keyof AppSelectPeers['loadedWhat']]: number} = {};
private peerId = 0; private peerId: PeerId;
private placeholder: LangPackKey; private placeholder: LangPackKey;
@ -95,7 +94,7 @@ export default class AppSelectPeers {
this.container.classList.add('selector'); this.container.classList.add('selector');
const f = (this.renderResultsFunc || this.renderResults).bind(this); const f = (this.renderResultsFunc || this.renderResults).bind(this);
this.renderResultsFunc = (peerIds: number[]) => { this.renderResultsFunc = (peerIds: PeerId[]) => {
if(this.needSwitchList) { if(this.needSwitchList) {
this.scrollable.splitUp.replaceWith(this.list); this.scrollable.splitUp.replaceWith(this.list);
this.scrollable.setVirtualContainer(this.list); this.scrollable.setVirtualContainer(this.list);
@ -144,7 +143,7 @@ export default class AppSelectPeers {
const peerId = target.dataset.key; const peerId = target.dataset.key;
const li = this.chatsContainer.querySelector('[data-peer-id="' + peerId + '"]') as HTMLElement; const li = this.chatsContainer.querySelector('[data-peer-id="' + peerId + '"]') as HTMLElement;
if(!li) { if(!li) {
this.remove(+peerId || peerId); this.remove(peerId.toPeerId());
} else { } else {
li.click(); li.click();
} }
@ -165,8 +164,8 @@ export default class AppSelectPeers {
if(!target) return; if(!target) return;
if(this.freezed) return; if(this.freezed) return;
let key: any = target.dataset.peerId; let key: PeerId | string = target.dataset.peerId;
key = +key || key; key = key.isPeerId() ? key.toPeerId() : key;
if(!this.multiSelect) { if(!this.multiSelect) {
this.add(key); this.add(key);
@ -208,7 +207,7 @@ export default class AppSelectPeers {
private onInput = () => { private onInput = () => {
const value = this.input.value; const value = this.input.value;
if(this.query !== value) { if(this.query !== value) {
if(this.peerType.includes('contacts')) { if(this.peerType.includes('contacts') || this.peerType.includes('dialogs')) {
this.cachedContacts = null; this.cachedContacts = null;
} }
@ -283,9 +282,9 @@ export default class AppSelectPeers {
this.renderSaved(); this.renderSaved();
this.offsetIndex = newOffsetIndex; this.offsetIndex = newOffsetIndex;
this.renderResultsFunc(dialogs.map(dialog => dialog.peerId));
} }
this.renderResultsFunc(dialogs.map(dialog => dialog.peerId));
if(value.isEnd) { if(value.isEnd) {
if(!this.loadedWhat.dialogs) { if(!this.loadedWhat.dialogs) {
@ -299,18 +298,18 @@ export default class AppSelectPeers {
} else { } else {
this.loadedWhat.archived = true; this.loadedWhat.archived = true;
if(!this.loadedWhat.contacts && this.peerType.includes('contacts')) { if(!this.loadedWhat.contacts/* && this.peerType.includes('contacts') */) {
return this.getMoreContacts(); return this.getMoreContacts();
} }
} }
} }
} }
private filterByRights(peerId: number) { private filterByRights(peerId: PeerId) {
return ( return (
peerId > 0 && peerId.isUser() &&
(this.chatRightsAction !== 'send_messages' || appUsersManager.canSendToUser(peerId)) (this.chatRightsAction !== 'send_messages' || appUsersManager.canSendToUser(peerId))
) || appChatsManager.hasRights(-peerId, this.chatRightsAction); ) || appChatsManager.hasRights(peerId.toChatId(), this.chatRightsAction);
} }
private async getMoreContacts() { private async getMoreContacts() {
@ -320,6 +319,8 @@ export default class AppSelectPeers {
return; return;
} }
const isGlobalSearch = this.peerType.includes('contacts');
if(!this.cachedContacts) { if(!this.cachedContacts) {
/* const promises: Promise<any>[] = [appUsersManager.getContacts(this.query)]; /* const promises: Promise<any>[] = [appUsersManager.getContacts(this.query)];
if(!this.peerType.includes('dialogs')) { if(!this.peerType.includes('dialogs')) {
@ -330,39 +331,40 @@ export default class AppSelectPeers {
this.cachedContacts = (await this.promise)[0].slice(); */ this.cachedContacts = (await this.promise)[0].slice(); */
const tempId = this.getTempId('contacts'); const tempId = this.getTempId('contacts');
const promise = Promise.all([ const promise = Promise.all([
appUsersManager.getContacts(this.query), isGlobalSearch ? appUsersManager.getContactsPeerIds(this.query) : [],
this.query ? appUsersManager.searchContacts(this.query) : undefined this.query ? appUsersManager.searchContacts(this.query) : undefined
]); ]);
this.promise = promise; this.promise = promise;
const [cachedContacts, searchResult] = await promise; let [cachedContacts, searchResult] = await promise;
if(this.tempIds.contacts !== tempId) { if(this.tempIds.contacts !== tempId) {
return; return;
} }
if(searchResult) { if(searchResult) {
let resultPeerIds = searchResult.my_results.concat(searchResult.results); // do not add global result if only dialogs needed
let resultPeerIds = isGlobalSearch ? searchResult.my_results.concat(searchResult.results) : searchResult.my_results;
if(this.chatRightsAction) { if(this.chatRightsAction) {
resultPeerIds = resultPeerIds.filter(peerId => this.filterByRights(peerId)); resultPeerIds = resultPeerIds.filter(peerId => this.filterByRights(peerId));
} }
if(!this.peerType.includes('dialogs')) { if(!this.peerType.includes('dialogs')) {
resultPeerIds = resultPeerIds.filter(peerId => peerId > 0); resultPeerIds = resultPeerIds.filter(peerId => peerId.isUser());
} }
this.cachedContacts = filterUnique(cachedContacts.concat(resultPeerIds)); this.cachedContacts = filterUnique(cachedContacts.concat(resultPeerIds));
} else this.cachedContacts = cachedContacts.slice(); } else this.cachedContacts = cachedContacts.slice();
this.cachedContacts.findAndSplice(userId => userId === rootScope.myId); // no my account indexOfAndSplice(this.cachedContacts, rootScope.myId); // no my account
this.promise = null; this.promise = null;
} }
if(this.cachedContacts.length) { // if(this.cachedContacts.length) {
const pageCount = windowSize.windowH / 72 * 1.25 | 0; const pageCount = windowSize.windowH / 72 * 1.25 | 0;
const arr = this.cachedContacts.splice(0, pageCount); const arr = this.cachedContacts.splice(0, pageCount);
this.renderResultsFunc(arr); this.renderResultsFunc(arr);
} // }
if(!this.cachedContacts.length) { if(!this.cachedContacts.length) {
this.loadedWhat.contacts = true; this.loadedWhat.contacts = true;
@ -384,7 +386,7 @@ export default class AppSelectPeers {
const pageCount = 50; // same as in group permissions to use cache const pageCount = 50; // same as in group permissions to use cache
const tempId = this.getTempId('channelParticipants'); const tempId = this.getTempId('channelParticipants');
const promise = appProfileManager.getChannelParticipants(-this.peerId, {_: 'channelParticipantsSearch', q: this.query}, pageCount, this.list.childElementCount); const promise = appProfileManager.getChannelParticipants(this.peerId.toChatId(), {_: 'channelParticipantsSearch', q: this.query}, pageCount, this.list.childElementCount);
const participants = await promise; const participants = await promise;
if(this.tempIds.channelParticipants !== tempId) { if(this.tempIds.channelParticipants !== tempId) {
return; return;
@ -393,7 +395,7 @@ export default class AppSelectPeers {
const peerIds = participants.participants.map(participant => { const peerIds = participants.participants.map(participant => {
return appChatsManager.getParticipantPeerId(participant); return appChatsManager.getParticipantPeerId(participant);
}); });
peerIds.findAndSplice(u => u === rootScope.myId); indexOfAndSplice(peerIds, rootScope.myId);
this.renderResultsFunc(peerIds); this.renderResultsFunc(peerIds);
if(this.list.childElementCount >= participants.count || participants.participants.length < pageCount) { if(this.list.childElementCount >= participants.count || participants.participants.length < pageCount) {
@ -409,18 +411,18 @@ export default class AppSelectPeers {
const get = () => { const get = () => {
const promises: Promise<any>[] = []; const promises: Promise<any>[] = [];
if(!loadedAllDialogs && (this.peerType.includes('dialogs')/* || this.peerType.includes('contacts') */)) { // if(!loadedAllDialogs && (this.peerType.includes('dialogs')/* || this.peerType.includes('contacts') */)) {
if(!loadAllDialogsPromise) { // if(!loadAllDialogsPromise) {
loadAllDialogsPromise = appMessagesManager.getConversationsAll() // loadAllDialogsPromise = appMessagesManager.getConversationsAll()
.then(() => { // .then(() => {
loadedAllDialogs = true; // loadedAllDialogs = true;
}).finally(() => { // }).finally(() => {
loadAllDialogsPromise = null; // loadAllDialogsPromise = null;
}); // });
} // }
promises.push(loadAllDialogsPromise); // promises.push(loadAllDialogsPromise);
} // }
if((this.peerType.includes('dialogs')/* || this.loadedWhat.contacts */) && !this.loadedWhat.archived) { // to load non-contacts if((this.peerType.includes('dialogs')/* || this.loadedWhat.contacts */) && !this.loadedWhat.archived) { // to load non-contacts
promises.push(this.getMoreDialogs()); promises.push(this.getMoreDialogs());
@ -430,7 +432,7 @@ export default class AppSelectPeers {
} }
} }
if(this.peerType.includes('contacts') && !this.loadedWhat.contacts) { if((this.peerType.includes('contacts') || this.peerType.includes('dialogs')) && !this.loadedWhat.contacts) {
promises.push(this.getMoreContacts()); promises.push(this.getMoreContacts());
} }
@ -450,7 +452,7 @@ export default class AppSelectPeers {
return promise; return promise;
} }
private renderResults(peerIds: number[]) { private renderResults(peerIds: PeerId[]) {
//console.log('will renderResults:', peerIds); //console.log('will renderResults:', peerIds);
// оставим только неконтакты с диалогов // оставим только неконтакты с диалогов
@ -482,8 +484,8 @@ export default class AppSelectPeers {
} }
let subtitleEl: HTMLElement; let subtitleEl: HTMLElement;
if(peerId < 0) { if(peerId.isAnyChat()) {
subtitleEl = appProfileManager.getChatMembersString(-peerId); subtitleEl = appProfileManager.getChatMembersString(peerId.toChatId());
} else if(peerId === rootScope.myId) { } else if(peerId === rootScope.myId) {
subtitleEl = i18n(this.selfPresence); subtitleEl = i18n(this.selfPresence);
} else { } else {
@ -494,9 +496,9 @@ export default class AppSelectPeers {
}); });
} }
public add(peerId: any, title?: string | HTMLElement, scroll = true) { public add(key: PeerId | string, title?: string | HTMLElement, scroll = true) {
//console.trace('add'); //console.trace('add');
this.selected.add(peerId); this.selected.add(key);
if(!this.multiSelect) { if(!this.multiSelect) {
this.onChange(this.selected.size); this.onChange(this.selected.size);
@ -516,13 +518,13 @@ export default class AppSelectPeers {
avatarEl.setAttribute('dialog', '1'); avatarEl.setAttribute('dialog', '1');
avatarEl.classList.add('avatar-32'); avatarEl.classList.add('avatar-32');
div.dataset.key = '' + peerId; div.dataset.key = '' + key;
if(typeof(peerId) === 'number') { if(key.isPeerId()) {
if(title === undefined) { if(title === undefined) {
title = new PeerTitle({peerId, dialog: true}).element; title = new PeerTitle({peerId: key.toPeerId(), dialog: true}).element;
} }
avatarEl.setAttribute('peer', '' + peerId); avatarEl.setAttribute('peer', '' + key);
} }
if(title) { if(title) {
@ -547,7 +549,7 @@ export default class AppSelectPeers {
return div; return div;
} }
public remove(key: any) { public remove(key: PeerId | string) {
if(!this.multiSelect) return; if(!this.multiSelect) return;
//const div = this.selected[peerId]; //const div = this.selected[peerId];
const div = this.selectedContainer.querySelector(`[data-key="${key}"]`) as HTMLElement; const div = this.selectedContainer.querySelector(`[data-key="${key}"]`) as HTMLElement;

View File

@ -352,7 +352,7 @@ export const findAudioTargets = (anchor: HTMLElement, useSearch: boolean) => {
const elements = Array.from(container.querySelectorAll(selector)) as HTMLElement[]; const elements = Array.from(container.querySelectorAll(selector)) as HTMLElement[];
const idx = elements.indexOf(anchor); const idx = elements.indexOf(anchor);
const mediaItems: MediaItem[] = elements.map(element => ({peerId: +element.dataset.peerId, mid: +element.dataset.mid})); const mediaItems: MediaItem[] = elements.map(element => ({peerId: element.dataset.peerId.toPeerId(), mid: +element.dataset.mid}));
prev = mediaItems.slice(0, idx); prev = mediaItems.slice(0, idx);
next = mediaItems.slice(idx + 1); next = mediaItems.slice(idx + 1);
@ -481,31 +481,31 @@ export default class AudioElement extends HTMLElement {
return togglePlay; return togglePlay;
}; };
if(doc.thumbs?.length) {
const imgs: HTMLImageElement[] = [];
const wrapped = wrapPhoto({
photo: doc,
message: null,
container: toggle,
boxWidth: 48,
boxHeight: 48,
loadPromises: this.loadPromises,
withoutPreloader: true,
lazyLoadQueue: this.lazyLoadQueue
});
toggle.style.width = toggle.style.height = '';
if(wrapped.images.thumb) imgs.push(wrapped.images.thumb);
if(wrapped.images.full) imgs.push(wrapped.images.full);
this.classList.add('audio-with-thumb');
imgs.forEach(img => img.classList.add('audio-thumb'));
}
if(!isOutgoing) { if(!isOutgoing) {
let preloader: ProgressivePreloader = this.preloader; let preloader: ProgressivePreloader = this.preloader;
onLoad(doc.type !== 'audio' && !this.noAutoDownload); onLoad(doc.type !== 'audio' && !this.noAutoDownload);
if(doc.thumbs) {
const imgs: HTMLImageElement[] = [];
const wrapped = wrapPhoto({
photo: doc,
message: null,
container: toggle,
boxWidth: 48,
boxHeight: 48,
loadPromises: this.loadPromises,
withoutPreloader: true,
lazyLoadQueue: this.lazyLoadQueue
});
toggle.style.width = toggle.style.height = '';
if(wrapped.images.thumb) imgs.push(wrapped.images.thumb);
if(wrapped.images.full) imgs.push(wrapped.images.full);
this.classList.add('audio-with-thumb');
imgs.forEach(img => img.classList.add('audio-thumb'));
}
const r = (shouldPlay: boolean) => { const r = (shouldPlay: boolean) => {
if(this.audio.src) { if(this.audio.src) {
return; return;

View File

@ -7,7 +7,7 @@
import appMessagesManager from "../lib/appManagers/appMessagesManager"; import appMessagesManager from "../lib/appManagers/appMessagesManager";
import appProfileManager from "../lib/appManagers/appProfileManager"; import appProfileManager from "../lib/appManagers/appProfileManager";
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
import { Message } from "../layer"; import { Message, Photo } from "../layer";
import appPeersManager from "../lib/appManagers/appPeersManager"; import appPeersManager from "../lib/appManagers/appPeersManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager"; import appPhotosManager from "../lib/appManagers/appPhotosManager";
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue"; import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
@ -16,8 +16,9 @@ import { cancelEvent } from "../helpers/dom/cancelEvent";
import appAvatarsManager from "../lib/appManagers/appAvatarsManager"; import appAvatarsManager from "../lib/appManagers/appAvatarsManager";
import AppMediaViewer from "./appMediaViewer"; import AppMediaViewer from "./appMediaViewer";
import AppMediaViewerAvatar from "./appMediaViewerAvatar"; import AppMediaViewerAvatar from "./appMediaViewerAvatar";
import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config";
const onAvatarUpdate = (peerId: number) => { const onAvatarUpdate = (peerId: PeerId) => {
appAvatarsManager.removeFromAvatarsCache(peerId); appAvatarsManager.removeFromAvatarsCache(peerId);
(Array.from(document.querySelectorAll('avatar-element[peer="' + peerId + '"]')) as AvatarElement[]).forEach(elem => { (Array.from(document.querySelectorAll('avatar-element[peer="' + peerId + '"]')) as AvatarElement[]).forEach(elem => {
//console.log('updating avatar:', elem); //console.log('updating avatar:', elem);
@ -32,7 +33,14 @@ rootScope.addEventListener('peer_title_edit', (peerId) => {
} }
}); });
export async function openAvatarViewer(target: HTMLElement, peerId: number, middleware: () => boolean, message?: any, prevTargets?: {element: HTMLElement, item: string | Message.messageService}[], nextTargets?: typeof prevTargets) { export async function openAvatarViewer(
target: HTMLElement,
peerId: PeerId,
middleware: () => boolean,
message?: any,
prevTargets?: {element: HTMLElement, item: Photo.photo['id'] | Message.messageService}[],
nextTargets?: typeof prevTargets
) {
let photo = await appProfileManager.getFullPhoto(peerId); let photo = await appProfileManager.getFullPhoto(peerId);
if(!middleware() || !photo) { if(!middleware() || !photo) {
return; return;
@ -43,7 +51,7 @@ export async function openAvatarViewer(target: HTMLElement, peerId: number, midd
return good ? target : null; return good ? target : null;
}; };
if(peerId < 0) { if(peerId.isAnyChat()) {
const hadMessage = !!message; const hadMessage = !!message;
const inputFilter = 'inputMessagesFilterChatPhotos'; const inputFilter = 'inputMessagesFilterChatPhotos';
if(!message) { if(!message) {
@ -105,11 +113,11 @@ export async function openAvatarViewer(target: HTMLElement, peerId: number, midd
} }
} }
const believeMe: Map<number, Set<AvatarElement>> = new Map(); const believeMe: Map<PeerId, Set<AvatarElement>> = new Map();
const seen: Set<number> = new Set(); const seen: Set<PeerId> = new Set();
export default class AvatarElement extends HTMLElement { export default class AvatarElement extends HTMLElement {
private peerId: number; private peerId: PeerId;
private isDialog = false; private isDialog = false;
private peerTitle: string; private peerTitle: string;
public loadPromises: Promise<any>[]; public loadPromises: Promise<any>[];
@ -160,13 +168,14 @@ export default class AvatarElement extends HTMLElement {
//console.log('avatar changed attribute:', name, oldValue, newValue); //console.log('avatar changed attribute:', name, oldValue, newValue);
// вызывается при изменении одного из перечисленных выше атрибутов // вызывается при изменении одного из перечисленных выше атрибутов
if(name === 'peer') { if(name === 'peer') {
if(this.peerId === +newValue) { const newPeerId = (newValue || '').toPeerId() || NULL_PEER_ID;
if(this.peerId === newPeerId) {
return; return;
} }
this.peerId = appPeersManager.getPeerMigratedTo(+newValue) || +newValue; this.peerId = appPeersManager.getPeerMigratedTo(newPeerId) || newPeerId;
const wasPeerId = +oldValue; const wasPeerId = (oldValue || '').toPeerId() || NULL_PEER_ID;
if(wasPeerId) { if(wasPeerId) {
const set = believeMe.get(wasPeerId); const set = believeMe.get(wasPeerId);
if(set) { if(set) {

View File

@ -42,7 +42,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
}); });
} }
public render(data: {peerId: number, name?: string, description?: string}[]) { public render(data: {peerId: PeerId, name?: string, description?: string}[]) {
if(this.init) { if(this.init) {
if(!data.length) { if(!data.length) {
return; return;
@ -71,7 +71,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
public static listElement(options: { public static listElement(options: {
className: string, className: string,
peerId: number, peerId: PeerId,
name?: string, name?: string,
description?: string description?: string
}) { }) {

View File

@ -8,9 +8,10 @@ import rootScope from "../../lib/rootScope";
//import { generatePathData } from "../../helpers/dom"; //import { generatePathData } from "../../helpers/dom";
import { MyMessage } from "../../lib/appManagers/appMessagesManager"; import { MyMessage } from "../../lib/appManagers/appMessagesManager";
import type Chat from "./chat"; import type Chat from "./chat";
import { indexOfAndSplice } from "../../helpers/array";
type Group = {bubble: HTMLElement, mid: number, timestamp: number}[]; type Group = {bubble: HTMLElement, mid: number, timestamp: number}[];
type BubbleGroup = {timestamp: number, fromId: number, mid: number, group: Group}; type BubbleGroup = {timestamp: number, fromId: PeerId, mid: number, group: Group};
export default class BubbleGroups { export default class BubbleGroups {
private bubbles: Array<BubbleGroup> = []; // map to group private bubbles: Array<BubbleGroup> = []; // map to group
private detailsMap: Map<HTMLElement, BubbleGroup> = new Map(); private detailsMap: Map<HTMLElement, BubbleGroup> = new Map();
@ -28,7 +29,7 @@ export default class BubbleGroups {
if(details.group.length) { if(details.group.length) {
details.group.findAndSplice(d => d.bubble === bubble); details.group.findAndSplice(d => d.bubble === bubble);
if(!details.group.length) { if(!details.group.length) {
this.groups.findAndSplice(g => g === details.group); indexOfAndSplice(this.groups, details.group);
} else { } else {
this.updateGroup(details.group); this.updateGroup(details.group);
} }
@ -37,6 +38,13 @@ export default class BubbleGroups {
this.detailsMap.delete(bubble); this.detailsMap.delete(bubble);
} }
} }
changeBubbleMid(bubble: HTMLElement, mid: number) {
const details = this.detailsMap.get(bubble);
if(details) {
details.mid = mid;
}
}
addBubble(bubble: HTMLElement, message: MyMessage, reverse: boolean) { addBubble(bubble: HTMLElement, message: MyMessage, reverse: boolean) {
//return; //return;
@ -48,7 +56,7 @@ export default class BubbleGroups {
// fix for saved messages forward to self // fix for saved messages forward to self
if(fromId === rootScope.myId && message.peerId === rootScope.myId && (message as any).fwdFromId === fromId) { if(fromId === rootScope.myId && message.peerId === rootScope.myId && (message as any).fwdFromId === fromId) {
fromId = -fromId; fromId = fromId.toPeerId(true);
} }
// try to find added // try to find added

View File

@ -20,7 +20,7 @@ import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import { getObjectKeysAndSort } from "../../helpers/object"; import { getObjectKeysAndSort } from "../../helpers/object";
import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport"; import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport";
import { logger } from "../../lib/logger"; import { logger } from "../../lib/logger";
import rootScope, { BroadcastEvents } from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import BubbleGroups from "./bubbleGroups"; import BubbleGroups from "./bubbleGroups";
import PopupDatePicker from "../popups/datePicker"; import PopupDatePicker from "../popups/datePicker";
import PopupForward from "../popups/forward"; import PopupForward from "../popups/forward";
@ -75,6 +75,9 @@ import { SEND_WHEN_ONLINE_TIMESTAMP } from "../../lib/mtproto/constants";
import windowSize from "../../helpers/windowSize"; import windowSize from "../../helpers/windowSize";
import { formatPhoneNumber } from "../../helpers/formatPhoneNumber"; import { formatPhoneNumber } from "../../helpers/formatPhoneNumber";
import AppMediaViewer from "../appMediaViewer"; import AppMediaViewer from "../appMediaViewer";
import SetTransition from "../singleTransition";
import handleHorizontalSwipe from "../../helpers/dom/handleHorizontalSwipe";
import { cancelContextMenuOpening } from "../misc";
const USE_MEDIA_TAILS = false; const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([ const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
@ -98,11 +101,11 @@ export default class ChatBubbles {
private getHistoryTopPromise: Promise<boolean>; private getHistoryTopPromise: Promise<boolean>;
private getHistoryBottomPromise: Promise<boolean>; private getHistoryBottomPromise: Promise<boolean>;
public peerId = 0; public peerId: PeerId;
//public messagesCount: number = -1; //public messagesCount: number = -1;
private unreadOut = new Set<number>(); private unreadOut = new Set<number>();
public needUpdate: {replyToPeerId: number, replyMid: number, mid: number}[] = []; // if need wrapSingleMessage public needUpdate: {replyToPeerId: PeerId, replyMid: number, mid: number}[] = []; // if need wrapSingleMessage
public bubbles: {[mid: string]: HTMLDivElement} = {}; public bubbles: {[mid: string]: HTMLDivElement} = {};
public skippedMids: Set<number> = new Set(); public skippedMids: Set<number> = new Set();
@ -170,7 +173,8 @@ export default class ChatBubbles {
private viewsMids: Set<number> = new Set(); private viewsMids: Set<number> = new Set();
private sendViewCountersDebounced: () => Promise<void>; private sendViewCountersDebounced: () => Promise<void>;
constructor(private chat: Chat, constructor(
private chat: Chat,
private appMessagesManager: AppMessagesManager, private appMessagesManager: AppMessagesManager,
private appStickersManager: AppStickersManager, private appStickersManager: AppStickersManager,
private appUsersManager: AppUsersManager, private appUsersManager: AppUsersManager,
@ -216,7 +220,7 @@ export default class ChatBubbles {
const message = this.chat.getMessage(mid); const message = this.chat.getMessage(mid);
if(+bubble.dataset.timestamp >= (message.date + serverTimeManager.serverTimeOffset - 1)) { if(+bubble.dataset.timestamp >= (message.date + serverTimeManager.serverTimeOffset - 1)) {
//this.bubbleGroups.addBubble(bubble, message, false); // ! TEMP COMMENTED this.bubbleGroups.changeBubbleMid(bubble, mid);
return; return;
} }
@ -266,10 +270,18 @@ export default class ChatBubbles {
} }
} }
if(message.media?.document && !message.media.document.type) { if(message.media?.document) {
const div = bubble.querySelector(`.document-container[data-mid="${tempId}"] .document`); const div = bubble.querySelector(`.document-container[data-mid="${tempId}"] .document`);
if(div) { if(div) {
div.replaceWith(wrapDocument({message})); const container = findUpClassName(div, 'document-container');
if(!tempMessage.media?.document?.thumbs?.length && message.media.document.thumbs?.length) {
div.replaceWith(wrapDocument({message}));
}
if(container) {
container.dataset.mid = '' + mid;
}
} }
} }
@ -434,6 +446,74 @@ export default class ChatBubbles {
// attachClickEvent(this.bubblesContainer, this.onBubblesClick, {listenerSetter: this.listenerSetter}); // attachClickEvent(this.bubblesContainer, this.onBubblesClick, {listenerSetter: this.listenerSetter});
this.listenerSetter.add(this.bubblesContainer)('click', this.onBubblesClick/* , {capture: true, passive: false} */); this.listenerSetter.add(this.bubblesContainer)('click', this.onBubblesClick/* , {capture: true, passive: false} */);
if(IS_TOUCH_SUPPORTED) {
const className = 'is-gesturing-reply';
const MAX = 64;
const replyAfter = MAX * .75;
let shouldReply = false;
let target: HTMLElement;
let icon: HTMLElement;
handleHorizontalSwipe({
element: this.bubblesContainer,
verifyTouchTarget: (e) => {
if(this.chat.selection.isSelecting || !this.appMessagesManager.canSendToPeer(this.peerId, this.chat.threadId)) {
return false;
}
// cancelEvent(e);
target = findUpClassName(e.target, 'bubble');
if(target) {
SetTransition(target, className, true, 250);
void target.offsetLeft; // reflow
if(!icon) {
icon = document.createElement('span');
icon.classList.add('tgico-reply_filled', 'bubble-gesture-reply-icon');
} else {
icon.classList.remove('is-visible');
icon.style.opacity = '';
}
target/* .querySelector('.bubble-content') */.append(icon);
}
return !!target;
},
onSwipe: (xDiff, yDiff) => {
shouldReply = xDiff >= replyAfter;
if(shouldReply && !icon.classList.contains('is-visible')) {
icon.classList.add('is-visible');
}
icon.style.opacity = '' + Math.min(1, xDiff / replyAfter);
const x = -Math.max(0, Math.min(MAX, xDiff));
target.style.transform = `translateX(${x}px)`;
cancelContextMenuOpening();
},
onReset: () => {
const _target = target;
SetTransition(_target, className, false, 250, () => {
if(icon.parentElement === _target) {
icon.classList.remove('is-visible');
icon.remove();
}
});
fastRaf(() => {
_target.style.transform = ``;
if(shouldReply) {
const {mid} = _target.dataset;
this.chat.input.initMessageReply(+mid);
shouldReply = false;
}
});
},
listenerOptions: {capture: true}
});
}
if(DEBUG) { if(DEBUG) {
this.listenerSetter.add(this.bubblesContainer)('dblclick', (e) => { this.listenerSetter.add(this.bubblesContainer)('dblclick', (e) => {
const bubble = findUpClassName(e.target, 'grouped-item') || findUpClassName(e.target, 'bubble'); const bubble = findUpClassName(e.target, 'grouped-item') || findUpClassName(e.target, 'bubble');
@ -540,7 +620,7 @@ export default class ChatBubbles {
}); });
this.listenerSetter.add(rootScope)('chat_update', (chatId) => { this.listenerSetter.add(rootScope)('chat_update', (chatId) => {
if(this.peerId === -chatId) { if(this.peerId === chatId.toPeerId(true)) {
const hadRights = this.chatInner.classList.contains('has-rights'); const hadRights = this.chatInner.classList.contains('has-rights');
const hasRights = this.appMessagesManager.canSendToPeer(this.peerId, this.chat.threadId); const hasRights = this.appMessagesManager.canSendToPeer(this.peerId, this.chat.threadId);
@ -551,7 +631,7 @@ export default class ChatBubbles {
} }
}); });
this.listenerSetter.add(rootScope)('settings_updated', (e: BroadcastEvents['settings_updated']) => { this.listenerSetter.add(rootScope)('settings_updated', (e) => {
if(e.key === 'settings.emoji.big') { if(e.key === 'settings.emoji.big') {
const isScrolledDown = this.scrollable.isScrolledDown; const isScrolledDown = this.scrollable.isScrolledDown;
if(!isScrolledDown) { if(!isScrolledDown) {
@ -872,7 +952,7 @@ export default class ChatBubbles {
const contactDiv: HTMLElement = findUpClassName(target, 'contact'); const contactDiv: HTMLElement = findUpClassName(target, 'contact');
if(contactDiv) { if(contactDiv) {
this.chat.appImManager.setInnerPeer(+contactDiv.dataset.peerId); this.chat.appImManager.setInnerPeer(contactDiv.dataset.peerId.toPeerId());
return; return;
} }
@ -890,7 +970,7 @@ export default class ChatBubbles {
const replies = message.replies; const replies = message.replies;
if(replies) { if(replies) {
this.appMessagesManager.getDiscussionMessage(this.peerId, message.mid).then(message => { this.appMessagesManager.getDiscussionMessage(this.peerId, message.mid).then(message => {
this.chat.appImManager.setInnerPeer(-replies.channel_id, undefined, 'discussion', (message as MyMessage).mid); this.chat.appImManager.setInnerPeer(replies.channel_id.toPeerId(true), undefined, 'discussion', (message as MyMessage).mid);
}); });
} }
} }
@ -901,21 +981,19 @@ export default class ChatBubbles {
const nameDiv = findUpClassName(target, 'peer-title') || findUpClassName(target, 'name') || findUpTag(target, 'AVATAR-ELEMENT'); const nameDiv = findUpClassName(target, 'peer-title') || findUpClassName(target, 'name') || findUpTag(target, 'AVATAR-ELEMENT');
if(nameDiv) { if(nameDiv) {
target = nameDiv || target; target = nameDiv || target;
const peerId = +(target.dataset.peerId || target.getAttribute('peer')); const peerId = (target.dataset.peerId || target.getAttribute('peer'));
const savedFrom = target.dataset.savedFrom; const savedFrom = target.dataset.savedFrom;
if(nameDiv.classList.contains('is-via')) { if(nameDiv.classList.contains('is-via')) {
const message = '@' + this.appUsersManager.getUser(peerId).username + ' '; const message = '@' + this.appUsersManager.getUser(peerId).username + ' ';
this.appDraftsManager.setDraft(this.peerId, this.chat.threadId, message); this.appDraftsManager.setDraft(this.peerId, this.chat.threadId, message);
cancelEvent(e); cancelEvent(e);
} else if(savedFrom) { } else if(savedFrom) {
const splitted = savedFrom.split('_'); const [peerId, mid] = savedFrom.split('_');
const peerId = +splitted[0];
const msgId = +splitted[1];
this.chat.appImManager.setInnerPeer(peerId, msgId); this.chat.appImManager.setInnerPeer(peerId.toPeerId(), +mid);
} else { } else {
if(peerId) { if(peerId) {
this.chat.appImManager.setInnerPeer(peerId); this.chat.appImManager.setInnerPeer(peerId.toPeerId());
} else { } else {
toast(I18n.format('HidAccount', true)); toast(I18n.format('HidAccount', true));
} }
@ -967,7 +1045,7 @@ export default class ChatBubbles {
return media._ === 'photo' || ['video', 'gif'].includes(media.type); return media._ === 'photo' || ['video', 'gif'].includes(media.type);
}; };
const targets: {element: HTMLElement, mid: number, peerId: number}[] = []; const targets: {element: HTMLElement, mid: number, peerId: PeerId}[] = [];
const ids = Object.keys(this.bubbles).map(k => +k).filter(id => { const ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
//if(!this.scrollable.visibleElements.find(e => e.element === this.bubbles[id])) return false; //if(!this.scrollable.visibleElements.find(e => e.element === this.bubbles[id])) return false;
@ -1051,11 +1129,9 @@ export default class ChatBubbles {
if(['DIV', 'SPAN'].indexOf(target.tagName) !== -1/* || target.tagName === 'A' */) { if(['DIV', 'SPAN'].indexOf(target.tagName) !== -1/* || target.tagName === 'A' */) {
if(target.classList.contains('goto-original')) { if(target.classList.contains('goto-original')) {
const savedFrom = bubble.dataset.savedFrom; const savedFrom = bubble.dataset.savedFrom;
const splitted = savedFrom.split('_'); const [peerId, mid] = savedFrom.split('_');
const peerId = +splitted[0];
const msgId = +splitted[1];
////this.log('savedFrom', peerId, msgID); ////this.log('savedFrom', peerId, msgID);
this.chat.appImManager.setInnerPeer(peerId, msgId); this.chat.appImManager.setInnerPeer(peerId.toPeerId(), +mid);
return; return;
} else if(target.classList.contains('forward')) { } else if(target.classList.contains('forward')) {
const mid = +bubble.dataset.mid; const mid = +bubble.dataset.mid;
@ -1681,13 +1757,13 @@ export default class ChatBubbles {
////console.timeEnd('appImManager cleanup'); ////console.timeEnd('appImManager cleanup');
} }
public setPeer(peerId: number, lastMsgId?: number): {cached?: boolean, promise: Chat['setPeerPromise']} { public setPeer(peerId: PeerId, lastMsgId?: number): {cached?: boolean, promise: Chat['setPeerPromise']} {
//console.time('appImManager setPeer'); //console.time('appImManager setPeer');
//console.time('appImManager setPeer pre promise'); //console.time('appImManager setPeer pre promise');
////console.time('appImManager: pre render start'); ////console.time('appImManager: pre render start');
if(!peerId) { if(!peerId) {
this.cleanup(true); this.cleanup(true);
this.peerId = 0; this.peerId = peerId;
return null; return null;
} }
@ -2186,6 +2262,10 @@ export default class ChatBubbles {
bubble.className = classNames.join(' '); bubble.className = classNames.join(' ');
contentWrapper = bubble.lastElementChild as HTMLElement; contentWrapper = bubble.lastElementChild as HTMLElement;
if(!contentWrapper.classList.contains('bubble-content-wrapper')) {
contentWrapper = bubble.querySelector('.bubble-content-wrapper');
}
bubbleContainer = contentWrapper.firstElementChild as HTMLDivElement; bubbleContainer = contentWrapper.firstElementChild as HTMLDivElement;
bubbleContainer.innerHTML = ''; bubbleContainer.innerHTML = '';
bubbleContainer.style.cssText = ''; bubbleContainer.style.cssText = '';
@ -2242,9 +2322,9 @@ export default class ChatBubbles {
s.classList.add('service-msg'); s.classList.add('service-msg');
if(action) { if(action) {
if(action._ === 'messageActionChannelMigrateFrom') { if(action._ === 'messageActionChannelMigrateFrom') {
s.append(i18n('ChatMigration.From', [new PeerTitle({peerId: -action.chat_id}).element])); s.append(i18n('ChatMigration.From', [new PeerTitle({peerId: action.chat_id.toPeerId(true)}).element]));
} else if(action._ === 'messageActionChatMigrateTo') { } else if(action._ === 'messageActionChatMigrateTo') {
s.append(i18n('ChatMigration.To', [new PeerTitle({peerId: -action.channel_id}).element])); s.append(i18n('ChatMigration.To', [new PeerTitle({peerId: action.channel_id.toPeerId(true)}).element]));
} else { } else {
s.append(this.appMessagesManager.wrapMessageActionTextNew(message)); s.append(this.appMessagesManager.wrapMessageActionTextNew(message));
} }
@ -2376,28 +2456,19 @@ export default class ChatBubbles {
case 'keyboardButtonSwitchInline': { case 'keyboardButtonSwitchInline': {
buttonEl = document.createElement('button'); buttonEl = document.createElement('button');
buttonEl.classList.add('is-switch-inline'/* , 'tgico' */); buttonEl.classList.add('is-switch-inline', 'tgico');
const i = document.createElement('i');
i.classList.add('forward-icon');
i.innerHTML = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<defs>
<path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path>
</defs>
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
</svg>`;
buttonEl.append(i);
attachClickEvent(buttonEl, (e) => { attachClickEvent(buttonEl, (e) => {
cancelEvent(e); cancelEvent(e);
const botId = message.viaBotId || message.fromId; const botId = message.viaBotId || message.fromId;
let promise: Promise<number>; let promise: Promise<PeerId>;
if(button.pFlags.same_peer) promise = Promise.resolve(this.peerId); if(button.pFlags.same_peer) promise = Promise.resolve(this.peerId);
else promise = this.appInlineBotsManager.checkSwitchReturn(botId).then(peerId => { else promise = this.appInlineBotsManager.checkSwitchReturn(botId).then(peerId => {
if(peerId) { if(peerId) {
return peerId; return peerId;
} }
return new Promise<number>((resolve, reject) => { return new Promise<PeerId>((resolve, reject) => {
new PopupForward({ new PopupForward({
[this.peerId]: [] [this.peerId]: []
}, (peerId) => { }, (peerId) => {
@ -2883,8 +2954,8 @@ export default class ChatBubbles {
let savedFrom = ''; let savedFrom = '';
// const needName = ((peerId < 0 && (peerId !== message.fromId || our)) && message.fromId !== rootScope.myId) || message.viaBotId; // const needName = ((peerId.isAnyChat() && (peerId !== message.fromId || our)) && message.fromId !== rootScope.myId) || message.viaBotId;
const needName = (message.fromId !== rootScope.myId && peerId < 0 && !this.appPeersManager.isBroadcast(peerId)) || message.viaBotId; const needName = (message.fromId !== rootScope.myId && this.appPeersManager.isAnyChat(peerId) && !this.appPeersManager.isBroadcast(peerId)) || message.viaBotId;
if(needName || message.fwd_from || message.reply_to_mid) { // chat if(needName || message.fwd_from || message.reply_to_mid) { // chat
let title: HTMLElement | DocumentFragment; let title: HTMLElement | DocumentFragment;
@ -3498,12 +3569,12 @@ export default class ChatBubbles {
elements.push(b, '\n\n'); elements.push(b, '\n\n');
} else if(this.appPeersManager.isAnyGroup(this.peerId) && this.appPeersManager.getPeer(this.peerId).pFlags.creator) { } else if(this.appPeersManager.isAnyGroup(this.peerId) && this.appPeersManager.getPeer(this.peerId).pFlags.creator) {
this.renderEmptyPlaceholder('group', bubble, message, elements); this.renderEmptyPlaceholder('group', bubble, message, elements);
} else if(rootScope.myId === this.peerId) {
this.renderEmptyPlaceholder('saved', bubble, message, elements);
} else if(this.peerId > 0 && !isBot && this.appMessagesManager.canSendToPeer(this.peerId) && this.chat.type === 'chat') {
this.renderEmptyPlaceholder('greeting', bubble, message, elements);
} else if(this.chat.type === 'scheduled') { } else if(this.chat.type === 'scheduled') {
this.renderEmptyPlaceholder('noScheduledMessages', bubble, message, elements); this.renderEmptyPlaceholder('noScheduledMessages', bubble, message, elements);
} else if(rootScope.myId === this.peerId) {
this.renderEmptyPlaceholder('saved', bubble, message, elements);
} else if(this.appPeersManager.isUser(this.peerId) && !isBot && this.appMessagesManager.canSendToPeer(this.peerId) && this.chat.type === 'chat') {
this.renderEmptyPlaceholder('greeting', bubble, message, elements);
} else { } else {
this.renderEmptyPlaceholder('noMessages', bubble, message, elements); this.renderEmptyPlaceholder('noMessages', bubble, message, elements);
} }
@ -3537,7 +3608,7 @@ export default class ChatBubbles {
const message: Omit<Message.message | Message.messageService, 'message'> & {message?: string} = { const message: Omit<Message.message | Message.messageService, 'message'> & {message?: string} = {
_: service ? 'messageService' : 'message', _: service ? 'messageService' : 'message',
date: 0, date: 0,
id: -(this.peerId + offset), id: -(+this.peerId + offset),
peer_id: this.appPeersManager.getOutputPeer(this.peerId), peer_id: this.appPeersManager.getOutputPeer(this.peerId),
pFlags: {} pFlags: {}
}; };
@ -3566,11 +3637,11 @@ export default class ChatBubbles {
return; return;
} */ } */
if(side === 'top' && value && this.appUsersManager.isBot(this.peerId)) { if(side === 'top' && value && this.appPeersManager.isBot(this.peerId)) {
this.log('inject bot description'); this.log('inject bot description');
const middleware = this.getMiddleware(); const middleware = this.getMiddleware();
return this.appProfileManager.getProfile(this.peerId).then(userFull => { return this.appProfileManager.getProfile(this.peerId.toUserId()).then(userFull => {
if(!middleware()) { if(!middleware()) {
return; return;
} }

View File

@ -33,7 +33,7 @@ import ChatContextMenu from "./contextMenu";
import ChatInput from "./input"; import ChatInput from "./input";
import ChatSelection from "./selection"; import ChatSelection from "./selection";
import ChatTopbar from "./topbar"; import ChatTopbar from "./topbar";
import { REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; import { NULL_PEER_ID, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
import SetTransition from "../singleTransition"; import SetTransition from "../singleTransition";
import { fastRaf } from "../../helpers/schedulers"; import { fastRaf } from "../../helpers/schedulers";
import AppPrivateSearchTab from "../sidebarRight/tabs/search"; import AppPrivateSearchTab from "../sidebarRight/tabs/search";
@ -57,7 +57,7 @@ export default class Chat extends EventListenerBase<{
public wasAlreadyUsed = false; public wasAlreadyUsed = false;
// public initPeerId = 0; // public initPeerId = 0;
public peerId = 0; public peerId: PeerId;
public threadId: number; public threadId: number;
public setPeerPromise: Promise<void>; public setPeerPromise: Promise<void>;
public peerChanged: boolean; public peerChanged: boolean;
@ -170,7 +170,7 @@ export default class Chat extends EventListenerBase<{
} }
} }
public init(/* peerId: number */) { public init(/* peerId: PeerId */) {
// this.initPeerId = peerId; // this.initPeerId = peerId;
this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager, this.appNotificationsManager, this.appProfileManager, this.appUsersManager); this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager, this.appNotificationsManager, this.appProfileManager, this.appUsersManager);
@ -217,7 +217,7 @@ export default class Chat extends EventListenerBase<{
this.bubbles.listenerSetter.add(rootScope)('dialog_drop', (e) => { this.bubbles.listenerSetter.add(rootScope)('dialog_drop', (e) => {
if(e.peerId === this.peerId) { if(e.peerId === this.peerId) {
this.appImManager.setPeer(0); this.appImManager.setPeer(NULL_PEER_ID);
} }
}); });
} }
@ -249,7 +249,7 @@ export default class Chat extends EventListenerBase<{
this.selection.cleanup(); this.selection.cleanup();
} }
public setPeer(peerId: number, lastMsgId?: number) { public setPeer(peerId: PeerId, lastMsgId?: number) {
if(!peerId) { if(!peerId) {
this.inited = false; this.inited = false;
} else if(!this.inited) { } else if(!this.inited) {
@ -324,15 +324,21 @@ export default class Chat extends EventListenerBase<{
} }
public setAutoDownloadMedia() { public setAutoDownloadMedia() {
const peerId = this.peerId;
if(!peerId) {
return;
}
let type: keyof State['settings']['autoDownload']; let type: keyof State['settings']['autoDownload'];
if(this.peerId < 0) {
if(this.appPeersManager.isBroadcast(this.peerId)) { if(!peerId.isUser()) {
if(peerId.isBroadcast()) {
type = 'channels'; type = 'channels';
} else { } else {
type = 'groups'; type = 'groups';
} }
} else { } else {
if(this.appUsersManager.isContact(this.peerId)) { if(peerId.isContact()) {
type = 'contacts'; type = 'contacts';
} else { } else {
type = 'private'; type = 'private';

View File

@ -31,7 +31,7 @@ export default class CommandsHelper extends AutocompletePeerHelper {
); );
} }
public checkQuery(query: string, peerId: number) { public checkQuery(query: string, peerId: PeerId) {
if(!this.appUsersManager.isBot(peerId)) { if(!this.appUsersManager.isBot(peerId)) {
return false; return false;
} }
@ -47,12 +47,12 @@ export default class CommandsHelper extends AutocompletePeerHelper {
ignoreCase: true ignoreCase: true
}); });
const commands: Map<string, {peerId: number, name: string, description: string}> = new Map(); const commands: Map<string, {peerId: PeerId, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => { botInfos.forEach(botInfo => {
botInfo.commands.forEach(botCommand => { botInfo.commands.forEach(botCommand => {
const c = '/' + botCommand.command; const c = '/' + botCommand.command;
commands.set(botCommand.command, { commands.set(botCommand.command, {
peerId: botInfo.user_id, peerId: botInfo.user_id.toPeerId(false),
name: c, name: c,
description: botCommand.description description: botCommand.description
}); });

View File

@ -6,7 +6,7 @@
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager"; import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager";
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type { AppPollsManager, Poll } from "../../lib/appManagers/appPollsManager"; import type { AppPollsManager } from "../../lib/appManagers/appPollsManager";
import type { AppDocsManager, MyDocument } from "../../lib/appManagers/appDocsManager"; import type { AppDocsManager, MyDocument } from "../../lib/appManagers/appDocsManager";
import type { AppMessagesIdsManager } from "../../lib/appManagers/appMessagesIdsManager"; import type { AppMessagesIdsManager } from "../../lib/appManagers/appMessagesIdsManager";
import type Chat from "./chat"; import type Chat from "./chat";
@ -24,7 +24,7 @@ import findUpClassName from "../../helpers/dom/findUpClassName";
import { cancelEvent } from "../../helpers/dom/cancelEvent"; import { cancelEvent } from "../../helpers/dom/cancelEvent";
import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEvent"; import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEvent";
import isSelectionEmpty from "../../helpers/dom/isSelectionEmpty"; import isSelectionEmpty from "../../helpers/dom/isSelectionEmpty";
import { Message } from "../../layer"; import { Message, Poll } from "../../layer";
import PopupReportMessages from "../popups/reportMessages"; import PopupReportMessages from "../popups/reportMessages";
export default class ChatContextMenu { export default class ChatContextMenu {
@ -38,7 +38,7 @@ export default class ChatContextMenu {
private isTextSelected: boolean; private isTextSelected: boolean;
private isAnchorTarget: boolean; private isAnchorTarget: boolean;
private isUsernameTarget: boolean; private isUsernameTarget: boolean;
private peerId: number; private peerId: PeerId;
private mid: number; private mid: number;
private message: any; private message: any;
@ -130,7 +130,8 @@ export default class ChatContextMenu {
//appImManager.log('contextmenu', e, bubble, side); //appImManager.log('contextmenu', e, bubble, side);
positionMenu((e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent, this.element, side); positionMenu((e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent, this.element, side);
openBtnMenu(this.element, () => { openBtnMenu(this.element, () => {
this.peerId = this.mid = 0; this.mid = 0;
this.peerId = undefined;
this.target = null; this.target = null;
}); });
}; };
@ -417,7 +418,7 @@ export default class ChatContextMenu {
if(threadMessage) url += '?comment=' + msgId; if(threadMessage) url += '?comment=' + msgId;
key = 'LinkCopied'; key = 'LinkCopied';
} else { } else {
url += 'c/' + Math.abs(this.peerId) + '/' + msgId; url += 'c/' + this.peerId.toChatId() + '/' + msgId;
if(threadMessage) url += '?thread=' + this.appMessagesIdsManager.getServerMessageId(threadMessage.mid); if(threadMessage) url += '?thread=' + this.appMessagesIdsManager.getServerMessageId(threadMessage.mid);
key = 'LinkCopiedPrivateInfo'; key = 'LinkCopiedPrivateInfo';
} }

View File

@ -35,7 +35,7 @@ export default class InlineHelper extends AutocompleteHelper {
private gifsMasonry: GifsMasonry; private gifsMasonry: GifsMasonry;
private superStickerRenderer: SuperStickerRenderer; private superStickerRenderer: SuperStickerRenderer;
private onChangeScreen: () => void; private onChangeScreen: () => void;
public checkQuery: (peerId: number, username: string, query: string) => ReturnType<InlineHelper['_checkQuery']>; public checkQuery: (peerId: PeerId, username: string, query: string) => ReturnType<InlineHelper['_checkQuery']>;
constructor(appendTo: HTMLElement, constructor(appendTo: HTMLElement,
controller: AutocompleteHelperController, controller: AutocompleteHelperController,
@ -50,7 +50,7 @@ export default class InlineHelper extends AutocompleteHelper {
const {peerId, botId, queryId} = this.list.dataset; const {peerId, botId, queryId} = this.list.dataset;
return this.chat.input.getReadyToSend(() => { return this.chat.input.getReadyToSend(() => {
const queryAndResultIds = this.appInlineBotsManager.generateQId(queryId, (target as HTMLElement).dataset.resultId); const queryAndResultIds = this.appInlineBotsManager.generateQId(queryId, (target as HTMLElement).dataset.resultId);
this.appInlineBotsManager.sendInlineResult(+peerId, +botId, queryAndResultIds, { this.appInlineBotsManager.sendInlineResult(peerId.toPeerId(), botId, queryAndResultIds, {
clearDraft: true, clearDraft: true,
scheduleDate: this.chat.input.scheduleDate, scheduleDate: this.chat.input.scheduleDate,
silent: this.chat.input.sendSilent, silent: this.chat.input.sendSilent,
@ -80,7 +80,7 @@ export default class InlineHelper extends AutocompleteHelper {
}); });
} }
public _checkQuery = async(peerId: number, username: string, query: string) => { public _checkQuery = async(peerId: PeerId, username: string, query: string) => {
const middleware = this.controller.getMiddleware(); const middleware = this.controller.getMiddleware();
const peer = await this.appUsersManager.resolveUsername(username); const peer = await this.appUsersManager.resolveUsername(username);
@ -105,7 +105,7 @@ export default class InlineHelper extends AutocompleteHelper {
const list = this.list.cloneNode() as HTMLElement; const list = this.list.cloneNode() as HTMLElement;
list.dataset.peerId = '' + peerId; list.dataset.peerId = '' + peerId;
list.dataset.botId = '' + peer.id; list.dataset.botId = '' + peer.id;
list.dataset.queryId = botResults.query_id; list.dataset.queryId = '' + botResults.query_id;
const gifsMasonry = new GifsMasonry(null, ANIMATION_GROUP, this.scrollable, false); const gifsMasonry = new GifsMasonry(null, ANIMATION_GROUP, this.scrollable, false);

View File

@ -81,6 +81,8 @@ import { copy } from '../../helpers/object';
import PopupPeer from '../popups/peer'; import PopupPeer from '../popups/peer';
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport'; import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
import appMediaPlaybackController from '../appMediaPlaybackController'; import appMediaPlaybackController from '../appMediaPlaybackController';
import { NULL_PEER_ID } from '../../lib/mtproto/mtproto_config';
import replaceContent from '../../helpers/dom/replaceContent';
const RECORD_MIN_TIME = 500; const RECORD_MIN_TIME = 500;
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
@ -110,20 +112,22 @@ export default class ChatInput {
private replyKeyboard: ReplyKeyboard; private replyKeyboard: ReplyKeyboard;
private attachMenu: HTMLButtonElement; private attachMenu: HTMLButtonElement;
private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerId: number, threadId: number) => boolean})[]; private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerId: PeerId, threadId: number) => boolean})[];
private sendMenu: SendMenu; private sendMenu: SendMenu;
private replyElements: { private replyElements: {
container: HTMLElement, container: HTMLElement,
cancelBtn: HTMLButtonElement cancelBtn: HTMLButtonElement,
iconBtn: HTMLButtonElement
} = {} as any; } = {} as any;
private getWebPagePromise: Promise<void>; private getWebPagePromise: Promise<void>;
private willSendWebPage: WebPage = null; private willSendWebPage: WebPage = null;
private forwarding: {[fromPeerId: number]: number[]}; private forwarding: {[frompeerId: PeerId]: number[]};
public replyToMsgId: number; public replyToMsgId: number;
public editMsgId: number; public editMsgId: number;
public editMessage: Message.message;
private noWebPage: true; private noWebPage: true;
public scheduleDate: number; public scheduleDate: number;
public sendSilent: true; public sendSilent: true;
@ -301,9 +305,10 @@ export default class ChatInput {
this.replyElements.container = document.createElement('div'); this.replyElements.container = document.createElement('div');
this.replyElements.container.classList.add('reply-wrapper'); this.replyElements.container.classList.add('reply-wrapper');
this.replyElements.cancelBtn = ButtonIcon('close reply-cancel'); this.replyElements.iconBtn = ButtonIcon('');
this.replyElements.cancelBtn = ButtonIcon('close reply-cancel', {noRipple: true});
this.replyElements.container.append(this.replyElements.cancelBtn); this.replyElements.container.append(this.replyElements.iconBtn, this.replyElements.cancelBtn);
this.newMessageWrapper = document.createElement('div'); this.newMessageWrapper = document.createElement('div');
this.newMessageWrapper.classList.add('new-message-wrapper'); this.newMessageWrapper.classList.add('new-message-wrapper');
@ -392,7 +397,7 @@ export default class ChatInput {
onClick: () => { onClick: () => {
new PopupCreatePoll(this.chat).show(); new PopupCreatePoll(this.chat).show();
}, },
verify: (peerId, threadId) => peerId < 0 && this.appMessagesManager.canSendToPeer(peerId, threadId, 'send_polls') verify: (peerId, threadId) => peerId.isAnyChat() && this.appMessagesManager.canSendToPeer(peerId, threadId, 'send_polls')
}]; }];
this.attachMenu = ButtonMenuToggle({noRipple: true, listenerSetter: this.listenerSetter}, 'top-left', this.attachMenuButtons); this.attachMenu = ButtonMenuToggle({noRipple: true, listenerSetter: this.listenerSetter}, 'top-left', this.attachMenuButtons);
@ -649,7 +654,7 @@ export default class ChatInput {
const peerId = this.chat.peerId; const peerId = this.chat.peerId;
new PopupPinMessage(peerId, 0, true, () => { new PopupPinMessage(peerId, 0, true, () => {
this.chat.appImManager.setPeer(0); // * close tab this.chat.appImManager.setPeer(NULL_PEER_ID); // * close tab
// ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится // ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится
const originalChat = this.chat.appImManager.chat; const originalChat = this.chat.appImManager.chat;
@ -690,7 +695,7 @@ export default class ChatInput {
} }
public scheduleSending = (callback: () => void = this.sendMessage.bind(this, true), initDate = new Date()) => { public scheduleSending = (callback: () => void = this.sendMessage.bind(this, true), initDate = new Date()) => {
const canSendWhenOnline = this.chat.peerId > 0 && this.appUsersManager.isUserOnlineVisible(this.chat.peerId); const canSendWhenOnline = rootScope.myId !== this.chat.peerId && this.chat.peerId.isUser() && this.appUsersManager.isUserOnlineVisible(this.chat.peerId);
new PopupSchedule(initDate, (timestamp) => { new PopupSchedule(initDate, (timestamp) => {
const minTimestamp = (Date.now() / 1000 | 0) + 10; const minTimestamp = (Date.now() / 1000 | 0) + 10;
@ -777,7 +782,10 @@ export default class ChatInput {
if(this.messageInputField.value === draft.rMessage && this.replyToMsgId === draft.reply_to_msg_id) return false; if(this.messageInputField.value === draft.rMessage && this.replyToMsgId === draft.reply_to_msg_id) return false;
this.clearHelper(); if(fromUpdate) {
this.clearHelper();
}
this.noWebPage = draft.pFlags.no_webpage; this.noWebPage = draft.pFlags.no_webpage;
if(draft.reply_to_msg_id) { if(draft.reply_to_msg_id) {
this.initMessageReply(draft.reply_to_msg_id); this.initMessageReply(draft.reply_to_msg_id);
@ -1195,7 +1203,7 @@ export default class ChatInput {
this.undoHistory.length = 0; this.undoHistory.length = 0;
} }
const urlEntities: Array<MessageEntity.messageEntityUrl | MessageEntity.messageEntityTextUrl> = entities.filter(e => e._ === 'messageEntityUrl' || e._ === 'messageEntityTextUrl') as any; const urlEntities: Array<MessageEntity.messageEntityUrl | MessageEntity.messageEntityTextUrl> = !this.editMessage?.media && entities.filter(e => e._ === 'messageEntityUrl' || e._ === 'messageEntityTextUrl') as any;
if(urlEntities.length) { if(urlEntities.length) {
for(const entity of urlEntities) { for(const entity of urlEntities) {
let url: string; let url: string;
@ -1376,7 +1384,7 @@ export default class ChatInput {
this.stickersHelper.checkEmoticon(value); this.stickersHelper.checkEmoticon(value);
} else if(firstChar === '@') { // mentions } else if(firstChar === '@') { // mentions
const topMsgId = this.chat.threadId ? this.appMessagesIdsManager.getServerMessageId(this.chat.threadId) : undefined; const topMsgId = this.chat.threadId ? this.appMessagesIdsManager.getServerMessageId(this.chat.threadId) : undefined;
if(this.mentionsHelper.checkQuery(query, this.chat.peerId > 0 ? 0 : this.chat.peerId, topMsgId)) { if(this.mentionsHelper.checkQuery(query, this.chat.peerId.isUser() ? NULL_PEER_ID : this.chat.peerId, topMsgId)) {
foundHelper = this.mentionsHelper; foundHelper = this.mentionsHelper;
} }
} else if(!matches[1] && firstChar === '/') { // commands } else if(!matches[1] && firstChar === '/') { // commands
@ -1456,7 +1464,7 @@ export default class ChatInput {
this.sendMessage(); this.sendMessage();
} }
} else { } else {
if(this.chat.peerId < 0 && !this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, 'send_media')) { if(this.chat.peerId.isAnyChat() && !this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, 'send_media')) {
toast(POSTING_MEDIA_NOT_ALLOWED); toast(POSTING_MEDIA_NOT_ALLOWED);
return; return;
} }
@ -1569,7 +1577,7 @@ export default class ChatInput {
} }
}; };
private onHelperCancel = (e?: Event) => { private onHelperCancel = (e?: Event, force?: boolean) => {
if(e) { if(e) {
cancelEvent(e); cancelEvent(e);
} }
@ -1593,6 +1601,24 @@ export default class ChatInput {
if(needReturn) return; if(needReturn) return;
} }
if(this.helperType === 'edit' && !force) {
const message = this.editMessage
const value = RichTextProcessor.parseMarkdown(this.messageInputField.value, []);
if(message.message !== value) {
new PopupPeer('discard-editing', {
buttons: [{
langKey: 'Alert.Confirm.Discard',
callback: () => {
this.onHelperCancel(undefined, true);
}
}],
descriptionLangKey: 'Chat.Edit.Cancel.Text'
}).show();
return;
}
}
this.clearHelper(); this.clearHelper();
this.updateSendBtn(); this.updateSendBtn();
}; };
@ -1726,7 +1752,7 @@ export default class ChatInput {
//return; //return;
if(this.editMsgId) { if(this.editMsgId) {
const message = this.chat.getMessage(this.editMsgId); const message = this.editMessage;
if(!!value.trim() || message.media) { if(!!value.trim() || message.media) {
this.appMessagesManager.editMessage(message, value, { this.appMessagesManager.editMessage(message, value, {
entities, entities,
@ -1758,7 +1784,7 @@ export default class ChatInput {
const scheduleDate = this.scheduleDate; const scheduleDate = this.scheduleDate;
setTimeout(() => { setTimeout(() => {
for(const fromPeerId in forwarding) { for(const fromPeerId in forwarding) {
this.appMessagesManager.forwardMessages(peerId, +fromPeerId, forwarding[fromPeerId], { this.appMessagesManager.forwardMessages(peerId, fromPeerId.toPeerId(), forwarding[fromPeerId], {
silent, silent,
scheduleDate: scheduleDate scheduleDate: scheduleDate
}); });
@ -1773,7 +1799,7 @@ export default class ChatInput {
document = this.appDocsManager.getDoc(document); document = this.appDocsManager.getDoc(document);
const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media'); const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media');
if(this.chat.peerId < 0 && !this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, flag)) { if(this.chat.peerId.isAnyChat() && !this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, flag)) {
toast(POSTING_MEDIA_NOT_ALLOWED); toast(POSTING_MEDIA_NOT_ALLOWED);
return false; return false;
} }
@ -1817,7 +1843,7 @@ export default class ChatInput {
} */ } */
public initMessageEditing(mid: number) { public initMessageEditing(mid: number) {
const message = this.chat.getMessage(mid); const message: Message.message = this.chat.getMessage(mid);
let input = RichTextProcessor.wrapDraftText(message.message, {entities: message.totalEntities}); let input = RichTextProcessor.wrapDraftText(message.message, {entities: message.totalEntities});
const f = () => { const f = () => {
@ -1825,26 +1851,27 @@ export default class ChatInput {
this.setTopInfo('edit', f, i18n('AccDescrEditing'), replyFragment, input, message); this.setTopInfo('edit', f, i18n('AccDescrEditing'), replyFragment, input, message);
this.editMsgId = mid; this.editMsgId = mid;
this.editMessage = message;
input = undefined; input = undefined;
}; };
f(); f();
} }
public initMessagesForward(fromPeerIdsMids: {[fromPeerId: number]: number[]}) { public initMessagesForward(fromPeerIdsMids: {[fromPeerId: PeerId]: number[]}) {
const f = () => { const f = () => {
//const peerTitles: string[] //const peerTitles: string[]
const fromPeerIds = Object.keys(fromPeerIdsMids).map(str => +str); const fromPeerIds = Object.keys(fromPeerIdsMids).map(fromPeerId => fromPeerId.toPeerId());
const smth: Set<string | number> = new Set(); const smth: Set<string> = new Set();
let length = 0; let length = 0;
fromPeerIds.forEach(fromPeerId => { fromPeerIds.forEach(fromPeerId => {
const mids = fromPeerIdsMids[fromPeerId]; const mids = fromPeerIdsMids[fromPeerId];
mids.forEach(mid => { mids.forEach(mid => {
const message = this.appMessagesManager.getMessageByPeer(fromPeerId, mid); const message: Message.message = this.appMessagesManager.getMessageByPeer(fromPeerId, mid);
if(message.fwd_from?.from_name && !message.fromId && !message.fwdFromId) { if(message.fwd_from?.from_name && !message.fromId && !message.fwdFromId) {
smth.add(message.fwd_from.from_name); smth.add('N' + message.fwd_from.from_name);
} else { } else {
smth.add(message.fromId); smth.add('P' + message.fromId);
} }
}); });
@ -1853,8 +1880,10 @@ export default class ChatInput {
const onlyFirstName = smth.size > 2; const onlyFirstName = smth.size > 2;
const peerTitles = [...smth].map(smth => { const peerTitles = [...smth].map(smth => {
return typeof(smth) === 'number' ? const type = smth[0];
new PeerTitle({peerId: smth, dialog: false, onlyFirstName}).element : smth = smth.slice(1);
return type === 'P' ?
new PeerTitle({peerId: smth.toPeerId(), dialog: false, onlyFirstName}).element :
(onlyFirstName ? smth.split(' ')[0] : smth); (onlyFirstName ? smth.split(' ')[0] : smth);
}); });
@ -1895,6 +1924,10 @@ export default class ChatInput {
} }
public initMessageReply(mid: number) { public initMessageReply(mid: number) {
if(this.replyToMsgId === mid) {
return;
}
let message: Message = this.chat.getMessage(mid); let message: Message = this.chat.getMessage(mid);
const f = () => { const f = () => {
let peerTitleEl: HTMLElement; let peerTitleEl: HTMLElement;
@ -1937,9 +1970,12 @@ export default class ChatInput {
this.willSendWebPage = null; this.willSendWebPage = null;
} }
this.replyToMsgId = undefined; if(type !== 'reply') {
this.forwarding = undefined; this.replyToMsgId = undefined;
this.editMsgId = undefined; this.forwarding = undefined;
}
this.editMsgId = this.editMessage = undefined;
this.helperType = this.helperFunc = undefined; this.helperType = this.helperFunc = undefined;
if(this.chat.container.classList.contains('is-helper-active')) { if(this.chat.container.classList.contains('is-helper-active')) {
@ -1961,12 +1997,18 @@ export default class ChatInput {
}); });
} }
public setTopInfo(type: ChatInputHelperType, public setTopInfo(
type: ChatInputHelperType,
callerFunc: () => void, callerFunc: () => void,
title: Parameters<typeof wrapReply>[0] = '', title: Parameters<typeof wrapReply>[0] = '',
subtitle: Parameters<typeof wrapReply>[1] = '', subtitle: Parameters<typeof wrapReply>[1] = '',
input?: string, input?: string,
message?: any) { message?: any
) {
if(this.willSendWebPage && type === 'reply') {
return;
}
if(type !== 'webpage') { if(type !== 'webpage') {
this.clearHelper(type); this.clearHelper(type);
this.helperType = type; this.helperType = type;
@ -1978,6 +2020,7 @@ export default class ChatInput {
replyParent.lastElementChild.remove(); replyParent.lastElementChild.remove();
} }
this.replyElements.iconBtn.replaceWith(this.replyElements.iconBtn = ButtonIcon((type === 'webpage' ? 'link' : type) + ' active reply-icon', {noRipple: true}));
replyParent.append(wrapReply(title, subtitle, message)); replyParent.append(wrapReply(title, subtitle, message));
this.chat.container.classList.add('is-helper-active'); this.chat.container.classList.add('is-helper-active');

View File

@ -21,7 +21,7 @@ export default class MentionsHelper extends AutocompletePeerHelper {
controller, controller,
'mentions-helper', 'mentions-helper',
(target) => { (target) => {
const user = appUsersManager.getUser(+(target as HTMLElement).dataset.peerId); const user = appUsersManager.getUser((target as HTMLElement).dataset.peerId);
let str = '', entity: MessageEntity; let str = '', entity: MessageEntity;
if(user.username) { if(user.username) {
str = '@' + user.username; str = '@' + user.username;
@ -41,12 +41,12 @@ export default class MentionsHelper extends AutocompletePeerHelper {
); );
} }
public checkQuery(query: string, peerId: number, topMsgId: number) { public checkQuery(query: string, peerId: PeerId, topMsgId: number) {
const trimmed = query.trim(); // check that there is no whitespace const trimmed = query.trim(); // check that there is no whitespace
if(query.length !== trimmed.length) return false; if(query.length !== trimmed.length) return false;
const middleware = this.controller.getMiddleware(); const middleware = this.controller.getMiddleware();
this.appProfileManager.getMentions(peerId ? -peerId : 0, trimmed, topMsgId).then(peerIds => { this.appProfileManager.getMentions(peerId && peerId.toChatId(), trimmed, topMsgId).then(peerIds => {
if(!middleware()) return; if(!middleware()) return;
const username = trimmed.slice(1).toLowerCase(); const username = trimmed.slice(1).toLowerCase();

View File

@ -31,14 +31,7 @@ export namespace MessageRender {
if(!message.fwd_from?.saved_from_msg_id && chat.type !== 'pinned') { if(!message.fwd_from?.saved_from_msg_id && chat.type !== 'pinned') {
const forward = document.createElement('div'); const forward = document.createElement('div');
forward.classList.add('bubble-beside-button', 'forward'); forward.classList.add('bubble-beside-button', 'forward', 'tgico-forward_filled');
forward.innerHTML = `
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<defs>
<path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path>
</defs>
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
</svg>`;
bubbleContainer.append(forward); bubbleContainer.append(forward);
bubble.classList.add('with-beside-button'); bubble.classList.add('with-beside-button');
} }

View File

@ -14,6 +14,7 @@ import { ripple } from "../ripple";
import AvatarElement from "../avatar"; import AvatarElement from "../avatar";
import { i18n } from "../../lib/langPack"; import { i18n } from "../../lib/langPack";
import replaceContent from "../../helpers/dom/replaceContent"; import replaceContent from "../../helpers/dom/replaceContent";
import appChatsManager from "../../lib/appManagers/appChatsManager";
const TAG_NAME = 'replies-element'; const TAG_NAME = 'replies-element';
@ -117,7 +118,7 @@ export default class RepliesElement extends HTMLElement {
} }
if(replies) { if(replies) {
const historyStorage = appMessagesManager.getHistoryStorage(-replies.channel_id); const historyStorage = appMessagesManager.getHistoryStorage(replies.channel_id.toPeerId(true));
let isUnread = false; let isUnread = false;
if(replies.replies) { if(replies.replies) {
if(replies.read_max_id !== undefined && replies.max_id !== undefined) { if(replies.read_max_id !== undefined && replies.max_id !== undefined) {

View File

@ -52,7 +52,7 @@ export function wrapReplyDivAndCaption(options: {
media = media.webpage; media = media.webpage;
} }
if(media.photo || (media.document && ['video', 'sticker', 'gif', 'round', 'photo'].indexOf(media.document.type) !== -1)) { if(media.photo || (media.document && media.document.thumbs?.length)/* ['video', 'sticker', 'gif', 'round', 'photo', 'audio'].indexOf(media.document.type) !== -1) */) {
middleware = appImManager.chat.bubbles.getMiddleware(); middleware = appImManager.chat.bubbles.getMiddleware();
const lazyLoadQueue = appImManager.chat.bubbles.lazyLoadQueue; const lazyLoadQueue = appImManager.chat.bubbles.lazyLoadQueue;

View File

@ -24,7 +24,7 @@ export default class ReplyKeyboard extends DropdownHover {
private listenerSetter: ListenerSetter; private listenerSetter: ListenerSetter;
private appMessagesManager: AppMessagesManager; private appMessagesManager: AppMessagesManager;
private btnHover: HTMLElement; private btnHover: HTMLElement;
private peerId: number; private peerId: PeerId;
private touchListener: Listener; private touchListener: Listener;
private chatInput: ChatInput; private chatInput: ChatInput;
@ -139,7 +139,7 @@ export default class ReplyKeyboard extends DropdownHover {
return !hide; return !hide;
} }
public setPeer(peerId: number) { public setPeer(peerId: PeerId) {
this.peerId = peerId; this.peerId = peerId;
this.checkAvailability(); this.checkAvailability();

View File

@ -140,7 +140,7 @@ export default class ChatSearch {
selectResult = (elem: HTMLElement) => { selectResult = (elem: HTMLElement) => {
if(this.setPeerPromise) return this.setPeerPromise; if(this.setPeerPromise) return this.setPeerPromise;
const peerId = +elem.dataset.peerId; const peerId = elem.dataset.peerId.toPeerId();
const lastMsgId = +elem.dataset.mid || undefined; const lastMsgId = +elem.dataset.mid || undefined;
const index = whichChild(elem); const index = whichChild(elem);

View File

@ -37,14 +37,14 @@ import { attachContextMenuListener } from "../misc";
import { attachClickEvent, AttachClickOptions } from "../../helpers/dom/clickEvent"; import { attachClickEvent, AttachClickOptions } from "../../helpers/dom/clickEvent";
import findUpAsChild from "../../helpers/dom/findUpAsChild"; import findUpAsChild from "../../helpers/dom/findUpAsChild";
const accumulateMapSet = (map: Map<number, Set<number>>) => { const accumulateMapSet = (map: Map<any, Set<number>>) => {
return [...map.values()].reduce((acc, v) => acc + v.size, 0); return [...map.values()].reduce((acc, v) => acc + v.size, 0);
}; };
//const MIN_CLICK_MOVE = 32; // minimum bubble height //const MIN_CLICK_MOVE = 32; // minimum bubble height
class AppSelection { class AppSelection {
public selectedMids: Map<number, Set<number>> = new Map(); public selectedMids: Map<PeerId, Set<number>> = new Map();
public isSelecting = false; public isSelecting = false;
public selectedText: string; public selectedText: string;
@ -57,7 +57,7 @@ class AppSelection {
protected onToggleSelection: (forwards: boolean) => void; protected onToggleSelection: (forwards: boolean) => void;
protected onUpdateContainer: (cantForward: boolean, cantDelete: boolean, cantSend: boolean) => void; protected onUpdateContainer: (cantForward: boolean, cantDelete: boolean, cantSend: boolean) => void;
protected onCancelSelection: () => void; protected onCancelSelection: () => void;
protected toggleByMid: (peerId: number, mid: number) => void; protected toggleByMid: (peerId: PeerId, mid: number) => void;
protected toggleByElement: (bubble: HTMLElement) => void; protected toggleByElement: (bubble: HTMLElement) => void;
protected navigationType: NavigationItem['type']; protected navigationType: NavigationItem['type'];
@ -157,7 +157,7 @@ class AppSelection {
return; return;
} }
const seen: Map<number, Set<number>> = new Map(); const seen: AppSelection['selectedMids'] = new Map();
let selecting: boolean; let selecting: boolean;
/* let good = false; /* let good = false;
@ -175,7 +175,7 @@ class AppSelection {
const processElement = (element: HTMLElement, checkBetween = true) => { const processElement = (element: HTMLElement, checkBetween = true) => {
const mid = +element.dataset.mid; const mid = +element.dataset.mid;
const peerId = +element.dataset.peerId; const peerId = (element.dataset.peerId || '').toPeerId();
if(!mid || !peerId) return; if(!mid || !peerId) return;
if(!isInDOM(firstTarget)) { if(!isInDOM(firstTarget)) {
@ -280,7 +280,7 @@ class AppSelection {
} }
protected isElementShouldBeSelected(element: HTMLElement) { protected isElementShouldBeSelected(element: HTMLElement) {
return this.isMidSelected(+element.dataset.peerId, +element.dataset.mid); return this.isMidSelected(element.dataset.peerId.toPeerId(), +element.dataset.mid);
} }
protected appendCheckbox(element: HTMLElement, checkboxField: CheckboxField) { protected appendCheckbox(element: HTMLElement, checkboxField: CheckboxField) {
@ -435,7 +435,7 @@ class AppSelection {
SetTransition(element, 'is-selected', isSelected, 200); SetTransition(element, 'is-selected', isSelected, 200);
} }
public isMidSelected(peerId: number, mid: number) { public isMidSelected(peerId: PeerId, mid: number) {
const set = this.selectedMids.get(peerId); const set = this.selectedMids.get(peerId);
return set?.has(mid); return set?.has(mid);
} }
@ -444,7 +444,7 @@ class AppSelection {
return accumulateMapSet(this.selectedMids); return accumulateMapSet(this.selectedMids);
} }
protected toggleMid(peerId: number, mid: number, unselect?: boolean) { protected toggleMid(peerId: PeerId, mid: number, unselect?: boolean) {
let set = this.selectedMids.get(peerId); let set = this.selectedMids.get(peerId);
if(unselect || (unselect === undefined && set?.has(mid))) { if(unselect || (unselect === undefined && set?.has(mid))) {
if(set) { if(set) {
@ -488,7 +488,7 @@ class AppSelection {
/** /**
* ! Call this method only to handle deleted messages * ! Call this method only to handle deleted messages
*/ */
public deleteSelectedMids(peerId: number, mids: number[]) { public deleteSelectedMids(peerId: PeerId, mids: number[]) {
const set = this.selectedMids.get(peerId); const set = this.selectedMids.get(peerId);
if(!set) { if(!set) {
return; return;
@ -556,7 +556,7 @@ export class SearchSelection extends AppSelection {
public toggleByElement = (element: HTMLElement) => { public toggleByElement = (element: HTMLElement) => {
const mid = +element.dataset.mid; const mid = +element.dataset.mid;
const peerId = +element.dataset.peerId; const peerId = element.dataset.peerId.toPeerId();
if(!this.toggleMid(peerId, mid)) { if(!this.toggleMid(peerId, mid)) {
return; return;
@ -565,7 +565,7 @@ export class SearchSelection extends AppSelection {
this.updateElementSelection(element, this.isMidSelected(peerId, mid)); this.updateElementSelection(element, this.isMidSelected(peerId, mid));
}; };
public toggleByMid = (peerId: number, mid: number) => { public toggleByMid = (peerId: PeerId, mid: number) => {
const element = this.searchSuper.mediaTab.contentTab.querySelector(`.search-super-item[data-peer-id="${peerId}"][data-mid="${mid}"]`) as HTMLElement; const element = this.searchSuper.mediaTab.contentTab.querySelector(`.search-super-item[data-peer-id="${peerId}"][data-mid="${mid}"]`) as HTMLElement;
this.toggleByElement(element); this.toggleByElement(element);
}; };
@ -620,7 +620,7 @@ export class SearchSelection extends AppSelection {
this.selectionForwardBtn = ButtonIcon(`forward ${BASE_CLASS}-forward`); this.selectionForwardBtn = ButtonIcon(`forward ${BASE_CLASS}-forward`);
attachClickEvent(this.selectionForwardBtn, () => { attachClickEvent(this.selectionForwardBtn, () => {
const obj: {[fromPeerId: number]: number[]} = {}; const obj: {[frompeerId: PeerId]: number[]} = {};
for(const [fromPeerId, mids] of this.selectedMids) { for(const [fromPeerId, mids] of this.selectedMids) {
obj[fromPeerId] = Array.from(mids); obj[fromPeerId] = Array.from(mids);
} }
@ -773,7 +773,7 @@ export default class ChatSelection extends AppSelection {
this.updateElementSelection(bubble, this.isMidSelected(this.bubbles.peerId, mid)); this.updateElementSelection(bubble, this.isMidSelected(this.bubbles.peerId, mid));
}; };
protected toggleByMid = (peerId: number, mid: number) => { protected toggleByMid = (peerId: PeerId, mid: number) => {
const mounted = this.bubbles.getMountedBubble(mid); const mounted = this.bubbles.getMountedBubble(mid);
if(mounted) { if(mounted) {
this.toggleByElement(mounted.bubble); this.toggleByElement(mounted.bubble);
@ -895,7 +895,7 @@ export default class ChatSelection extends AppSelection {
this.selectionForwardBtn = Button('btn-primary btn-transparent text-bold selection-container-forward', {icon: 'forward'}); this.selectionForwardBtn = Button('btn-primary btn-transparent text-bold selection-container-forward', {icon: 'forward'});
this.selectionForwardBtn.append(i18n('Forward')); this.selectionForwardBtn.append(i18n('Forward'));
attachClickEvent(this.selectionForwardBtn, () => { attachClickEvent(this.selectionForwardBtn, () => {
const obj: {[fromPeerId: number]: number[]} = {}; const obj: {[frompeerId: PeerId]: number[]} = {};
for(const [fromPeerId, mids] of this.selectedMids) { for(const [fromPeerId, mids] of this.selectedMids) {
obj[fromPeerId] = Array.from(mids); obj[fromPeerId] = Array.from(mids);
} }

View File

@ -57,7 +57,7 @@ export default class SendMenu {
}, options.listenerSetter); }, options.listenerSetter);
} }
public setPeerId(peerId: number) { public setPeerId(peerId: PeerId) {
this.type = peerId === rootScope.myId ? 'reminder' : 'schedule'; this.type = peerId === rootScope.myId ? 'reminder' : 'schedule';
} }
}; };

View File

@ -44,6 +44,7 @@ import generateVerifiedIcon from "../generateVerifiedIcon";
import { fastRaf } from "../../helpers/schedulers"; import { fastRaf } from "../../helpers/schedulers";
import AppEditContactTab from "../sidebarRight/tabs/editContact"; import AppEditContactTab from "../sidebarRight/tabs/editContact";
import appMediaPlaybackController from "../appMediaPlaybackController"; import appMediaPlaybackController from "../appMediaPlaybackController";
import { NULL_PEER_ID } from "../../lib/mtproto/mtproto_config";
export default class ChatTopbar { export default class ChatTopbar {
public container: HTMLDivElement; public container: HTMLDivElement;
@ -63,8 +64,8 @@ export default class ChatTopbar {
public pinnedMessage: ChatPinnedMessage; public pinnedMessage: ChatPinnedMessage;
private setUtilsRAF: number; private setUtilsRAF: number;
public peerId: number; public peerId: PeerId;
private wasPeerId: number; private wasPeerId: PeerId;
private setPeerStatusInterval: number; private setPeerStatusInterval: number;
public listenerSetter: ListenerSetter; public listenerSetter: ListenerSetter;
@ -156,7 +157,8 @@ export default class ChatTopbar {
this.container.append(this.btnBack, this.chatInfo, this.chatUtils); this.container.append(this.btnBack, this.chatInfo, this.chatUtils);
if(this.chatAudio) { if(this.chatAudio) {
this.container.append(this.chatAudio.divAndCaption.container, this.chatUtils); // this.container.append(this.chatAudio.divAndCaption.container, this.chatUtils);
this.container.append(this.chatAudio.divAndCaption.container);
} }
// * construction end // * construction end
@ -177,12 +179,12 @@ export default class ChatTopbar {
} }
const mid = +container.dataset.mid; const mid = +container.dataset.mid;
const peerId = +container.dataset.peerId;
if(container.classList.contains('pinned-message')) { if(container.classList.contains('pinned-message')) {
//if(!this.pinnedMessage.locked) { //if(!this.pinnedMessage.locked) {
this.pinnedMessage.followPinnedMessage(mid); this.pinnedMessage.followPinnedMessage(mid);
//} //}
} else { } else {
const peerId = container.dataset.peerId.toPeerId();
const searchContext = appMediaPlaybackController.getSearchContext(); const searchContext = appMediaPlaybackController.getSearchContext();
this.chat.appImManager.setInnerPeer( this.chat.appImManager.setInnerPeer(
peerId, peerId,
@ -214,14 +216,14 @@ export default class ChatTopbar {
} else { } else {
const isFirstChat = this.chat.appImManager.chats.indexOf(this.chat) === 0; const isFirstChat = this.chat.appImManager.chats.indexOf(this.chat) === 0;
appNavigationController.back(isFirstChat ? 'im' : 'chat'); appNavigationController.back(isFirstChat ? 'im' : 'chat');
return; /* return;
if(mediaSizes.activeScreen === ScreenSize.medium && !appNavigationController.findItemByType('chat')) { if(mediaSizes.activeScreen === ScreenSize.medium && !appNavigationController.findItemByType('chat')) {
this.chat.appImManager.setPeer(0); this.chat.appImManager.setPeer(0);
blurActiveElement(); blurActiveElement();
} else { } else {
appNavigationController.back('chat'); appNavigationController.back('chat');
} } */
} }
}; };
@ -259,14 +261,14 @@ export default class ChatTopbar {
icon: 'comments', icon: 'comments',
text: 'ViewDiscussion', text: 'ViewDiscussion',
onClick: () => { onClick: () => {
this.appProfileManager.getChannelFull(-this.peerId).then(channelFull => { this.appProfileManager.getChannelFull(this.peerId.toChatId()).then(channelFull => {
if(channelFull.linked_chat_id) { if(channelFull.linked_chat_id) {
this.chat.appImManager.setInnerPeer(-channelFull.linked_chat_id); this.chat.appImManager.setInnerPeer(channelFull.linked_chat_id.toPeerId(true));
} }
}); });
}, },
verify: () => { verify: () => {
const chatFull = this.appProfileManager.chatsFull[-this.peerId]; const chatFull = this.appProfileManager.chatsFull[this.peerId.toChatId()];
return this.chat.type === 'chat' && this.appPeersManager.isBroadcast(this.peerId) && !!(chatFull as ChatFull.channelFull)?.linked_chat_id; return this.chat.type === 'chat' && this.appPeersManager.isBroadcast(this.peerId) && !!(chatFull as ChatFull.channelFull)?.linked_chat_id;
} }
}, { }, {
@ -310,7 +312,7 @@ export default class ChatTopbar {
this.appSidebarRight.toggleSidebar(true); this.appSidebarRight.toggleSidebar(true);
} }
}, },
verify: () => this.peerId > 0 && !this.appUsersManager.isContact(this.peerId) verify: () => this.appPeersManager.isContact(this.peerId)
}, { }, {
icon: 'forward', icon: 'forward',
text: 'ShareContact', text: 'ShareContact',
@ -349,7 +351,7 @@ export default class ChatTopbar {
selfPresence: 'ChatYourSelf' selfPresence: 'ChatYourSelf'
}); });
}, },
verify: () => rootScope.myId !== this.peerId && this.peerId > 0 && this.appUsersManager.isContact(this.peerId) verify: () => rootScope.myId !== this.peerId && this.appPeersManager.isContact(this.peerId)
}, { }, {
icon: 'lock', icon: 'lock',
text: 'BlockUser', text: 'BlockUser',
@ -373,8 +375,9 @@ export default class ChatTopbar {
}).show(); }).show();
}, },
verify: () => { verify: () => {
const userFull = this.appProfileManager.usersFull[this.peerId]; const userId = this.peerId.toUserId();
return this.peerId > 0 && this.peerId !== rootScope.myId && userFull && !userFull.pFlags?.blocked; const userFull = this.appProfileManager.usersFull[userId];
return this.appPeersManager.isUser(this.peerId) && this.peerId !== rootScope.myId && userFull && !userFull.pFlags?.blocked;
} }
}, { }, {
icon: 'lockoff', icon: 'lockoff',
@ -387,8 +390,8 @@ export default class ChatTopbar {
}); });
}, },
verify: () => { verify: () => {
const userFull = this.appProfileManager.usersFull[this.peerId]; const userFull = this.appProfileManager.usersFull[this.peerId.toUserId()];
return this.peerId > 0 && !!userFull?.pFlags?.blocked; return this.appPeersManager.isUser(this.peerId) && !!userFull?.pFlags?.blocked;
} }
}, { }, {
icon: 'delete danger', icon: 'delete danger',
@ -440,7 +443,7 @@ export default class ChatTopbar {
const middleware = this.chat.bubbles.getMiddleware(); const middleware = this.chat.bubbles.getMiddleware();
this.btnJoin.setAttribute('disabled', 'true'); this.btnJoin.setAttribute('disabled', 'true');
const chatId = -this.peerId; const chatId = this.peerId.toChatId();
let promise: Promise<any>; let promise: Promise<any>;
if(this.appChatsManager.isChannel(chatId)) { if(this.appChatsManager.isChannel(chatId)) {
promise = this.appChatsManager.joinChannel(chatId); promise = this.appChatsManager.joinChannel(chatId);
@ -458,7 +461,7 @@ export default class ChatTopbar {
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});
this.listenerSetter.add(rootScope)('chat_update', (chatId) => { this.listenerSetter.add(rootScope)('chat_update', (chatId) => {
if(this.peerId === -chatId) { if(this.peerId === chatId.toPeerId(true)) {
const chat = this.appChatsManager.getChat(chatId) as Channel/* | Chat */; const chat = this.appChatsManager.getChat(chatId) as Channel/* | Chat */;
this.btnJoin.classList.toggle('hide', !(chat as Channel)?.pFlags?.left); this.btnJoin.classList.toggle('hide', !(chat as Channel)?.pFlags?.left);
@ -554,7 +557,7 @@ export default class ChatTopbar {
delete this.pinnedMessage; delete this.pinnedMessage;
} }
public setPeer(peerId: number) { public setPeer(peerId: PeerId) {
this.wasPeerId = this.peerId; this.wasPeerId = this.peerId;
this.peerId = peerId; this.peerId = peerId;
@ -570,12 +573,15 @@ export default class ChatTopbar {
} }
const isBroadcast = this.appPeersManager.isBroadcast(peerId); const isBroadcast = this.appPeersManager.isBroadcast(peerId);
this.btnMute && this.btnMute.classList.toggle('hide', !isBroadcast); this.btnMute && this.btnMute.classList.toggle('hide', !isBroadcast);
if(this.btnJoin) { if(this.appPeersManager.isAnyChat(peerId)) {
replaceContent(this.btnJoin, i18n(this.appChatsManager.isChannel(-peerId) ? 'Chat.Subscribe' : 'ChannelJoin')); if(this.btnJoin) {
this.btnJoin.classList.toggle('hide', !this.appChatsManager.getChat(-peerId)?.pFlags?.left); const chatId = peerId.toChatId();
replaceContent(this.btnJoin, i18n(this.appChatsManager.isChannel(chatId) ? 'Chat.Subscribe' : 'ChannelJoin'));
this.btnJoin.classList.toggle('hide', !this.appChatsManager.getChat(chatId)?.pFlags?.left);
}
} }
this.setUtilsWidth(); this.setUtilsWidth();
const middleware = this.chat.bubbles.getMiddleware(); const middleware = this.chat.bubbles.getMiddleware();
@ -626,7 +632,7 @@ export default class ChatTopbar {
// ! костыль х2, это нужно делать в другом месте // ! костыль х2, это нужно делать в другом месте
if(!count) { if(!count) {
this.chat.appImManager.setPeer(0); // * close tab this.chat.appImManager.setPeer(NULL_PEER_ID); // * close tab
// ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится // ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится
const originalChat = this.chat.appImManager.chat; const originalChat = this.chat.appImManager.chat;

View File

@ -23,7 +23,7 @@ export default class DialogsContextMenu {
private element: HTMLElement; private element: HTMLElement;
private buttons: (ButtonMenuItemOptions & {verify: () => boolean})[]; private buttons: (ButtonMenuItemOptions & {verify: () => boolean})[];
private selectedId: number; private selectedId: PeerId;
private filterId: number; private filterId: number;
private dialog: Dialog; private dialog: Dialog;
@ -32,24 +32,18 @@ export default class DialogsContextMenu {
icon: 'unread', icon: 'unread',
text: 'MarkAsUnread', text: 'MarkAsUnread',
onClick: this.onUnreadClick, onClick: this.onUnreadClick,
verify: () => { verify: () => !appMessagesManager.isDialogUnread(this.dialog)
const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count);
return !isUnread;
}
}, { }, {
icon: 'readchats', icon: 'readchats',
text: 'MarkAsRead', text: 'MarkAsRead',
onClick: this.onUnreadClick, onClick: this.onUnreadClick,
verify: () => { verify: () => appMessagesManager.isDialogUnread(this.dialog)
const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count);
return isUnread;
}
}, { }, {
icon: 'pin', icon: 'pin',
text: 'ChatList.Context.Pin', text: 'ChatList.Context.Pin',
onClick: this.onPinClick, onClick: this.onPinClick,
verify: () => { verify: () => {
const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.getFilter(this.filterId).pinned_peers.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned; const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.getFilter(this.filterId).pinnedPeerIds.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned;
return !isPinned; return !isPinned;
} }
}, { }, {
@ -57,7 +51,7 @@ export default class DialogsContextMenu {
text: 'ChatList.Context.Unpin', text: 'ChatList.Context.Unpin',
onClick: this.onPinClick, onClick: this.onPinClick,
verify: () => { verify: () => {
const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.getFilter(this.filterId).pinned_peers.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned; const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.getFilter(this.filterId).pinnedPeerIds.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned;
return isPinned; return isPinned;
} }
}, { }, {
@ -174,7 +168,7 @@ export default class DialogsContextMenu {
this.filterId = appDialogsManager.filterId; this.filterId = appDialogsManager.filterId;
this.selectedId = +li.dataset.peerId; this.selectedId = li.dataset.peerId.toPeerId();
this.dialog = appMessagesManager.getDialogOnly(this.selectedId); this.dialog = appMessagesManager.getDialogOnly(this.selectedId);
this.buttons.forEach(button => { this.buttons.forEach(button => {

View File

@ -22,13 +22,13 @@ export default class EditPeer {
private inputFields: InputField[]; private inputFields: InputField[];
private listenerSetter: ListenerSetter; private listenerSetter: ListenerSetter;
private peerId: number; private peerId: PeerId;
private _disabled = false; private _disabled = false;
private avatarSize = 120; private avatarSize = 120;
constructor(options: { constructor(options: {
peerId?: number, peerId?: EditPeer['peerId'],
inputFields: EditPeer['inputFields'], inputFields: EditPeer['inputFields'],
listenerSetter: ListenerSetter, listenerSetter: ListenerSetter,
doNotEditAvatar?: boolean, doNotEditAvatar?: boolean,

View File

@ -23,7 +23,7 @@ export default class GifsTab implements EmoticonsTab {
const masonry = new GifsMasonry(gifsContainer, EMOTICONSSTICKERGROUP, scroll); const masonry = new GifsMasonry(gifsContainer, EMOTICONSSTICKERGROUP, scroll);
const preloader = putPreloader(this.content, true); const preloader = putPreloader(this.content, true);
apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((res) => { apiManager.invokeApi('messages.getSavedGifs', {hash: '0'}).then((res) => {
//console.log('getSavedGifs res:', res); //console.log('getSavedGifs res:', res);
if(res._ === 'messages.savedGifs') { if(res._ === 'messages.savedGifs') {

View File

@ -198,8 +198,8 @@ export default class StickersTab implements EmoticonsTab {
async renderStickerSet(set: StickerSet.stickerSet, prepend = false) { async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
const categoryDiv = document.createElement('div'); const categoryDiv = document.createElement('div');
categoryDiv.classList.add('sticker-category'); categoryDiv.classList.add('sticker-category');
categoryDiv.dataset.id = set.id; categoryDiv.dataset.id = '' + set.id;
categoryDiv.dataset.access_hash = set.access_hash; categoryDiv.dataset.access_hash = '' + set.access_hash;
const button = document.createElement('button'); const button = document.createElement('button');
button.classList.add('btn-icon', 'menu-horizontal-div-item'); button.classList.add('btn-icon', 'menu-horizontal-div-item');

View File

@ -198,7 +198,7 @@ export default class GifsMasonry {
div.style.width = size.width + 'px'; div.style.width = size.width + 'px';
div.style.opacity = '0'; div.style.opacity = '0';
//div.style.height = h + 'px'; //div.style.height = h + 'px';
div.dataset.docId = doc.id; div.dataset.docId = '' + doc.id;
appendTo.append(div); appendTo.append(div);

View File

@ -6,7 +6,7 @@
import { logger, LogTypes } from "../lib/logger"; import { logger, LogTypes } from "../lib/logger";
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector"; import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
import { findAndSpliceAll } from "../helpers/array"; import { findAndSpliceAll, indexOfAndSplice } from "../helpers/array";
import throttle from "../helpers/schedulers/throttle"; import throttle from "../helpers/schedulers/throttle";
type LazyLoadElementBase = { type LazyLoadElementBase = {
@ -121,7 +121,7 @@ export class LazyLoadQueueBase {
let added = 0; let added = 0;
do { do {
if(item) { if(item) {
this.queue.findAndSplice(i => i === item); indexOfAndSplice(this.queue, item);
} else { } else {
item = this.getItem(); item = this.getItem();
} }

View File

@ -52,7 +52,7 @@ export default class PeerProfile {
private setBioTimeout: number; private setBioTimeout: number;
private setPeerStatusInterval: number; private setPeerStatusInterval: number;
private peerId = 0; private peerId: PeerId;
private threadId: number; private threadId: number;
constructor(public scrollable: Scrollable) { constructor(public scrollable: Scrollable) {
@ -264,7 +264,7 @@ export default class PeerProfile {
} }
//let membersLi = this.profileTabs.firstElementChild.children[0] as HTMLLIElement; //let membersLi = this.profileTabs.firstElementChild.children[0] as HTMLLIElement;
if(peerId > 0) { if(peerId.isUser()) {
//membersLi.style.display = 'none'; //membersLi.style.display = 'none';
let user = appUsersManager.getUser(peerId); let user = appUsersManager.getUser(peerId);
@ -304,7 +304,7 @@ export default class PeerProfile {
} }
let promise: Promise<boolean>; let promise: Promise<boolean>;
if(peerId > 0) { if(peerId.isUser()) {
promise = appProfileManager.getProfile(peerId, override).then(userFull => { promise = appProfileManager.getProfile(peerId, override).then(userFull => {
if(this.peerId !== peerId || this.threadId !== threadId) { if(this.peerId !== peerId || this.threadId !== threadId) {
//this.log.warn('peer changed'); //this.log.warn('peer changed');
@ -319,7 +319,7 @@ export default class PeerProfile {
return true; return true;
}); });
} else { } else {
promise = appProfileManager.getChatFull(-peerId, override).then((chatFull) => { promise = appProfileManager.getChatFull(peerId.toChatId(), override).then((chatFull) => {
if(this.peerId !== peerId || this.threadId !== threadId) { if(this.peerId !== peerId || this.threadId !== threadId) {
//this.log.warn('peer changed'); //this.log.warn('peer changed');
return false; return false;
@ -342,8 +342,8 @@ export default class PeerProfile {
}); });
} }
public setPeer(peerId: number, threadId = 0) { public setPeer(peerId: PeerId, threadId = 0) {
if(this.peerId === peerId && this.threadId === peerId) return; if(this.peerId === peerId && this.threadId === threadId) return;
if(this.init) { if(this.init) {
this.init(); this.init();

View File

@ -28,8 +28,8 @@ export default class PeerProfileAvatars {
public arrowPrevious: HTMLElement; public arrowPrevious: HTMLElement;
public arrowNext: HTMLElement; public arrowNext: HTMLElement;
private tabs: HTMLDivElement; private tabs: HTMLDivElement;
private listLoader: ListLoader<string | Message.messageService, string | Message.messageService>; private listLoader: ListLoader<Photo.photo['id'] | Message.messageService, Photo.photo['id'] | Message.messageService>;
private peerId: number; private peerId: PeerId;
constructor(public scrollable: Scrollable) { constructor(public scrollable: Scrollable) {
this.container = document.createElement('div'); this.container = document.createElement('div');
@ -48,14 +48,14 @@ export default class PeerProfileAvatars {
this.tabs.classList.add(PeerProfileAvatars.BASE_CLASS + '-tabs'); this.tabs.classList.add(PeerProfileAvatars.BASE_CLASS + '-tabs');
this.arrowPrevious = document.createElement('div'); this.arrowPrevious = document.createElement('div');
this.arrowPrevious.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow'); this.arrowPrevious.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow', 'tgico-avatarprevious');
/* const previousIcon = document.createElement('i'); /* const previousIcon = document.createElement('i');
previousIcon.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow-icon', 'tgico-previous'); previousIcon.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow-icon', 'tgico-previous');
this.arrowBack.append(previousIcon); */ this.arrowBack.append(previousIcon); */
this.arrowNext = document.createElement('div'); this.arrowNext = document.createElement('div');
this.arrowNext.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow', PeerProfileAvatars.BASE_CLASS + '-arrow-next'); this.arrowNext.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow', PeerProfileAvatars.BASE_CLASS + '-arrow-next', 'tgico-avatarnext');
/* const nextIcon = document.createElement('i'); /* const nextIcon = document.createElement('i');
nextIcon.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow-icon', 'tgico-next'); nextIcon.classList.add(PeerProfileAvatars.BASE_CLASS + '-arrow-icon', 'tgico-next');
@ -101,7 +101,7 @@ export default class PeerProfileAvatars {
|| (clickX > (rect.width * SWITCH_ZONE) && clickX < (rect.width - rect.width * SWITCH_ZONE))) { || (clickX > (rect.width * SWITCH_ZONE) && clickX < (rect.width - rect.width * SWITCH_ZONE))) {
const peerId = this.peerId; const peerId = this.peerId;
const targets: {element: HTMLElement, item: string | Message.messageService}[] = []; const targets: {element: HTMLElement, item: Photo.photo['id'] | Message.messageService}[] = [];
this.listLoader.previous.concat(this.listLoader.current, this.listLoader.next).forEach((item, idx) => { this.listLoader.previous.concat(this.listLoader.current, this.listLoader.next).forEach((item, idx) => {
targets.push({ targets.push({
element: /* null */this.avatars.children[idx] as HTMLElement, element: /* null */this.avatars.children[idx] as HTMLElement,
@ -199,7 +199,7 @@ export default class PeerProfileAvatars {
}); });
} }
public setPeer(peerId: number) { public setPeer(peerId: PeerId) {
this.peerId = peerId; this.peerId = peerId;
const photo = appPeersManager.getPeerPhoto(peerId); const photo = appPeersManager.getPeerPhoto(peerId);
@ -212,8 +212,8 @@ export default class PeerProfileAvatars {
loadMore: (anchor, older, loadCount) => { loadMore: (anchor, older, loadCount) => {
if(!older) return Promise.resolve({count: undefined, items: []}); if(!older) return Promise.resolve({count: undefined, items: []});
if(peerId > 0) { if(peerId.isUser()) {
const maxId: string = (anchor || listLoader.current) as any; const maxId: Photo.photo['id'] = (anchor || listLoader.current) as any;
return appPhotosManager.getUserPhotos(peerId, maxId, loadCount).then(value => { return appPhotosManager.getUserPhotos(peerId, maxId, loadCount).then(value => {
return { return {
count: value.count, count: value.count,
@ -223,7 +223,7 @@ export default class PeerProfileAvatars {
} else { } else {
const promises: [Promise<ChatFull>, ReturnType<AppMessagesManager['getSearch']>] = [] as any; const promises: [Promise<ChatFull>, ReturnType<AppMessagesManager['getSearch']>] = [] as any;
if(!listLoader.current) { if(!listLoader.current) {
promises.push(appProfileManager.getChatFull(-peerId)); promises.push(appProfileManager.getChatFull(peerId.toChatId()));
} }
promises.push(appMessagesManager.getSearch({ promises.push(appMessagesManager.getSearch({
@ -295,13 +295,13 @@ export default class PeerProfileAvatars {
this.container.classList.toggle('is-single', this.tabs.childElementCount <= 1); this.container.classList.toggle('is-single', this.tabs.childElementCount <= 1);
} }
public processItem = (photoId: string | Message.messageService) => { public processItem = (photoId: Photo.photo['id'] | Message.messageService) => {
const avatar = document.createElement('div'); const avatar = document.createElement('div');
avatar.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar'); avatar.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar');
let photo: Photo.photo; let photo: Photo.photo;
if(photoId) { if(photoId) {
photo = typeof(photoId) === 'string' ? photo = typeof(photoId) !== 'object' ?
appPhotosManager.getPhoto(photoId) : appPhotosManager.getPhoto(photoId) :
(photoId.action as MessageAction.messageActionChannelEditPhoto).photo as Photo.photo; (photoId.action as MessageAction.messageActionChannelEditPhoto).photo as Photo.photo;
} }

View File

@ -12,7 +12,7 @@ import replaceContent from "../helpers/dom/replaceContent";
import appUsersManager from "../lib/appManagers/appUsersManager"; import appUsersManager from "../lib/appManagers/appUsersManager";
export type PeerTitleOptions = { export type PeerTitleOptions = {
peerId: number, peerId: PeerId,
plainText?: boolean, plainText?: boolean,
onlyFirstName?: boolean, onlyFirstName?: boolean,
dialog?: boolean dialog?: boolean
@ -36,7 +36,7 @@ rootScope.addEventListener('peer_title_edit', (peerId) => {
export default class PeerTitle { export default class PeerTitle {
public element: HTMLElement; public element: HTMLElement;
public peerId: number; public peerId: PeerId;
public plainText = false; public plainText = false;
public onlyFirstName = false; public onlyFirstName = false;
public dialog = false; public dialog = false;
@ -61,7 +61,7 @@ export default class PeerTitle {
} }
if(this.peerId !== rootScope.myId || !this.dialog) { if(this.peerId !== rootScope.myId || !this.dialog) {
if(this.peerId > 0 && appUsersManager.getUser(this.peerId).pFlags.deleted) { if(this.peerId.isUser() && appUsersManager.getUser(this.peerId).pFlags.deleted) {
replaceContent(this.element, i18n(this.onlyFirstName ? 'Deleted' : 'HiddenName')); replaceContent(this.element, i18n(this.onlyFirstName ? 'Deleted' : 'HiddenName'));
} else { } else {
this.element.innerHTML = appPeersManager.getPeerTitle(this.peerId, this.plainText, this.onlyFirstName); this.element.innerHTML = appPeersManager.getPeerTitle(this.peerId, this.plainText, this.onlyFirstName);

View File

@ -7,7 +7,7 @@
import mediaSizes from "../helpers/mediaSizes"; import mediaSizes from "../helpers/mediaSizes";
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport"; import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
import appImManager from "../lib/appManagers/appImManager"; import appImManager from "../lib/appManagers/appImManager";
import appPollsManager, { Poll, PollResults } from "../lib/appManagers/appPollsManager"; import appPollsManager from "../lib/appManagers/appPollsManager";
import serverTimeManager from "../lib/mtproto/serverTimeManager"; import serverTimeManager from "../lib/mtproto/serverTimeManager";
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
@ -22,6 +22,7 @@ import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent";
import replaceContent from "../helpers/dom/replaceContent"; import replaceContent from "../helpers/dom/replaceContent";
import windowSize from "../helpers/windowSize"; import windowSize from "../helpers/windowSize";
import { Poll, PollResults } from "../layer";
let lineTotalLength = 0; let lineTotalLength = 0;
const tailLength = 9; const tailLength = 9;
@ -92,9 +93,7 @@ rootScope.on('poll_update', (e) => {
} }
}); */ }); */
rootScope.addEventListener('poll_update', (e) => { rootScope.addEventListener('poll_update', ({poll, results}) => {
const {poll, results} = e as {poll: Poll, results: PollResults};
const pollElements = Array.from(document.querySelectorAll(`poll-element[poll-id="${poll.id}"]`)) as PollElement[]; const pollElements = Array.from(document.querySelectorAll(`poll-element[poll-id="${poll.id}"]`)) as PollElement[];
pollElements.forEach(pollElement => { pollElements.forEach(pollElement => {
//console.log('poll_update', poll, results); //console.log('poll_update', poll, results);

View File

@ -4,7 +4,6 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import type { Poll } from "../../lib/appManagers/appPollsManager";
import type Chat from "../chat/chat"; import type Chat from "../chat/chat";
import PopupElement from "."; import PopupElement from ".";
import CheckboxField from "../checkboxField"; import CheckboxField from "../checkboxField";
@ -19,6 +18,7 @@ import getRichValue from "../../helpers/dom/getRichValue";
import isInputEmpty from "../../helpers/dom/isInputEmpty"; import isInputEmpty from "../../helpers/dom/isInputEmpty";
import whichChild from "../../helpers/dom/whichChild"; import whichChild from "../../helpers/dom/whichChild";
import { attachClickEvent } from "../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../helpers/dom/clickEvent";
import { Poll } from "../../layer";
const MAX_LENGTH_QUESTION = 255; const MAX_LENGTH_QUESTION = 255;
const MAX_LENGTH_OPTION = 100; const MAX_LENGTH_OPTION = 100;

View File

@ -13,7 +13,7 @@ import PopupPeer, { PopupPeerButtonCallbackCheckboxes, PopupPeerOptions } from "
export default class PopupDeleteDialog { export default class PopupDeleteDialog {
constructor( constructor(
peerId: number, peerId: PeerId,
// actionType: 'leave' | 'delete', // actionType: 'leave' | 'delete',
peerType: PeerType = appPeersManager.getDialogType(peerId), peerType: PeerType = appPeersManager.getDialogType(peerId),
onSelect?: (promise: Promise<any>) => void onSelect?: (promise: Promise<any>) => void
@ -26,7 +26,7 @@ export default class PopupDeleteDialog {
}; */ }; */
const callbackLeave = (checked: PopupPeerButtonCallbackCheckboxes, flush = checkboxes && !!checked.size) => { const callbackLeave = (checked: PopupPeerButtonCallbackCheckboxes, flush = checkboxes && !!checked.size) => {
let promise = appChatsManager.leave(-peerId); let promise = appChatsManager.leave(peerId.toChatId());
if(flush) { if(flush) {
promise = promise.finally(() => { promise = promise.finally(() => {
@ -40,11 +40,11 @@ export default class PopupDeleteDialog {
const callbackDelete = (checked: PopupPeerButtonCallbackCheckboxes) => { const callbackDelete = (checked: PopupPeerButtonCallbackCheckboxes) => {
let promise: Promise<any>; let promise: Promise<any>;
if(peerId > 0) { if(peerId.isUser()) {
promise = appMessagesManager.flushHistory(peerId, false, checkboxes ? !!checked.size : undefined); promise = appMessagesManager.flushHistory(peerId, false, checkboxes ? !!checked.size : undefined);
} else { } else {
if(checked.size) { if(checked.size) {
promise = appChatsManager.delete(-peerId); promise = appChatsManager.delete(peerId.toChatId());
} else { } else {
return callbackLeave(checked); return callbackLeave(checked);
} }
@ -56,7 +56,7 @@ export default class PopupDeleteDialog {
let title: LangPackKey, description: LangPackKey, descriptionArgs: any[], buttons: PopupPeerOptions['buttons'], checkboxes: PopupPeerOptions['checkboxes']; let title: LangPackKey, description: LangPackKey, descriptionArgs: any[], buttons: PopupPeerOptions['buttons'], checkboxes: PopupPeerOptions['checkboxes'];
switch(peerType) { switch(peerType) {
case 'channel': { case 'channel': {
if(/* actionType === 'delete' && */appChatsManager.hasRights(-peerId, 'delete_chat')) { if(/* actionType === 'delete' && */appChatsManager.hasRights(peerId.toChatId(), 'delete_chat')) {
appChatsManager.deleteChannel appChatsManager.deleteChannel
title = 'ChannelDeleteMenu'; title = 'ChannelDeleteMenu';
description = 'AreYouSureDeleteAndExitChannel'; description = 'AreYouSureDeleteAndExitChannel';
@ -130,7 +130,7 @@ export default class PopupDeleteDialog {
case 'megagroup': case 'megagroup':
case 'group': { case 'group': {
if(/* actionType === 'delete' && */appChatsManager.hasRights(-peerId, 'delete_chat')) { if(/* actionType === 'delete' && */appChatsManager.hasRights(peerId.toChatId(), 'delete_chat')) {
title = 'DeleteMegaMenu'; title = 'DeleteMegaMenu';
description = 'AreYouSureDeleteAndExit'; description = 'AreYouSureDeleteAndExit';
buttons = [{ buttons = [{

View File

@ -15,7 +15,7 @@ import PeerTitle from "../peerTitle";
import appPeersManager from "../../lib/appManagers/appPeersManager"; import appPeersManager from "../../lib/appManagers/appPeersManager";
export default class PopupDeleteMessages { export default class PopupDeleteMessages {
constructor(peerId: number, mids: number[], type: ChatType, onConfirm?: () => void) { constructor(peerId: PeerId, mids: number[], type: ChatType, onConfirm?: () => void) {
const peerTitleElement = new PeerTitle({peerId}).element; const peerTitleElement = new PeerTitle({peerId}).element;
mids = mids.slice(); mids = mids.slice();
@ -51,15 +51,15 @@ export default class PopupDeleteMessages {
if(peerId === rootScope.myId || type === 'scheduled') { if(peerId === rootScope.myId || type === 'scheduled') {
} else { } else {
if(peerId > 0) { if(peerId.isUser()) {
checkboxes.push({ checkboxes.push({
text: 'DeleteMessagesOptionAlso', text: 'DeleteMessagesOptionAlso',
textArgs: [peerTitleElement] textArgs: [peerTitleElement]
}); });
} else { } else {
const chat = appChatsManager.getChat(-peerId); const chat = appChatsManager.getChat(peerId.toChatId());
const hasRights = appChatsManager.hasRights(-peerId, 'delete_messages'); const hasRights = appChatsManager.hasRights(peerId.toChatId(), 'delete_messages');
if(chat._ === 'chat') { if(chat._ === 'chat') {
const canRevoke = hasRights ? mids.slice() : mids.filter(mid => { const canRevoke = hasRights ? mids.slice() : mids.filter(mid => {
const message = appMessagesManager.getMessageByPeer(peerId, mid); const message = appMessagesManager.getMessageByPeer(peerId, mid);

View File

@ -9,8 +9,8 @@ import PopupPickUser from "./pickUser";
export default class PopupForward extends PopupPickUser { export default class PopupForward extends PopupPickUser {
constructor( constructor(
peerIdMids: {[fromPeerId: number]: number[]}, peerIdMids: {[frompeerId: PeerId]: number[]},
onSelect?: (peerId: number) => Promise<void> | void, onSelect?: (peerId: PeerId) => Promise<void> | void,
onClose?: () => void, onClose?: () => void,
overrideOnSelect = false overrideOnSelect = false
) { ) {

View File

@ -8,9 +8,11 @@ import PopupElement, { addCancelButton } from ".";
import { ChatInvite, Updates } from "../../layer"; import { ChatInvite, Updates } from "../../layer";
import apiUpdatesManager from "../../lib/appManagers/apiUpdatesManager"; import apiUpdatesManager from "../../lib/appManagers/apiUpdatesManager";
import appAvatarsManager from "../../lib/appManagers/appAvatarsManager"; import appAvatarsManager from "../../lib/appManagers/appAvatarsManager";
import appChatsManager from "../../lib/appManagers/appChatsManager";
import appPhotosManager from "../../lib/appManagers/appPhotosManager"; import appPhotosManager from "../../lib/appManagers/appPhotosManager";
import { i18n } from "../../lib/langPack"; import { i18n } from "../../lib/langPack";
import apiManager from "../../lib/mtproto/mtprotoworker"; import apiManager from "../../lib/mtproto/mtprotoworker";
import { NULL_PEER_ID } from "../../lib/mtproto/mtproto_config";
import RichTextProcessor from "../../lib/richtextprocessor"; import RichTextProcessor from "../../lib/richtextprocessor";
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import AvatarElement from "../avatar"; import AvatarElement from "../avatar";
@ -27,7 +29,7 @@ export default class PopupJoinChatInvite extends PopupElement {
.then((updates) => { .then((updates) => {
apiUpdatesManager.processUpdateMessage(updates); apiUpdatesManager.processUpdateMessage(updates);
const chat = (updates as Updates.updates).chats[0]; const chat = (updates as Updates.updates).chats[0];
const peerId = -chat.id; const peerId = chat.id.toPeerId(true);
rootScope.dispatchEvent('history_focus', {peerId}); rootScope.dispatchEvent('history_focus', {peerId});
}); });
} }
@ -63,7 +65,7 @@ export default class PopupJoinChatInvite extends PopupElement {
}); });
avatarElem.style.width = avatarElem.style.height = ''; avatarElem.style.width = avatarElem.style.height = '';
} else { } else {
appAvatarsManager.putPhoto(avatarElem, -0, false, chatInvite.title); appAvatarsManager.putPhoto(avatarElem, NULL_PEER_ID, false, chatInvite.title);
} }
const title = document.createElement('div'); const title = document.createElement('div');

View File

@ -14,7 +14,7 @@ export type PopupPeerButtonCallback = (checkboxes?: PopupPeerButtonCallbackCheck
export type PopupPeerCheckboxOptions = CheckboxFieldOptions & {checkboxField?: CheckboxField}; export type PopupPeerCheckboxOptions = CheckboxFieldOptions & {checkboxField?: CheckboxField};
export type PopupPeerOptions = PopupOptions & Partial<{ export type PopupPeerOptions = PopupOptions & Partial<{
peerId: number, peerId: PeerId,
title: string, title: string,
titleLangKey?: LangPackKey, titleLangKey?: LangPackKey,
titleLangArgs?: any[], titleLangArgs?: any[],

View File

@ -14,7 +14,7 @@ export default class PopupPickUser extends PopupElement {
constructor(options: { constructor(options: {
peerTypes: AppSelectPeers['peerType'], peerTypes: AppSelectPeers['peerType'],
onSelect?: (peerId: number) => Promise<void> | void, onSelect?: (peerId: PeerId) => Promise<void> | void,
onClose?: () => void, onClose?: () => void,
placeholder: LangPackKey, placeholder: LangPackKey,
chatRightsAction?: AppSelectPeers['chatRightsAction'], chatRightsAction?: AppSelectPeers['chatRightsAction'],
@ -29,7 +29,7 @@ export default class PopupPickUser extends PopupElement {
appendTo: this.body, appendTo: this.body,
onChange: async() => { onChange: async() => {
const selected = this.selector.getSelected(); const selected = this.selector.getSelected();
const peerId = selected[selected.length - 1]; const peerId = selected[selected.length - 1].toPeerId();
if(options.onSelect) { if(options.onSelect) {
const res = options.onSelect(peerId); const res = options.onSelect(peerId);

View File

@ -15,7 +15,7 @@ import PopupPeer from "./peer";
import PopupReportMessagesConfirm from "./reportMessagesConfirm"; import PopupReportMessagesConfirm from "./reportMessagesConfirm";
export default class PopupReportMessages extends PopupPeer { export default class PopupReportMessages extends PopupPeer {
constructor(peerId: number, mids: number[], onConfirm?: () => void) { constructor(peerId: PeerId, mids: number[], onConfirm?: () => void) {
super('popup-report-messages', {titleLangKey: 'ChatTitle.ReportMessages', buttons: [], body: true}); super('popup-report-messages', {titleLangKey: 'ChatTitle.ReportMessages', buttons: [], body: true});
mids = mids.slice(); mids = mids.slice();

View File

@ -14,7 +14,7 @@ import PopupPeer from "./peer";
export default class PopupReportMessagesConfirm extends PopupPeer { export default class PopupReportMessagesConfirm extends PopupPeer {
public static STICKER_EMOJI = '👮‍♀️'; public static STICKER_EMOJI = '👮‍♀️';
constructor(peerId: number, mids: number[], reason: ReportReason['_'], onConfirm?: () => void) { constructor(peerId: PeerId, mids: number[], reason: ReportReason['_'], onConfirm?: () => void) {
super('popup-report-messages-confirm', { super('popup-report-messages-confirm', {
noTitle: true, noTitle: true,
descriptionLangKey: 'ReportInfo', descriptionLangKey: 'ReportInfo',

View File

@ -9,7 +9,7 @@ import { PopupButton } from ".";
import PopupPeer from "./peer"; import PopupPeer from "./peer";
export default class PopupSendNow { export default class PopupSendNow {
constructor(peerId: number, mids: number[], onConfirm?: () => void) { constructor(peerId: PeerId, mids: number[], onConfirm?: () => void) {
let title: string, description: string, buttons: PopupButton[] = []; let title: string, description: string, buttons: PopupButton[] = [];
title = `Send Message${mids.length > 1 ? 's' : ''} Now`; title = `Send Message${mids.length > 1 ? 's' : ''} Now`;

View File

@ -14,7 +14,7 @@ import appChatsManager from "../../lib/appManagers/appChatsManager";
import PeerTitle from "../peerTitle"; import PeerTitle from "../peerTitle";
export default class PopupPinMessage { export default class PopupPinMessage {
constructor(peerId: number, mid: number, unpin?: true, onConfirm?: () => void) { constructor(peerId: PeerId, mid: number, unpin?: true, onConfirm?: () => void) {
let title: LangPackKey, description: LangPackKey, descriptionArgs: FormatterArguments, let title: LangPackKey, description: LangPackKey, descriptionArgs: FormatterArguments,
buttons: PopupPeerOptions['buttons'] = [], checkboxes: PopupPeerOptions['checkboxes'] = []; buttons: PopupPeerOptions['buttons'] = [], checkboxes: PopupPeerOptions['checkboxes'] = [];
@ -65,13 +65,13 @@ export default class PopupPinMessage {
title = 'PinMessageAlertTitle'; title = 'PinMessageAlertTitle';
const pinButtonText: LangPackKey = 'PinMessage'; const pinButtonText: LangPackKey = 'PinMessage';
if(peerId < 0) { if(peerId.isAnyChat()) {
buttons.push({ buttons.push({
langKey: pinButtonText, langKey: pinButtonText,
callback: (checked) => callback(checked, false, !checked.size) callback: (checked) => callback(checked, false, !checked.size)
}); });
if(appChatsManager.isBroadcast(-peerId)) { if(appChatsManager.isBroadcast(peerId.toChatId())) {
description = 'PinMessageAlertChannel'; description = 'PinMessageAlertChannel';
} else { } else {
description = 'PinMessageAlert'; description = 'PinMessageAlert';

View File

@ -6,6 +6,7 @@
import { randomLong } from "../helpers/random"; import { randomLong } from "../helpers/random";
import { InputPrivacyKey, InputPrivacyRule } from "../layer"; import { InputPrivacyKey, InputPrivacyRule } from "../layer";
import appChatsManager from "../lib/appManagers/appChatsManager";
import appPrivacyManager, { PrivacyType } from "../lib/appManagers/appPrivacyManager"; import appPrivacyManager, { PrivacyType } from "../lib/appManagers/appPrivacyManager";
import appUsersManager from "../lib/appManagers/appUsersManager"; import appUsersManager from "../lib/appManagers/appUsersManager";
import { i18n, join, LangPackKey, _i18n } from "../lib/langPack"; import { i18n, join, LangPackKey, _i18n } from "../lib/langPack";
@ -29,8 +30,8 @@ export default class PrivacySection {
clickable: true clickable: true
}>; }>;
public peerIds: { public peerIds: {
disallow?: number[], disallow?: PeerId[],
allow?: number[] allow?: PeerId[]
}; };
public type: PrivacyType; public type: PrivacyType;
@ -152,8 +153,8 @@ export default class PrivacySection {
(['allow', 'disallow'] as ('allow' | 'disallow')[]).forEach(k => { (['allow', 'disallow'] as ('allow' | 'disallow')[]).forEach(k => {
const arr = []; const arr = [];
const from = k === 'allow' ? details.allowPeers : details.disallowPeers; const from = k === 'allow' ? details.allowPeers : details.disallowPeers;
arr.push(...from.users); arr.push(...from.users.map(id => id.toPeerId()));
arr.push(...from.chats.map(id => -id)); arr.push(...from.chats.map(id => id.toPeerId(false)));
this.peerIds[k] = arr; this.peerIds[k] = arr;
const s = this.exceptions.get(k).row.subtitle; const s = this.exceptions.get(k).row.subtitle;
s.innerHTML = ''; s.innerHTML = '';
@ -189,12 +190,11 @@ export default class PrivacySection {
return; return;
} }
const _peerIds: number[] = this.peerIds[k]; const _peerIds = this.peerIds[k];
if(_peerIds) { if(_peerIds) {
const splitted = this.splitPeersByType(_peerIds); const splitted = this.splitPeersByType(_peerIds);
if(splitted.chats.length) { if(splitted.chats.length) {
rules.push({_: chatKey, chats: splitted.chats.map(peerId => -peerId)}); rules.push({_: chatKey, chats: splitted.chats.map(peerId => peerId.toChatId())});
} }
if(splitted.users.length) { if(splitted.users.length) {
@ -236,16 +236,16 @@ export default class PrivacySection {
row.radioField.input.checked = true; row.radioField.input.checked = true;
} }
private splitPeersByType(peerIds: number[]) { private splitPeersByType(peerIds: PeerId[]) {
const peers = {users: [] as number[], chats: [] as number[]}; const peers = {users: [] as UserId[], chats: [] as ChatId[]};
peerIds.forEach(peerId => { peerIds.forEach(peerId => {
peers[peerId < 0 ? 'chats' : 'users'].push(peerId < 0 ? -peerId : peerId); peers[peerId.isAnyChat() ? 'chats' : 'users'].push(peerId.isAnyChat() ? peerId.toChatId() : peerId);
}); });
return peers; return peers;
} }
private generateStr(peers: {users: number[], chats: number[]}) { private generateStr(peers: {users: UserId[], chats: ChatId[]}) {
if(!peers.users.length && !peers.chats.length) { if(!peers.users.length && !peers.chats.length) {
return [i18n('PrivacySettingsController.AddUsers')]; return [i18n('PrivacySettingsController.AddUsers')];
} }

View File

@ -39,6 +39,7 @@ import replaceContent from "../../helpers/dom/replaceContent";
import sessionStorage from "../../lib/sessionStorage"; import sessionStorage from "../../lib/sessionStorage";
import { CLICK_EVENT_NAME } from "../../helpers/dom/clickEvent"; import { CLICK_EVENT_NAME } from "../../helpers/dom/clickEvent";
import { closeBtnMenu } from "../misc"; import { closeBtnMenu } from "../misc";
import { indexOfAndSplice } from "../../helpers/array";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
@ -71,7 +72,6 @@ export class AppSidebarLeft extends SidebarSlider {
const onNewGroupClick = () => { const onNewGroupClick = () => {
new AppAddMembersTab(this).open({ new AppAddMembersTab(this).open({
peerId: 0,
type: 'chat', type: 'chat',
skippable: false, skippable: false,
takeOut: (peerIds) => { takeOut: (peerIds) => {
@ -96,8 +96,8 @@ export class AppSidebarLeft extends SidebarSlider {
new AppArchivedTab(this).open(); new AppArchivedTab(this).open();
}, },
verify: () => { verify: () => {
const folder = appMessagesManager.dialogsStorage.getFolder(1); const folder = appMessagesManager.dialogsStorage.getFolderDialogs(1, false);
return !!folder.length; return !!folder.length || !appMessagesManager.dialogsStorage.isDialogsLoaded(1);
} }
}; };
@ -174,7 +174,10 @@ export class AppSidebarLeft extends SidebarSlider {
icon: 'char z', icon: 'char z',
text: 'ChatList.Menu.SwitchTo.Z', text: 'ChatList.Menu.SwitchTo.Z',
onClick: () => { onClick: () => {
sessionStorage.set({kz_version: 'Z'}).then(() => { Promise.all([
sessionStorage.set({kz_version: 'Z'}),
sessionStorage.delete('tgme_sync')
]).then(() => {
location.href = 'https://web.telegram.org/z/'; location.href = 'https://web.telegram.org/z/';
}); });
}, },
@ -183,7 +186,9 @@ export class AppSidebarLeft extends SidebarSlider {
icon: 'char w', icon: 'char w',
text: 'ChatList.Menu.SwitchTo.Webogram', text: 'ChatList.Menu.SwitchTo.Webogram',
onClick: () => { onClick: () => {
location.href = 'https://web.telegram.org/?legacy=1'; sessionStorage.delete('tgme_sync').then(() => {
location.href = 'https://web.telegram.org/?legacy=1';
});
}, },
verify: () => App.isMainDomain verify: () => App.isMainDomain
}]; }];
@ -250,9 +255,13 @@ export class AppSidebarLeft extends SidebarSlider {
btnArchive.element.append(this.archivedCount); btnArchive.element.append(this.archivedCount);
rootScope.addEventListener('dialogs_archived_unread', (e) => { rootScope.addEventListener('folder_unread', (folder) => {
this.archivedCount.innerText = '' + formatNumber(e.count, 1); if(folder.id === 1) {
this.archivedCount.classList.toggle('hide', !e.count); // const count = folder.unreadMessagesCount;
const count = folder.unreadDialogsCount;
this.archivedCount.innerText = '' + formatNumber(count, 1);
this.archivedCount.classList.toggle('hide', !count);
}
}); });
appUsersManager.getTopPeers('correspondents'); appUsersManager.getTopPeers('correspondents');
@ -322,7 +331,7 @@ export class AppSidebarLeft extends SidebarSlider {
const resetSearch = () => { const resetSearch = () => {
searchSuper.setQuery({ searchSuper.setQuery({
peerId: 0, peerId: ''.toPeerId(),
folderId: 0 folderId: 0
}); });
searchSuper.selectTab(0); searchSuper.selectTab(0);
@ -332,7 +341,7 @@ export class AppSidebarLeft extends SidebarSlider {
resetSearch(); resetSearch();
let pickedElements: HTMLElement[] = []; let pickedElements: HTMLElement[] = [];
let selectedPeerId = 0; let selectedPeerId: PeerId = ''.toPeerId();
let selectedMinDate = 0; let selectedMinDate = 0;
let selectedMaxDate = 0; let selectedMaxDate = 0;
const updatePicked = () => { const updatePicked = () => {
@ -361,7 +370,7 @@ export class AppSidebarLeft extends SidebarSlider {
selectedMinDate = +minDate; selectedMinDate = +minDate;
selectedMaxDate = +maxDate; selectedMaxDate = +maxDate;
} else { } else {
selectedPeerId = +key; selectedPeerId = key.toPeerId();
} }
target.addEventListener('click', () => { target.addEventListener('click', () => {
@ -376,7 +385,7 @@ export class AppSidebarLeft extends SidebarSlider {
searchSuper.nav.parentElement.append(helper); searchSuper.nav.parentElement.append(helper);
const renderEntity = (peerId: any, title?: string | HTMLElement) => { const renderEntity = (key: PeerId | string, title?: string | HTMLElement) => {
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('selector-user'/* , 'scale-in' */); div.classList.add('selector-user'/* , 'scale-in' */);
@ -385,13 +394,13 @@ export class AppSidebarLeft extends SidebarSlider {
avatarEl.setAttribute('dialog', '1'); avatarEl.setAttribute('dialog', '1');
avatarEl.classList.add('avatar-30'); avatarEl.classList.add('avatar-30');
div.dataset.key = '' + peerId; div.dataset.key = '' + key;
if(typeof(peerId) === 'number') { if(key.isPeerId()) {
if(title === undefined) { if(title === undefined) {
title = new PeerTitle({peerId}).element; title = new PeerTitle({peerId: key.toPeerId()}).element;
} }
avatarEl.setAttribute('peer', '' + peerId); avatarEl.setAttribute('peer', '' + key);
} else { } else {
avatarEl.classList.add('tgico-calendarfilter'); avatarEl.classList.add('tgico-calendarfilter');
} }
@ -415,11 +424,11 @@ export class AppSidebarLeft extends SidebarSlider {
if(key.indexOf('date_') === 0) { if(key.indexOf('date_') === 0) {
selectedMinDate = selectedMaxDate = 0; selectedMinDate = selectedMaxDate = 0;
} else { } else {
selectedPeerId = 0; selectedPeerId = ''.toPeerId();
} }
target.remove(); target.remove();
pickedElements.findAndSplice(t => t === target); indexOfAndSplice(pickedElements, target);
setTimeout(() => { setTimeout(() => {
updatePicked(); updatePicked();
@ -452,8 +461,9 @@ export class AppSidebarLeft extends SidebarSlider {
if(!selectedPeerId && value.trim()) { if(!selectedPeerId && value.trim()) {
const middleware = searchSuper.middleware.get(); const middleware = searchSuper.middleware.get();
Promise.all([ Promise.all([
appMessagesManager.getConversationsAll(value).then(dialogs => dialogs.map(d => d.peerId)), // appMessagesManager.getConversationsAll(value).then(dialogs => dialogs.map(d => d.peerId)),
appUsersManager.getContacts(value, true) appMessagesManager.getConversations(value).promise.then(({dialogs}) => dialogs.map(d => d.peerId)),
appUsersManager.getContactsPeerIds(value, true)
]).then(results => { ]).then(results => {
if(!middleware()) return; if(!middleware()) return;
const peerIds = new Set(results[0].concat(results[1])); const peerIds = new Set(results[0].concat(results[1]));
@ -489,11 +499,11 @@ export class AppSidebarLeft extends SidebarSlider {
return; return;
} }
const peerId = +target.getAttribute('data-peer-id'); const peerId = target.getAttribute('data-peer-id').toPeerId();
appStateManager.getState().then(state => { appStateManager.getState().then(state => {
const recentSearch = state.recentSearch || []; const recentSearch = state.recentSearch || [];
if(recentSearch[0] !== peerId) { if(recentSearch[0] !== peerId) {
recentSearch.findAndSplice(p => p === peerId); indexOfAndSplice(recentSearch, peerId);
recentSearch.unshift(peerId); recentSearch.unshift(peerId);
if(recentSearch.length > 20) { if(recentSearch.length > 20) {
recentSearch.length = 20; recentSearch.length = 20;

View File

@ -38,7 +38,7 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
titleRight: auth.pFlags.current ? undefined : formatDateAccordingToTodayNew(new Date(Math.max(auth.date_active, auth.date_created) * 1000)) titleRight: auth.pFlags.current ? undefined : formatDateAccordingToTodayNew(new Date(Math.max(auth.date_active, auth.date_created) * 1000))
}); });
row.container.dataset.hash = auth.hash; row.container.dataset.hash = '' + auth.hash;
const midtitle = document.createElement('div'); const midtitle = document.createElement('div');
midtitle.classList.add('row-midtitle'); midtitle.classList.add('row-midtitle');

View File

@ -14,7 +14,7 @@ export default class AppAddMembersTab extends SliderSuperTab {
private nextBtn: HTMLButtonElement; private nextBtn: HTMLButtonElement;
private selector: AppSelectPeers; private selector: AppSelectPeers;
private peerType: 'channel' | 'chat' | 'privacy'; private peerType: 'channel' | 'chat' | 'privacy';
private takeOut: (peerIds: number[]) => Promise<any> | false | void; private takeOut: (peerIds: PeerId[]) => Promise<any> | false | void;
private skippable: boolean; private skippable: boolean;
protected init() { protected init() {
@ -23,7 +23,7 @@ export default class AppAddMembersTab extends SliderSuperTab {
this.scrollable.container.remove(); this.scrollable.container.remove();
this.nextBtn.addEventListener('click', () => { this.nextBtn.addEventListener('click', () => {
const peerIds = this.selector.getSelected(); const peerIds = this.selector.getSelected().map(sel => sel.toPeerId());
if(this.skippable) { if(this.skippable) {
this.takeOut(peerIds); this.takeOut(peerIds);
@ -53,11 +53,10 @@ export default class AppAddMembersTab extends SliderSuperTab {
public open(options: { public open(options: {
title: LangPackKey, title: LangPackKey,
placeholder: LangPackKey, placeholder: LangPackKey,
peerId?: number,
type: AppAddMembersTab['peerType'], type: AppAddMembersTab['peerType'],
takeOut?: AppAddMembersTab['takeOut'], takeOut?: AppAddMembersTab['takeOut'],
skippable: boolean, skippable: boolean,
selectedPeerIds?: number[] selectedPeerIds?: PeerId[]
}) { }) {
const ret = super.open(); const ret = super.open();

View File

@ -35,7 +35,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
private grid: HTMLElement; private grid: HTMLElement;
private tempId = 0; private tempId = 0;
private theme: Theme; private theme: Theme;
private clicked: Set<string> = new Set(); private clicked: Set<DocId> = new Set();
private blurCheckboxField: CheckboxField; private blurCheckboxField: CheckboxField;
init() { init() {
@ -162,7 +162,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
wallpaper = _wallpaper as WallPaper.wallPaper; wallpaper = _wallpaper as WallPaper.wallPaper;
wallpaper.document = appDocsManager.saveDoc(wallpaper.document); wallpaper.document = appDocsManager.saveDoc(wallpaper.document);
container.dataset.docId = wallpaper.document.id; container.dataset.docId = '' + wallpaper.document.id;
container.dataset.slug = wallpaper.slug; container.dataset.slug = wallpaper.slug;
this.setBackgroundDocument(wallpaper.slug, wallpaper.document).then(deferred.resolve, deferred.reject); this.setBackgroundDocument(wallpaper.slug, wallpaper.document).then(deferred.resolve, deferred.reject);
@ -223,7 +223,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
size: appPhotosManager.choosePhotoSize(wallpaper.document, 200, 200) size: appPhotosManager.choosePhotoSize(wallpaper.document, 200, 200)
}); });
container.dataset.docId = wallpaper.document.id; container.dataset.docId = '' + wallpaper.document.id;
container.dataset.slug = wallpaper.slug; container.dataset.slug = wallpaper.slug;
if(this.theme.background.type === 'image' && this.theme.background.slug === wallpaper.slug) { if(this.theme.background.type === 'image' && this.theme.background.slug === wallpaper.slug) {

View File

@ -17,7 +17,7 @@ import ButtonCorner from "../../buttonCorner";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
export default class AppBlockedUsersTab extends SliderSuperTab { export default class AppBlockedUsersTab extends SliderSuperTab {
public peerIds: number[]; public peerIds: PeerId[];
private menuElement: HTMLElement; private menuElement: HTMLElement;
protected init() { protected init() {
@ -50,7 +50,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
this.scrollable.container.classList.add('chatlist-container'); this.scrollable.container.classList.add('chatlist-container');
this.scrollable.append(list); this.scrollable.append(list);
const add = (peerId: number, append: boolean) => { const add = (peerId: PeerId, append: boolean) => {
const {dom} = appDialogsManager.addDialogNew({ const {dom} = appDialogsManager.addDialogNew({
dialog: peerId, dialog: peerId,
container: list, container: list,
@ -78,7 +78,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
let target: HTMLElement; let target: HTMLElement;
const onUnblock = () => { const onUnblock = () => {
const peerId = +target.dataset.peerId; const peerId = target.dataset.peerId.toPeerId();
appUsersManager.toggleBlock(peerId, false); appUsersManager.toggleBlock(peerId, false);
}; };

View File

@ -11,7 +11,6 @@ import { toast } from "../../toast";
import type { MyDialogFilter } from "../../../lib/storages/filters"; import type { MyDialogFilter } from "../../../lib/storages/filters";
import type { DialogFilterSuggested, DialogFilter } from "../../../layer"; import type { DialogFilterSuggested, DialogFilter } from "../../../layer";
import type _rootScope from "../../../lib/rootScope"; import type _rootScope from "../../../lib/rootScope";
import type { BroadcastEvents } from "../../../lib/rootScope";
import Button from "../../button"; import Button from "../../button";
import appMessagesManager from "../../../lib/appManagers/appMessagesManager"; import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
import appPeersManager from "../../../lib/appManagers/appPeersManager"; import appPeersManager from "../../../lib/appManagers/appPeersManager";
@ -65,7 +64,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
} }
if(!d.length) { if(!d.length) {
const folder = appMessagesManager.dialogsStorage.getFolder(filter.id); const folder = appMessagesManager.dialogsStorage.getFolderDialogs(filter.id);
let chats = 0, channels = 0, groups = 0; let chats = 0, channels = 0, groups = 0;
for(const dialog of folder) { for(const dialog of folder) {
if(appPeersManager.isAnyGroup(dialog.peerId)) groups++; if(appPeersManager.isAnyGroup(dialog.peerId)) groups++;
@ -170,8 +169,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
onFiltersContainerUpdate(); onFiltersContainerUpdate();
}); });
this.listenerSetter.add(rootScope)('filter_update', (e) => { this.listenerSetter.add(rootScope)('filter_update', (filter) => {
const filter = e;
if(this.filtersRendered.hasOwnProperty(filter.id)) { if(this.filtersRendered.hasOwnProperty(filter.id)) {
this.renderFolder(filter, null, this.filtersRendered[filter.id]); this.renderFolder(filter, null, this.filtersRendered[filter.id]);
} else { } else {
@ -183,8 +181,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.getSuggestedFilters(); this.getSuggestedFilters();
}); });
this.listenerSetter.add(rootScope)('filter_delete', (e) => { this.listenerSetter.add(rootScope)('filter_delete', (filter) => {
const filter = e;
if(this.filtersRendered.hasOwnProperty(filter.id)) { if(this.filtersRendered.hasOwnProperty(filter.id)) {
/* for(const suggested of this.suggestedFilters) { /* for(const suggested of this.suggestedFilters) {
if(deepEqual(suggested.filter, filter)) { if(deepEqual(suggested.filter, filter)) {
@ -200,8 +197,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
onFiltersContainerUpdate(); onFiltersContainerUpdate();
}); });
this.listenerSetter.add(rootScope)('filter_order', (e: BroadcastEvents['filter_order']) => { this.listenerSetter.add(rootScope)('filter_order', (order) => {
const order = e;
order.forEach((filterId, idx) => { order.forEach((filterId, idx) => {
const container = this.filtersRendered[filterId].container; const container = this.filtersRendered[filterId].container;
positionElementByIndex(container, container.parentElement, idx + 1); // ! + 1 due to header positionElementByIndex(container, container.parentElement, idx + 1); // ! + 1 due to header

View File

@ -44,8 +44,9 @@ export default class AppContactsTab extends SliderSuperTab {
this.listenerSetter.add(rootScope)('contacts_update', (userId) => { this.listenerSetter.add(rootScope)('contacts_update', (userId) => {
const isContact = appUsersManager.isContact(userId); const isContact = appUsersManager.isContact(userId);
if(isContact) this.sortedUserList.add(userId); const peerId = userId.toPeerId();
else this.sortedUserList.delete(userId); if(isContact) this.sortedUserList.add(peerId);
else this.sortedUserList.delete(peerId);
}); });
this.title.replaceWith(this.inputSearch.container); this.title.replaceWith(this.inputSearch.container);
@ -90,7 +91,7 @@ export default class AppContactsTab extends SliderSuperTab {
this.scrollable.onScrolledBottom = null; this.scrollable.onScrolledBottom = null;
this.scrollable.container.textContent = ''; this.scrollable.container.textContent = '';
appUsersManager.getContacts(query, undefined, 'online').then(contacts => { appUsersManager.getContactsPeerIds(query, undefined, 'online').then(contacts => {
if(!middleware()) { if(!middleware()) {
return; return;
} }

View File

@ -31,8 +31,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
private menuBtn: HTMLElement; private menuBtn: HTMLElement;
private nameInputField: InputField; private nameInputField: InputField;
private include_peers: SettingSection; private includePeerIds: SettingSection;
private exclude_peers: SettingSection; private excludePeerIds: SettingSection;
private flags: {[k in 'contacts' | 'non_contacts' | 'groups' | 'broadcasts' | 'bots' | 'exclude_muted' | 'exclude_archived' | 'exclude_read']: HTMLElement} = {} as any; private flags: {[k in 'contacts' | 'non_contacts' | 'groups' | 'broadcasts' | 'bots' | 'exclude_muted' | 'exclude_archived' | 'exclude_read']: HTMLElement} = {} as any;
private animation: RLottiePlayer; private animation: RLottiePlayer;
@ -108,7 +108,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
return section; return section;
}; };
this.include_peers = generateList('folder-list-included', 'FilterInclude', [{ this.includePeerIds = generateList('folder-list-included', 'FilterInclude', [{
icon: 'add primary', icon: 'add primary',
text: 'ChatList.Filter.Include.AddChat', text: 'ChatList.Filter.Include.AddChat',
withRipple: true withRipple: true
@ -134,7 +134,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
name: 'bots' name: 'bots'
}], this.flags); }], this.flags);
this.exclude_peers = generateList('folder-list-excluded', 'FilterExclude', [{ this.excludePeerIds = generateList('folder-list-excluded', 'FilterExclude', [{
icon: 'minus primary', icon: 'minus primary',
text: 'ChatList.Filter.Exclude.AddChat', text: 'ChatList.Filter.Exclude.AddChat',
withRipple: true withRipple: true
@ -152,10 +152,10 @@ export default class AppEditFolderTab extends SliderSuperTab {
name: 'exclude_read' name: 'exclude_read'
}], this.flags); }], this.flags);
this.scrollable.append(this.stickerContainer, this.caption, inputWrapper, this.include_peers.container, this.exclude_peers.container); this.scrollable.append(this.stickerContainer, this.caption, inputWrapper, this.includePeerIds.container, this.excludePeerIds.container);
const includedFlagsContainer = this.include_peers.container.querySelector('.folder-categories'); const includedFlagsContainer = this.includePeerIds.container.querySelector('.folder-categories');
const excludedFlagsContainer = this.exclude_peers.container.querySelector('.folder-categories'); const excludedFlagsContainer = this.excludePeerIds.container.querySelector('.folder-categories');
includedFlagsContainer.querySelector('.btn').addEventListener('click', () => { includedFlagsContainer.querySelector('.btn').addEventListener('click', () => {
new AppIncludedChatsTab(this.slider).open(this.filter, 'included', this); new AppIncludedChatsTab(this.slider).open(this.filter, 'included', this);
@ -261,7 +261,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
this.flags[flag as keyof AppEditFolderTab['flags']].style.display = !!filter.pFlags[flag as keyof AppEditFolderTab['flags']] ? '' : 'none'; this.flags[flag as keyof AppEditFolderTab['flags']].style.display = !!filter.pFlags[flag as keyof AppEditFolderTab['flags']] ? '' : 'none';
} }
(['include_peers', 'exclude_peers'] as ['include_peers', 'exclude_peers']).forEach(key => { (['includePeerIds' as const, 'excludePeerIds' as const]).forEach(key => {
const section = this[key]; const section = this[key];
const ul = appDialogsManager.createChatList(); const ul = appDialogsManager.createChatList();
@ -339,7 +339,10 @@ export default class AppEditFolderTab extends SliderSuperTab {
pFlags: {}, pFlags: {},
pinned_peers: [], pinned_peers: [],
include_peers: [], include_peers: [],
exclude_peers: [] exclude_peers: [],
pinnedPeerIds: [],
includePeerIds: [],
excludePeerIds: []
}, true); }, true);
this.type = 'create'; this.type = 'create';
this.onCreateOpen(); this.onCreateOpen();

View File

@ -12,6 +12,7 @@ import EditPeer from "../../editPeer";
import { UsernameInputField } from "../../usernameInputField"; import { UsernameInputField } from "../../usernameInputField";
import { i18n, i18n_ } from "../../../lib/langPack"; import { i18n, i18n_ } from "../../../lib/langPack";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import rootScope from "../../../lib/rootScope";
// TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист) // TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист)
@ -65,7 +66,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
this.scrollable.append(document.createElement('hr')); this.scrollable.append(document.createElement('hr'));
this.editPeer = new EditPeer({ this.editPeer = new EditPeer({
peerId: appUsersManager.getSelf().id, peerId: rootScope.myId,
inputFields, inputFields,
listenerSetter: this.listenerSetter listenerSetter: this.listenerSetter
}); });
@ -81,7 +82,6 @@ export default class AppEditProfileTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.usernameInputField = new UsernameInputField({ this.usernameInputField = new UsernameInputField({
peerId: 0,
label: 'EditProfile.Username.Label', label: 'EditProfile.Username.Label',
name: 'username', name: 'username',
plainText: true, plainText: true,

View File

@ -19,6 +19,8 @@ import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
import RichTextProcessor from "../../../lib/richtextprocessor"; import RichTextProcessor from "../../../lib/richtextprocessor";
import { SettingSection } from ".."; import { SettingSection } from "..";
import { toast } from "../../toast"; import { toast } from "../../toast";
import { forEachReverse } from "../../../helpers/array";
import appPeersManager from "../../../lib/appManagers/appPeersManager";
export default class AppIncludedChatsTab extends SliderSuperTab { export default class AppIncludedChatsTab extends SliderSuperTab {
private editFolderTab: AppEditFolderTab; private editFolderTab: AppEditFolderTab;
@ -29,7 +31,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
private filter: DialogFilter; private filter: DialogFilter;
private originalFilter: DialogFilter; private originalFilter: DialogFilter;
private dialogsByFilters: Map<DialogFilter, Set<number>>; private dialogsByFilters: Map<DialogFilter, Set<PeerId>>;
protected init() { protected init() {
this.content.remove(); this.content.remove();
@ -64,39 +66,41 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
} }
} }
const peers: number[] = []; const peerIds: PeerId[] = [];
for(const key of selected) { for(const key of selected) {
if(typeof(key) === 'number') { if(key.isPeerId()) {
peers.push(key); peerIds.push(key.toPeerId());
} else { } else {
// @ts-ignore // @ts-ignore
this.filter.pFlags[key] = true; this.filter.pFlags[key] = true;
} }
} }
let cmp: (peerId: PeerId) => boolean;
if(this.type === 'included') { if(this.type === 'included') {
this.filter.pinned_peers = this.filter.pinned_peers.filter(peerId => { cmp = (peerId) => peerIds.includes(peerId);
return peers.includes(peerId); // * because I have pinned peer in include_peers too
/* const index = peers.indexOf(peerId);
if(index !== -1) {
peers.splice(index, 1);
return true;
} else {
return false;
} */
});
} else { } else {
this.filter.pinned_peers = this.filter.pinned_peers.filter(peerId => { cmp = (peerId) => !peerIds.includes(peerId);
return !peers.includes(peerId);
});
} }
const other = this.type === 'included' ? 'exclude_peers' : 'include_peers'; forEachReverse(this.filter.pinnedPeerIds, (peerId, idx) => {
this.filter[other] = this.filter[other].filter(peerId => { if(!cmp(peerId)) {
return !peers.includes(peerId); this.filter.pinnedPeerIds.splice(idx, 1);
this.filter.pinned_peers.splice(idx, 1);
}
});
const other = this.type === 'included' ? 'excludePeerIds' : 'includePeerIds';
const otherLegacy = this.type === 'included' ? 'exclude_peers' : 'include_peers';
forEachReverse(this.filter[other], (peerId, idx) => {
if(peerIds.includes(peerId)) {
this.filter[other].splice(idx, 1);
this.filter[otherLegacy].splice(idx, 1);
}
}); });
this.filter[this.type === 'included' ? 'include_peers' : 'exclude_peers'] = peers; this.filter[this.type === 'included' ? 'includePeerIds' : 'excludePeerIds'] = peerIds;
this.filter[this.type === 'included' ? 'include_peers' : 'exclude_peers'] = peerIds.map(peerId => appPeersManager.getInputPeerById(peerId));
//this.filter.pinned_peers = this.filter.pinned_peers.filter(peerId => this.filter.include_peers.includes(peerId)); //this.filter.pinned_peers = this.filter.pinned_peers.filter(peerId => this.filter.include_peers.includes(peerId));
this.editFolderTab.setFilter(this.filter, false); this.editFolderTab.setFilter(this.filter, false);
@ -106,7 +110,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
this.dialogsByFilters = new Map(); this.dialogsByFilters = new Map();
return appMessagesManager.filtersStorage.getDialogFilters().then(filters => { return appMessagesManager.filtersStorage.getDialogFilters().then(filters => {
for(const filter of filters) { for(const filter of filters) {
this.dialogsByFilters.set(filter, new Set(appMessagesManager.dialogsStorage.getFolder(filter.id).map(d => d.peerId))); this.dialogsByFilters.set(filter, new Set(appMessagesManager.dialogsStorage.getFolderDialogs(filter.id).map(d => d.peerId)));
} }
}); });
} }
@ -122,7 +126,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
return checkboxField.label; return checkboxField.label;
} }
renderResults = async(peerIds: number[]) => { renderResults = async(peerIds: PeerId[]) => {
//const other = this.type === 'included' ? this.filter.exclude_peers : this.filter.include_peers; //const other = this.type === 'included' ? this.filter.exclude_peers : this.filter.include_peers;
await appUsersManager.getContacts(); await appUsersManager.getContacts();
@ -211,7 +215,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
///////////////// /////////////////
const selectedPeers = (this.type === 'included' ? filter.include_peers : filter.exclude_peers).slice(); const selectedPeers = (this.type === 'included' ? filter.includePeerIds : filter.excludePeerIds).slice();
this.selector = new AppSelectPeers({ this.selector = new AppSelectPeers({
appendTo: this.container, appendTo: this.container,

View File

@ -75,13 +75,12 @@ export default class AppNewChannelTab extends SliderSuperTab {
appSidebarLeft.removeTabFromHistory(this); appSidebarLeft.removeTabFromHistory(this);
new AppAddMembersTab(this.slider).open({ new AppAddMembersTab(this.slider).open({
peerId: channelId,
type: 'channel', type: 'channel',
skippable: true, skippable: true,
title: 'GroupAddMembers', title: 'GroupAddMembers',
placeholder: 'SendMessageTo', placeholder: 'SendMessageTo',
takeOut: (peerIds) => { takeOut: (peerIds) => {
return appChatsManager.inviteToChannel(Math.abs(channelId), peerIds); return appChatsManager.inviteToChannel(channelId, peerIds);
} }
}); });
}); });

View File

@ -20,7 +20,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
private searchGroup = new SearchGroup(true, 'contacts', true, 'new-group-members disable-hover', false); private searchGroup = new SearchGroup(true, 'contacts', true, 'new-group-members disable-hover', false);
private avatarEdit: AvatarEdit; private avatarEdit: AvatarEdit;
private uploadAvatar: () => Promise<InputFile> = null; private uploadAvatar: () => Promise<InputFile> = null;
private userIds: number[]; private peerIds: PeerId[];
private nextBtn: HTMLButtonElement; private nextBtn: HTMLButtonElement;
private groupNameInputField: InputField; private groupNameInputField: InputField;
@ -53,7 +53,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
const title = this.groupNameInputField.value; const title = this.groupNameInputField.value;
this.nextBtn.disabled = true; this.nextBtn.disabled = true;
appChatsManager.createChat(title, this.userIds).then((chatId) => { appChatsManager.createChat(title, this.peerIds).then((chatId) => {
if(this.uploadAvatar) { if(this.uploadAvatar) {
this.uploadAvatar().then((inputFile) => { this.uploadAvatar().then((inputFile) => {
appChatsManager.editPhoto(chatId, inputFile); appChatsManager.editPhoto(chatId, inputFile);
@ -81,12 +81,12 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.nextBtn.disabled = false; this.nextBtn.disabled = false;
} }
public open(userIds: number[]) { public open(userIds: PeerId[]) {
const result = super.open(); const result = super.open();
result.then(() => { result.then(() => {
this.userIds = userIds; this.peerIds = userIds;
this.userIds.forEach(userId => { this.peerIds.forEach(userId => {
let {dom} = appDialogsManager.addDialogNew({ let {dom} = appDialogsManager.addDialogNew({
dialog: userId, dialog: userId,
container: this.searchGroup.list, container: this.searchGroup.list,
@ -99,7 +99,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
}); });
this.searchGroup.nameEl.textContent = ''; this.searchGroup.nameEl.textContent = '';
this.searchGroup.nameEl.append(i18n('Members', [this.userIds.length])); this.searchGroup.nameEl.append(i18n('Members', [this.peerIds.length]));
this.searchGroup.setActive(); this.searchGroup.setActive();
}); });

View File

@ -46,7 +46,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
{ {
const section = new SettingSection({noDelimiter: true}); const section = new SettingSection({noDelimiter: true});
let blockedPeerIds: number[]; let blockedPeerIds: PeerId[];
const blockedUsersRow = new Row({ const blockedUsersRow = new Row({
icon: 'deleteuser', icon: 'deleteuser',
titleLangKey: 'BlockedUsers', titleLangKey: 'BlockedUsers',

View File

@ -18,6 +18,7 @@ import AppNotificationsTab from "./notifications";
import PeerTitle from "../../peerTitle"; import PeerTitle from "../../peerTitle";
import AppLanguageTab from "./language"; import AppLanguageTab from "./language";
import lottieLoader from "../../../lib/lottieLoader"; import lottieLoader from "../../../lib/lottieLoader";
import PopupPeer from "../../popups/peer";
//import AppMediaViewer from "../../appMediaViewerNew"; //import AppMediaViewer from "../../appMediaViewerNew";
export default class AppSettingsTab extends SliderSuperTab { export default class AppSettingsTab extends SliderSuperTab {
@ -42,7 +43,17 @@ export default class AppSettingsTab extends SliderSuperTab {
icon: 'logout', icon: 'logout',
text: 'EditAccount.Logout', text: 'EditAccount.Logout',
onClick: () => { onClick: () => {
apiManager.logOut(); new PopupPeer('logout', {
titleLangKey: 'LogOut',
descriptionLangKey: 'LogOut.Description',
buttons: [{
langKey: 'LogOut',
callback: () => {
apiManager.logOut();
},
isDanger: true
}]
}).show();
} }
}]); }]);
@ -149,10 +160,11 @@ export default class AppSettingsTab extends SliderSuperTab {
} }
public fillElements() { public fillElements() {
let user = appUsersManager.getSelf(); const user = appUsersManager.getSelf();
this.avatarElem.setAttribute('peer', '' + user.id); const peerId = user.id.toPeerId(false);
this.avatarElem.setAttribute('peer', '' + peerId);
this.nameDiv.append(new PeerTitle({peerId: user.id}).element); this.nameDiv.append(new PeerTitle({peerId: peerId}).element);
this.phoneDiv.innerHTML = user.phone ? appUsersManager.formatUserPhone(user.phone) : ''; this.phoneDiv.innerHTML = user.phone ? appUsersManager.formatUserPhone(user.phone) : '';
} }
} }

View File

@ -24,7 +24,7 @@ import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import toggleDisability from "../../../helpers/dom/toggleDisability"; import toggleDisability from "../../../helpers/dom/toggleDisability";
export default class AppChatTypeTab extends SliderSuperTabEventable { export default class AppChatTypeTab extends SliderSuperTabEventable {
public chatId: number; public chatId: ChatId;
public chatFull: ChatFull; public chatFull: ChatFull;
protected init() { protected init() {
@ -130,7 +130,7 @@ export default class AppChatTypeTab extends SliderSuperTabEventable {
invalidText: 'Link.Invalid', invalidText: 'Link.Invalid',
takenText: 'Link.Taken', takenText: 'Link.Taken',
onChange: onChange, onChange: onChange,
peerId: -this.chatId, peerId: this.chatId.toPeerId(true),
head: placeholder head: placeholder
}); });

View File

@ -18,7 +18,6 @@ import rootScope from "../../../lib/rootScope";
import AppGroupPermissionsTab from "./groupPermissions"; import AppGroupPermissionsTab from "./groupPermissions";
import { i18n, LangPackKey } from "../../../lib/langPack"; import { i18n, LangPackKey } from "../../../lib/langPack";
import PopupDeleteDialog from "../../popups/deleteDialog"; import PopupDeleteDialog from "../../popups/deleteDialog";
import PopupPeer from "../../popups/peer";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import toggleDisability from "../../../helpers/dom/toggleDisability"; import toggleDisability from "../../../helpers/dom/toggleDisability";
import CheckboxField from "../../checkboxField"; import CheckboxField from "../../checkboxField";
@ -27,7 +26,7 @@ export default class AppEditChatTab extends SliderSuperTab {
private chatNameInputField: InputField; private chatNameInputField: InputField;
private descriptionInputField: InputField; private descriptionInputField: InputField;
private editPeer: EditPeer; private editPeer: EditPeer;
public chatId: number; public chatId: ChatId;
protected async _init() { protected async _init() {
// * cleanup prev // * cleanup prev
@ -54,6 +53,8 @@ export default class AppEditChatTab extends SliderSuperTab {
} }
}); });
const peerId = this.chatId.toPeerId(true);
{ {
const section = new SettingSection({noDelimiter: true}); const section = new SettingSection({noDelimiter: true});
const inputFields: InputField[] = []; const inputFields: InputField[] = [];
@ -81,7 +82,7 @@ export default class AppEditChatTab extends SliderSuperTab {
inputFields.push(this.chatNameInputField, this.descriptionInputField); inputFields.push(this.chatNameInputField, this.descriptionInputField);
this.editPeer = new EditPeer({ this.editPeer = new EditPeer({
peerId: -this.chatId, peerId,
inputFields, inputFields,
listenerSetter: this.listenerSetter listenerSetter: this.listenerSetter
}); });
@ -283,7 +284,7 @@ export default class AppEditChatTab extends SliderSuperTab {
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteAndExitButton'}); const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteAndExitButton'});
attachClickEvent(btnDelete, () => { attachClickEvent(btnDelete, () => {
new PopupDeleteDialog(-this.chatId/* , 'delete' */, undefined, (promise) => { new PopupDeleteDialog(peerId/* , 'delete' */, undefined, (promise) => {
const toggle = toggleDisability([btnDelete], true); const toggle = toggleDisability([btnDelete], true);
promise.then(() => { promise.then(() => {
this.close(); this.close();
@ -301,8 +302,8 @@ export default class AppEditChatTab extends SliderSuperTab {
if(!isChannel) { if(!isChannel) {
// ! this one will fire earlier than tab's closeAfterTimeout (destroy) event and listeners will be erased, so destroy won't fire // ! this one will fire earlier than tab's closeAfterTimeout (destroy) event and listeners will be erased, so destroy won't fire
this.listenerSetter.add(rootScope)('dialog_migrate', ({migrateFrom, migrateTo}) => { this.listenerSetter.add(rootScope)('dialog_migrate', ({migrateFrom, migrateTo}) => {
if(-this.chatId === migrateFrom) { if(peerId === migrateFrom) {
this.chatId = -migrateTo; this.chatId = migrateTo.toChatId();
this._init(); this._init();
} }
}); });

View File

@ -27,7 +27,7 @@ export default class AppEditContactTab extends SliderSuperTab {
private nameInputField: InputField; private nameInputField: InputField;
private lastNameInputField: InputField; private lastNameInputField: InputField;
private editPeer: EditPeer; private editPeer: EditPeer;
public peerId: number; public peerId: PeerId;
protected init() { protected init() {
this.container.classList.add('edit-peer-container', 'edit-contact-container'); this.container.classList.add('edit-peer-container', 'edit-contact-container');

View File

@ -6,6 +6,7 @@
import appSidebarRight from ".."; import appSidebarRight from "..";
import appMessagesManager from "../../../lib/appManagers/appMessagesManager"; import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
import { NULL_PEER_ID } from "../../../lib/mtproto/mtproto_config";
import AppSelectPeers from "../../appSelectPeers"; import AppSelectPeers from "../../appSelectPeers";
import { putPreloader } from "../../misc"; import { putPreloader } from "../../misc";
import { SliderTab } from "../../slider"; import { SliderTab } from "../../slider";
@ -41,7 +42,7 @@ export default class AppForwardTab implements SliderTab {
this.sendBtn = this.container.querySelector('.btn-circle') as HTMLButtonElement; this.sendBtn = this.container.querySelector('.btn-circle') as HTMLButtonElement;
this.sendBtn.addEventListener('click', () => { this.sendBtn.addEventListener('click', () => {
let peerIds = this.selector.getSelected(); let peerIds = this.selector.getSelected().map(s => s.toPeerId());
if(this.mids.length && peerIds.length) { if(this.mids.length && peerIds.length) {
this.sendBtn.classList.remove('tgico-send'); this.sendBtn.classList.remove('tgico-send');
@ -51,7 +52,7 @@ export default class AppForwardTab implements SliderTab {
let s = () => { let s = () => {
let promises = peerIds.splice(0, 3).map(peerId => { let promises = peerIds.splice(0, 3).map(peerId => {
return appMessagesManager.forwardMessages(peerId, 0, this.mids); return appMessagesManager.forwardMessages(peerId, NULL_PEER_ID, this.mids);
}); });
Promise.all(promises).then(() => { Promise.all(promises).then(() => {

View File

@ -16,6 +16,7 @@ import type { MyDocument } from "../../../lib/appManagers/appDocsManager";
import mediaSizes from "../../../helpers/mediaSizes"; import mediaSizes from "../../../helpers/mediaSizes";
import findUpClassName from "../../../helpers/dom/findUpClassName"; import findUpClassName from "../../../helpers/dom/findUpClassName";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import { NULL_PEER_ID } from "../../../lib/mtproto/mtproto_config";
const ANIMATIONGROUP = 'GIFS-SEARCH'; const ANIMATIONGROUP = 'GIFS-SEARCH';
@ -26,7 +27,7 @@ export default class AppGifsTab extends SliderSuperTab {
private nextOffset = ''; private nextOffset = '';
private loadedAll = false; private loadedAll = false;
private gifBotPeerId: number; private gifBotPeerId: PeerId;
private masonry: GifsMasonry; private masonry: GifsMasonry;
private searchPromise: ReturnType<AppInlineBotsManager['getInlineResults']>; private searchPromise: ReturnType<AppInlineBotsManager['getInlineResults']>;
@ -100,11 +101,11 @@ export default class AppGifsTab extends SliderSuperTab {
if(this.searchPromise || this.loadedAll) return; if(this.searchPromise || this.loadedAll) return;
if(!this.gifBotPeerId) { if(!this.gifBotPeerId) {
this.gifBotPeerId = (await appUsersManager.resolveUsername('gif')).id; this.gifBotPeerId = (await appUsersManager.resolveUsername('gif')).id.toPeerId(false);
} }
try { try {
this.searchPromise = appInlineBotsManager.getInlineResults(0, this.gifBotPeerId, query, this.nextOffset); this.searchPromise = appInlineBotsManager.getInlineResults(NULL_PEER_ID, this.gifBotPeerId, query, this.nextOffset);
const { results, next_offset } = await this.searchPromise; const { results, next_offset } = await this.searchPromise;
if(this.inputSearch.value !== query) { if(this.inputSearch.value !== query) {

View File

@ -34,7 +34,7 @@ export class ChatPermissions {
private toggleWith: Partial<{[chatRight in ChatRights]: ChatRights[]}>; private toggleWith: Partial<{[chatRight in ChatRights]: ChatRights[]}>;
constructor(options: { constructor(options: {
chatId: number, chatId: ChatId,
listenerSetter: ListenerSetter, listenerSetter: ListenerSetter,
appendTo: HTMLElement, appendTo: HTMLElement,
participant?: ChannelParticipant.channelParticipantBanned participant?: ChannelParticipant.channelParticipantBanned
@ -123,7 +123,7 @@ export class ChatPermissions {
} }
export default class AppGroupPermissionsTab extends SliderSuperTabEventable { export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
public chatId: number; public chatId: ChatId;
protected async init() { protected async init() {
this.container.classList.add('edit-peer-container', 'group-permissions-container'); this.container.classList.add('edit-peer-container', 'group-permissions-container');
@ -171,7 +171,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
} }
}); });
const openPermissions = async(peerId: number) => { const openPermissions = async(peerId: PeerId) => {
let participant: AppUserPermissionsTab['participant']; let participant: AppUserPermissionsTab['participant'];
try { try {
participant = await appProfileManager.getChannelParticipant(this.chatId, peerId) as any; participant = await appProfileManager.getChannelParticipant(this.chatId, peerId) as any;
@ -208,7 +208,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
const target = findUpTag(e.target, 'LI'); const target = findUpTag(e.target, 'LI');
if(!target) return; if(!target) return;
const peerId = +target.dataset.peerId; const peerId = target.dataset.peerId.toPeerId();
openPermissions(peerId); openPermissions(peerId);
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});

View File

@ -77,7 +77,7 @@ export default class AppPollResultsTab extends SliderSuperTab {
appPollsManager.getVotes(message, answer.option, offset, limit).then(votesList => { appPollsManager.getVotes(message, answer.option, offset, limit).then(votesList => {
votesList.votes.forEach(vote => { votesList.votes.forEach(vote => {
const {dom} = appDialogsManager.addDialogNew({ const {dom} = appDialogsManager.addDialogNew({
dialog: vote.user_id, dialog: vote.user_id.toPeerId(false),
container: list, container: list,
drawStatus: false, drawStatus: false,
rippleEnabled: false, rippleEnabled: false,

View File

@ -17,7 +17,7 @@ export default class AppPrivateSearchTab extends SliderSuperTab {
private appSearch: AppSearch; private appSearch: AppSearch;
private btnPickDate: HTMLElement; private btnPickDate: HTMLElement;
private peerId = 0; private peerId: PeerId;
private threadId = 0; private threadId = 0;
private query = ''; private query = '';
private onDatePick: (timestamp: number) => void; private onDatePick: (timestamp: number) => void;
@ -43,7 +43,7 @@ export default class AppPrivateSearchTab extends SliderSuperTab {
}); });
} }
open(peerId: number, threadId?: number, onDatePick?: AppPrivateSearchTab['onDatePick'], query?: string) { open(peerId: PeerId, threadId?: number, onDatePick?: AppPrivateSearchTab['onDatePick'], query?: string) {
const ret = super.open(); const ret = super.open();
if(!this.peerId) { if(!this.peerId) {

View File

@ -29,12 +29,12 @@ import PeerProfile from "../../peerProfile";
export default class AppSharedMediaTab extends SliderSuperTab { export default class AppSharedMediaTab extends SliderSuperTab {
private editBtn: HTMLElement; private editBtn: HTMLElement;
private peerId = 0; private peerId: PeerId;
private threadId = 0; private threadId = 0;
private historiesStorage: { private historiesStorage: {
[peerId: number]: Partial<{ [peerId: PeerId]: Partial<{
[type in SearchSuperType]: {mid: number, peerId: number}[] [type in SearchSuperType]: {mid: number, peerId: PeerId}[]
}> }>
} = {}; } = {};
@ -124,7 +124,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
attachClickEvent(this.editBtn, (e) => { attachClickEvent(this.editBtn, (e) => {
let tab: AppEditChatTab | AppEditContactTab; let tab: AppEditChatTab | AppEditContactTab;
if(this.peerId < 0) { if(this.peerId.isAnyChat()) {
tab = new AppEditChatTab(this.slider); tab = new AppEditChatTab(this.slider);
} else { } else {
tab = new AppEditContactTab(this.slider); tab = new AppEditContactTab(this.slider);
@ -132,7 +132,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
if(tab) { if(tab) {
if(tab instanceof AppEditChatTab) { if(tab instanceof AppEditChatTab) {
tab.chatId = -this.peerId; tab.chatId = this.peerId.toChatId();
} else { } else {
tab.peerId = this.peerId; tab.peerId = this.peerId;
} }
@ -148,14 +148,14 @@ export default class AppSharedMediaTab extends SliderSuperTab {
}); });
rootScope.addEventListener('chat_update', (chatId) => { rootScope.addEventListener('chat_update', (chatId) => {
if(this.peerId === -chatId) { if(this.peerId === chatId.toPeerId(true)) {
this.toggleEditBtn(); this.toggleEditBtn();
} }
}); });
rootScope.addEventListener('history_multiappend', (msgIdsByPeer) => { rootScope.addEventListener('history_multiappend', (msgIdsByPeer) => {
for(const peerId in msgIdsByPeer) { for(const peerId in msgIdsByPeer) {
this.renderNewMessages(+peerId, Array.from(msgIdsByPeer[peerId])); this.renderNewMessages(peerId.toPeerId(), Array.from(msgIdsByPeer[peerId]));
} }
}); });
@ -211,10 +211,11 @@ export default class AppSharedMediaTab extends SliderSuperTab {
this.content.append(btnAddMembers); this.content.append(btnAddMembers);
btnAddMembers.addEventListener('click', () => { btnAddMembers.addEventListener('click', () => {
const id = -this.peerId; const peerId = this.peerId;
const id = this.peerId.toChatId();
const isChannel = appChatsManager.isChannel(id); const isChannel = appChatsManager.isChannel(id);
const showConfirmation = (peerIds: number[], callback: (checked: PopupPeerButtonCallbackCheckboxes) => void) => { const showConfirmation = (peerIds: PeerId[], callback: (checked: PopupPeerButtonCallbackCheckboxes) => void) => {
let titleLangKey: LangPackKey, titleLangArgs: any[], let titleLangKey: LangPackKey, titleLangArgs: any[],
descriptionLangKey: LangPackKey, descriptionLangArgs: any[], descriptionLangKey: LangPackKey, descriptionLangArgs: any[],
checkboxes: PopupPeerCheckboxOptions[]; checkboxes: PopupPeerCheckboxOptions[];
@ -254,11 +255,11 @@ export default class AppSharedMediaTab extends SliderSuperTab {
} }
descriptionLangArgs.push(new PeerTitle({ descriptionLangArgs.push(new PeerTitle({
peerId: -id peerId
}).element); }).element);
new PopupPeer('popup-add-members', { new PopupPeer('popup-add-members', {
peerId: -id, peerId,
titleLangKey, titleLangKey,
descriptionLangKey, descriptionLangKey,
descriptionLangArgs, descriptionLangArgs,
@ -279,7 +280,6 @@ export default class AppSharedMediaTab extends SliderSuperTab {
if(isChannel) { if(isChannel) {
const tab = new AppAddMembersTab(this.slider); const tab = new AppAddMembersTab(this.slider);
tab.open({ tab.open({
peerId: this.peerId,
type: 'channel', type: 'channel',
skippable: false, skippable: false,
takeOut: (peerIds) => { takeOut: (peerIds) => {
@ -313,7 +313,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
//console.log('construct shared media time:', performance.now() - perf); //console.log('construct shared media time:', performance.now() - perf);
} }
public renderNewMessages(peerId: number, mids: number[]) { public renderNewMessages(peerId: PeerId, mids: number[]) {
if(this.init) return; // * not inited yet if(this.init) return; // * not inited yet
if(!this.historiesStorage[peerId]) return; if(!this.historiesStorage[peerId]) return;
@ -336,7 +336,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
} }
} }
public deleteDeletedMessages(peerId: number, mids: number[]) { public deleteDeletedMessages(peerId: PeerId, mids: number[]) {
if(this.init) return; // * not inited yet if(this.init) return; // * not inited yet
if(!this.historiesStorage[peerId]) return; if(!this.historiesStorage[peerId]) return;
@ -384,7 +384,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
this.searchSuper.cleanupHTML(true); this.searchSuper.cleanupHTML(true);
this.container.classList.toggle('can-add-members', this.searchSuper.canViewMembers() && appChatsManager.hasRights(-this.peerId, 'invite_users')); this.container.classList.toggle('can-add-members', this.searchSuper.canViewMembers() && appChatsManager.hasRights(this.peerId.toChatId(), 'invite_users'));
// console.log('cleanupHTML shared media time:', performance.now() - perf); // console.log('cleanupHTML shared media time:', performance.now() - perf);
} }
@ -393,7 +393,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
this.searchSuper.loadMutex = promise; this.searchSuper.loadMutex = promise;
} }
public setPeer(peerId: number, threadId = 0) { public setPeer(peerId: PeerId, threadId = 0) {
if(this.peerId === peerId && this.threadId === threadId) return false; if(this.peerId === peerId && this.threadId === threadId) return false;
this.peerId = peerId; this.peerId = peerId;
@ -432,10 +432,10 @@ export default class AppSharedMediaTab extends SliderSuperTab {
private toggleEditBtn() { private toggleEditBtn() {
let show: boolean; let show: boolean;
if(this.peerId > 0) { if(this.peerId.isUser()) {
show = this.peerId !== rootScope.myId && appUsersManager.isContact(this.peerId); show = this.peerId !== rootScope.myId && appUsersManager.isContact(this.peerId);
} else { } else {
show = appChatsManager.hasRights(-this.peerId, 'change_info'); show = appChatsManager.hasRights(this.peerId.toChatId(), 'change_info');
} }
this.editBtn.classList.toggle('hide', !show); this.editBtn.classList.toggle('hide', !show);

View File

@ -181,8 +181,8 @@ export default class AppStickersTab extends SliderSuperTab {
div.addEventListener('mouseover', onMouseOver, {once: true}); */ div.addEventListener('mouseover', onMouseOver, {once: true}); */
div.dataset.stickerSet = set.id; div.dataset.stickerSet = '' + set.id;
div.dataset.access_hash = set.access_hash; div.dataset.access_hash = '' + set.access_hash;
div.dataset.title = set.title; div.dataset.title = set.title;
div.append(header, stickersDiv); div.append(header, stickersDiv);

View File

@ -18,8 +18,8 @@ import { ChatPermissions } from "./groupPermissions";
export default class AppUserPermissionsTab extends SliderSuperTabEventable { export default class AppUserPermissionsTab extends SliderSuperTabEventable {
public participant: ChannelParticipant; public participant: ChannelParticipant;
public chatId: number; public chatId: ChatId;
public userId: number; public userId: UserId;
protected init() { protected init() {
this.container.classList.add('edit-peer-container', 'user-permissions-container'); this.container.classList.add('edit-peer-container', 'user-permissions-container');
@ -40,7 +40,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
div.append(list); div.append(list);
const {dom} = appDialogsManager.addDialogNew({ const {dom} = appDialogsManager.addDialogNew({
dialog: this.userId, dialog: this.userId.toPeerId(false),
container: list, container: list,
drawStatus: false, drawStatus: false,
rippleEnabled: true, rippleEnabled: true,

View File

@ -10,6 +10,7 @@ import appNavigationController, { NavigationItem } from "./appNavigationControll
import SliderSuperTab, { SliderSuperTabConstructable, SliderTab } from "./sliderTab"; import SliderSuperTab, { SliderSuperTabConstructable, SliderTab } from "./sliderTab";
import { safeAssign } from "../helpers/object"; import { safeAssign } from "../helpers/object";
import { attachClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent } from "../helpers/dom/clickEvent";
import { indexOfAndSplice } from "../helpers/array";
const TRANSITION_TIME = 250; const TRANSITION_TIME = 250;
@ -110,7 +111,7 @@ export default class SidebarSlider {
} }
public removeTabFromHistory(id: number | SliderSuperTab) { public removeTabFromHistory(id: number | SliderSuperTab) {
this.historyTabIds.findAndSplice(i => i === id); indexOfAndSplice(this.historyTabIds, id);
this.onCloseTab(id, undefined); this.onCloseTab(id, undefined);
} }

View File

@ -20,6 +20,17 @@ rootScope.addEventListener('context_menu_toggle', (visible) => {
RESET_GLOBAL = visible; RESET_GLOBAL = visible;
}); });
export type SwipeHandlerOptions = {
element: SwipeHandler['element'],
onSwipe: SwipeHandler['onSwipe'],
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
onReset?: SwipeHandler['onReset'],
cursor?: SwipeHandler['cursor'],
cancelEvent?: SwipeHandler['cancelEvent'],
listenerOptions?: SwipeHandler['listenerOptions']
};
export default class SwipeHandler { export default class SwipeHandler {
private element: HTMLElement; private element: HTMLElement;
private onSwipe: (xDiff: number, yDiff: number, e: TouchEvent | MouseEvent) => boolean | void; private onSwipe: (xDiff: number, yDiff: number, e: TouchEvent | MouseEvent) => boolean | void;
@ -28,20 +39,13 @@ export default class SwipeHandler {
private onReset: () => void; private onReset: () => void;
private cursor: 'grabbing' | 'move' = 'grabbing'; private cursor: 'grabbing' | 'move' = 'grabbing';
private cancelEvent = true; private cancelEvent = true;
private listenerOptions: boolean | AddEventListenerOptions = false;
private hadMove = false; private hadMove = false;
private xDown: number = null; private xDown: number = null;
private yDown: number = null; private yDown: number = null;
constructor(options: { constructor(options: SwipeHandlerOptions) {
element: SwipeHandler['element'],
onSwipe: SwipeHandler['onSwipe'],
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
onReset?: SwipeHandler['onReset'],
cursor?: SwipeHandler['cursor'],
cancelEvent?: SwipeHandler['cancelEvent']
}) {
safeAssign(this, options); safeAssign(this, options);
this.setListeners(); this.setListeners();
@ -49,20 +53,20 @@ export default class SwipeHandler {
public setListeners() { public setListeners() {
if(!IS_TOUCH_SUPPORTED) { if(!IS_TOUCH_SUPPORTED) {
this.element.addEventListener('mousedown', this.handleStart, false); this.element.addEventListener('mousedown', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.addEventListener('mouseup', this.reset); attachGlobalListenerTo.addEventListener('mouseup', this.reset);
} else { } else {
this.element.addEventListener('touchstart', this.handleStart, false); this.element.addEventListener('touchstart', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.addEventListener('touchend', this.reset); attachGlobalListenerTo.addEventListener('touchend', this.reset);
} }
} }
public removeListeners() { public removeListeners() {
if(!IS_TOUCH_SUPPORTED) { if(!IS_TOUCH_SUPPORTED) {
this.element.removeEventListener('mousedown', this.handleStart, false); this.element.removeEventListener('mousedown', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.removeEventListener('mouseup', this.reset); attachGlobalListenerTo.removeEventListener('mouseup', this.reset);
} else { } else {
this.element.removeEventListener('touchstart', this.handleStart, false); this.element.removeEventListener('touchstart', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.removeEventListener('touchend', this.reset); attachGlobalListenerTo.removeEventListener('touchend', this.reset);
} }
} }

View File

@ -16,7 +16,7 @@ export class UsernameInputField extends InputField {
private checkUsernamePromise: Promise<any>; private checkUsernamePromise: Promise<any>;
private checkUsernameDebounced: (username: string) => void; private checkUsernameDebounced: (username: string) => void;
public options: InputFieldOptions & { public options: InputFieldOptions & {
peerId: number, peerId?: PeerId,
listenerSetter: ListenerSetter, listenerSetter: ListenerSetter,
onChange?: () => void, onChange?: () => void,
invalidText: LangPackKey, invalidText: LangPackKey,
@ -68,7 +68,7 @@ export class UsernameInputField extends InputField {
if(this.options.peerId) { if(this.options.peerId) {
this.checkUsernamePromise = apiManager.invokeApi('channels.checkUsername', { this.checkUsernamePromise = apiManager.invokeApi('channels.checkUsername', {
channel: appChatsManager.getChannelInput(-this.options.peerId), channel: appChatsManager.getChannelInput(this.options.peerId.toChatId()),
username username
}); });
} else { } else {

View File

@ -570,7 +570,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
let docDiv = document.createElement('div'); let docDiv = document.createElement('div');
docDiv.classList.add('document', `ext-${ext}`); docDiv.classList.add('document', `ext-${ext}`);
docDiv.dataset.docId = doc.id; docDiv.dataset.docId = '' + doc.id;
const icoDiv = document.createElement('div'); const icoDiv = document.createElement('div');
icoDiv.classList.add('document-ico'); icoDiv.classList.add('document-ico');
@ -580,7 +580,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
docDiv.classList.add('document-with-thumb'); docDiv.classList.add('document-with-thumb');
let imgs: HTMLImageElement[] = []; let imgs: HTMLImageElement[] = [];
if(message.pFlags.is_outgoing) { if(uploading) {
icoDiv.innerHTML = `<img src="${cacheContext.url}">`; icoDiv.innerHTML = `<img src="${cacheContext.url}">`;
imgs.push(icoDiv.firstElementChild as HTMLImageElement); imgs.push(icoDiv.firstElementChild as HTMLImageElement);
} else { } else {
@ -640,9 +640,9 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
docDiv.prepend(icoDiv); docDiv.prepend(icoDiv);
if(!uploading && message.pFlags.is_outgoing) { /* if(!uploading && message.pFlags.is_outgoing) {
return docDiv; return docDiv;
} } */
let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null; let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null;
const onLoad = () => { const onLoad = () => {
@ -1133,7 +1133,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
throw new Error('wrong doc for wrapSticker!'); throw new Error('wrong doc for wrapSticker!');
} }
div.dataset.docId = doc.id; div.dataset.docId = '' + doc.id;
div.classList.add('media-sticker-wrapper'); div.classList.add('media-sticker-wrapper');
//console.log('wrap sticker', doc, div, onlyThumb); //console.log('wrap sticker', doc, div, onlyThumb);
@ -1185,7 +1185,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(doc, thumb as PhotoSize.photoStrippedSize, true), afterRender); renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(doc, thumb as PhotoSize.photoStrippedSize, true), afterRender);
haveThumbCached = true; haveThumbCached = true;
} else { } else {
webpWorkerController.convert(doc.id, (thumb as PhotoSize.photoStrippedSize).bytes as Uint8Array).then(bytes => { webpWorkerController.convert('' + doc.id, (thumb as PhotoSize.photoStrippedSize).bytes as Uint8Array).then(bytes => {
(thumb as PhotoSize.photoStrippedSize).bytes = bytes; (thumb as PhotoSize.photoStrippedSize).bytes = bytes;
doc.pFlags.stickerThumbConverted = true; doc.pFlags.stickerThumbConverted = true;
@ -1594,6 +1594,7 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
const div = attachmentDiv.children[idx] as HTMLElement; const div = attachmentDiv.children[idx] as HTMLElement;
div.dataset.mid = '' + message.mid; div.dataset.mid = '' + message.mid;
div.dataset.peerId = '' + message.peerId;
const mediaDiv = div.firstElementChild as HTMLElement; const mediaDiv = div.firstElementChild as HTMLElement;
if(media._ === 'photo') { if(media._ === 'photo') {
wrapPhoto({ wrapPhoto({

View File

@ -18,7 +18,8 @@ const App = {
hash: process.env.API_HASH, hash: process.env.API_HASH,
version: process.env.VERSION, version: process.env.VERSION,
versionFull: process.env.VERSION_FULL, versionFull: process.env.VERSION_FULL,
langPackVersion: '0.3.3', build: +process.env.BUILD,
langPackVersion: '0.3.5',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',
domains: [MAIN_DOMAIN] as string[], domains: [MAIN_DOMAIN] as string[],

19
src/global.d.ts vendored
View File

@ -1,4 +1,5 @@
import type ListenerSetter from "./helpers/listenerSetter"; import type ListenerSetter from "./helpers/listenerSetter";
import type { Chat, Document, User } from "./layer";
declare global { declare global {
interface AddEventListenerOptions extends EventListenerOptions { interface AddEventListenerOptions extends EventListenerOptions {
@ -7,6 +8,24 @@ declare global {
// ls?: ListenerSetter; // ls?: ListenerSetter;
} }
type UserId = User.user['id'];
type ChatId = Chat.chat['id'];
// type PeerId = `u${UserId}` | `c${ChatId}`;
// type PeerId = `${UserId}` | `-${ChatId}`;
type PeerId = number;
// type PeerId = number;
type BotId = UserId;
type DocId = Document.document['id'];
type Long = string | number;
type MTLong = string;
type LocalErrorType = 'DOWNLOAD_CANCELED';
type ServerErrorType = 'FILE_REFERENCE_EXPIRED';
interface Error {
type?: LocalErrorType | ServerErrorType;
}
declare module 'worker-loader!*' { declare module 'worker-loader!*' {
class WebpackWorker extends Worker { class WebpackWorker extends Worker {
constructor(); constructor();

View File

@ -21,6 +21,12 @@ export function listMergeSorted(list1: any[] = [], list2: any[] = []) {
export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue); export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue);
export function indexOfAndSplice<T>(array: Array<T>, item: T) {
const idx = array.indexOf(item);
const spliced = idx !== -1 && array.splice(idx, 1);
return spliced && spliced[0];
}
export function findAndSpliceAll<T>(array: Array<T>, verify: (value: T, index: number, arr: typeof array) => boolean) { export function findAndSpliceAll<T>(array: Array<T>, verify: (value: T, index: number, arr: typeof array) => boolean) {
const out: typeof array = []; const out: typeof array = [];
let idx = -1; let idx = -1;

View File

@ -4,17 +4,18 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import { Photo } from "../layer";
import appPhotosManager from "../lib/appManagers/appPhotosManager"; import appPhotosManager from "../lib/appManagers/appPhotosManager";
import ListLoader, { ListLoaderOptions } from "./listLoader"; import ListLoader, { ListLoaderOptions } from "./listLoader";
export default class AvatarListLoader<Item extends {photoId: string}> extends ListLoader<Item, any> { export default class AvatarListLoader<Item extends {photoId: Photo.photo['id']}> extends ListLoader<Item, any> {
private peerId: number; private peerId: PeerId;
constructor(options: Omit<ListLoaderOptions<Item, any>, 'loadMore'> & {peerId: number}) { constructor(options: Omit<ListLoaderOptions<Item, any>, 'loadMore'> & {peerId: PeerId}) {
super({ super({
...options, ...options,
loadMore: (anchor, older, loadCount) => { loadMore: (anchor, older, loadCount) => {
if(this.peerId < 0 || !older) return Promise.resolve({count: 0, items: []}); // ! это значит, что открыло аватар чата, но следующих фотографий нет. if(this.peerId.isAnyChat() || !older) return Promise.resolve({count: 0, items: []}); // ! это значит, что открыло аватар чата, но следующих фотографий нет.
const maxId = anchor?.photoId; const maxId = anchor?.photoId;
return appPhotosManager.getUserPhotos(this.peerId, maxId, loadCount).then(value => { return appPhotosManager.getUserPhotos(this.peerId, maxId, loadCount).then(value => {

View File

@ -0,0 +1,15 @@
export default function compareVersion(v1: string, v2: string): number {
v1 = v1.split(' ', 1)[0];
v2 = v2.split(' ', 1)[0];
const s1 = v1.split('.');
const s2 = v2.split('.');
for(let i = 0; i < s1.length; ++i) {
const v1 = +s1[i];
const v2 = +s2[i];
if(v1 > v2) return 1;
else if(v1 < v2) return -1;
}
return 0;
}

View File

@ -77,7 +77,7 @@ export default function getRichElementValue(node: HTMLElement, lines: string[],
_: tag.entityName, _: tag.entityName,
offset: offset.offset, offset: offset.offset,
length: nodeValue.length, length: nodeValue.length,
user_id: +parentElement.dataset.follow user_id: parentElement.dataset.follow
}); });
} else { } else {
entities.push({ entities.push({

View File

@ -0,0 +1,50 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import SwipeHandler, { SwipeHandlerOptions } from "../../components/swipeHandler";
import { IS_APPLE_MOBILE, IS_SAFARI } from "../../environment/userAgent";
import { cancelEvent } from "./cancelEvent";
import findUpClassName from "./findUpClassName";
import isSwipingBackSafari from "./isSwipingBackSafari";
export type SwipeHandlerHorizontalOptions = SwipeHandlerOptions & {
// xThreshold?: number
};
export default function handleHorizontalSwipe(options: SwipeHandlerHorizontalOptions) {
let cancelY = false;
return new SwipeHandler({
...options,
verifyTouchTarget: (e) => {
return !findUpClassName(e.target, 'progress-line') &&
!isSwipingBackSafari(e) &&
(options.verifyTouchTarget ? options.verifyTouchTarget(e) : true);
},
onSwipe: (xDiff, yDiff, e) => {
if(!cancelY && Math.abs(yDiff) > 20) {
return true;
}
if(Math.abs(xDiff) > Math.abs(yDiff)) {
cancelEvent(e);
cancelY = true;
} else if(!cancelY && Math.abs(yDiff) > Math.abs(xDiff)/* || Math.abs(yDiff) > 20 */) {
return true;
}
/* if(!cancelY && options.xThreshold !== undefined && xDiff >= options.xThreshold) {
cancelY = true;
} */
return options.onSwipe(xDiff, yDiff, e);
},
onReset: () => {
cancelY = false;
options.onReset && options.onReset();
},
cancelEvent: true
});
}

View File

@ -5,52 +5,18 @@
*/ */
import { cancelContextMenuOpening } from "../../components/misc"; import { cancelContextMenuOpening } from "../../components/misc";
import SwipeHandler from "../../components/swipeHandler"; import handleHorizontalSwipe, { SwipeHandlerHorizontalOptions } from "./handleHorizontalSwipe";
import { cancelEvent } from "./cancelEvent";
export default function handleTabSwipe(container: HTMLElement, onSwipe: (next: boolean) => void) { export default function handleTabSwipe(options: SwipeHandlerHorizontalOptions) {
/* let hadScroll = false; return handleHorizontalSwipe({
const onScroll = () => { ...options,
swipeHandler.reset();
};
let firstSwipeChecked = false; */
return new SwipeHandler({
element: container,
/* onFirstSwipe: () => {
this.scroll.container.addEventListener('scroll', onScroll, {passive: true});
}, */
onSwipe: (xDiff, yDiff, e) => { onSwipe: (xDiff, yDiff, e) => {
/* if(!firstSwipeChecked) {
firstSwipeChecked = true;
if(yDiff !== 0) {
return true;
}
}
cancelEvent(e); */
if(Math.abs(yDiff) > 20) {
return true;
}
if(Math.abs(xDiff) > Math.abs(yDiff)) {
cancelEvent(e);
} else if(Math.abs(yDiff) > Math.abs(xDiff)/* || Math.abs(yDiff) > 20 */) {
return true;
}
if(Math.abs(xDiff) > 50) { if(Math.abs(xDiff) > 50) {
onSwipe(xDiff > 0); options.onSwipe(xDiff, yDiff, e);
cancelContextMenuOpening(); cancelContextMenuOpening();
return true; return true;
} }
}, }
/* onReset: () => {
hadScroll = false;
firstSwipeChecked = false;
this.scroll.container.removeEventListener('scroll', onScroll);
}, */
cancelEvent: false
}); });
} }

View File

@ -0,0 +1,11 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { IS_MOBILE_SAFARI } from "../../environment/userAgent";
export default function isSwipingBackSafari(e: TouchEvent | MouseEvent) {
return IS_MOBILE_SAFARI && e instanceof TouchEvent && e.touches[0].clientX < 30;
}

View File

@ -0,0 +1,25 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { cancelEvent } from "./cancelEvent";
export default function lockTouchScroll(container: HTMLElement) {
const onTouchMove = (e: TouchEvent) => {
cancelEvent(e);
};
let lockers = 2;
const cb = () => {
if(!--lockers) {
container.removeEventListener('touchmove', onTouchMove, {capture: true});
}
};
container.addEventListener('touchmove', onTouchMove, {capture: true, passive: false});
container.addEventListener('touchend', cb, {once: true});
return cb;
}

View File

@ -46,10 +46,10 @@ export function deepEqual(x: any, y: any): boolean {
) : (x === y); ) : (x === y);
} }
export function defineNotNumerableProperties(obj: {[key: string]: any}, names: string[]) { export function defineNotNumerableProperties<T extends any>(obj: T, names: (keyof T)[]) {
//const perf = performance.now(); //const perf = performance.now();
const props = {writable: true, configurable: true}; const props = {writable: true, configurable: true};
const out: {[name: string]: typeof props} = {}; const out: {[name in keyof T]?: typeof props} = {};
names.forEach(name => { names.forEach(name => {
if(obj[name] === undefined) { if(obj[name] === undefined) {
out[name] = props; out[name] = props;
@ -108,7 +108,7 @@ export function safeReplaceArrayInObject<K>(key: K, wasObject: any, newObject: a
} }
} }
export function isObject(object: any) { export function isObject<T extends Record<any, any>>(object: any): object is T {
return typeof(object) === 'object' && object !== null; return typeof(object) === 'object' && object !== null;
} }

Some files were not shown because too many files have changed in this diff Show More