commit
ef77df84ca
@ -15,7 +15,7 @@ import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
|
|||||||
import type sessionStorage from '../../lib/sessionStorage';
|
import type sessionStorage from '../../lib/sessionStorage';
|
||||||
import type Chat from "./chat";
|
import type Chat from "./chat";
|
||||||
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
||||||
import { cancelEvent, whichChild, attachClickEvent, positionElementByIndex, reflowScrollableElement, replaceContent, htmlToDocumentFragment } from "../../helpers/dom";
|
import { cancelEvent, whichChild, attachClickEvent, positionElementByIndex, reflowScrollableElement, replaceContent, htmlToDocumentFragment, setInnerHTML } from "../../helpers/dom";
|
||||||
import { getObjectKeysAndSort } from "../../helpers/object";
|
import { getObjectKeysAndSort } from "../../helpers/object";
|
||||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||||
import { logger } from "../../lib/logger";
|
import { logger } from "../../lib/logger";
|
||||||
@ -130,6 +130,8 @@ export default class ChatBubbles {
|
|||||||
public isFirstLoad = true;
|
public isFirstLoad = true;
|
||||||
private needReflowScroll: boolean;
|
private needReflowScroll: boolean;
|
||||||
|
|
||||||
|
private fetchNewPromise: Promise<void>;
|
||||||
|
|
||||||
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) {
|
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) {
|
||||||
//this.chat.log.error('Bubbles construction');
|
//this.chat.log.error('Bubbles construction');
|
||||||
|
|
||||||
@ -1399,6 +1401,7 @@ export default class ChatBubbles {
|
|||||||
this.messagesQueuePromise = null;
|
this.messagesQueuePromise = null;
|
||||||
|
|
||||||
this.getHistoryTopPromise = this.getHistoryBottomPromise = undefined;
|
this.getHistoryTopPromise = this.getHistoryBottomPromise = undefined;
|
||||||
|
this.fetchNewPromise = undefined;
|
||||||
|
|
||||||
if(this.stickyIntersector) {
|
if(this.stickyIntersector) {
|
||||||
this.stickyIntersector.disconnect();
|
this.stickyIntersector.disconnect();
|
||||||
@ -1618,8 +1621,7 @@ export default class ChatBubbles {
|
|||||||
|
|
||||||
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
|
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
|
||||||
|
|
||||||
const isFetchIntervalNeeded = () => peerId < 0 && !this.appChatsManager.isInChat(peerId);
|
const needFetchInterval = this.appMessagesManager.isFetchIntervalNeeded(peerId);
|
||||||
const needFetchInterval = isFetchIntervalNeeded();
|
|
||||||
const needFetchNew = savedPosition || needFetchInterval;
|
const needFetchNew = savedPosition || needFetchInterval;
|
||||||
if(!needFetchNew) {
|
if(!needFetchNew) {
|
||||||
// warning
|
// warning
|
||||||
@ -1627,20 +1629,44 @@ export default class ChatBubbles {
|
|||||||
this.scrollable.loadedAll.bottom = true;
|
this.scrollable.loadedAll.bottom = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const middleware = this.getMiddleware();
|
||||||
Promise.all([setPeerPromise, getHeavyAnimationPromise()]).then(() => {
|
Promise.all([setPeerPromise, getHeavyAnimationPromise()]).then(() => {
|
||||||
|
if(!middleware()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.scrollable.checkForTriggers();
|
this.scrollable.checkForTriggers();
|
||||||
|
|
||||||
if(needFetchInterval) {
|
if(needFetchInterval) {
|
||||||
const middleware = this.getMiddleware();
|
const f = () => {
|
||||||
const interval = window.setInterval(() => {
|
this.fetchNewPromise = new Promise<void>((resolve) => {
|
||||||
if(!middleware() || !isFetchIntervalNeeded()) {
|
if(!middleware() || !this.appMessagesManager.isFetchIntervalNeeded(peerId)) {
|
||||||
clearInterval(interval);
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.appMessagesManager.getNewHistory(peerId, this.chat.threadId).then((historyStorage) => {
|
||||||
|
if(!middleware()) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slice = historyStorage.history.slice;
|
||||||
|
const isBottomEnd = slice.isEnd(SliceEnd.Bottom);
|
||||||
|
if(this.scrollable.loadedAll.bottom !== isBottomEnd) {
|
||||||
|
this.scrollable.loadedAll.bottom = isBottomEnd;
|
||||||
|
this.onScroll();
|
||||||
|
}
|
||||||
|
|
||||||
this.scrollable.loadedAll.bottom = false;
|
setTimeout(f, 30e3);
|
||||||
this.loadMoreHistory(false);
|
resolve();
|
||||||
}, 30e3);
|
});
|
||||||
|
}).finally(() => {
|
||||||
|
this.fetchNewPromise = undefined;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
f();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1975,7 +2001,7 @@ export default class ChatBubbles {
|
|||||||
bubble.classList.add('is-message-empty', 'emoji-big');
|
bubble.classList.add('is-message-empty', 'emoji-big');
|
||||||
canHaveTail = false;
|
canHaveTail = false;
|
||||||
} else {
|
} else {
|
||||||
messageDiv.innerHTML = richText;
|
setInnerHTML(messageDiv, richText);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if(strLength === emojiStrLength) {
|
/* if(strLength === emojiStrLength) {
|
||||||
@ -1983,7 +2009,7 @@ export default class ChatBubbles {
|
|||||||
messageDiv.classList.add('message-empty');
|
messageDiv.classList.add('message-empty');
|
||||||
} */
|
} */
|
||||||
} else {
|
} else {
|
||||||
messageDiv.innerHTML = richText;
|
setInnerHTML(messageDiv, richText);
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeSpan = MessageRender.setTime(this.chat, message, bubble, bubbleContainer, messageDiv);
|
const timeSpan = MessageRender.setTime(this.chat, message, bubble, bubbleContainer, messageDiv);
|
||||||
@ -2219,21 +2245,21 @@ export default class ChatBubbles {
|
|||||||
nameEl.classList.add('name');
|
nameEl.classList.add('name');
|
||||||
nameEl.setAttribute('target', '_blank');
|
nameEl.setAttribute('target', '_blank');
|
||||||
nameEl.href = webpage.url || '#';
|
nameEl.href = webpage.url || '#';
|
||||||
nameEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.site_name);
|
setInnerHTML(nameEl, RichTextProcessor.wrapEmojiText(webpage.site_name));
|
||||||
quoteTextDiv.append(nameEl);
|
quoteTextDiv.append(nameEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(webpage.rTitle) {
|
if(webpage.rTitle) {
|
||||||
let titleDiv = document.createElement('div');
|
let titleDiv = document.createElement('div');
|
||||||
titleDiv.classList.add('title');
|
titleDiv.classList.add('title');
|
||||||
titleDiv.innerHTML = webpage.rTitle;
|
setInnerHTML(titleDiv, webpage.rTitle);
|
||||||
quoteTextDiv.append(titleDiv);
|
quoteTextDiv.append(titleDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(webpage.rDescription) {
|
if(webpage.rDescription) {
|
||||||
let textDiv = document.createElement('div');
|
let textDiv = document.createElement('div');
|
||||||
textDiv.classList.add('text');
|
textDiv.classList.add('text');
|
||||||
textDiv.innerHTML = webpage.rDescription;
|
setInnerHTML(textDiv, webpage.rDescription);
|
||||||
quoteTextDiv.append(textDiv);
|
quoteTextDiv.append(textDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2626,9 +2652,15 @@ export default class ChatBubbles {
|
|||||||
} */
|
} */
|
||||||
|
|
||||||
const historyStorage = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId);
|
const historyStorage = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId);
|
||||||
if(history.includes(historyStorage.maxId)) {
|
const firstSlice = historyStorage.history.first;
|
||||||
|
const lastSlice = historyStorage.history.last;
|
||||||
|
if(firstSlice.isEnd(SliceEnd.Bottom) && history.includes(firstSlice[0])) {
|
||||||
this.scrollable.loadedAll.bottom = true;
|
this.scrollable.loadedAll.bottom = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lastSlice.isEnd(SliceEnd.Top) && history.includes(lastSlice[lastSlice.length - 1])) {
|
||||||
|
this.scrollable.loadedAll.top = true;
|
||||||
|
}
|
||||||
|
|
||||||
//console.time('appImManager render history');
|
//console.time('appImManager render history');
|
||||||
|
|
||||||
@ -2818,8 +2850,9 @@ export default class ChatBubbles {
|
|||||||
additionMsgIds = [additionMsgId];
|
additionMsgIds = [additionMsgId];
|
||||||
} else {
|
} else {
|
||||||
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
|
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
|
||||||
if(historyStorage.history.length < loadCount && !historyStorage.history.slice.isEnd(SliceEnd.Both)) {
|
const slice = historyStorage.history.slice;
|
||||||
additionMsgIds = historyStorage.history.slice.slice();
|
if(slice.length < loadCount && !slice.isEnd(SliceEnd.Both)) {
|
||||||
|
additionMsgIds = slice.slice();
|
||||||
|
|
||||||
// * filter last album, because we don't know is it the last item
|
// * filter last album, because we don't know is it the last item
|
||||||
for(let i = additionMsgIds.length - 1; i >= 0; --i) {
|
for(let i = additionMsgIds.length - 1; i >= 0; --i) {
|
||||||
|
@ -158,7 +158,7 @@ export default class ChatContextMenu {
|
|||||||
private init() {
|
private init() {
|
||||||
this.buttons = [{
|
this.buttons = [{
|
||||||
icon: 'send2',
|
icon: 'send2',
|
||||||
text: 'Chat.Context.Scheduled.SendNow',
|
text: 'MessageScheduleSend',
|
||||||
onClick: this.onSendScheduledClick,
|
onClick: this.onSendScheduledClick,
|
||||||
verify: () => this.chat.type === 'scheduled' && !this.message.pFlags.is_outgoing
|
verify: () => this.chat.type === 'scheduled' && !this.message.pFlags.is_outgoing
|
||||||
}, {
|
}, {
|
||||||
@ -170,7 +170,7 @@ export default class ChatContextMenu {
|
|||||||
withSelection: true
|
withSelection: true
|
||||||
}, {
|
}, {
|
||||||
icon: 'schedule',
|
icon: 'schedule',
|
||||||
text: 'Chat.Context.Scheduled.Reschedule',
|
text: 'MessageScheduleEditTime',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.chat.input.scheduleSending(() => {
|
this.chat.input.scheduleSending(() => {
|
||||||
this.appMessagesManager.editMessage(this.message, this.message.message, {
|
this.appMessagesManager.editMessage(this.message, this.message.message, {
|
||||||
|
@ -359,7 +359,7 @@ export default class ChatSelection {
|
|||||||
|
|
||||||
if(this.chat.type === 'scheduled') {
|
if(this.chat.type === 'scheduled') {
|
||||||
this.selectionSendNowBtn = Button('btn-primary btn-transparent btn-short text-bold selection-container-send', {icon: 'send2'});
|
this.selectionSendNowBtn = Button('btn-primary btn-transparent btn-short text-bold selection-container-send', {icon: 'send2'});
|
||||||
this.selectionSendNowBtn.append(i18n('Chat.Context.Scheduled.SendNow'));
|
this.selectionSendNowBtn.append(i18n('MessageScheduleSend'));
|
||||||
this.listenerSetter.add(this.selectionSendNowBtn, 'click', () => {
|
this.listenerSetter.add(this.selectionSendNowBtn, 'click', () => {
|
||||||
new PopupSendNow(this.bubbles.peerId, [...this.selectedMids], () => {
|
new PopupSendNow(this.bubbles.peerId, [...this.selectedMids], () => {
|
||||||
this.cancelSelection();
|
this.cancelSelection();
|
||||||
|
@ -23,9 +23,11 @@ export default class DivAndCaption<T> {
|
|||||||
|
|
||||||
this.title = document.createElement('div');
|
this.title = document.createElement('div');
|
||||||
this.title.classList.add(className + '-title');
|
this.title.classList.add(className + '-title');
|
||||||
|
this.title.setAttribute('dir', 'auto');
|
||||||
|
|
||||||
this.subtitle = document.createElement('div');
|
this.subtitle = document.createElement('div');
|
||||||
this.subtitle.classList.add(className + '-subtitle');
|
this.subtitle.classList.add(className + '-subtitle');
|
||||||
|
this.subtitle.setAttribute('dir', 'auto');
|
||||||
|
|
||||||
this.content.append(this.title, this.subtitle);
|
this.content.append(this.title, this.subtitle);
|
||||||
this.container.append(this.border, this.content);
|
this.container.append(this.border, this.content);
|
||||||
|
@ -42,7 +42,8 @@ let init = () => {
|
|||||||
init = null;
|
init = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkAndSetRTL = (input: HTMLElement) => {
|
// ! it doesn't respect symbols other than strongs
|
||||||
|
/* const checkAndSetRTL = (input: HTMLElement) => {
|
||||||
//const isEmpty = isInputEmpty(input);
|
//const isEmpty = isInputEmpty(input);
|
||||||
//console.log('input', isEmpty);
|
//console.log('input', isEmpty);
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ const checkAndSetRTL = (input: HTMLElement) => {
|
|||||||
//console.log('RTL', direction, char);
|
//console.log('RTL', direction, char);
|
||||||
|
|
||||||
input.style.direction = direction;
|
input.style.direction = direction;
|
||||||
};
|
}; */
|
||||||
|
|
||||||
export enum InputState {
|
export enum InputState {
|
||||||
Neutral = 0,
|
Neutral = 0,
|
||||||
@ -112,7 +113,7 @@ class InputField {
|
|||||||
|
|
||||||
input = this.container.firstElementChild as HTMLElement;
|
input = this.container.firstElementChild as HTMLElement;
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
checkAndSetRTL(input);
|
//checkAndSetRTL(input);
|
||||||
|
|
||||||
if(processInput) {
|
if(processInput) {
|
||||||
processInput();
|
processInput();
|
||||||
@ -148,9 +149,11 @@ class InputField {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
input = this.container.firstElementChild as HTMLElement;
|
input = this.container.firstElementChild as HTMLElement;
|
||||||
input.addEventListener('input', () => checkAndSetRTL(input));
|
//input.addEventListener('input', () => checkAndSetRTL(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.setAttribute('dir', 'auto');
|
||||||
|
|
||||||
if(placeholder) {
|
if(placeholder) {
|
||||||
_i18n(input, placeholder, undefined, 'placeholder');
|
_i18n(input, placeholder, undefined, 'placeholder');
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ export default class PeerTitle {
|
|||||||
constructor(options: PeerTitleOptions) {
|
constructor(options: PeerTitleOptions) {
|
||||||
this.element = document.createElement('span');
|
this.element = document.createElement('span');
|
||||||
this.element.classList.add('peer-title');
|
this.element.classList.add('peer-title');
|
||||||
|
this.element.setAttribute('dir', 'auto');
|
||||||
|
|
||||||
this.update(options);
|
this.update(options);
|
||||||
weakMap.set(this.element, this);
|
weakMap.set(this.element, this);
|
||||||
|
@ -40,6 +40,7 @@ export default class Row {
|
|||||||
|
|
||||||
this.subtitle = document.createElement('div');
|
this.subtitle = document.createElement('div');
|
||||||
this.subtitle.classList.add('row-subtitle');
|
this.subtitle.classList.add('row-subtitle');
|
||||||
|
this.subtitle.setAttribute('dir', 'auto');
|
||||||
if(options.subtitle) {
|
if(options.subtitle) {
|
||||||
this.subtitle.innerHTML = options.subtitle;
|
this.subtitle.innerHTML = options.subtitle;
|
||||||
} else if(options.subtitleLangKey) {
|
} else if(options.subtitleLangKey) {
|
||||||
@ -89,6 +90,7 @@ export default class Row {
|
|||||||
|
|
||||||
this.title = document.createElement('div');
|
this.title = document.createElement('div');
|
||||||
this.title.classList.add('row-title');
|
this.title.classList.add('row-title');
|
||||||
|
this.title.setAttribute('dir', 'auto');
|
||||||
if(options.title) {
|
if(options.title) {
|
||||||
this.title.innerHTML = options.title;
|
this.title.innerHTML = options.title;
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,6 +20,7 @@ export default class AppAddMembersTab extends SliderSuperTab {
|
|||||||
protected init() {
|
protected init() {
|
||||||
this.nextBtn = ButtonCorner({icon: 'arrow_next'});
|
this.nextBtn = ButtonCorner({icon: 'arrow_next'});
|
||||||
this.content.append(this.nextBtn);
|
this.content.append(this.nextBtn);
|
||||||
|
this.scrollable.container.remove();
|
||||||
|
|
||||||
this.nextBtn.addEventListener('click', () => {
|
this.nextBtn.addEventListener('click', () => {
|
||||||
const peerIds = this.selector.getSelected();
|
const peerIds = this.selector.getSelected();
|
||||||
|
@ -22,34 +22,37 @@ import I18n from "../../../lib/langPack";
|
|||||||
import PopupPeer from "../../popups/peer";
|
import PopupPeer from "../../popups/peer";
|
||||||
import ButtonCorner from "../../buttonCorner";
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
|
||||||
export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
export default class AppChatTypeTab extends SliderSuperTabEventable {
|
||||||
public peerId: number;
|
public chatId: number;
|
||||||
public chatFull: ChatFull;
|
public chatFull: ChatFull;
|
||||||
|
|
||||||
protected init() {
|
protected init() {
|
||||||
this.container.classList.add('edit-peer-container', 'group-type-container');
|
this.container.classList.add('edit-peer-container', 'group-type-container');
|
||||||
this.setTitle('GroupType');
|
|
||||||
|
const isBroadcast = appChatsManager.isBroadcast(this.chatId);
|
||||||
|
|
||||||
|
this.setTitle(isBroadcast ? 'ChannelType' : 'GroupType');
|
||||||
|
|
||||||
const section = new SettingSection({
|
const section = new SettingSection({
|
||||||
name: 'GroupType'
|
name: isBroadcast ? 'ChannelType' : 'GroupType'
|
||||||
});
|
});
|
||||||
|
|
||||||
const random = randomLong();
|
const random = randomLong();
|
||||||
const privateRow = new Row({
|
const privateRow = new Row({
|
||||||
radioField: new RadioField({
|
radioField: new RadioField({
|
||||||
langKey: 'MegaPrivate',
|
langKey: isBroadcast ? 'ChannelPrivate' : 'MegaPrivate',
|
||||||
name: random,
|
name: random,
|
||||||
value: 'private'
|
value: 'private'
|
||||||
}),
|
}),
|
||||||
subtitleLangKey: 'MegaPrivateInfo'
|
subtitleLangKey: isBroadcast ? 'ChannelPrivateInfo' : 'MegaPrivateInfo'
|
||||||
});
|
});
|
||||||
const publicRow = new Row({
|
const publicRow = new Row({
|
||||||
radioField: new RadioField({
|
radioField: new RadioField({
|
||||||
langKey: 'MegaPublic',
|
langKey: isBroadcast ? 'ChannelPublic' : 'MegaPublic',
|
||||||
name: random,
|
name: random,
|
||||||
value: 'public'
|
value: 'public'
|
||||||
}),
|
}),
|
||||||
subtitleLangKey: 'MegaPublicInfo'
|
subtitleLangKey: isBroadcast ? 'ChannelPublicInfo' : 'MegaPublicInfo'
|
||||||
});
|
});
|
||||||
const form = RadioFormFromRows([privateRow, publicRow], (value) => {
|
const form = RadioFormFromRows([privateRow, publicRow], (value) => {
|
||||||
const a = [privateSection, publicSection];
|
const a = [privateSection, publicSection];
|
||||||
@ -61,7 +64,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
onChange();
|
onChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
const chat: Chat = appChatsManager.getChat(-this.peerId);
|
const chat: Chat = appChatsManager.getChat(this.chatId);
|
||||||
|
|
||||||
section.content.append(form);
|
section.content.append(form);
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
//let revoked = false;
|
//let revoked = false;
|
||||||
const linkRow = new Row({
|
const linkRow = new Row({
|
||||||
title: (this.chatFull.exported_invite as ExportedChatInvite.chatInviteExported).link,
|
title: (this.chatFull.exported_invite as ExportedChatInvite.chatInviteExported).link,
|
||||||
subtitleLangKey: 'MegaPrivateLinkHelp',
|
subtitleLangKey: isBroadcast ? 'ChannelPrivateLinkHelp' : 'MegaPrivateLinkHelp',
|
||||||
clickable: () => {
|
clickable: () => {
|
||||||
copyTextToClipboard((this.chatFull.exported_invite as ExportedChatInvite.chatInviteExported).link);
|
copyTextToClipboard((this.chatFull.exported_invite as ExportedChatInvite.chatInviteExported).link);
|
||||||
toast(I18n.format('LinkCopied', true));
|
toast(I18n.format('LinkCopied', true));
|
||||||
@ -86,7 +89,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
callback: () => {
|
callback: () => {
|
||||||
const toggle = toggleDisability([btnRevoke], true);
|
const toggle = toggleDisability([btnRevoke], true);
|
||||||
|
|
||||||
appProfileManager.getChatInviteLink(-this.peerId, true).then(link => {
|
appProfileManager.getChatInviteLink(this.chatId, true).then(link => {
|
||||||
toggle();
|
toggle();
|
||||||
linkRow.title.innerHTML = link;
|
linkRow.title.innerHTML = link;
|
||||||
//revoked = true;
|
//revoked = true;
|
||||||
@ -102,7 +105,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
privateSection.content.append(linkRow.container, btnRevoke);
|
privateSection.content.append(linkRow.container, btnRevoke);
|
||||||
|
|
||||||
const publicSection = new SettingSection({
|
const publicSection = new SettingSection({
|
||||||
caption: 'Channel.UsernameAboutGroup',
|
caption: isBroadcast ? 'Channel.UsernameAboutChannel' : 'Channel.UsernameAboutGroup',
|
||||||
noDelimiter: true
|
noDelimiter: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -126,7 +129,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
invalidText: 'Link.Invalid',
|
invalidText: 'Link.Invalid',
|
||||||
takenText: 'Link.Taken',
|
takenText: 'Link.Taken',
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
peerId: this.peerId,
|
peerId: -this.chatId,
|
||||||
head: placeholder
|
head: placeholder
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -141,7 +144,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
|
|||||||
attachClickEvent(applyBtn, () => {
|
attachClickEvent(applyBtn, () => {
|
||||||
/* const unsetLoader = */setButtonLoader(applyBtn);
|
/* const unsetLoader = */setButtonLoader(applyBtn);
|
||||||
const username = publicRow.radioField.checked ? linkInputField.getValue() : '';
|
const username = publicRow.radioField.checked ? linkInputField.getValue() : '';
|
||||||
appChatsManager.migrateChat(-this.peerId).then(channelId => {
|
appChatsManager.migrateChat(this.chatId).then(channelId => {
|
||||||
return appChatsManager.updateUsername(channelId, username);
|
return appChatsManager.updateUsername(channelId, username);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
//unsetLoader();
|
//unsetLoader();
|
@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* https://github.com/morethanwords/tweb
|
|
||||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { SliderSuperTab } from "../../slider"
|
|
||||||
import InputField from "../../inputField";
|
|
||||||
import EditPeer from "../../editPeer";
|
|
||||||
import { SettingSection } from "../../sidebarLeft";
|
|
||||||
import Row from "../../row";
|
|
||||||
import CheckboxField from "../../checkboxField";
|
|
||||||
import Button from "../../button";
|
|
||||||
import appChatsManager from "../../../lib/appManagers/appChatsManager";
|
|
||||||
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
|
||||||
import { attachClickEvent, toggleDisability } from "../../../helpers/dom";
|
|
||||||
import PopupPeer from "../../popups/peer";
|
|
||||||
import { addCancelButton } from "../../popups";
|
|
||||||
import { i18n } from "../../../lib/langPack";
|
|
||||||
import { numberThousandSplitter } from "../../../helpers/number";
|
|
||||||
|
|
||||||
export default class AppEditChannelTab extends SliderSuperTab {
|
|
||||||
private nameInputField: InputField;
|
|
||||||
private descriptionInputField: InputField;
|
|
||||||
private editPeer: EditPeer;
|
|
||||||
public peerId: number;
|
|
||||||
|
|
||||||
protected async init() {
|
|
||||||
this.container.classList.add('edit-peer-container', 'edit-channel-container');
|
|
||||||
this.setTitle('Edit');
|
|
||||||
|
|
||||||
const chatFull = await appProfileManager.getChannelFull(-this.peerId, true);
|
|
||||||
|
|
||||||
{
|
|
||||||
const section = new SettingSection({noDelimiter: true});
|
|
||||||
|
|
||||||
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
|
|
||||||
const inputFields: InputField[] = [];
|
|
||||||
|
|
||||||
const inputWrapper = document.createElement('div');
|
|
||||||
inputWrapper.classList.add('input-wrapper');
|
|
||||||
|
|
||||||
this.nameInputField = new InputField({
|
|
||||||
label: 'Channel.ChannelNameHolder',
|
|
||||||
name: 'channel-name',
|
|
||||||
maxLength: 255
|
|
||||||
});
|
|
||||||
this.descriptionInputField = new InputField({
|
|
||||||
label: 'DescriptionPlaceholder',
|
|
||||||
name: 'channel-description',
|
|
||||||
maxLength: 255
|
|
||||||
});
|
|
||||||
|
|
||||||
this.nameInputField.setOriginalValue(appChatsManager.getChat(-this.peerId).title);
|
|
||||||
|
|
||||||
this.descriptionInputField.setOriginalValue(chatFull.about);
|
|
||||||
|
|
||||||
inputWrapper.append(this.nameInputField.container, this.descriptionInputField.container);
|
|
||||||
|
|
||||||
inputFields.push(this.nameInputField, this.descriptionInputField);
|
|
||||||
|
|
||||||
this.editPeer = new EditPeer({
|
|
||||||
peerId: this.peerId,
|
|
||||||
inputFields,
|
|
||||||
listenerSetter: this.listenerSetter
|
|
||||||
});
|
|
||||||
this.content.append(this.editPeer.nextBtn);
|
|
||||||
|
|
||||||
section.content.append(this.editPeer.avatarEdit.container, inputWrapper);
|
|
||||||
|
|
||||||
attachClickEvent(this.editPeer.nextBtn, () => {
|
|
||||||
this.editPeer.nextBtn.disabled = true;
|
|
||||||
|
|
||||||
let promises: Promise<any>[] = [];
|
|
||||||
|
|
||||||
const id = -this.peerId;
|
|
||||||
if(this.nameInputField.isValid()) {
|
|
||||||
promises.push(appChatsManager.editTitle(id, this.nameInputField.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.descriptionInputField.isValid()) {
|
|
||||||
promises.push(appChatsManager.editAbout(id, this.descriptionInputField.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.editPeer.uploadAvatar) {
|
|
||||||
promises.push(this.editPeer.uploadAvatar().then(inputFile => {
|
|
||||||
return appChatsManager.editPhoto(id, inputFile);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.race(promises).finally(() => {
|
|
||||||
this.editPeer.nextBtn.removeAttribute('disabled');
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
}, {listenerSetter: this.listenerSetter});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if(appChatsManager.hasRights(-this.peerId, 'change_type')) {
|
|
||||||
const channelTypeRow = new Row({
|
|
||||||
titleLangKey: 'ChannelType',
|
|
||||||
subtitleLangKey: 'TypePrivate',
|
|
||||||
clickable: true,
|
|
||||||
icon: 'lock'
|
|
||||||
});
|
|
||||||
|
|
||||||
section.content.append(channelTypeRow.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
|
|
||||||
const discussionRow = new Row({
|
|
||||||
titleLangKey: 'PeerInfo.Discussion',
|
|
||||||
subtitleLangKey: 'PeerInfo.Discussion.Add',
|
|
||||||
clickable: true,
|
|
||||||
icon: 'message'
|
|
||||||
});
|
|
||||||
|
|
||||||
section.content.append(discussionRow.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
const administratorsRow = new Row({
|
|
||||||
titleLangKey: 'PeerInfo.Administrators',
|
|
||||||
subtitle: '' + chatFull.admins_count,
|
|
||||||
icon: 'admin',
|
|
||||||
clickable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
section.content.append(administratorsRow.container);
|
|
||||||
|
|
||||||
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
|
|
||||||
const signMessagesCheckboxField = new CheckboxField({
|
|
||||||
text: 'PeerInfo.SignMessages',
|
|
||||||
checked: false
|
|
||||||
});
|
|
||||||
|
|
||||||
section.content.append(signMessagesCheckboxField.label);
|
|
||||||
} */
|
|
||||||
|
|
||||||
this.scrollable.append(section.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* {
|
|
||||||
const section = new SettingSection({
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const subscribersRow = new Row({
|
|
||||||
titleLangKey: 'PeerInfo.Subscribers',
|
|
||||||
icon: 'newgroup',
|
|
||||||
clickable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
subscribersRow.subtitle.append(i18n('Subscribers', [numberThousandSplitter(335356)]));
|
|
||||||
|
|
||||||
section.content.append(subscribersRow.container);
|
|
||||||
|
|
||||||
this.scrollable.append(section.container);
|
|
||||||
} */
|
|
||||||
|
|
||||||
if(appChatsManager.hasRights(-this.peerId, 'delete_chat')) {
|
|
||||||
const section = new SettingSection({
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'PeerInfo.DeleteChannel'});
|
|
||||||
|
|
||||||
attachClickEvent(btnDelete, () => {
|
|
||||||
new PopupPeer('popup-delete-channel', {
|
|
||||||
peerId: this.peerId,
|
|
||||||
titleLangKey: 'ChannelDeleteMenu',
|
|
||||||
descriptionLangKey: 'AreYouSureDeleteAndExitChannel',
|
|
||||||
buttons: addCancelButton([{
|
|
||||||
langKey: 'ChannelDeleteMenu',
|
|
||||||
callback: () => {
|
|
||||||
const toggle = toggleDisability([btnDelete], true);
|
|
||||||
|
|
||||||
},
|
|
||||||
isDanger: true
|
|
||||||
}, {
|
|
||||||
langKey: 'DeleteChannelForAll',
|
|
||||||
callback: () => {
|
|
||||||
const toggle = toggleDisability([btnDelete], true);
|
|
||||||
|
|
||||||
appChatsManager.deleteChannel(-this.peerId).then(() => {
|
|
||||||
this.close();
|
|
||||||
}, () => {
|
|
||||||
toggle();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isDanger: true
|
|
||||||
}])
|
|
||||||
}).show();
|
|
||||||
}, {listenerSetter: this.listenerSetter});
|
|
||||||
|
|
||||||
section.content.append(btnDelete);
|
|
||||||
|
|
||||||
this.scrollable.append(section.container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,15 +13,17 @@ import Button from "../../button";
|
|||||||
import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager";
|
import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager";
|
||||||
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
||||||
import { attachClickEvent, toggleDisability } from "../../../helpers/dom";
|
import { attachClickEvent, toggleDisability } from "../../../helpers/dom";
|
||||||
import { ChatFull } from "../../../layer";
|
import { Chat } from "../../../layer";
|
||||||
import AppGroupTypeTab from "./groupType";
|
import AppChatTypeTab from "./chatType";
|
||||||
import rootScope from "../../../lib/rootScope";
|
import rootScope from "../../../lib/rootScope";
|
||||||
import AppGroupPermissionsTab from "./groupPermissions";
|
import AppGroupPermissionsTab from "./groupPermissions";
|
||||||
import { i18n } from "../../../lib/langPack";
|
import { i18n, LangPackKey } from "../../../lib/langPack";
|
||||||
import PopupDeleteDialog from "../../popups/deleteDialog";
|
import PopupDeleteDialog from "../../popups/deleteDialog";
|
||||||
|
import { addCancelButton } from "../../popups";
|
||||||
|
import PopupPeer from "../../popups/peer";
|
||||||
|
|
||||||
export default class AppEditGroupTab extends SliderSuperTab {
|
export default class AppEditChatTab extends SliderSuperTab {
|
||||||
private groupNameInputField: InputField;
|
private chatNameInputField: InputField;
|
||||||
private descriptionInputField: InputField;
|
private descriptionInputField: InputField;
|
||||||
private editPeer: EditPeer;
|
private editPeer: EditPeer;
|
||||||
public chatId: number;
|
public chatId: number;
|
||||||
@ -33,9 +35,13 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
|
|
||||||
this.container.classList.add('edit-peer-container', 'edit-group-container');
|
this.container.classList.add('edit-peer-container', 'edit-group-container');
|
||||||
this.setTitle('Edit');
|
this.setTitle('Edit');
|
||||||
|
|
||||||
const chatFull = await appProfileManager.getChatFull(this.chatId, true);
|
const chatFull = await appProfileManager.getChatFull(this.chatId, true);
|
||||||
|
|
||||||
|
const chat: Chat.chat | Chat.channel = appChatsManager.getChat(this.chatId);
|
||||||
|
const isBroadcast = appChatsManager.isBroadcast(this.chatId);
|
||||||
|
const isChannel = appChatsManager.isChannel(this.chatId);
|
||||||
|
|
||||||
{
|
{
|
||||||
const section = new SettingSection({noDelimiter: true});
|
const section = new SettingSection({noDelimiter: true});
|
||||||
const inputFields: InputField[] = [];
|
const inputFields: InputField[] = [];
|
||||||
@ -43,26 +49,23 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
const inputWrapper = document.createElement('div');
|
const inputWrapper = document.createElement('div');
|
||||||
inputWrapper.classList.add('input-wrapper');
|
inputWrapper.classList.add('input-wrapper');
|
||||||
|
|
||||||
this.groupNameInputField = new InputField({
|
this.chatNameInputField = new InputField({
|
||||||
label: 'CreateGroup.NameHolder',
|
label: isBroadcast ? 'Channel.ChannelNameHolder' : 'CreateGroup.NameHolder',
|
||||||
name: 'group-name',
|
name: 'chat-name',
|
||||||
maxLength: 255
|
maxLength: 255
|
||||||
});
|
});
|
||||||
this.descriptionInputField = new InputField({
|
this.descriptionInputField = new InputField({
|
||||||
label: 'DescriptionPlaceholder',
|
label: 'DescriptionPlaceholder',
|
||||||
name: 'group-description',
|
name: 'chat-description',
|
||||||
maxLength: 255
|
maxLength: 255
|
||||||
});
|
});
|
||||||
|
|
||||||
const chat = appChatsManager.getChat(this.chatId);
|
|
||||||
|
|
||||||
this.groupNameInputField.setOriginalValue(chat.title);
|
this.chatNameInputField.setOriginalValue(chat.title);
|
||||||
|
|
||||||
this.descriptionInputField.setOriginalValue(chatFull.about);
|
this.descriptionInputField.setOriginalValue(chatFull.about);
|
||||||
|
|
||||||
inputWrapper.append(this.groupNameInputField.container, this.descriptionInputField.container);
|
inputWrapper.append(this.chatNameInputField.container, this.descriptionInputField.container);
|
||||||
|
|
||||||
inputFields.push(this.groupNameInputField, this.descriptionInputField);
|
inputFields.push(this.chatNameInputField, this.descriptionInputField);
|
||||||
|
|
||||||
this.editPeer = new EditPeer({
|
this.editPeer = new EditPeer({
|
||||||
peerId: -this.chatId,
|
peerId: -this.chatId,
|
||||||
@ -74,29 +77,37 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
section.content.append(this.editPeer.avatarEdit.container, inputWrapper);
|
section.content.append(this.editPeer.avatarEdit.container, inputWrapper);
|
||||||
|
|
||||||
if(appChatsManager.hasRights(this.chatId, 'change_type')) {
|
if(appChatsManager.hasRights(this.chatId, 'change_type')) {
|
||||||
const groupTypeRow = new Row({
|
const chatTypeRow = new Row({
|
||||||
titleLangKey: 'GroupType',
|
titleLangKey: isBroadcast ? 'ChannelType' : 'GroupType',
|
||||||
clickable: () => {
|
clickable: () => {
|
||||||
const tab = new AppGroupTypeTab(this.slider);
|
const tab = new AppChatTypeTab(this.slider);
|
||||||
tab.peerId = -this.chatId;
|
tab.chatId = this.chatId;
|
||||||
tab.chatFull = chatFull;
|
tab.chatFull = chatFull;
|
||||||
tab.open();
|
tab.open();
|
||||||
|
|
||||||
this.listenerSetter.add(tab.eventListener, 'destroy', setGroupTypeSubtitle);
|
this.listenerSetter.add(tab.eventListener, 'destroy', setChatTypeSubtitle);
|
||||||
},
|
},
|
||||||
icon: 'lock'
|
icon: 'lock'
|
||||||
});
|
});
|
||||||
|
|
||||||
const setGroupTypeSubtitle = () => {
|
const setChatTypeSubtitle = () => {
|
||||||
groupTypeRow.subtitle.textContent = '';
|
chatTypeRow.subtitle.textContent = '';
|
||||||
groupTypeRow.subtitle.append(i18n(chat.username ? 'TypePublicGroup' : 'TypePrivateGroup'));
|
|
||||||
|
let key: LangPackKey;
|
||||||
|
if(isBroadcast) {
|
||||||
|
key = (chat as Chat.channel).username ? 'TypePublic' : 'TypePrivate';
|
||||||
|
} else {
|
||||||
|
key = (chat as Chat.channel).username ? 'TypePublicGroup' : 'TypePrivateGroup';
|
||||||
|
}
|
||||||
|
|
||||||
|
chatTypeRow.subtitle.append(i18n(key));
|
||||||
};
|
};
|
||||||
|
|
||||||
setGroupTypeSubtitle();
|
setChatTypeSubtitle();
|
||||||
section.content.append(groupTypeRow.container);
|
section.content.append(chatTypeRow.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(appChatsManager.hasRights(this.chatId, 'change_permissions')) {
|
if(appChatsManager.hasRights(this.chatId, 'change_permissions') && !isBroadcast) {
|
||||||
const flags = [
|
const flags = [
|
||||||
'send_messages',
|
'send_messages',
|
||||||
'send_media',
|
'send_media',
|
||||||
@ -149,8 +160,8 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
const id = this.chatId;
|
const id = this.chatId;
|
||||||
if(this.groupNameInputField.isValid()) {
|
if(this.chatNameInputField.isValid()) {
|
||||||
promises.push(appChatsManager.editTitle(id, this.groupNameInputField.value));
|
promises.push(appChatsManager.editTitle(id, this.chatNameInputField.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.descriptionInputField.isValid()) {
|
if(this.descriptionInputField.isValid()) {
|
||||||
@ -168,6 +179,36 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}, {listenerSetter: this.listenerSetter});
|
}, {listenerSetter: this.listenerSetter});
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
|
||||||
|
const discussionRow = new Row({
|
||||||
|
titleLangKey: 'PeerInfo.Discussion',
|
||||||
|
subtitleLangKey: 'PeerInfo.Discussion.Add',
|
||||||
|
clickable: true,
|
||||||
|
icon: 'message'
|
||||||
|
});
|
||||||
|
|
||||||
|
section.content.append(discussionRow.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
const administratorsRow = new Row({
|
||||||
|
titleLangKey: 'PeerInfo.Administrators',
|
||||||
|
subtitle: '' + chatFull.admins_count,
|
||||||
|
icon: 'admin',
|
||||||
|
clickable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
section.content.append(administratorsRow.container);
|
||||||
|
|
||||||
|
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
|
||||||
|
const signMessagesCheckboxField = new CheckboxField({
|
||||||
|
text: 'PeerInfo.SignMessages',
|
||||||
|
checked: false
|
||||||
|
});
|
||||||
|
|
||||||
|
section.content.append(signMessagesCheckboxField.label);
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* {
|
/* {
|
||||||
@ -176,12 +217,13 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const membersRow = new Row({
|
const membersRow = new Row({
|
||||||
titleLangKey: 'GroupMembers',
|
titleLangKey: isBroadcast ? 'PeerInfo.Subscribers' : 'GroupMembers',
|
||||||
subtitle: '2 500',
|
|
||||||
icon: 'newgroup',
|
icon: 'newgroup',
|
||||||
clickable: true
|
clickable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
membersRow.subtitle.append(i18n('Subscribers', [numberThousandSplitter(335356)]));
|
||||||
|
|
||||||
section.content.append(membersRow.container);
|
section.content.append(membersRow.container);
|
||||||
|
|
||||||
if(appChatsManager.hasRights(this.chatId, 'change_permissions')) {
|
if(appChatsManager.hasRights(this.chatId, 'change_permissions')) {
|
||||||
@ -203,17 +245,45 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
if(appChatsManager.hasRights(this.chatId, 'delete_chat')) {
|
if(appChatsManager.hasRights(this.chatId, 'delete_chat')) {
|
||||||
const section = new SettingSection({});
|
const section = new SettingSection({});
|
||||||
|
|
||||||
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'DeleteMega'});
|
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteMega'});
|
||||||
|
|
||||||
attachClickEvent(btnDelete, () => {
|
attachClickEvent(btnDelete, () => {
|
||||||
new PopupDeleteDialog(-this.chatId, undefined, (promise) => {
|
if(isBroadcast) {
|
||||||
const toggle = toggleDisability([btnDelete], true);
|
new PopupPeer('popup-delete-channel', {
|
||||||
promise.then(() => {
|
peerId: -this.chatId,
|
||||||
this.close();
|
titleLangKey: 'ChannelDeleteMenu',
|
||||||
}, () => {
|
descriptionLangKey: 'AreYouSureDeleteAndExitChannel',
|
||||||
toggle();
|
buttons: addCancelButton([{
|
||||||
|
langKey: 'ChannelDeleteMenu',
|
||||||
|
callback: () => {
|
||||||
|
const toggle = toggleDisability([btnDelete], true);
|
||||||
|
|
||||||
|
},
|
||||||
|
isDanger: true
|
||||||
|
}, {
|
||||||
|
langKey: 'DeleteChannelForAll',
|
||||||
|
callback: () => {
|
||||||
|
const toggle = toggleDisability([btnDelete], true);
|
||||||
|
|
||||||
|
appChatsManager.deleteChannel(this.chatId).then(() => {
|
||||||
|
this.close();
|
||||||
|
}, () => {
|
||||||
|
toggle();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isDanger: true
|
||||||
|
}])
|
||||||
|
}).show();
|
||||||
|
} else {
|
||||||
|
new PopupDeleteDialog(-this.chatId, undefined, (promise) => {
|
||||||
|
const toggle = toggleDisability([btnDelete], true);
|
||||||
|
promise.then(() => {
|
||||||
|
this.close();
|
||||||
|
}, () => {
|
||||||
|
toggle();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}, {listenerSetter: this.listenerSetter});
|
}, {listenerSetter: this.listenerSetter});
|
||||||
|
|
||||||
section.content.append(btnDelete);
|
section.content.append(btnDelete);
|
||||||
@ -221,13 +291,15 @@ export default class AppEditGroupTab extends SliderSuperTab {
|
|||||||
this.scrollable.append(section.container);
|
this.scrollable.append(section.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! this one will fire earlier than tab's closeAfterTimeout (destroy) event and listeners will be erased, so destroy won't fire
|
if(!isChannel) {
|
||||||
this.listenerSetter.add(rootScope, 'dialog_migrate', ({migrateFrom, migrateTo}) => {
|
// ! this one will fire earlier than tab's closeAfterTimeout (destroy) event and listeners will be erased, so destroy won't fire
|
||||||
if(-this.chatId === migrateFrom) {
|
this.listenerSetter.add(rootScope, 'dialog_migrate', ({migrateFrom, migrateTo}) => {
|
||||||
this.chatId = -migrateTo;
|
if(-this.chatId === migrateFrom) {
|
||||||
this._init();
|
this.chatId = -migrateTo;
|
||||||
}
|
this._init();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected init() {
|
protected init() {
|
@ -19,9 +19,8 @@ import { attachClickEvent, replaceContent, cancelEvent } from "../../../helpers/
|
|||||||
import appSidebarRight from "..";
|
import appSidebarRight from "..";
|
||||||
import { TransitionSlider } from "../../transition";
|
import { TransitionSlider } from "../../transition";
|
||||||
import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager";
|
import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager";
|
||||||
import AppEditGroupTab from "./editGroup";
|
import AppEditChatTab from "./editChat";
|
||||||
import PeerTitle from "../../peerTitle";
|
import PeerTitle from "../../peerTitle";
|
||||||
import AppEditChannelTab from "./editChannel";
|
|
||||||
import AppEditContactTab from "./editContact";
|
import AppEditContactTab from "./editContact";
|
||||||
import appChatsManager, { Channel } from "../../../lib/appManagers/appChatsManager";
|
import appChatsManager, { Channel } from "../../../lib/appManagers/appChatsManager";
|
||||||
import { Chat, Message, MessageAction, ChatFull, Photo } from "../../../layer";
|
import { Chat, Message, MessageAction, ChatFull, Photo } from "../../../layer";
|
||||||
@ -854,17 +853,15 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
|||||||
});
|
});
|
||||||
|
|
||||||
attachClickEvent(this.editBtn, (e) => {
|
attachClickEvent(this.editBtn, (e) => {
|
||||||
let tab: AppEditGroupTab | AppEditChannelTab | AppEditContactTab;
|
let tab: AppEditChatTab | AppEditContactTab;
|
||||||
if(appPeersManager.isAnyGroup(this.peerId)) {
|
if(this.peerId < 0) {
|
||||||
tab = new AppEditGroupTab(appSidebarRight);
|
tab = new AppEditChatTab(appSidebarRight);
|
||||||
} else if(this.peerId > 0) {
|
|
||||||
tab = new AppEditContactTab(appSidebarRight);
|
|
||||||
} else {
|
} else {
|
||||||
tab = new AppEditChannelTab(appSidebarRight);
|
tab = new AppEditContactTab(appSidebarRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tab) {
|
if(tab) {
|
||||||
if(tab instanceof AppEditGroupTab) {
|
if(tab instanceof AppEditChatTab) {
|
||||||
tab.chatId = -this.peerId;
|
tab.chatId = -this.peerId;
|
||||||
} else {
|
} else {
|
||||||
tab.peerId = this.peerId;
|
tab.peerId = this.peerId;
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
const App = {
|
const App = {
|
||||||
id: 1025907,
|
id: 1025907,
|
||||||
hash: '452b0359b988148995f22ff0f4229750',
|
hash: '452b0359b988148995f22ff0f4229750',
|
||||||
version: '0.5.0',
|
version: '0.5.1',
|
||||||
langPackVersion: '0.1.6',
|
langPackVersion: '0.1.6',
|
||||||
langPack: 'macos',
|
langPack: 'macos',
|
||||||
langPackCode: 'en',
|
langPackCode: 'en',
|
||||||
|
@ -694,3 +694,8 @@ export function replaceContent(elem: HTMLElement, node: string | Node) {
|
|||||||
elem.append(node);
|
elem.append(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setInnerHTML(elem: HTMLElement, html: string) {
|
||||||
|
elem.setAttribute('dir', 'auto');
|
||||||
|
elem.innerHTML = html;
|
||||||
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descend sorted storage
|
* Descend sorted storage
|
||||||
*/
|
*/
|
||||||
@ -14,15 +16,19 @@ export enum SliceEnd {
|
|||||||
None = 0,
|
None = 0,
|
||||||
Top = 1,
|
Top = 1,
|
||||||
Bottom = 2,
|
Bottom = 2,
|
||||||
Both = 4
|
Both = SliceEnd.Top | SliceEnd.Bottom
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Slice extends Array<ItemType> {
|
export interface Slice extends Array<ItemType> {
|
||||||
slicedArray: SlicedArray;
|
//slicedArray: SlicedArray;
|
||||||
end: SliceEnd;
|
end: SliceEnd;
|
||||||
|
|
||||||
isEnd: (side: SliceEnd) => boolean;
|
isEnd: (side: SliceEnd) => boolean;
|
||||||
setEnd: (side: SliceEnd) => void;
|
setEnd: (side: SliceEnd) => void;
|
||||||
|
unsetEnd: (side: SliceEnd) => void;
|
||||||
|
|
||||||
|
slice: (from?: number, to?: number) => Slice;
|
||||||
|
splice: (start: number, deleteCount: number, ...items: ItemType[]) => Slice;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SliceConstructor {
|
export interface SliceConstructor {
|
||||||
@ -35,51 +41,84 @@ export default class SlicedArray {
|
|||||||
private sliceConstructor: SliceConstructor;
|
private sliceConstructor: SliceConstructor;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const self = this;
|
// @ts-ignore
|
||||||
this.sliceConstructor = class Slice extends Array<ItemType> implements Slice {
|
this.sliceConstructor = SlicedArray.getSliceConstructor(this);
|
||||||
slicedArray: SlicedArray;
|
|
||||||
end: SliceEnd = SliceEnd.None;
|
|
||||||
|
|
||||||
constructor(...items: ItemType[]) {
|
|
||||||
super(...items);
|
|
||||||
this.slicedArray = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
isEnd(side: SliceEnd) {
|
|
||||||
if(this.end & side) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(side === SliceEnd.Top) {
|
|
||||||
const slice = self.last;
|
|
||||||
return slice.end & side ? this.includes(slice[slice.length - 1]) || !slice.length : false;
|
|
||||||
} else if(side === SliceEnd.Bottom) {
|
|
||||||
const slice = self.first;
|
|
||||||
return slice.end & side ? this.includes(slice[0]) || !slice.length : false;
|
|
||||||
}/* else if(side === SliceEnd.Both) {
|
|
||||||
|
|
||||||
} */
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEnd(side: SliceEnd) {
|
|
||||||
this.end |= side;
|
|
||||||
|
|
||||||
if(side !== SliceEnd.Both && this.end & SliceEnd.Top && this.end & SliceEnd.Bottom) {
|
|
||||||
this.end |= SliceEnd.Both;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const first = this.constructSlice();
|
const first = this.constructSlice();
|
||||||
first.setEnd(SliceEnd.Bottom);
|
//first.setEnd(SliceEnd.Bottom);
|
||||||
this.slices = [first];
|
this.slices = [first];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static getSliceConstructor(slicedArray: SlicedArray) {
|
||||||
|
return class Slice extends Array<ItemType> implements Slice {
|
||||||
|
//slicedArray: SlicedArray;
|
||||||
|
end: SliceEnd = SliceEnd.None;
|
||||||
|
|
||||||
|
/* constructor(...items: ItemType[]) {
|
||||||
|
super(...items);
|
||||||
|
//this.slicedArray = slicedArray;
|
||||||
|
} */
|
||||||
|
|
||||||
|
isEnd(side: SliceEnd): boolean {
|
||||||
|
if((this.end & side) === side) {
|
||||||
|
return true;
|
||||||
|
}/* else if(!this.slicedArray) {
|
||||||
|
return false;
|
||||||
|
} */
|
||||||
|
|
||||||
|
let isEnd = false;
|
||||||
|
if(side === SliceEnd.Top) {
|
||||||
|
const slice = slicedArray.last;
|
||||||
|
isEnd = slice.end & side ? this.includes(slice[slice.length - 1])/* || !slice.length */ : false;
|
||||||
|
} else if(side === SliceEnd.Bottom) {
|
||||||
|
const slice = slicedArray.first;
|
||||||
|
isEnd = slice.end & side ? this.includes(slice[0])/* || !slice.length */ : false;
|
||||||
|
} else if(side === SliceEnd.Both) {
|
||||||
|
return this.isEnd(SliceEnd.Top) && this.isEnd(SliceEnd.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isEnd) {
|
||||||
|
this.setEnd(side);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEnd(side: SliceEnd) {
|
||||||
|
this.end |= side;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsetEnd(side: SliceEnd) {
|
||||||
|
this.end ^= side;
|
||||||
|
}
|
||||||
|
|
||||||
|
splice(start: number, deleteCount: number, ...items: ItemType[]) {
|
||||||
|
const ret = super.splice(start, deleteCount, ...items);
|
||||||
|
|
||||||
|
if(!this.length) {
|
||||||
|
const slices = slicedArray.slices as number[][];
|
||||||
|
const idx = slices.indexOf(this);
|
||||||
|
if(idx !== -1) {
|
||||||
|
if(slices.length === 1) { // left empty slice without ends
|
||||||
|
this.unsetEnd(SliceEnd.Both);
|
||||||
|
} else { // delete this slice
|
||||||
|
slices.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public constructSlice(...items: ItemType[]) {
|
public constructSlice(...items: ItemType[]) {
|
||||||
//const slice = new Slice(this, ...items);
|
//const slice = new Slice(this, ...items);
|
||||||
const slice = new this.sliceConstructor(...items);
|
// can't pass items directly to constructor because first argument is length
|
||||||
|
const slice = new this.sliceConstructor(items.length);
|
||||||
|
for(let i = 0, length = items.length; i < length; ++i) {
|
||||||
|
slice[i] = items[i];
|
||||||
|
}
|
||||||
return slice;
|
return slice;
|
||||||
|
|
||||||
// ! code below will slow execution in 15 times
|
// ! code below will slow execution in 15 times
|
||||||
@ -128,7 +167,7 @@ export default class SlicedArray {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertSlice(slice: ItemType[]) {
|
public insertSlice(slice: ItemType[], flatten = true) {
|
||||||
if(!slice.length) {
|
if(!slice.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -136,15 +175,15 @@ export default class SlicedArray {
|
|||||||
const first = this.slices[0];
|
const first = this.slices[0];
|
||||||
if(!first.length) {
|
if(!first.length) {
|
||||||
first.push(...slice);
|
first.push(...slice);
|
||||||
return;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lowerBound = slice[slice.length - 1];
|
const lowerBound = slice[slice.length - 1];
|
||||||
const upperBound = slice[0];
|
const upperBound = slice[0];
|
||||||
|
|
||||||
let foundSlice: Slice, lowerIndex = -1, upperIndex = -1;
|
let foundSlice: Slice, lowerIndex = -1, upperIndex = -1, foundSliceIndex = 0;
|
||||||
for(let i = 0; i < this.slices.length; ++i) {
|
for(; foundSliceIndex < this.slices.length; ++foundSliceIndex) {
|
||||||
foundSlice = this.slices[i];
|
foundSlice = this.slices[foundSliceIndex];
|
||||||
lowerIndex = foundSlice.indexOf(lowerBound);
|
lowerIndex = foundSlice.indexOf(lowerBound);
|
||||||
upperIndex = foundSlice.indexOf(upperBound);
|
upperIndex = foundSlice.indexOf(upperBound);
|
||||||
|
|
||||||
@ -173,29 +212,38 @@ export default class SlicedArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.slices.splice(insertIndex, 0, this.constructSlice(...slice));
|
this.slices.splice(insertIndex, 0, this.constructSlice(...slice));
|
||||||
|
foundSliceIndex = insertIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.flatten();
|
if(flatten) {
|
||||||
|
return this.flatten(foundSliceIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private flatten() {
|
private flatten(foundSliceIndex: number) {
|
||||||
if(this.slices.length < 2) {
|
if(this.slices.length >= 2) {
|
||||||
return;
|
for(let i = 0, length = this.slices.length; i < (length - 1); ++i) {
|
||||||
}
|
const prevSlice = this.slices[i];
|
||||||
|
const nextSlice = this.slices[i + 1];
|
||||||
|
|
||||||
|
const upperIndex = prevSlice.indexOf(nextSlice[0]);
|
||||||
|
if(upperIndex !== -1) {
|
||||||
|
prevSlice.setEnd(nextSlice.end);
|
||||||
|
this.slices.splice(i + 1, 1);
|
||||||
|
|
||||||
for(let i = 0, length = this.slices.length; i < (length - 1); ++i) {
|
if(i < foundSliceIndex) {
|
||||||
const prevSlice = this.slices[i];
|
--foundSliceIndex;
|
||||||
const nextSlice = this.slices[i + 1];
|
}
|
||||||
|
|
||||||
const upperIndex = prevSlice.indexOf(nextSlice[0]);
|
--length; // respect array bounds
|
||||||
if(upperIndex !== -1) {
|
--i; // repeat from the same place
|
||||||
prevSlice.setEnd(nextSlice.end);
|
|
||||||
this.slices.splice(i + 1, 1);
|
this.insertSlice(nextSlice, false);
|
||||||
length--;
|
}
|
||||||
|
|
||||||
this.insertSlice(nextSlice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.slices[foundSliceIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
// *
|
// *
|
||||||
@ -217,7 +265,7 @@ export default class SlicedArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public findSlice(item: ItemType) {
|
public findSlice(item: ItemType) {
|
||||||
for(let i = 0; i < this.slices.length; ++i) {
|
for(let i = 0, length = this.slices.length; i < length; ++i) {
|
||||||
const slice = this.slices[i];
|
const slice = this.slices[i];
|
||||||
const index = slice.indexOf(item);
|
const index = slice.indexOf(item);
|
||||||
if(index !== -1) {
|
if(index !== -1) {
|
||||||
@ -295,6 +343,8 @@ export default class SlicedArray {
|
|||||||
const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;
|
const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;
|
||||||
const bottomWasMeantToLoad = Math.abs(add_offset);
|
const bottomWasMeantToLoad = Math.abs(add_offset);
|
||||||
|
|
||||||
|
// can use 'slice' here to check because if it's end, then 'sliced' is out of 'slice'
|
||||||
|
// useful when there is only 1 message in chat on its reopening
|
||||||
const topFulfilled = (slice.length - sliceOffset) >= topWasMeantToLoad || (slice.isEnd(SliceEnd.Top) ? (sliced.setEnd(SliceEnd.Top), true) : false);
|
const topFulfilled = (slice.length - sliceOffset) >= topWasMeantToLoad || (slice.isEnd(SliceEnd.Top) ? (sliced.setEnd(SliceEnd.Top), true) : false);
|
||||||
const bottomFulfilled = (sliceOffset - bottomWasMeantToLoad) >= 0 || (slice.isEnd(SliceEnd.Bottom) ? (sliced.setEnd(SliceEnd.Bottom), true) : false);
|
const bottomFulfilled = (sliceOffset - bottomWasMeantToLoad) >= 0 || (slice.isEnd(SliceEnd.Bottom) ? (sliced.setEnd(SliceEnd.Bottom), true) : false);
|
||||||
|
|
||||||
@ -308,19 +358,40 @@ export default class SlicedArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public unshift(...items: ItemType[]) {
|
public unshift(...items: ItemType[]) {
|
||||||
this.first.unshift(...items);
|
let slice = this.first;
|
||||||
|
if(!slice.length) {
|
||||||
|
slice.setEnd(SliceEnd.Bottom);
|
||||||
|
} else if(!slice.isEnd(SliceEnd.Bottom)) {
|
||||||
|
slice = this.constructSlice();
|
||||||
|
slice.setEnd(SliceEnd.Bottom);
|
||||||
|
this.slices.unshift(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.unshift(...items);
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(...items: ItemType[]) {
|
public push(...items: ItemType[]) {
|
||||||
this.last.push(...items);
|
let slice = this.last;
|
||||||
|
if(!slice.length) {
|
||||||
|
slice.setEnd(SliceEnd.Top);
|
||||||
|
} else if(!slice.isEnd(SliceEnd.Top)) {
|
||||||
|
slice = this.constructSlice();
|
||||||
|
slice.setEnd(SliceEnd.Top);
|
||||||
|
this.slices.push(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.push(...items);
|
||||||
}
|
}
|
||||||
|
|
||||||
public delete(item: ItemType) {
|
public delete(item: ItemType) {
|
||||||
const found = this.findSlice(item);
|
const found = this.findSlice(item);
|
||||||
if(found) {
|
if(found) {
|
||||||
found.slice.splice(found.index, 1);
|
found.slice.splice(found.index, 1);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(window as any).slicedArray = new SlicedArray();
|
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.SlicedArray = SlicedArray);
|
||||||
|
18
src/lang.ts
18
src/lang.ts
@ -133,6 +133,7 @@ const lang = {
|
|||||||
"AttachAudio": "Voice message",
|
"AttachAudio": "Voice message",
|
||||||
"AttachRound": "Video message",
|
"AttachRound": "Video message",
|
||||||
"AttachGame": "Game",
|
"AttachGame": "Game",
|
||||||
|
"Bot": "bot",
|
||||||
//"ChannelJoined": "You joined this channel",
|
//"ChannelJoined": "You joined this channel",
|
||||||
"ChannelMegaJoined": "You joined this group",
|
"ChannelMegaJoined": "You joined this group",
|
||||||
"Channel.DescriptionPlaceholder": "Description (optional)",
|
"Channel.DescriptionPlaceholder": "Description (optional)",
|
||||||
@ -408,6 +409,14 @@ const lang = {
|
|||||||
"OpenUrlAlert2": "Do you want to open %1$s?",
|
"OpenUrlAlert2": "Do you want to open %1$s?",
|
||||||
"FilterNoChatsToDisplay": "Folder is empty",
|
"FilterNoChatsToDisplay": "Folder is empty",
|
||||||
"FilterNoChatsToDisplayInfo": "No chats currently belong to this folder.",
|
"FilterNoChatsToDisplayInfo": "No chats currently belong to this folder.",
|
||||||
|
"SupportStatus": "support",
|
||||||
|
"Lately": "last seen recently",
|
||||||
|
"WithinAWeek": "last seen within a week",
|
||||||
|
"WithinAMonth": "last seen within a month",
|
||||||
|
"ALongTimeAgo": "last seen a long time ago",
|
||||||
|
"Online": "online",
|
||||||
|
"MessageScheduleSend": "Send Now",
|
||||||
|
"MessageScheduleEditTime": "Reschedule",
|
||||||
|
|
||||||
// * macos
|
// * macos
|
||||||
"AccountSettings.Filters": "Chat Folders",
|
"AccountSettings.Filters": "Chat Folders",
|
||||||
@ -420,8 +429,6 @@ const lang = {
|
|||||||
"Channel.UsernameAboutGroup": "People can share this link with others and find your group using Telegram search.",
|
"Channel.UsernameAboutGroup": "People can share this link with others and find your group using Telegram search.",
|
||||||
"Chat.CopySelectedText": "Copy Selected Text",
|
"Chat.CopySelectedText": "Copy Selected Text",
|
||||||
"Chat.Confirm.Unpin": "Would you like to unpin this message?",
|
"Chat.Confirm.Unpin": "Would you like to unpin this message?",
|
||||||
"Chat.Context.Scheduled.SendNow": "Send Now",
|
|
||||||
"Chat.Context.Scheduled.Reschedule": "Re-schedule",
|
|
||||||
"Chat.Date.ScheduledFor": "Scheduled for %@",
|
"Chat.Date.ScheduledFor": "Scheduled for %@",
|
||||||
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
|
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
|
||||||
"Chat.Date.ScheduledForToday": "Scheduled for today",
|
"Chat.Date.ScheduledForToday": "Scheduled for today",
|
||||||
@ -559,12 +566,7 @@ const lang = {
|
|||||||
"Peer.Activity.Chat.Multi.SendingFile1": "%@ and %d others are sending files",
|
"Peer.Activity.Chat.Multi.SendingFile1": "%@ and %d others are sending files",
|
||||||
"Peer.ServiceNotifications": "service notifications",
|
"Peer.ServiceNotifications": "service notifications",
|
||||||
"Peer.RepliesNotifications": "Reply Notifications",
|
"Peer.RepliesNotifications": "Reply Notifications",
|
||||||
"Peer.Status.online": "online",
|
|
||||||
"Peer.Status.recently": "last seen recently",
|
|
||||||
"Peer.Status.justNow": "last seen just now",
|
"Peer.Status.justNow": "last seen just now",
|
||||||
"Peer.Status.lastWeek": "last seen within a week",
|
|
||||||
"Peer.Status.lastMonth": "last seen within a month",
|
|
||||||
"Peer.Status.longTimeAgo": "last seen a long time ago",
|
|
||||||
"Peer.Status.Today": "today",
|
"Peer.Status.Today": "today",
|
||||||
"Peer.Status.Yesterday": "yesterday",
|
"Peer.Status.Yesterday": "yesterday",
|
||||||
"Peer.Status.LastSeenAt": "last seen %@ at %@",
|
"Peer.Status.LastSeenAt": "last seen %@ at %@",
|
||||||
@ -614,8 +616,6 @@ const lang = {
|
|||||||
"one_value": "Send Video",
|
"one_value": "Send Video",
|
||||||
"other_value": "Send %d Videos"
|
"other_value": "Send %d Videos"
|
||||||
},
|
},
|
||||||
"Presence.bot": "bot",
|
|
||||||
"Presence.Support": "support",
|
|
||||||
"PrivacyAndSecurity.Item.On": "On",
|
"PrivacyAndSecurity.Item.On": "On",
|
||||||
"PrivacyAndSecurity.Item.Off": "Off",
|
"PrivacyAndSecurity.Item.Off": "Off",
|
||||||
"PrivacySettings.VoiceCalls": "Calls",
|
"PrivacySettings.VoiceCalls": "Calls",
|
||||||
|
@ -1357,6 +1357,7 @@ export class AppDialogsManager {
|
|||||||
|
|
||||||
const span = document.createElement('span');
|
const span = document.createElement('span');
|
||||||
span.classList.add('user-last-message');
|
span.classList.add('user-last-message');
|
||||||
|
span.setAttribute('dir', 'auto');
|
||||||
|
|
||||||
//captionDiv.append(titleSpan);
|
//captionDiv.append(titleSpan);
|
||||||
//captionDiv.append(span);
|
//captionDiv.append(span);
|
||||||
|
@ -50,6 +50,7 @@ import { copy, getObjectKeysAndSort } from '../../helpers/object';
|
|||||||
import { getFilesFromEvent } from '../../helpers/files';
|
import { getFilesFromEvent } from '../../helpers/files';
|
||||||
import PeerTitle from '../../components/peerTitle';
|
import PeerTitle from '../../components/peerTitle';
|
||||||
import PopupPeer from '../../components/popups/peer';
|
import PopupPeer from '../../components/popups/peer';
|
||||||
|
import { SliceEnd } from '../../helpers/slicedArray';
|
||||||
|
|
||||||
//console.log('appImManager included33!');
|
//console.log('appImManager included33!');
|
||||||
|
|
||||||
@ -445,10 +446,11 @@ export class AppImManager {
|
|||||||
return;
|
return;
|
||||||
} else if(e.code === 'ArrowUp') {
|
} else if(e.code === 'ArrowUp') {
|
||||||
if(!chat.input.editMsgId && chat.input.isInputEmpty()) {
|
if(!chat.input.editMsgId && chat.input.isInputEmpty()) {
|
||||||
const history = appMessagesManager.getHistoryStorage(chat.peerId, chat.threadId);
|
const historyStorage = appMessagesManager.getHistoryStorage(chat.peerId, chat.threadId);
|
||||||
if(history.history.length) {
|
const slice = historyStorage.history.slice;
|
||||||
|
if(slice.isEnd(SliceEnd.Bottom) && slice.length) {
|
||||||
let goodMid: number;
|
let goodMid: number;
|
||||||
for(const mid of history.history.slice) {
|
for(const mid of slice) {
|
||||||
const message = chat.getMessage(mid);
|
const message = chat.getMessage(mid);
|
||||||
const good = this.myId === chat.peerId ? message.fromId === this.myId : message.pFlags.out;
|
const good = this.myId === chat.peerId ? message.fromId === this.myId : message.pFlags.out;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import ProgressivePreloader from "../../components/preloader";
|
|||||||
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
||||||
import { tsNow } from "../../helpers/date";
|
import { tsNow } from "../../helpers/date";
|
||||||
import { createPosterForVideo } from "../../helpers/files";
|
import { createPosterForVideo } from "../../helpers/files";
|
||||||
import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object";
|
import { copy, getObjectKeysAndSort } from "../../helpers/object";
|
||||||
import { randomLong } from "../../helpers/random";
|
import { randomLong } from "../../helpers/random";
|
||||||
import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string";
|
import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string";
|
||||||
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo } from "../../layer";
|
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo } from "../../layer";
|
||||||
@ -168,7 +168,7 @@ export class AppMessagesManager {
|
|||||||
public migratedFromTo: {[peerId: number]: number} = {};
|
public migratedFromTo: {[peerId: number]: number} = {};
|
||||||
public migratedToFrom: {[peerId: number]: number} = {};
|
public migratedToFrom: {[peerId: number]: number} = {};
|
||||||
|
|
||||||
public newMessagesHandlePromise = 0;
|
public newMessagesHandleTimeout = 0;
|
||||||
public newMessagesToHandle: {[peerId: string]: Set<number>} = {};
|
public newMessagesToHandle: {[peerId: string]: Set<number>} = {};
|
||||||
public newDialogsHandlePromise: Promise<any>;
|
public newDialogsHandlePromise: Promise<any>;
|
||||||
public newDialogsToHandle: {[peerId: string]: Dialog} = {};
|
public newDialogsToHandle: {[peerId: string]: Dialog} = {};
|
||||||
@ -1301,13 +1301,16 @@ export class AppMessagesManager {
|
|||||||
/* if(options.threadId && this.threadsStorage[peerId]) {
|
/* if(options.threadId && this.threadsStorage[peerId]) {
|
||||||
delete this.threadsStorage[peerId][options.threadId];
|
delete this.threadsStorage[peerId][options.threadId];
|
||||||
} */
|
} */
|
||||||
if(options.threadId) {
|
const storages: HistoryStorage[] = [
|
||||||
const historyStorage = this.getHistoryStorage(peerId, options.threadId);
|
this.getHistoryStorage(peerId),
|
||||||
historyStorage.history.unshift(messageId);
|
options.threadId ? this.getHistoryStorage(peerId, options.threadId) : undefined
|
||||||
}
|
];
|
||||||
|
|
||||||
const historyStorage = this.getHistoryStorage(peerId);
|
for(const storage of storages) {
|
||||||
historyStorage.history.unshift(messageId);
|
if(storage) {
|
||||||
|
storage.history.unshift(messageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//if(!options.isGroupedItem) {
|
//if(!options.isGroupedItem) {
|
||||||
this.saveMessages([message], {storage, isOutgoing: true});
|
this.saveMessages([message], {storage, isOutgoing: true});
|
||||||
@ -1514,7 +1517,6 @@ export class AppMessagesManager {
|
|||||||
if(pendingData) {
|
if(pendingData) {
|
||||||
const {peerId, tempId, storage} = pendingData;
|
const {peerId, tempId, storage} = pendingData;
|
||||||
const historyStorage = this.getHistoryStorage(peerId);
|
const historyStorage = this.getHistoryStorage(peerId);
|
||||||
const pos = historyStorage.history.findSlice(tempId);
|
|
||||||
|
|
||||||
apiUpdatesManager.processUpdateMessage({
|
apiUpdatesManager.processUpdateMessage({
|
||||||
_: 'updateShort',
|
_: 'updateShort',
|
||||||
@ -1524,9 +1526,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(pos) {
|
historyStorage.history.delete(tempId);
|
||||||
pos.slice.splice(pos.index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this.pendingByRandomId[randomId];
|
delete this.pendingByRandomId[randomId];
|
||||||
delete storage[tempId];
|
delete storage[tempId];
|
||||||
@ -1627,7 +1627,7 @@ export class AppMessagesManager {
|
|||||||
// ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:
|
// ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:
|
||||||
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???
|
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???
|
||||||
// ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0
|
// ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0
|
||||||
return apiManager.invokeApi('messages.getDialogs', {
|
return apiManager.invokeApiSingle('messages.getDialogs', {
|
||||||
folder_id: folderId,
|
folder_id: folderId,
|
||||||
offset_date: offsetDate,
|
offset_date: offsetDate,
|
||||||
offset_id: offsetId,
|
offset_id: offsetId,
|
||||||
@ -3271,7 +3271,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getDiscussionMessage(peerId: number, mid: number) {
|
public getDiscussionMessage(peerId: number, mid: number) {
|
||||||
return apiManager.invokeApi('messages.getDiscussionMessage', {
|
return apiManager.invokeApiSingle('messages.getDiscussionMessage', {
|
||||||
peer: appPeersManager.getInputPeerById(peerId),
|
peer: appPeersManager.getInputPeerById(peerId),
|
||||||
msg_id: this.getServerMessageId(mid)
|
msg_id: this.getServerMessageId(mid)
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
@ -3295,9 +3295,20 @@ export class AppMessagesManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleNewMessage(peerId: number, mid: number) {
|
||||||
|
if(this.newMessagesToHandle[peerId] === undefined) {
|
||||||
|
this.newMessagesToHandle[peerId] = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newMessagesToHandle[peerId].add(mid);
|
||||||
|
if(!this.newMessagesHandleTimeout) {
|
||||||
|
this.newMessagesHandleTimeout = window.setTimeout(this.handleNewMessages, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleNewMessages = () => {
|
handleNewMessages = () => {
|
||||||
clearTimeout(this.newMessagesHandlePromise);
|
clearTimeout(this.newMessagesHandleTimeout);
|
||||||
this.newMessagesHandlePromise = 0;
|
this.newMessagesHandleTimeout = 0;
|
||||||
|
|
||||||
rootScope.broadcast('history_multiappend', this.newMessagesToHandle);
|
rootScope.broadcast('history_multiappend', this.newMessagesToHandle);
|
||||||
this.newMessagesToHandle = {};
|
this.newMessagesToHandle = {};
|
||||||
@ -3404,6 +3415,7 @@ export class AppMessagesManager {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: cancel notification by peer when this function is being called
|
||||||
public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) {
|
public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) {
|
||||||
//return Promise.resolve();
|
//return Promise.resolve();
|
||||||
// console.trace('start read')
|
// console.trace('start read')
|
||||||
@ -3452,7 +3464,7 @@ export class AppMessagesManager {
|
|||||||
_: 'updateReadChannelInbox',
|
_: 'updateReadChannelInbox',
|
||||||
max_id: maxId,
|
max_id: maxId,
|
||||||
channel_id: -peerId
|
channel_id: -peerId
|
||||||
}
|
} as Update.updateReadChannelInbox
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if(!historyStorage.readPromise) {
|
if(!historyStorage.readPromise) {
|
||||||
@ -3477,21 +3489,10 @@ export class AppMessagesManager {
|
|||||||
_: 'updateReadHistoryInbox',
|
_: 'updateReadHistoryInbox',
|
||||||
max_id: maxId,
|
max_id: maxId,
|
||||||
peer: appPeersManager.getOutputPeer(peerId)
|
peer: appPeersManager.getOutputPeer(peerId)
|
||||||
}
|
} as Update.updateReadHistoryInbox
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!threadId && historyStorage && historyStorage.history.length) {
|
|
||||||
const slice = historyStorage.history.slice;
|
|
||||||
for(const mid of slice) {
|
|
||||||
const message = this.getMessageByPeer(peerId, mid);
|
|
||||||
if(message && !message.pFlags.out) {
|
|
||||||
message.pFlags.unread = false;
|
|
||||||
appNotificationsManager.cancel('msg' + mid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appNotificationsManager.soundReset(appPeersManager.getPeerString(peerId));
|
appNotificationsManager.soundReset(appPeersManager.getPeerString(peerId));
|
||||||
|
|
||||||
if(historyStorage.readPromise) {
|
if(historyStorage.readPromise) {
|
||||||
@ -3534,7 +3535,7 @@ export class AppMessagesManager {
|
|||||||
_: 'updateChannelReadMessagesContents',
|
_: 'updateChannelReadMessagesContents',
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
messages: msgIds
|
messages: msgIds
|
||||||
}
|
} as Update.updateChannelReadMessagesContents
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -3548,7 +3549,7 @@ export class AppMessagesManager {
|
|||||||
messages: msgIds,
|
messages: msgIds,
|
||||||
pts: affectedMessages.pts,
|
pts: affectedMessages.pts,
|
||||||
pts_count: affectedMessages.pts_count
|
pts_count: affectedMessages.pts_count
|
||||||
}
|
} as Update.updateReadMessagesContents
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -3694,14 +3695,19 @@ export class AppMessagesManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const history = historyStorage.history.slice;
|
// * catch situation with disconnect. if message's id is lower than we already have (in bottom end slice), will sort it
|
||||||
const topMsgId = history[0];
|
const firstSlice = historyStorage.history.first;
|
||||||
history.unshift(message.mid);
|
if(firstSlice.isEnd(SliceEnd.Bottom)) {
|
||||||
if(message.mid < topMsgId) {
|
let i = 0;
|
||||||
//this.log.error('this should\'nt have happenned!', message, history);
|
for(const length = firstSlice.length; i < length; ++i) {
|
||||||
history.sort((a, b) => {
|
if(message.mid > firstSlice[i]) {
|
||||||
return b - a;
|
break;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
firstSlice.splice(i, 0, message.mid);
|
||||||
|
} else {
|
||||||
|
historyStorage.history.unshift(message.mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(historyStorage.count !== null) {
|
if(historyStorage.count !== null) {
|
||||||
@ -3717,14 +3723,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!pendingMessage) {
|
if(!pendingMessage) {
|
||||||
if(this.newMessagesToHandle[peerId] === undefined) {
|
this.handleNewMessage(peerId, message.mid);
|
||||||
this.newMessagesToHandle[peerId] = new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.newMessagesToHandle[peerId].add(message.mid);
|
|
||||||
if(!this.newMessagesHandlePromise) {
|
|
||||||
this.newMessagesHandlePromise = window.setTimeout(this.handleNewMessages, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isLocalThreadUpdate) {
|
if(isLocalThreadUpdate) {
|
||||||
@ -4471,7 +4470,7 @@ export class AppMessagesManager {
|
|||||||
return Promise.resolve(Object.keys(storage).map(id => +id));
|
return Promise.resolve(Object.keys(storage).map(id => +id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return apiManager.invokeApi('messages.getScheduledHistory', {
|
return apiManager.invokeApiSingle('messages.getScheduledHistory', {
|
||||||
peer: appPeersManager.getInputPeerById(peerId),
|
peer: appPeersManager.getInputPeerById(peerId),
|
||||||
hash: 0
|
hash: 0
|
||||||
}).then(historyResult => {
|
}).then(historyResult => {
|
||||||
@ -4506,6 +4505,36 @@ export class AppMessagesManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isFetchIntervalNeeded(peerId: number) {
|
||||||
|
return peerId < 0 && !appChatsManager.isInChat(peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getNewHistory(peerId: number, threadId?: number) {
|
||||||
|
if(!this.isFetchIntervalNeeded(peerId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const historyStorage = this.getHistoryStorage(peerId, threadId);
|
||||||
|
const slice = historyStorage.history.slice;
|
||||||
|
if(!slice.isEnd(SliceEnd.Bottom)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete historyStorage.maxId;
|
||||||
|
slice.unsetEnd(SliceEnd.Bottom);
|
||||||
|
|
||||||
|
let historyResult = this.getHistory(peerId, slice[0], 0, 50, threadId);
|
||||||
|
if(historyResult instanceof Promise) {
|
||||||
|
historyResult = await historyResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let i = 0, length = historyResult.history.length; i < length; ++i) {
|
||||||
|
this.handleNewMessage(peerId, historyResult.history[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return historyStorage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * https://core.telegram.org/api/offsets, offset_id is inclusive
|
* * https://core.telegram.org/api/offsets, offset_id is inclusive
|
||||||
*/
|
*/
|
||||||
@ -4567,7 +4596,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const haveSlice = historyStorage.history.sliceMe(maxId, offset, limit);
|
const haveSlice = historyStorage.history.sliceMe(maxId, offset, limit);
|
||||||
if(haveSlice && (haveSlice.slice.length === limit || (haveSlice.fulfilled & SliceEnd.Both))) {
|
if(haveSlice && (haveSlice.slice.length === limit || (haveSlice.fulfilled & SliceEnd.Both) === SliceEnd.Both)) {
|
||||||
return {
|
return {
|
||||||
count: historyStorage.count,
|
count: historyStorage.count,
|
||||||
history: haveSlice.slice,
|
history: haveSlice.slice,
|
||||||
@ -4587,10 +4616,15 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
public fillHistoryStorage(peerId: number, offset_id: number, limit: number, add_offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> {
|
public fillHistoryStorage(peerId: number, offset_id: number, limit: number, add_offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> {
|
||||||
return this.requestHistory(peerId, offset_id, limit, add_offset, undefined, threadId).then((historyResult) => {
|
return this.requestHistory(peerId, offset_id, limit, add_offset, undefined, threadId).then((historyResult) => {
|
||||||
historyStorage.count = (historyResult as MessagesMessages.messagesMessagesSlice).count || historyResult.messages.length;
|
const {offset_id_offset, count, messages} = historyResult as MessagesMessages.messagesMessagesSlice;
|
||||||
|
|
||||||
const offsetIdOffset = (historyResult as MessagesMessages.messagesMessagesSlice).offset_id_offset || 0;
|
historyStorage.count = count || messages.length;
|
||||||
const isTopEnd = offsetIdOffset >= (historyStorage.count - limit) || historyStorage.count < (limit + add_offset);
|
const offsetIdOffset = offset_id_offset || 0;
|
||||||
|
|
||||||
|
const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;
|
||||||
|
|
||||||
|
const isTopEnd = offsetIdOffset >= (historyStorage.count - topWasMeantToLoad) || historyStorage.count < topWasMeantToLoad;
|
||||||
|
const isBottomEnd = !offsetIdOffset || (add_offset < 0 && (offsetIdOffset + add_offset) <= 0);
|
||||||
|
|
||||||
/* if(!maxId && historyResult.messages.length) {
|
/* if(!maxId && historyResult.messages.length) {
|
||||||
maxId = this.incrementMessageId((historyResult.messages[0] as MyMessage).mid, 1);
|
maxId = this.incrementMessageId((historyResult.messages[0] as MyMessage).mid, 1);
|
||||||
@ -4598,13 +4632,14 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
const wasTotalCount = historyStorage.history.length; */
|
const wasTotalCount = historyStorage.history.length; */
|
||||||
|
|
||||||
historyResult.messages.forEach((message) => {
|
const mids = messages.map((message) => {
|
||||||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||||||
rootScope.broadcast('history_reply_markup', {peerId});
|
rootScope.broadcast('history_reply_markup', {peerId});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (message as MyMessage).mid;
|
||||||
});
|
});
|
||||||
|
|
||||||
const mids = historyResult.messages.map((message) => (message as MyMessage).mid);
|
|
||||||
// * add bound manually.
|
// * add bound manually.
|
||||||
// * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id')
|
// * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id')
|
||||||
if(offset_id && !mids.includes(offset_id) && offsetIdOffset < historyStorage.count) {
|
if(offset_id && !mids.includes(offset_id) && offsetIdOffset < historyStorage.count) {
|
||||||
@ -4618,10 +4653,16 @@ export class AppMessagesManager {
|
|||||||
mids.splice(i, 0, offset_id);
|
mids.splice(i, 0, offset_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
historyStorage.history.insertSlice(mids);
|
const slice = historyStorage.history.insertSlice(mids);
|
||||||
|
if(slice) {
|
||||||
if(isTopEnd) {
|
if(isTopEnd) {
|
||||||
historyStorage.history.last.setEnd(SliceEnd.Top);
|
slice.setEnd(SliceEnd.Top);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isBottomEnd) {
|
||||||
|
slice.setEnd(SliceEnd.Bottom);
|
||||||
|
historyStorage.maxId = slice[0]; // ! WARNING
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* const isBackLimit = offset < 0 && -offset !== fullLimit;
|
/* const isBackLimit = offset < 0 && -offset !== fullLimit;
|
||||||
@ -4681,7 +4722,7 @@ export class AppMessagesManager {
|
|||||||
options.msg_id = this.getServerMessageId(threadId) || 0;
|
options.msg_id = this.getServerMessageId(threadId) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise: ReturnType<AppMessagesManager['requestHistory']> = apiManager.invokeApi(threadId ? 'messages.getReplies' : 'messages.getHistory', options, {
|
const promise: ReturnType<AppMessagesManager['requestHistory']> = apiManager.invokeApiSingle(threadId ? 'messages.getReplies' : 'messages.getHistory', options, {
|
||||||
//timeout: APITIMEOUT,
|
//timeout: APITIMEOUT,
|
||||||
noErrorBox: true
|
noErrorBox: true
|
||||||
}) as any;
|
}) as any;
|
||||||
@ -4699,21 +4740,24 @@ export class AppMessagesManager {
|
|||||||
apiUpdatesManager.addChannelState(-peerId, (historyResult as MessagesMessages.messagesChannelMessages).pts);
|
apiUpdatesManager.addChannelState(-peerId, (historyResult as MessagesMessages.messagesChannelMessages).pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = historyResult.messages.length;
|
let length = historyResult.messages.length, count = (historyResult as MessagesMessages.messagesMessagesSlice).count;
|
||||||
if(length && historyResult.messages[length - 1].deleted) {
|
if(length && historyResult.messages[length - 1].deleted) {
|
||||||
historyResult.messages.splice(length - 1, 1);
|
historyResult.messages.splice(length - 1, 1);
|
||||||
length--;
|
length--;
|
||||||
(historyResult as MessagesMessages.messagesMessagesSlice).count--;
|
count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// will load more history if last message is album grouped (because it can be not last item)
|
// will load more history if last message is album grouped (because it can be not last item)
|
||||||
const historyStorage = this.getHistoryStorage(peerId, threadId);
|
|
||||||
// historyResult.messages: desc sorted
|
// historyResult.messages: desc sorted
|
||||||
if(length && (historyResult.messages[length - 1] as Message.message).grouped_id
|
const historyStorage = this.getHistoryStorage(peerId, threadId);
|
||||||
&& (historyStorage.history.length + historyResult.messages.length) < (historyResult as MessagesMessages.messagesMessagesSlice).count) {
|
const oldestMessage: Message.message = historyResult.messages[length - 1] as any;
|
||||||
return this.requestHistory(peerId, (historyResult.messages[length - 1] as Message.message).mid, 10, 0, offsetDate, threadId).then((_historyResult) => {
|
if(length && oldestMessage.grouped_id) {
|
||||||
return historyResult;
|
const foundSlice = historyStorage.history.findSlice(oldestMessage.mid);
|
||||||
});
|
if(foundSlice && (foundSlice.slice.length + historyResult.messages.length) < count) {
|
||||||
|
return this.requestHistory(peerId, oldestMessage.mid, 10, 0, offsetDate, threadId).then((_historyResult) => {
|
||||||
|
return historyResult;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return historyResult;
|
return historyResult;
|
||||||
@ -4760,12 +4804,12 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
|
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
|
||||||
if(+peerId < 0 && appPeersManager.isChannel(+peerId)) {
|
if(+peerId < 0 && appPeersManager.isChannel(+peerId)) {
|
||||||
promise = apiManager.invokeApi('channels.getMessages', {
|
promise = apiManager.invokeApiSingle('channels.getMessages', {
|
||||||
channel: appChatsManager.getChannelInput(-+peerId),
|
channel: appChatsManager.getChannelInput(-+peerId),
|
||||||
id: msgIds
|
id: msgIds
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
promise = apiManager.invokeApi('messages.getMessages', {
|
promise = apiManager.invokeApiSingle('messages.getMessages', {
|
||||||
id: msgIds
|
id: msgIds
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import type { DownloadOptions } from "../mtproto/apiFileManager";
|
|||||||
import { bytesFromHex } from "../../helpers/bytes";
|
import { bytesFromHex } from "../../helpers/bytes";
|
||||||
import { CancellablePromise } from "../../helpers/cancellablePromise";
|
import { CancellablePromise } from "../../helpers/cancellablePromise";
|
||||||
import { getFileNameByLocation } from "../../helpers/fileName";
|
import { getFileNameByLocation } from "../../helpers/fileName";
|
||||||
import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from "../../helpers/object";
|
import { safeReplaceArrayInObject, isObject } from "../../helpers/object";
|
||||||
import { isSafari } from "../../helpers/userAgent";
|
import { isSafari } from "../../helpers/userAgent";
|
||||||
import { InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from "../../layer";
|
import { InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from "../../layer";
|
||||||
import apiManager from "../mtproto/mtprotoworker";
|
import apiManager from "../mtproto/mtprotoworker";
|
||||||
|
@ -431,7 +431,7 @@ export class AppUsersManager {
|
|||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
if(this.isBot(userId)) {
|
if(this.isBot(userId)) {
|
||||||
key = 'Presence.bot';
|
key = 'Bot';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,23 +442,23 @@ export class AppUsersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(user.pFlags.support) {
|
if(user.pFlags.support) {
|
||||||
key = 'Presence.Support';
|
key = 'SupportStatus';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(user.status?._) {
|
switch(user.status?._) {
|
||||||
case 'userStatusRecently': {
|
case 'userStatusRecently': {
|
||||||
key = 'Peer.Status.recently';
|
key = 'Lately';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'userStatusLastWeek': {
|
case 'userStatusLastWeek': {
|
||||||
key = 'Peer.Status.lastWeek';
|
key = 'WithinAWeek';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'userStatusLastMonth': {
|
case 'userStatusLastMonth': {
|
||||||
key = 'Peer.Status.lastMonth';
|
key = 'WithinAMonth';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,12 +487,12 @@ export class AppUsersManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'userStatusOnline': {
|
case 'userStatusOnline': {
|
||||||
key = 'Peer.Status.online';
|
key = 'Online';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
key = 'Peer.Status.longTimeAgo';
|
key = 'ALongTimeAgo';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import { forEachReverse, insertInDescendSortedArray } from "../../helpers/array"
|
|||||||
import rootScope from "../rootScope";
|
import rootScope from "../rootScope";
|
||||||
import { safeReplaceObject } from "../../helpers/object";
|
import { safeReplaceObject } from "../../helpers/object";
|
||||||
import { AppStateManager } from "../appManagers/appStateManager";
|
import { AppStateManager } from "../appManagers/appStateManager";
|
||||||
|
import { SliceEnd } from "../../helpers/slicedArray";
|
||||||
|
|
||||||
export default class DialogsStorage {
|
export default class DialogsStorage {
|
||||||
private storage: AppStateManager['storages']['dialogs'];
|
private storage: AppStateManager['storages']['dialogs'];
|
||||||
@ -490,13 +491,17 @@ export default class DialogsStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId);
|
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId);
|
||||||
|
const slice = historyStorage.history.slice;
|
||||||
/* if(historyStorage === undefined) { // warning
|
/* if(historyStorage === undefined) { // warning
|
||||||
historyStorage.history.push(mid);
|
historyStorage.history.push(mid);
|
||||||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||||||
rootScope.broadcast('history_reply_markup', {peerId});
|
rootScope.broadcast('history_reply_markup', {peerId});
|
||||||
}
|
}
|
||||||
} else */if(!historyStorage.history.slice.length) {
|
} else */if(!slice.length) {
|
||||||
historyStorage.history.unshift(mid);
|
historyStorage.history.unshift(mid);
|
||||||
|
} else if(!slice.isEnd(SliceEnd.Bottom)) { // * this will probably never happen, however, if it does, then it will fix slice with top_message
|
||||||
|
const slice = historyStorage.history.insertSlice([mid]);
|
||||||
|
slice.setEnd(SliceEnd.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
historyStorage.maxId = mid;
|
historyStorage.maxId = mid;
|
||||||
|
@ -384,7 +384,7 @@
|
|||||||
--size: 54px;
|
--size: 54px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
width: var(--size) !important;
|
width: var(--size);
|
||||||
line-height: var(--size);
|
line-height: var(--size);
|
||||||
|
|
||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
|
@ -995,6 +995,7 @@ $bubble-margin: .25rem;
|
|||||||
position: relative !important;
|
position: relative !important;
|
||||||
height: 0px !important;
|
height: 0px !important;
|
||||||
visibility: hidden !important;
|
visibility: hidden !important;
|
||||||
|
float: none;
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
visibility: hidden !important;
|
visibility: hidden !important;
|
||||||
@ -1250,6 +1251,8 @@ $bubble-margin: .25rem;
|
|||||||
/* display: inline-flex;
|
/* display: inline-flex;
|
||||||
align-items: center; */
|
align-items: center; */
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
direction: ltr;
|
||||||
|
float: right; // * rtl fix
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 1.15rem;
|
font-size: 1.15rem;
|
||||||
@ -1284,6 +1287,10 @@ $bubble-margin: .25rem;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.webpage .time {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
.video-time {
|
.video-time {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
@ -1893,7 +1900,7 @@ $bubble-margin: .25rem;
|
|||||||
margin-left: -4px;
|
margin-left: -4px;
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
color: var(--message-out-primary-color);
|
color: var(--message-out-status-color);
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1902,9 +1909,14 @@ $bubble-margin: .25rem;
|
|||||||
//vertical-align: middle;
|
//vertical-align: middle;
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
line-height: 16px; // of message
|
line-height: 16px; // of message
|
||||||
|
color: var(--message-out-primary-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-message-empty .time .inner {
|
||||||
|
color: var(--message-out-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* &.is-message-empty .time:after {
|
/* &.is-message-empty .time:after {
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
} */
|
} */
|
||||||
@ -1954,7 +1966,7 @@ $bubble-margin: .25rem;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-time, &-subtitle {
|
&-time, &-subtitle {
|
||||||
color: var(--message-out-primary-color);
|
color: var(--message-out-status-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-toggle, &-download {
|
&-toggle, &-download {
|
||||||
@ -2002,8 +2014,8 @@ $bubble-margin: .25rem;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-subtitle, .contact-number, .document-size {
|
.contact-number, .document-size {
|
||||||
color: var(--message-out-primary-color);
|
color: var(--message-out-status-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
poll-element {
|
poll-element {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.selector {
|
.selector {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -57,8 +57,8 @@
|
|||||||
margin-top: 60px;
|
margin-top: 60px;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
> div {
|
/* > div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +170,7 @@ $chat-input-inner-padding-handhelds: .25rem;
|
|||||||
@include splitColor(message-out-background-color, #eeffde, true, true);
|
@include splitColor(message-out-background-color, #eeffde, true, true);
|
||||||
--message-out-link-color: var(--link-color);
|
--message-out-link-color: var(--link-color);
|
||||||
--message-out-primary-color: #4fae4e;
|
--message-out-primary-color: #4fae4e;
|
||||||
|
--message-out-status-color: var(--message-out-primary-color);
|
||||||
--message-out-audio-play-button-color: #fff;
|
--message-out-audio-play-button-color: #fff;
|
||||||
|
|
||||||
// * Day theme end
|
// * Day theme end
|
||||||
@ -215,6 +216,7 @@ html.night {
|
|||||||
@include splitColor(message-out-background-color, #8774E1, true, true);
|
@include splitColor(message-out-background-color, #8774E1, true, true);
|
||||||
--message-out-link-color: #fff;
|
--message-out-link-color: #fff;
|
||||||
--message-out-primary-color: #fff;
|
--message-out-primary-color: #fff;
|
||||||
|
--message-out-status-color: rgba(255, 255, 255, .6);
|
||||||
--message-out-audio-play-button-color: var(--message-out-background-color);
|
--message-out-audio-play-button-color: var(--message-out-background-color);
|
||||||
// * Night theme end
|
// * Night theme end
|
||||||
}
|
}
|
||||||
|
121
src/tests/slicedArray.test.ts
Normal file
121
src/tests/slicedArray.test.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import SlicedArray, { Slice } from "../helpers/slicedArray";
|
||||||
|
|
||||||
|
test('Slicing returns new Slice', () => {
|
||||||
|
const sliced = new SlicedArray();
|
||||||
|
const newSlice = sliced.slice.slice();
|
||||||
|
expect(newSlice.isEnd).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inserting', () => {
|
||||||
|
const sliced = new SlicedArray();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const slices = sliced.slices;
|
||||||
|
|
||||||
|
const arr = [100, 99, 98, 97, 96, 95];
|
||||||
|
const distantArr = arr.slice(-2).map(v => v - 2);
|
||||||
|
const missingArr = [arr[arr.length - 1], arr[arr.length - 1] - 1, distantArr[0]];
|
||||||
|
|
||||||
|
const startValue = 90;
|
||||||
|
const values: number[] = [];
|
||||||
|
const valuesPerArray = 3;
|
||||||
|
const totalArrays = 10;
|
||||||
|
for(let i = 0, length = valuesPerArray * totalArrays; i < length; ++i) {
|
||||||
|
values.push(startValue - i);
|
||||||
|
}
|
||||||
|
const arrays: number[][] = [];
|
||||||
|
for(let i = 0; i < totalArrays; ++i) {
|
||||||
|
arrays.push(values.slice(valuesPerArray * i, valuesPerArray * (i + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
test('Insert & flatten', () => {
|
||||||
|
const idx = 2;
|
||||||
|
|
||||||
|
sliced.insertSlice(arr.slice(0, idx + 1));
|
||||||
|
sliced.insertSlice(arr.slice(idx));
|
||||||
|
|
||||||
|
expect([...sliced.first]).toEqual(arr);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Insert inner values', () => {
|
||||||
|
sliced.insertSlice(arr.slice(1, -1));
|
||||||
|
|
||||||
|
expect([...sliced.first]).toEqual(arr);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Insert distant slice', () => {
|
||||||
|
const length = slices.length;
|
||||||
|
sliced.insertSlice(distantArr);
|
||||||
|
|
||||||
|
expect(slices.length).toEqual(length + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Insert intersection & join them', () => {
|
||||||
|
const length = slices.length;
|
||||||
|
sliced.insertSlice(missingArr);
|
||||||
|
|
||||||
|
expect(slices.length).toEqual(length - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let returnedSlice: Slice;
|
||||||
|
test('Insert arrays with gap & join them', () => {
|
||||||
|
slices[0].length = 0;
|
||||||
|
|
||||||
|
for(const arr of arrays) {
|
||||||
|
sliced.insertSlice(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(slices.length).toEqual(totalArrays);
|
||||||
|
|
||||||
|
returnedSlice = sliced.insertSlice(values.slice(0, -valuesPerArray + 1));
|
||||||
|
|
||||||
|
expect(slices.length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Return inserted & flattened slice', () => {
|
||||||
|
expect(slices[0]).toEqual(returnedSlice);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Slicing', () => {
|
||||||
|
const sliced = new SlicedArray();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const slices = sliced.slices;
|
||||||
|
|
||||||
|
const VALUES_LENGTH = 100;
|
||||||
|
const INCREMENTOR = 0xFFFF;
|
||||||
|
const values: number[] = [];
|
||||||
|
for(let i = 0; i < VALUES_LENGTH; ++i) {
|
||||||
|
values[i] = i + INCREMENTOR * i;
|
||||||
|
}
|
||||||
|
values.sort((a, b) => b - a);
|
||||||
|
sliced.insertSlice(values);
|
||||||
|
|
||||||
|
const addOffset = 40;
|
||||||
|
const limit = 40;
|
||||||
|
|
||||||
|
const r = (func: (idx: number) => void) => {
|
||||||
|
const max = VALUES_LENGTH * 3;
|
||||||
|
for(let i = 0; i < max; ++i) {
|
||||||
|
const idx = Math.random() * max | 0;
|
||||||
|
func(idx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Positive addOffset', () => {
|
||||||
|
test('From the start', () => {
|
||||||
|
const {slice} = sliced.sliceMe(0, addOffset, limit);
|
||||||
|
expect([...slice]).toEqual(values.slice(addOffset, addOffset + limit));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('From existing offsetId', () => {
|
||||||
|
r((idx) => {
|
||||||
|
const value = values[idx] || 1;
|
||||||
|
idx += 1; // because is it inclusive
|
||||||
|
const {slice} = sliced.sliceMe(value, addOffset, limit);
|
||||||
|
expect([...slice]).toEqual(values.slice(idx + addOffset, idx + addOffset + limit));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user