Browse Source

Refactor message's rReply

Actions in chatlist
master
Eduard Kuzmenko 3 years ago
parent
commit
445e6d8ece
  1. 6
      src/components/appMediaViewer.ts
  2. 7
      src/components/appMediaViewerNew.ts
  3. 5
      src/components/chat/bubbles.ts
  4. 20
      src/components/chat/input.ts
  5. 11
      src/components/chat/replyContainer.ts
  6. 3
      src/components/sidebarRight/tabs/stickers.ts
  7. 2
      src/config/app.ts
  8. 12
      src/helpers/array.ts
  9. 7
      src/helpers/dom.ts
  10. 5
      src/lang.ts
  11. 67
      src/layer.d.ts
  12. 33
      src/lib/appManagers/appDialogsManager.ts
  13. 2
      src/lib/appManagers/appDraftsManager.ts
  14. 268
      src/lib/appManagers/appMessagesManager.ts
  15. 10
      src/lib/appManagers/appStateManager.ts
  16. 3
      src/lib/appManagers/appStickersManager.ts
  17. 10
      src/lib/langPack.ts
  18. 3
      src/lib/mtproto/networker.ts
  19. 8
      src/lib/polyfill.ts
  20. 6
      src/lib/rootScope.ts
  21. 5
      src/lib/storages/filters.ts
  22. 64
      src/scripts/in/schema_additional_params.json
  23. 7
      src/scss/partials/_chatlist.scss

6
src/components/appMediaViewer.ts

@ -29,6 +29,8 @@ import { months, ONE_DAY } from "../helpers/date";
import { SearchSuperContext } from "./appSearchSuper."; import { SearchSuperContext } from "./appSearchSuper.";
import DEBUG from "../config/debug"; import DEBUG from "../config/debug";
import appNavigationController from "./appNavigationController"; import appNavigationController from "./appNavigationController";
import { Message } from "../layer";
import { forEachReverse } from "../helpers/array";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию // TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода) // TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -1338,8 +1340,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
//} //}
} }
const method = older ? value.history.forEach : value.history.forEachReverse; const method: any = older ? value.history.forEach.bind(value.history) : forEachReverse.bind(null, value.history);
method.call(value.history, message => { method((message: Message.message) => {
const {mid, peerId} = message; const {mid, peerId} = message;
const media = this.getMediaFromMessage(message); const media = this.getMediaFromMessage(message);

7
src/components/appMediaViewerNew.ts

@ -27,7 +27,8 @@ import appSidebarRight, { AppSidebarRight } from "./sidebarRight";
import SwipeHandler from "./swipeHandler"; import SwipeHandler from "./swipeHandler";
import { months, ONE_DAY } from "../helpers/date"; import { months, ONE_DAY } from "../helpers/date";
import { SearchSuperContext } from "./appSearchSuper."; import { SearchSuperContext } from "./appSearchSuper.";
import { PhotoSize } from "../layer"; import { Message, PhotoSize } from "../layer";
import { forEachReverse } from "../helpers/array";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию // TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода) // TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -1241,8 +1242,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
//} //}
} }
const method = older ? value.history.forEach : value.history.forEachReverse; const method: any = older ? value.history.forEach.bind(value.history) : forEachReverse.bind(null, value.history);
method.call(value.history, message => { method((message: Message.message) => {
const {mid, peerId} = message; const {mid, peerId} = message;
const media = this.getMediaFromMessage(message); const media = this.getMediaFromMessage(message);

5
src/components/chat/bubbles.ts

@ -48,6 +48,7 @@ import DEBUG from "../../config/debug";
import { SliceEnd } from "../../helpers/slicedArray"; import { SliceEnd } from "../../helpers/slicedArray";
import serverTimeManager from "../../lib/mtproto/serverTimeManager"; import serverTimeManager from "../../lib/mtproto/serverTimeManager";
import PeerTitle from "../peerTitle"; import PeerTitle from "../peerTitle";
import { forEachReverse } from "../../helpers/array";
const USE_MEDIA_TAILS = false; const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */]; const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */];
@ -341,7 +342,7 @@ export default class ChatBubbles {
promise.then(() => { promise.then(() => {
}); */ }); */
this.needUpdate.forEachReverse((obj, idx) => { forEachReverse(this.needUpdate, (obj, idx) => {
if(obj.replyMid === mid && obj.replyToPeerId === peerId) { if(obj.replyMid === mid && obj.replyToPeerId === peerId) {
const {mid, replyMid} = this.needUpdate.splice(idx, 1)[0]; const {mid, replyMid} = this.needUpdate.splice(idx, 1)[0];
@ -888,7 +889,7 @@ export default class ChatBubbles {
public onGoDownClick() { public onGoDownClick() {
if(this.replyFollowHistory.length) { if(this.replyFollowHistory.length) {
this.replyFollowHistory.forEachReverse((mid, idx) => { forEachReverse(this.replyFollowHistory, (mid, idx) => {
const bubble = this.bubbles[mid]; const bubble = this.bubbles[mid];
let bad = true; let bad = true;
if(bubble) { if(bubble) {

20
src/components/chat/input.ts

@ -1386,10 +1386,11 @@ export default class ChatInput {
let input = RichTextProcessor.wrapDraftText(message.message, {entities: message.totalEntities}); let input = RichTextProcessor.wrapDraftText(message.message, {entities: message.totalEntities});
const f = () => { const f = () => {
// ! костыль // ! костыль
const replyText = this.appMessagesManager.getRichReplyText(message, undefined, [message.mid]); const replyFragment = this.appMessagesManager.wrapMessageForReply(message, undefined, [message.mid]);
this.setTopInfo('edit', f, 'Editing', undefined, input, message); this.setTopInfo('edit', f, 'Editing', undefined, input, message);
const subtitleEl = this.replyElements.container.querySelector('.reply-subtitle'); const subtitleEl = this.replyElements.container.querySelector('.reply-subtitle');
subtitleEl.innerHTML = replyText; subtitleEl.textContent = '';
subtitleEl.append(replyFragment);
this.editMsgId = mid; this.editMsgId = mid;
input = undefined; input = undefined;
@ -1419,13 +1420,22 @@ export default class ChatInput {
const title = peerTitles.length < 3 ? peerTitles.join(' and ') : peerTitles[0] + ' and ' + (peerTitles.length - 1) + ' others'; const title = peerTitles.length < 3 ? peerTitles.join(' and ') : peerTitles[0] + ' and ' + (peerTitles.length - 1) + ' others';
const firstMessage = this.appMessagesManager.getMessageByPeer(fromPeerId, mids[0]); const firstMessage = this.appMessagesManager.getMessageByPeer(fromPeerId, mids[0]);
const replyText = this.appMessagesManager.getRichReplyText(firstMessage, undefined, mids); let usingFullAlbum = true;
if(replyText.includes('Album') || mids.length === 1) { if(firstMessage.grouped_id) {
const albumMids = this.appMessagesManager.getMidsByMessage(firstMessage);
if(albumMids.length !== mids.length || albumMids.find(mid => !mids.includes(mid))) {
usingFullAlbum = false;
}
}
const replyFragment = this.appMessagesManager.wrapMessageForReply(firstMessage, undefined, mids);
if(usingFullAlbum || mids.length === 1) {
this.setTopInfo('forward', f, title); this.setTopInfo('forward', f, title);
// ! костыль // ! костыль
const subtitleEl = this.replyElements.container.querySelector('.reply-subtitle'); const subtitleEl = this.replyElements.container.querySelector('.reply-subtitle');
subtitleEl.innerHTML = replyText; subtitleEl.textContent = '';
subtitleEl.append(replyFragment);
} else { } else {
this.setTopInfo('forward', f, title, mids.length + ' ' + (mids.length > 1 ? 'forwarded messages' : 'forwarded message')); this.setTopInfo('forward', f, title, mids.length + ' ' + (mids.length > 1 ? 'forwarded messages' : 'forwarded message'));
} }

11
src/components/chat/replyContainer.ts

@ -1,5 +1,6 @@
import { limitSymbols } from "../../helpers/string"; import { limitSymbols } from "../../helpers/string";
import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import appPhotosManager from "../../lib/appManagers/appPhotosManager"; import appPhotosManager from "../../lib/appManagers/appPhotosManager";
import { RichTextProcessor } from "../../lib/richtextprocessor"; import { RichTextProcessor } from "../../lib/richtextprocessor";
import DivAndCaption from "../divAndCaption"; import DivAndCaption from "../divAndCaption";
@ -22,12 +23,11 @@ export function wrapReplyDivAndCaption(options: {
titleEl.innerHTML = title; titleEl.innerHTML = title;
} }
subtitle = limitSymbols(subtitle, 140);
const media = message && message.media; const media = message && message.media;
let setMedia = false; let setMedia = false;
if(media && mediaEl) { if(media && mediaEl) {
subtitle = message.rReply; subtitleEl.textContent = '';
subtitleEl.append(appMessagesManager.wrapMessageForReply(message));
//console.log('wrap reply', media); //console.log('wrap reply', media);
@ -81,10 +81,11 @@ export function wrapReplyDivAndCaption(options: {
} }
} }
} else { } else {
subtitle = limitSymbols(subtitle, 140);
subtitle = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : ''; subtitle = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : '';
subtitleEl.innerHTML = subtitle;
} }
subtitleEl.innerHTML = subtitle;
return setMedia; return setMedia;
} }

3
src/components/sidebarRight/tabs/stickers.ts

@ -10,6 +10,7 @@ import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { wrapSticker } from "../../wrappers"; import { wrapSticker } from "../../wrappers";
import appSidebarRight from ".."; import appSidebarRight from "..";
import { StickerSet, StickerSetCovered } from "../../../layer"; import { StickerSet, StickerSetCovered } from "../../../layer";
import { forEachReverse } from "../../../helpers/array";
export default class AppStickersTab extends SliderSuperTab { export default class AppStickersTab extends SliderSuperTab {
private inputSearch: InputSearch; private inputSearch: InputSearch;
@ -202,7 +203,7 @@ export default class AppStickersTab extends SliderSuperTab {
coveredSets = coveredSets.slice(); coveredSets = coveredSets.slice();
const children = Array.from(this.setsDiv.children) as HTMLElement[]; const children = Array.from(this.setsDiv.children) as HTMLElement[];
children.forEachReverse(el => { forEachReverse(children, el => {
const id = el.dataset.stickerSet; const id = el.dataset.stickerSet;
const index = coveredSets.findIndex(covered => covered.set.id === id); const index = coveredSets.findIndex(covered => covered.set.id === id);

2
src/config/app.ts

@ -2,7 +2,7 @@ const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.4.0', version: '0.4.0',
langPackVersion: '0.0.1', langPackVersion: '0.0.2',
domains: [] as string[], domains: [] as string[],
baseDcId: 2 baseDcId: 2
}; };

12
src/helpers/array.ts

@ -1,4 +1,4 @@
import { copy } from "./object"; /* import { copy } from "./object";
export function listMergeSorted(list1: any[] = [], list2: any[] = []) { export function listMergeSorted(list1: any[] = [], list2: any[] = []) {
const result = copy(list1); const result = copy(list1);
@ -11,7 +11,7 @@ export function listMergeSorted(list1: any[] = [], list2: any[] = []) {
} }
return result; return result;
} } */
export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue); export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue);
@ -23,4 +23,10 @@ export function findAndSpliceAll<T>(array: Array<T>, verify: (value: T, index: n
} }
return out; return out;
} }
export function forEachReverse<T>(array: Array<T>, callback: (value: T, index?: number, array?: Array<T>) => void) {
for(let length = array.length, i = length - 1; i >= 0; --i) {
callback(array[i], i, array);
}
};

7
src/helpers/dom.ts

@ -798,3 +798,10 @@ export function toggleDisability(elements: HTMLElement[], disable: boolean) {
export function canFocus(isFirstInput: boolean) { export function canFocus(isFirstInput: boolean) {
return !isMobileSafari || !isFirstInput; return !isMobileSafari || !isFirstInput;
} }
export function htmlToDocumentFragment(html: string) {
var template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content;
}

5
src/lang.ts

@ -49,13 +49,15 @@ const lang = {
"ActionChangedVideo": "un1 changed the group video", "ActionChangedVideo": "un1 changed the group video",
"ActionAddUser": "un1 added un2", "ActionAddUser": "un1 added un2",
"ActionAddUserSelf": "un1 returned to the group", "ActionAddUserSelf": "un1 returned to the group",
"ActionAddUserSelfMega": "un1 joined the group",
"ActionAddUserSelfYou": "You returned to the group", "ActionAddUserSelfYou": "You returned to the group",
"ActionAddUserSelfMega": "un1 joined the group",
"ActionLeftUser": "un1 left the group", "ActionLeftUser": "un1 left the group",
"ActionKickUser": "un1 removed un2", "ActionKickUser": "un1 removed un2",
"ActionInviteUser": "un1 joined the group via invite link", "ActionInviteUser": "un1 joined the group via invite link",
"ActionPinnedNoText": "un1 pinned a message", "ActionPinnedNoText": "un1 pinned a message",
"ActionMigrateFromGroup": "This group was upgraded to a supergroup", "ActionMigrateFromGroup": "This group was upgraded to a supergroup",
//"ChannelJoined": "You joined this channel",
"ChannelMegaJoined": "You joined this group",
"FilterAlwaysShow": "Include Chats", "FilterAlwaysShow": "Include Chats",
"FilterNeverShow": "Exclude Chats", "FilterNeverShow": "Exclude Chats",
"FilterInclude": "Included Chats", "FilterInclude": "Included Chats",
@ -155,6 +157,7 @@ const lang = {
"Chat.Service.Channel.UpdatedTitle": "Channel renamed to \"%@\"", "Chat.Service.Channel.UpdatedTitle": "Channel renamed to \"%@\"",
"Chat.Service.Channel.UpdatedPhoto": "Channel photo updated", "Chat.Service.Channel.UpdatedPhoto": "Channel photo updated",
"Chat.Service.Channel.RemovedPhoto": "Channel photo removed", "Chat.Service.Channel.RemovedPhoto": "Channel photo removed",
"Chat.Service.Channel.UpdatedVideo": "Channel video updated",
"Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@", "Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@",
"ChatList.Service.Call.incoming": "Incoming Call (%@)", "ChatList.Service.Call.incoming": "Incoming Call (%@)",
"ChatList.Service.Call.outgoing": "Outgoing Call (%@)", "ChatList.Service.Call.outgoing": "Outgoing Call (%@)",

67
src/layer.d.ts vendored

@ -967,7 +967,7 @@ export namespace MessageMedia {
/** /**
* @link https://core.telegram.org/type/MessageAction * @link https://core.telegram.org/type/MessageAction
*/ */
export type MessageAction = MessageAction.messageActionEmpty | MessageAction.messageActionChatCreate | MessageAction.messageActionChatEditTitle | MessageAction.messageActionChatEditPhoto | MessageAction.messageActionChatDeletePhoto | MessageAction.messageActionChatAddUser | MessageAction.messageActionChatDeleteUser | MessageAction.messageActionChatJoinedByLink | MessageAction.messageActionChannelCreate | MessageAction.messageActionChatMigrateTo | MessageAction.messageActionChannelMigrateFrom | MessageAction.messageActionPinMessage | MessageAction.messageActionHistoryClear | MessageAction.messageActionGameScore | MessageAction.messageActionPaymentSentMe | MessageAction.messageActionPaymentSent | MessageAction.messageActionPhoneCall | MessageAction.messageActionScreenshotTaken | MessageAction.messageActionCustomAction | MessageAction.messageActionBotAllowed | MessageAction.messageActionSecureValuesSentMe | MessageAction.messageActionSecureValuesSent | MessageAction.messageActionContactSignUp | MessageAction.messageActionGeoProximityReached; export type MessageAction = MessageAction.messageActionEmpty | MessageAction.messageActionChatCreate | MessageAction.messageActionChatEditTitle | MessageAction.messageActionChatEditPhoto | MessageAction.messageActionChatDeletePhoto | MessageAction.messageActionChatAddUser | MessageAction.messageActionChatDeleteUser | MessageAction.messageActionChatJoinedByLink | MessageAction.messageActionChannelCreate | MessageAction.messageActionChatMigrateTo | MessageAction.messageActionChannelMigrateFrom | MessageAction.messageActionPinMessage | MessageAction.messageActionHistoryClear | MessageAction.messageActionGameScore | MessageAction.messageActionPaymentSentMe | MessageAction.messageActionPaymentSent | MessageAction.messageActionPhoneCall | MessageAction.messageActionScreenshotTaken | MessageAction.messageActionCustomAction | MessageAction.messageActionBotAllowed | MessageAction.messageActionSecureValuesSentMe | MessageAction.messageActionSecureValuesSent | MessageAction.messageActionContactSignUp | MessageAction.messageActionGeoProximityReached | MessageAction.messageActionChatLeave | MessageAction.messageActionChannelDeletePhoto | MessageAction.messageActionChannelEditTitle | MessageAction.messageActionChannelEditPhoto | MessageAction.messageActionChannelEditVideo | MessageAction.messageActionChatEditVideo | MessageAction.messageActionChatAddUsers | MessageAction.messageActionChatJoined | MessageAction.messageActionChatReturn | MessageAction.messageActionChatJoinedYou | MessageAction.messageActionChatReturnYou;
export namespace MessageAction { export namespace MessageAction {
export type messageActionEmpty = { export type messageActionEmpty = {
@ -1102,6 +1102,60 @@ export namespace MessageAction {
to_id: Peer, to_id: Peer,
distance: number distance: number
}; };
export type messageActionChatLeave = {
_: 'messageActionChatLeave',
user_id?: number
};
export type messageActionChannelDeletePhoto = {
_: 'messageActionChannelDeletePhoto'
};
export type messageActionChannelEditTitle = {
_: 'messageActionChannelEditTitle',
title?: string
};
export type messageActionChannelEditPhoto = {
_: 'messageActionChannelEditPhoto',
photo?: Photo
};
export type messageActionChannelEditVideo = {
_: 'messageActionChannelEditVideo',
photo?: Photo
};
export type messageActionChatEditVideo = {
_: 'messageActionChatEditVideo',
photo?: Photo
};
export type messageActionChatAddUsers = {
_: 'messageActionChatAddUsers',
users?: Array<number>
};
export type messageActionChatJoined = {
_: 'messageActionChatJoined',
users?: Array<number>
};
export type messageActionChatReturn = {
_: 'messageActionChatReturn',
users?: Array<number>
};
export type messageActionChatJoinedYou = {
_: 'messageActionChatJoinedYou',
users?: Array<number>
};
export type messageActionChatReturnYou = {
_: 'messageActionChatReturnYou',
users?: Array<number>
};
} }
/** /**
@ -9018,6 +9072,17 @@ export interface ConstructorDeclMap {
'messageEntityEmoji': MessageEntity.messageEntityEmoji, 'messageEntityEmoji': MessageEntity.messageEntityEmoji,
'messageEntityHighlight': MessageEntity.messageEntityHighlight, 'messageEntityHighlight': MessageEntity.messageEntityHighlight,
'messageEntityLinebreak': MessageEntity.messageEntityLinebreak, 'messageEntityLinebreak': MessageEntity.messageEntityLinebreak,
'messageActionChatLeave': MessageAction.messageActionChatLeave,
'messageActionChannelDeletePhoto': MessageAction.messageActionChannelDeletePhoto,
'messageActionChannelEditTitle': MessageAction.messageActionChannelEditTitle,
'messageActionChannelEditPhoto': MessageAction.messageActionChannelEditPhoto,
'messageActionChannelEditVideo': MessageAction.messageActionChannelEditVideo,
'messageActionChatEditVideo': MessageAction.messageActionChatEditVideo,
'messageActionChatAddUsers': MessageAction.messageActionChatAddUsers,
'messageActionChatJoined': MessageAction.messageActionChatJoined,
'messageActionChatReturn': MessageAction.messageActionChatReturn,
'messageActionChatJoinedYou': MessageAction.messageActionChatJoinedYou,
'messageActionChatReturnYou': MessageAction.messageActionChatReturnYou,
} }
export type InvokeAfterMsg = { export type InvokeAfterMsg = {

33
src/lib/appManagers/appDialogsManager.ts

@ -1056,38 +1056,13 @@ export class AppDialogsManager {
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ { /* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ {
dom.lastMessageSpan.textContent = '';
if(highlightWord && lastMessage.message) { if(highlightWord && lastMessage.message) {
let lastMessageText = appMessagesManager.getRichReplyText(lastMessage, ''); dom.lastMessageSpan.append(appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, highlightWord));
let messageText = lastMessage.message;
let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '));
let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');
let match: any;
if(!entities) entities = [];
let found = false;
while((match = regExp.exec(messageText)) !== null) {
entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index});
found = true;
}
if(found) {
entities.sort((a, b) => a.offset - b.offset);
}
let messageWrapped = RichTextProcessor.wrapRichText(messageText, {
noLinebreaks: true,
entities: entities,
noTextFormat: true
});
dom.lastMessageSpan.innerHTML = lastMessageText + messageWrapped;
} else if(draftMessage) { } else if(draftMessage) {
dom.lastMessageSpan.innerHTML = draftMessage.rReply; dom.lastMessageSpan.append(appMessagesManager.wrapMessageForReply(draftMessage));
} else if(!lastMessage.deleted) { } else if(!lastMessage.deleted) {
dom.lastMessageSpan.innerHTML = lastMessage.rReply; dom.lastMessageSpan.append(appMessagesManager.wrapMessageForReply(lastMessage));
} else {
dom.lastMessageSpan.innerHTML = '';
} }
/* if(lastMessage.from_id === auth.id) { // You: */ /* if(lastMessage.from_id === auth.id) { // You: */

2
src/lib/appManagers/appDraftsManager.ts

@ -163,7 +163,7 @@ export class AppDraftsManager {
const totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities); // ! only in this order, otherwise bold and emoji formatting won't work const totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities); // ! only in this order, otherwise bold and emoji formatting won't work
draft.rMessage = RichTextProcessor.wrapDraftText(draft.message, {entities: totalEntities}); draft.rMessage = RichTextProcessor.wrapDraftText(draft.message, {entities: totalEntities});
draft.rReply = appMessagesManager.getRichReplyText(draft); //draft.rReply = appMessagesManager.getRichReplyText(draft);
if(draft.reply_to_msg_id) { if(draft.reply_to_msg_id) {
draft.reply_to_msg_id = appMessagesManager.generateMessageId(draft.reply_to_msg_id); draft.reply_to_msg_id = appMessagesManager.generateMessageId(draft.reply_to_msg_id);
} }

268
src/lib/appManagers/appMessagesManager.ts

@ -5,10 +5,10 @@ import { tsNow } from "../../helpers/date";
import { createPosterForVideo } from "../../helpers/files"; import { createPosterForVideo } from "../../helpers/files";
import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object"; import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random"; import { randomLong } from "../../helpers/random";
import { splitStringByLength, limitSymbols } 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, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer"; import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer";
import { InvokeApiOptions } from "../../types"; import { InvokeApiOptions } from "../../types";
import I18n, { langPack, LangPackKey, _i18n } from "../langPack"; import I18n, { join, langPack, LangPackKey, _i18n } from "../langPack";
import { logger, LogLevels } from "../logger"; import { logger, LogLevels } from "../logger";
import type { ApiFileManager } from '../mtproto/apiFileManager'; import type { ApiFileManager } from '../mtproto/apiFileManager';
//import apiManager from '../mtproto/apiManager'; //import apiManager from '../mtproto/apiManager';
@ -39,6 +39,8 @@ import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
import SlicedArray, { Slice, SliceEnd } from "../../helpers/slicedArray"; import SlicedArray, { Slice, SliceEnd } from "../../helpers/slicedArray";
import appNotificationsManager, { NotifyOptions } from "./appNotificationsManager"; import appNotificationsManager, { NotifyOptions } from "./appNotificationsManager";
import PeerTitle from "../../components/peerTitle"; import PeerTitle from "../../components/peerTitle";
import { forEachReverse } from "../../helpers/array";
import { htmlToDocumentFragment } from "../../helpers/dom";
//console.trace('include'); //console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -92,6 +94,8 @@ export type MessagesStorage = {
[mid: string]: any [mid: string]: any
}; };
export type MyMessageActionType = Message.messageService['action']['_'];
export class AppMessagesManager { export class AppMessagesManager {
public static MESSAGE_ID_INCREMENT = 0x10000; public static MESSAGE_ID_INCREMENT = 0x10000;
public static MESSAGE_ID_OFFSET = 0xFFFFFFFF; public static MESSAGE_ID_OFFSET = 0xFFFFFFFF;
@ -292,7 +296,7 @@ export class AppMessagesManager {
} }
if(state.dialogs) { if(state.dialogs) {
state.dialogs.forEachReverse(dialog => { forEachReverse(state.dialogs, dialog => {
dialog.top_message = this.getServerMessageId(dialog.top_message); // * fix outgoing message to avoid copying dialog dialog.top_message = this.getServerMessageId(dialog.top_message); // * fix outgoing message to avoid copying dialog
this.saveConversation(dialog); this.saveConversation(dialog);
@ -1755,7 +1759,7 @@ export class AppMessagesManager {
let maxSeenIdIncremented = offsetDate ? true : false; let maxSeenIdIncremented = offsetDate ? true : false;
let hasPrepend = false; let hasPrepend = false;
const noIdsDialogs: {[peerId: number]: Dialog} = {}; const noIdsDialogs: {[peerId: number]: Dialog} = {};
(dialogsResult.dialogs as Dialog[]).forEachReverse(dialog => { forEachReverse((dialogsResult.dialogs as Dialog[]), dialog => {
//const d = Object.assign({}, dialog); //const d = Object.assign({}, dialog);
// ! нужно передавать folderId, так как по папке !== 0 нет свойства folder_id // ! нужно передавать folderId, так как по папке !== 0 нет свойства folder_id
this.saveConversation(dialog, dialog.folder_id ?? folderId); this.saveConversation(dialog, dialog.folder_id ?? folderId);
@ -2221,7 +2225,7 @@ export class AppMessagesManager {
isScheduled: true, isScheduled: true,
isOutgoing: true isOutgoing: true
}> = {}) { }> = {}) {
let groups: Set<string>; //let groups: Set<string>;
messages.forEach((message) => { messages.forEach((message) => {
if(message.pFlags === undefined) { if(message.pFlags === undefined) {
message.pFlags = {}; message.pFlags = {};
@ -2384,8 +2388,12 @@ export class AppMessagesManager {
//case 'messageActionChannelEditPhoto': //case 'messageActionChannelEditPhoto':
case 'messageActionChatEditPhoto': case 'messageActionChatEditPhoto':
message.action.photo = appPhotosManager.savePhoto(message.action.photo, mediaContext); message.action.photo = appPhotosManager.savePhoto(message.action.photo, mediaContext);
if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода. if(message.action.photo.video_sizes) {
message.action._ = 'messageActionChannelEditPhoto'; message.action._ = isBroadcast ? 'messageActionChannelEditVideo' : 'messageActionChatEditVideo';
} else {
if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода.
message.action._ = 'messageActionChannelEditPhoto';
}
} }
break; break;
@ -2405,10 +2413,11 @@ export class AppMessagesManager {
if(message.action.users.length === 1) { if(message.action.users.length === 1) {
message.action.user_id = message.action.users[0]; message.action.user_id = message.action.users[0];
if(message.fromId === message.action.user_id) { if(message.fromId === message.action.user_id) {
let suffix = message.fromId === appUsersManager.getSelf().id ? 'You' : '';
if(isChannel) { if(isChannel) {
message.action._ = 'messageActionChatJoined'; message.action._ = 'messageActionChatJoined' + suffix;
} else { } else {
message.action._ = 'messageActionChatReturn'; message.action._ = 'messageActionChatReturn' + suffix;
} }
} }
} else if(message.action.users.length > 1) { } else if(message.action.users.length > 1) {
@ -2460,7 +2469,7 @@ export class AppMessagesManager {
} }
} }
if(message.grouped_id) { /* if(message.grouped_id) {
if(!groups) { if(!groups) {
groups = new Set(); groups = new Set();
} }
@ -2468,7 +2477,7 @@ export class AppMessagesManager {
groups.add(message.grouped_id); groups.add(message.grouped_id);
} else { } else {
message.rReply = this.getRichReplyText(message); message.rReply = this.getRichReplyText(message);
} } */
if(message.message && message.message.length && !message.totalEntities) { if(message.message && message.message.length && !message.totalEntities) {
const myEntities = RichTextProcessor.parseEntities(message.message); const myEntities = RichTextProcessor.parseEntities(message.message);
@ -2479,7 +2488,7 @@ export class AppMessagesManager {
storage[mid] = message; storage[mid] = message;
}); });
if(groups) { /* if(groups) {
for(const groupId of groups) { for(const groupId of groups) {
const mids = this.groupedMessagesStorage[groupId]; const mids = this.groupedMessagesStorage[groupId];
for(const mid in mids) { for(const mid in mids) {
@ -2487,11 +2496,28 @@ export class AppMessagesManager {
message.rReply = this.getRichReplyText(message); message.rReply = this.getRichReplyText(message);
} }
} }
} } */
} }
public getRichReplyText(message: any, text: string = message.message, usingMids?: number[], plain = false) { public wrapMessageForReply(message: any, text: string, usingMids: number[], plain: true, highlightWord?: string): string;
let messageText = ''; public wrapMessageForReply(message: any, text?: string, usingMids?: number[], plain?: false, highlightWord?: string): DocumentFragment;
public wrapMessageForReply(message: any, text: string = message.message, usingMids?: number[], plain?: boolean, highlightWord?: string): DocumentFragment | string {
const parts: (HTMLElement | string)[] = [];
const addPart = (part: string | HTMLElement, text?: string) => {
if(text) {
part += ', ';
}
if(plain) {
parts.push(part);
} else {
const el = document.createElement('i');
if(typeof(part) === 'string') el.innerHTML = part;
else el.append(part);
parts.push(el);
}
};
if(message.media) { if(message.media) {
let usingFullAlbum = true; let usingFullAlbum = true;
@ -2512,7 +2538,7 @@ export class AppMessagesManager {
if(usingFullAlbum) { if(usingFullAlbum) {
text = this.getAlbumText(message.grouped_id).message; text = this.getAlbumText(message.grouped_id).message;
messageText += '<i>Album' + (text ? ', ' : '') + '</i>'; addPart('Album', text);
} }
} else { } else {
usingFullAlbum = false; usingFullAlbum = false;
@ -2522,36 +2548,36 @@ export class AppMessagesManager {
const media = message.media; const media = message.media;
switch(media._) { switch(media._) {
case 'messageMediaPhoto': case 'messageMediaPhoto':
messageText += '<i>Photo' + (message.message ? ', ' : '') + '</i>'; addPart('Photo', message.message);
break; break;
case 'messageMediaDice': case 'messageMediaDice':
messageText += plain ? media.emoticon : RichTextProcessor.wrapEmojiText(media.emoticon); addPart(plain ? media.emoticon : RichTextProcessor.wrapEmojiText(media.emoticon));
break; break;
case 'messageMediaGeo': case 'messageMediaGeo':
messageText += '<i>Geolocation</i>'; addPart('Geolocation');
break; break;
case 'messageMediaPoll': case 'messageMediaPoll':
messageText += '<i>' + (plain ? '📊' + ' ' + media.poll.question || 'poll' : media.poll.rReply) + '</i>'; addPart(plain ? '📊' + ' ' + (media.poll.question || 'poll') : media.poll.rReply);
break; break;
case 'messageMediaContact': case 'messageMediaContact':
messageText += '<i>Contact</i>'; addPart('Contact');
break; break;
case 'messageMediaDocument': case 'messageMediaDocument':
let document = media.document; let document = media.document;
if(document.type === 'video') { if(document.type === 'video') {
messageText = '<i>Video' + (message.message ? ', ' : '') + '</i>'; addPart('Video', message.message);
} else if(document.type === 'voice') { } else if(document.type === 'voice') {
messageText = '<i>Voice message' + (message.message ? ', ' : '') + '</i>'; addPart('Voice message', message.message);
} else if(document.type === 'gif') { } else if(document.type === 'gif') {
messageText = '<i>GIF' + (message.message ? ', ' : '') + '</i>'; addPart('GIF', message.message);
} else if(document.type === 'round') { } else if(document.type === 'round') {
messageText = '<i>Video message' + (message.message ? ', ' : '') + '</i>'; addPart('Video message', message.message);
} else if(document.type === 'sticker') { } else if(document.type === 'sticker') {
messageText = (document.stickerEmoji || '') + '<i>Sticker</i>'; addPart(((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker');
text = ''; text = '';
} else { } else {
messageText = '<i>' + document.file_name + (message.message ? ', ' : '') + '</i>'; addPart(document.file_name, message.message);
} }
break; break;
@ -2565,30 +2591,53 @@ export class AppMessagesManager {
} }
if(message.action) { if(message.action) {
const str = this.wrapMessageActionText(message, plain); const actionWrapped = this.wrapMessageActionTextNew(message, plain);
if(actionWrapped) {
messageText = str ? '<i>' + str + '</i>' : ''; addPart(actionWrapped);
}
} }
let messageWrapped = '';
if(text) { if(text) {
text = limitSymbols(text, 100); text = limitSymbols(text, 100);
const entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' ')); if(plain) {
parts.push(text);
} else {
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '));
if(highlightWord) {
if(!entities) entities = [];
let found = false;
let match: any;
let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');
while((match = regExp.exec(text)) !== null) {
entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index});
found = true;
}
if(found) {
entities.sort((a, b) => a.offset - b.offset);
}
}
messageWrapped = plain ? text : RichTextProcessor.wrapRichText(text, { const messageWrapped = RichTextProcessor.wrapRichText(text, {
noLinebreaks: true, noLinebreaks: true,
entities, entities,
noLinks: true, noLinks: true,
noTextFormat: true noTextFormat: true
}); });
parts.push(htmlToDocumentFragment(messageWrapped) as any);
}
} }
if(plain) { if(plain) {
messageText = messageText.replace(/<i>/g, '').replace(/<\/i>/g, ''); return parts.join('');
} else {
const fragment = document.createDocumentFragment();
fragment.append(...parts);
return fragment;
} }
return messageText + messageWrapped;
} }
public getSenderToPeerText(message: MyMessage) { public getSenderToPeerText(message: MyMessage) {
@ -2606,84 +2655,9 @@ export class AppMessagesManager {
return senderTitle; return senderTitle;
} }
public wrapMessageActionText(message: any, plain?: boolean) {
const action = message.action as MessageAction;
let str = '';
if((action as MessageAction.messageActionCustomAction).message) {
str = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true});
} else {
let _ = action._;
let suffix = '';
let l = '';
const getNameDivHTML = (peerId: number) => {
const title = appPeersManager.getPeerTitle(peerId);
return title ? (plain ? title + ' ' : `<div class="name inline" data-peer-id="${peerId}">${title}</div> `) : '';
};
switch(action._) {
case "messageActionPhoneCall": {
_ += '.' + (action as any).type;
const duration = action.duration;
if(duration) {
const d: string[] = [];
d.push(duration % 60 + ' s');
if(duration >= 60) d.push((duration / 60 | 0) + ' min');
//if(duration >= 3600) d.push((duration / 3600 | 0) + ' h');
suffix = ' (' + d.reverse().join(' ') + ')';
}
return langPack[_] + suffix;
}
case 'messageActionChatDeleteUser':
// @ts-ignore
case 'messageActionChatAddUsers':
case 'messageActionChatAddUser': {
const users: number[] = (action as MessageAction.messageActionChatAddUser).users || [(action as MessageAction.messageActionChatDeleteUser).user_id];
l = langPack[_].replace('{}', users.map((userId: number) => getNameDivHTML(userId).trim()).join(', '));
break;
}
case 'messageActionBotAllowed': {
const anchorHTML = RichTextProcessor.wrapRichText(action.domain, {
entities: [{
_: 'messageEntityUrl',
length: action.domain.length,
offset: 0
}]
});
l = langPack[_].replace('{}', anchorHTML);
break;
}
default:
str = langPack[_] || `[${action._}]`;
break;
}
if(!l) {
l = langPack[_];
if(l === undefined) {
l = '[' + _ + ']';
}
}
str = !l || l[0].toUpperCase() === l[0] ? l : getNameDivHTML(message.fromId) + l + (suffix ? ' ' : '');
}
//this.log('message action:', action);
return str;
}
public wrapMessageActionTextNew(message: any, plain: true): string; public wrapMessageActionTextNew(message: any, plain: true): string;
public wrapMessageActionTextNew(message: any, plain?: false): HTMLElement; public wrapMessageActionTextNew(message: any, plain?: false): HTMLElement;
public wrapMessageActionTextNew(message: any, plain: boolean): HTMLElement | string;
public wrapMessageActionTextNew(message: any, plain?: boolean): HTMLElement | string { public wrapMessageActionTextNew(message: any, plain?: boolean): HTMLElement | string {
const element: HTMLElement = plain ? undefined : document.createElement('span'); const element: HTMLElement = plain ? undefined : document.createElement('span');
const action = message.action as MessageAction; const action = message.action as MessageAction;
@ -2705,7 +2679,7 @@ export class AppMessagesManager {
let args: any[]; let args: any[];
const getNameDivHTML = (peerId: number, plain: boolean) => { const getNameDivHTML = (peerId: number, plain: boolean) => {
return plain ? appPeersManager.getPeerTitle(peerId) + ' ' : (new PeerTitle({peerId})).element; return plain ? appPeersManager.getPeerTitle(peerId, plain) + ' ' : (new PeerTitle({peerId})).element;
}; };
switch(action._) { switch(action._) {
@ -2724,27 +2698,61 @@ export class AppMessagesManager {
break; break;
} }
case 'messageActionPinMessage':
case 'messageActionContactSignUp':
case 'messageActionChatLeave':
case 'messageActionChatJoined':
case 'messageActionChatCreate': case 'messageActionChatCreate':
case 'messageActionChatJoinedByLink': { case 'messageActionChatEditPhoto':
case 'messageActionChatDeletePhoto':
case 'messageActionChatEditVideo':
case 'messageActionChatJoinedByLink':
case 'messageActionChannelEditVideo':
case 'messageActionChannelDeletePhoto': {
langPackKey = langPack[_]; langPackKey = langPack[_];
args = [getNameDivHTML(message.fromId, plain)]; args = [getNameDivHTML(message.fromId, plain)];
break; break;
} }
case 'messageActionChannelEditTitle':
case 'messageActionChatEditTitle': {
langPackKey = langPack[_];
args = [];
if(action._ === 'messageActionChatEditTitle') {
args.push(getNameDivHTML(message.fromId, plain));
}
args.push(plain ? action.title : RichTextProcessor.wrapEmojiText(action.title));
break;
}
case 'messageActionChatDeleteUser': case 'messageActionChatDeleteUser':
// @ts-ignore
case 'messageActionChatAddUsers': case 'messageActionChatAddUsers':
case 'messageActionChatAddUser': { case 'messageActionChatAddUser': {
const users: number[] = (action as MessageAction.messageActionChatAddUser).users const users: number[] = (action as MessageAction.messageActionChatAddUser).users
|| [(action as MessageAction.messageActionChatDeleteUser).user_id]; || [(action as MessageAction.messageActionChatDeleteUser).user_id];
langPackKey = langPack[_]; langPackKey = langPack[_];
args = [ args = [getNameDivHTML(message.fromId, plain)];
getNameDivHTML(message.fromId, plain),
users.length > 1 ? if(users.length > 1) {
users.map((userId: number) => (getNameDivHTML(userId, true) as string).trim()).join(', ') : if(plain) {
getNameDivHTML(users[0], plain) args.push(...users.map((userId: number) => (getNameDivHTML(userId, true) as string).trim()).join(', '));
]; } else {
const fragment = document.createElement('span');
fragment.append(
...join(
users.map((userId: number) => getNameDivHTML(userId, false)) as HTMLElement[],
false
)
);
args.push(fragment);
}
} else {
args.push(getNameDivHTML(users[0], plain));
}
break; break;
} }
@ -2756,9 +2764,11 @@ export class AppMessagesManager {
offset: 0 offset: 0
}] }]
}); });
const node = htmlToDocumentFragment(anchorHTML);
langPackKey = langPack[_]; langPackKey = langPack[_];
args = [anchorHTML]; args = [node];
break; break;
} }
@ -2927,7 +2937,7 @@ export class AppMessagesManager {
// * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages // * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages
// ! fix 'dialogFolder', maybe there is better way to do it, this only can happen by 'messages.getPinnedDialogs' by folder_id: 0 // ! fix 'dialogFolder', maybe there is better way to do it, this only can happen by 'messages.getPinnedDialogs' by folder_id: 0
dialogsResult.dialogs.forEachReverse((dialog, idx) => { forEachReverse(dialogsResult.dialogs, (dialog, idx) => {
if(dialog._ === 'dialogFolder') { if(dialog._ === 'dialogFolder') {
dialogsResult.dialogs.splice(idx, 1); dialogsResult.dialogs.splice(idx, 1);
} }
@ -4779,7 +4789,7 @@ export class AppMessagesManager {
if(message._ === 'message' && message.fwd_from && options.fwdCount) { if(message._ === 'message' && message.fwd_from && options.fwdCount) {
notificationMessage = 'Forwarded ' + options.fwdCount + ' messages';//fwdMessagesPluralize(options.fwd_count); notificationMessage = 'Forwarded ' + options.fwdCount + ' messages';//fwdMessagesPluralize(options.fwd_count);
} else { } else {
notificationMessage = this.getRichReplyText(message, undefined, undefined, true); notificationMessage = this.wrapMessageForReply(message, undefined, undefined, true);
} }
} else { } else {
notificationMessage = 'New notification'; notificationMessage = 'New notification';

10
src/lib/appManagers/appStateManager.ts

@ -1,15 +1,15 @@
import type { Dialog } from './appMessagesManager'; import type { Dialog } from './appMessagesManager';
import { UserAuth } from '../mtproto/mtproto_config'; import type { UserAuth } from '../mtproto/mtproto_config';
import EventListenerBase from '../../helpers/eventListenerBase';
import rootScope from '../rootScope';
import sessionStorage from '../sessionStorage';
import { logger } from '../logger';
import type { AppUsersManager } from './appUsersManager'; import type { AppUsersManager } from './appUsersManager';
import type { AppChatsManager } from './appChatsManager'; import type { AppChatsManager } from './appChatsManager';
import type { AuthState } from '../../types'; import type { AuthState } from '../../types';
import type FiltersStorage from '../storages/filters'; import type FiltersStorage from '../storages/filters';
import type DialogsStorage from '../storages/dialogs'; import type DialogsStorage from '../storages/dialogs';
import type { AppDraftsManager } from './appDraftsManager'; import type { AppDraftsManager } from './appDraftsManager';
import EventListenerBase from '../../helpers/eventListenerBase';
import rootScope from '../rootScope';
import sessionStorage from '../sessionStorage';
import { logger } from '../logger';
import { copy, setDeepProperty, validateInitObject } from '../../helpers/object'; import { copy, setDeepProperty, validateInitObject } from '../../helpers/object';
import { getHeavyAnimationPromise } from '../../hooks/useHeavyAnimationCheck'; import { getHeavyAnimationPromise } from '../../hooks/useHeavyAnimationCheck';
import App from '../../config/app'; import App from '../../config/app';

3
src/lib/appManagers/appStickersManager.ts

@ -5,6 +5,7 @@ import rootScope from '../rootScope';
import appDocsManager from './appDocsManager'; import appDocsManager from './appDocsManager';
import AppStorage from '../storage'; import AppStorage from '../storage';
import { MOUNT_CLASS_TO } from '../../config/debug'; import { MOUNT_CLASS_TO } from '../../config/debug';
import { forEachReverse } from '../../helpers/array';
// TODO: если пак будет сохранён и потом обновлён, то недостающие стикеры не подгрузит // TODO: если пак будет сохранён и потом обновлён, то недостающие стикеры не подгрузит
@ -33,7 +34,7 @@ export class AppStickersManager {
} }
public saveStickers(docs: Document[]) { public saveStickers(docs: Document[]) {
docs.forEachReverse((doc, idx) => { forEachReverse(docs, (doc, idx) => {
doc = appDocsManager.saveDoc(doc); doc = appDocsManager.saveDoc(doc);
if(!doc) docs.splice(idx, 1); if(!doc) docs.splice(idx, 1);

10
src/lib/langPack.ts

@ -1,4 +1,4 @@
import { MOUNT_CLASS_TO } from "../config/debug"; import DEBUG, { MOUNT_CLASS_TO } from "../config/debug";
import { safeAssign } from "../helpers/object"; import { safeAssign } from "../helpers/object";
import { capitalizeFirstLetter } from "../helpers/string"; import { capitalizeFirstLetter } from "../helpers/string";
import type lang from "../lang"; import type lang from "../lang";
@ -11,9 +11,12 @@ export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionChatCreate": "ActionCreateGroup", "messageActionChatCreate": "ActionCreateGroup",
"messageActionChatEditTitle": "ActionChangedTitle", "messageActionChatEditTitle": "ActionChangedTitle",
"messageActionChatEditPhoto": "ActionChangedPhoto", "messageActionChatEditPhoto": "ActionChangedPhoto",
"messageActionChatEditVideo": "ActionChangedVideo",
"messageActionChatDeletePhoto": "ActionRemovedPhoto", "messageActionChatDeletePhoto": "ActionRemovedPhoto",
"messageActionChatReturn": "ActionAddUserSelf", "messageActionChatReturn": "ActionAddUserSelf",
"messageActionChatReturnYou": "ActionAddUserSelfYou",
"messageActionChatJoined": "ActionAddUserSelfMega", "messageActionChatJoined": "ActionAddUserSelfMega",
"messageActionChatJoinedYou": "ChannelMegaJoined",
"messageActionChatAddUser": "ActionAddUser", "messageActionChatAddUser": "ActionAddUser",
"messageActionChatAddUsers": "ActionAddUser", "messageActionChatAddUsers": "ActionAddUser",
"messageActionChatLeave": "ActionLeftUser", "messageActionChatLeave": "ActionLeftUser",
@ -24,6 +27,7 @@ export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionChannelCreate": "ActionCreateChannel", "messageActionChannelCreate": "ActionCreateChannel",
"messageActionChannelEditTitle": "Chat.Service.Channel.UpdatedTitle", "messageActionChannelEditTitle": "Chat.Service.Channel.UpdatedTitle",
"messageActionChannelEditPhoto": "Chat.Service.Channel.UpdatedPhoto", "messageActionChannelEditPhoto": "Chat.Service.Channel.UpdatedPhoto",
"messageActionChannelEditVideo": "Chat.Service.Channel.UpdatedVideo",
"messageActionChannelDeletePhoto": "Chat.Service.Channel.RemovedPhoto", "messageActionChannelDeletePhoto": "Chat.Service.Channel.RemovedPhoto",
"messageActionHistoryClear": "HistoryCleared", "messageActionHistoryClear": "HistoryCleared",
@ -51,6 +55,8 @@ namespace I18n {
]).then(([langPack]) => { ]).then(([langPack]) => {
if(!langPack/* || true */) { if(!langPack/* || true */) {
return getLangPack('en'); return getLangPack('en');
} else if(DEBUG) {
return getLangPack(langPack.lang_code);
} else if(langPack.appVersion !== App.langPackVersion) { } else if(langPack.appVersion !== App.langPackVersion) {
return getLangPack(langPack.lang_code); return getLangPack(langPack.lang_code);
} }
@ -177,7 +183,7 @@ namespace I18n {
return ''; return '';
}); });
if(lastIndex !== (input.length - 1)) { if(lastIndex !== input.length) {
out.push(input.slice(lastIndex)); out.push(input.slice(lastIndex));
} }

3
src/lib/mtproto/networker.ts

@ -21,6 +21,7 @@ import HTTP from './transports/http';
import type TcpObfuscated from './transports/tcpObfuscated'; import type TcpObfuscated from './transports/tcpObfuscated';
import { bigInt2str, cmp, rightShift_, str2bigInt } from '../../vendor/leemon'; import { bigInt2str, cmp, rightShift_, str2bigInt } from '../../vendor/leemon';
import { forEachReverse } from '../../helpers/array';
//console.error('networker included!', new Error().stack); //console.error('networker included!', new Error().stack);
@ -196,7 +197,7 @@ export default class MTPNetworker {
} }
if(sentMessage.container) { if(sentMessage.container) {
sentMessage.inner.forEachReverse((innerSentMessageId, idx) => { forEachReverse(sentMessage.inner, (innerSentMessageId, idx) => {
const innerSentMessage = this.updateSentMessage(innerSentMessageId); const innerSentMessage = this.updateSentMessage(innerSentMessageId);
if(!innerSentMessage) { if(!innerSentMessage) {
sentMessage.inner.splice(idx, 1); sentMessage.inner.splice(idx, 1);

8
src/lib/polyfill.ts

@ -36,13 +36,6 @@ Uint8Array.prototype.toJSON = function() {
//return {type: 'bytes', value: [...this]}; //return {type: 'bytes', value: [...this]};
}; };
Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number, array?: Array<T>) => void) {
let length = this.length;
for(var i = length - 1; i >= 0; --i) {
callback(this[i], i, this);
}
};
Array.prototype.findAndSplice = function<T>(verify: (value: T, index?: number, array?: Array<T>) => boolean) { Array.prototype.findAndSplice = function<T>(verify: (value: T, index?: number, array?: Array<T>) => boolean) {
let index = this.findIndex(verify); let index = this.findIndex(verify);
return index !== -1 ? this.splice(index, 1)[0] : undefined; return index !== -1 ? this.splice(index, 1)[0] : undefined;
@ -88,7 +81,6 @@ declare global {
} }
interface Array<T> { interface Array<T> {
forEachReverse(callback: (value: T, index?: number, array?: Array<T>) => void): void;
findAndSplice(verify: (value: T, index?: number, array?: Array<T>) => boolean): T; findAndSplice(verify: (value: T, index?: number, array?: Array<T>) => boolean): T;
} }

6
src/lib/rootScope.ts

@ -6,10 +6,10 @@ import type { MyDialogFilter } from "./storages/filters";
import type { ConnectionStatusChange } from "../types"; import type { ConnectionStatusChange } from "../types";
import type { UserTyping } from "./appManagers/appChatsManager"; import type { UserTyping } from "./appManagers/appChatsManager";
import type Chat from "../components/chat/chat"; import type Chat from "../components/chat/chat";
import { UserAuth } from "./mtproto/mtproto_config"; import type { UserAuth } from "./mtproto/mtproto_config";
import { State } from "./appManagers/appStateManager"; import type { State } from "./appManagers/appStateManager";
import type { MyDraftMessage } from "./appManagers/appDraftsManager";
import EventListenerBase from "../helpers/eventListenerBase"; import EventListenerBase from "../helpers/eventListenerBase";
import { MyDraftMessage } from "./appManagers/appDraftsManager";
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
export type BroadcastEvents = { export type BroadcastEvents = {

5
src/lib/storages/filters.ts

@ -7,6 +7,7 @@ import type { AppUsersManager } from "../appManagers/appUsersManager";
import type _rootScope from "../rootScope"; import type _rootScope from "../rootScope";
import type {Dialog} from '../appManagers/appMessagesManager'; import type {Dialog} from '../appManagers/appMessagesManager';
import apiManager from "../mtproto/mtprotoworker"; import apiManager from "../mtproto/mtprotoworker";
import { forEachReverse } from "../../helpers/array";
export type MyDialogFilter = Modify<DialogFilter, { export type MyDialogFilter = Modify<DialogFilter, {
pinned_peers: number[], pinned_peers: number[],
@ -199,7 +200,7 @@ export default class FiltersStorage {
c[key] = c[key].map((peerId: number) => this.appPeersManager.getInputPeerById(peerId)); c[key] = c[key].map((peerId: number) => this.appPeersManager.getInputPeerById(peerId));
}); });
c.include_peers.forEachReverse((peerId, idx) => { forEachReverse(c.include_peers, (peerId, idx) => {
if(c.pinned_peers.includes(peerId)) { if(c.pinned_peers.includes(peerId)) {
c.include_peers.splice(idx, 1); c.include_peers.splice(idx, 1);
} }
@ -229,7 +230,7 @@ export default class FiltersStorage {
filter[key] = filter[key].map((peer: any) => this.appPeersManager.getPeerId(peer)); filter[key] = filter[key].map((peer: any) => this.appPeersManager.getPeerId(peer));
}); });
filter.include_peers.forEachReverse((peerId, idx) => { forEachReverse(filter.include_peers, (peerId, idx) => {
if(filter.pinned_peers.includes(peerId)) { if(filter.pinned_peers.includes(peerId)) {
filter.include_peers.splice(idx, 1); filter.include_peers.splice(idx, 1);
} }

64
src/scripts/in/schema_additional_params.json

@ -144,4 +144,68 @@
"params": [ "params": [
{"name": "rTitle", "type": "string"} {"name": "rTitle", "type": "string"}
] ]
}, {
"predicate": "messageActionChatLeave",
"params": [
{"name": "user_id", "type": "number"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChannelDeletePhoto",
"params": [],
"type": "MessageAction"
}, {
"predicate": "messageActionChannelEditTitle",
"params": [
{"name": "title", "type": "string"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChannelEditPhoto",
"params": [
{"name": "photo", "type": "Photo"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChannelEditVideo",
"params": [
{"name": "photo", "type": "Photo"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChatEditVideo",
"params": [
{"name": "photo", "type": "Photo"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChatAddUsers",
"params": [
{"name": "users", "type": "Array<number>"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChatJoined",
"params": [
{"name": "users", "type": "Array<number>"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChatReturn",
"params": [
{"name": "users", "type": "Array<number>"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChatJoinedYou",
"params": [
{"name": "users", "type": "Array<number>"}
],
"type": "MessageAction"
}, {
"predicate": "messageActionChatReturnYou",
"params": [
{"name": "users", "type": "Array<number>"}
],
"type": "MessageAction"
}] }]

7
src/scss/partials/_chatlist.scss

@ -156,7 +156,6 @@ ul.chatlist {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
vertical-align: middle;
//margin: .1rem 0; //margin: .1rem 0;
line-height: 27px; line-height: 27px;
} }
@ -286,6 +285,8 @@ ul.chatlist {
margin-right: .1rem; margin-right: .1rem;
//margin-top: .3rem; //margin-top: .3rem;
margin-top: -.3rem; margin-top: -.3rem;
display: inline-block;
vertical-align: middle;
&[class*=" tgico-"] { &[class*=" tgico-"] {
color: $color-green; color: $color-green;
@ -297,6 +298,10 @@ ul.chatlist {
} }
} }
.message-time {
vertical-align: middle;
}
.dialog-subtitle-badge { .dialog-subtitle-badge {
margin-top: 4px; margin-top: 4px;
margin-right: -3px; margin-right: -3px;

Loading…
Cancel
Save