Fix document name XSS

This commit is contained in:
Eduard Kuzmenko 2021-07-20 19:11:58 +03:00
parent ccb8d847e2
commit 25e5bcf22f
24 changed files with 275 additions and 130 deletions

View File

@ -868,8 +868,7 @@ export default class AppSearchSuper {
} }
if(!this.membersList) { if(!this.membersList) {
this.membersList = new SortedUserList(); this.membersList = new SortedUserList({lazyLoadQueue: this.lazyLoadQueue, rippleEnabled: false});
this.membersList.lazyLoadQueue = this.lazyLoadQueue;
this.membersList.list.addEventListener('click', (e) => { this.membersList.list.addEventListener('click', (e) => {
const li = findUpTag(e.target, 'LI'); const li = findUpTag(e.target, 'LI');
if(!li) { if(!li) {

View File

@ -272,7 +272,7 @@ function wrapAudio(audioEl: AudioElement) {
const senderTitle = audioEl.showSender ? appMessagesManager.getSenderToPeerText(message) : ''; const senderTitle = audioEl.showSender ? appMessagesManager.getSenderToPeerText(message) : '';
let title = doc.type === 'voice' ? senderTitle : (doc.audioTitle || doc.file_name); let title = doc.type === 'voice' ? senderTitle : (doc.audioTitle || doc.fileName);
let subtitle: string; let subtitle: string;
if(doc.type === 'voice') { if(doc.type === 'voice') {

View File

@ -56,7 +56,7 @@ export default class ChatAudio extends PinnedContainer {
//subtitle = 'Voice message'; //subtitle = 'Voice message';
subtitle = formatDate(message.date, false, false); subtitle = formatDate(message.date, false, false);
} else { } else {
title = doc.audioTitle || doc.file_name; title = doc.audioTitle || doc.fileName;
subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : 'Unknown Artist'; subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : 'Unknown Artist';
} }

View File

@ -20,6 +20,7 @@ import calcImageInBox from "../../helpers/calcImageInBox";
import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed"; import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd"; import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd";
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import RichTextProcessor from "../../lib/richtextprocessor";
type SendFileParams = Partial<{ type SendFileParams = Partial<{
file: File, file: File,
@ -298,6 +299,7 @@ export default class PopupNewMedia extends PopupElement {
_: 'document', _: 'document',
file: file, file: file,
file_name: file.name || '', file_name: file.name || '',
fileName: file.name ? RichTextProcessor.wrapEmojiText(file.name) : '',
size: file.size, size: file.size,
type: isPhoto ? 'photo' : 'doc' type: isPhoto ? 'photo' : 'doc'
} as MyDocument; } as MyDocument;

View File

@ -575,12 +575,16 @@ export class SettingSection {
constructor(options: { constructor(options: {
name?: LangPackKey, name?: LangPackKey,
caption?: LangPackKey | true, caption?: LangPackKey | true,
noDelimiter?: boolean noDelimiter?: boolean,
fakeGradientDelimiter?: boolean
}) { }) {
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add('sidebar-left-section'); this.container.classList.add('sidebar-left-section');
if(!options.noDelimiter) { if(options.fakeGradientDelimiter) {
this.container.append(generateDelimiter());
this.container.classList.add('with-fake-delimiter');
} else if(!options.noDelimiter) {
const hr = document.createElement('hr'); const hr = document.createElement('hr');
this.container.append(hr); this.container.append(hr);
} else { } else {
@ -620,6 +624,12 @@ export const generateSection = (appendTo: Scrollable, name?: LangPackKey, captio
return section.content; return section.content;
}; };
export const generateDelimiter = () => {
const delimiter = document.createElement('div');
delimiter.classList.add('gradient-delimiter');
return delimiter;
};
const appSidebarLeft = new AppSidebarLeft(); const appSidebarLeft = new AppSidebarLeft();
MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft; MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft;
export default appSidebarLeft; export default appSidebarLeft;

View File

@ -17,7 +17,7 @@ export default class AppArchivedTab extends SliderSuperTab {
if(!appDialogsManager.chatLists[AppArchivedTab.filterId]) { if(!appDialogsManager.chatLists[AppArchivedTab.filterId]) {
const chatList = appDialogsManager.createChatList(); const chatList = appDialogsManager.createChatList();
appDialogsManager.generateScrollable(chatList, AppArchivedTab.filterId); appDialogsManager.generateScrollable(chatList, AppArchivedTab.filterId).container.append(chatList);
appDialogsManager.setListClickListener(chatList, null, true); appDialogsManager.setListClickListener(chatList, null, true);
//appDialogsManager.setListClickListener(archivedChatList, null, true); // * to test peer changing //appDialogsManager.setListClickListener(archivedChatList, null, true); // * to test peer changing
} }

View File

@ -8,7 +8,6 @@ import { SliderSuperTab } from "../../slider";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
import rootScope from "../../../lib/rootScope";
import InputSearch from "../../inputSearch"; import InputSearch from "../../inputSearch";
import { isMobile } from "../../../helpers/userAgent"; import { isMobile } from "../../../helpers/userAgent";
import { canFocus } from "../../../helpers/dom/canFocus"; import { canFocus } from "../../../helpers/dom/canFocus";
@ -67,7 +66,7 @@ export default class AppContactsTab extends SliderSuperTab {
if(this.promise) return this.promise; if(this.promise) return this.promise;
this.scrollable.onScrolledBottom = null; this.scrollable.onScrolledBottom = null;
this.promise = appUsersManager.getContacts(query).then(_contacts => { this.promise = appUsersManager.getContacts(query, undefined, 'online').then(contacts => {
this.promise = null; this.promise = null;
if(!this.alive) { if(!this.alive) {
@ -75,42 +74,26 @@ export default class AppContactsTab extends SliderSuperTab {
return; return;
} }
const contacts = [..._contacts];
if(!query) {
contacts.findAndSplice(u => u === rootScope.myId);
}
/* if(query && 'saved messages'.includes(query.toLowerCase())) {
contacts.unshift(rootScope.myID);
} */
let sorted = contacts
.map(userId => {
let user = appUsersManager.getUser(userId);
let status = appUsersManager.getUserStatusForSort(user.status);
return {user, status};
})
.sort((a, b) => b.status - a.status);
let renderPage = () => { let renderPage = () => {
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; const pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
let arr = sorted.splice(0, pageCount); // надо splice! const arr = contacts.splice(0, pageCount); // надо splice!
arr.forEach(({user}) => { arr.forEach((peerId) => {
let {dialog, dom} = appDialogsManager.addDialogNew({ const {dom} = appDialogsManager.addDialogNew({
dialog: user.id, dialog: peerId,
container: this.list, container: this.list,
drawStatus: false, drawStatus: false,
avatarSize: 48, avatarSize: 48,
autonomous: true autonomous: true
}); });
let status = appUsersManager.getUserStatusString(user.id); const status = appUsersManager.getUserStatusString(peerId);
dom.lastMessageSpan.append(status); dom.lastMessageSpan.append(status);
}); });
if(!sorted.length) renderPage = undefined; if(!contacts.length) {
renderPage = undefined;
}
}; };
renderPage(); renderPage();

View File

@ -201,7 +201,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
const c = section.generateContentElement(); const c = section.generateContentElement();
c.classList.add('chatlist-container'); c.classList.add('chatlist-container');
const list = appDialogsManager.createChatList(); const list = appDialogsManager.createChatList({new: true});
c.append(list); c.append(list);
attachClickEvent(list, (e) => { attachClickEvent(list, (e) => {

View File

@ -26,7 +26,7 @@ import { Chat, Message, MessageAction, ChatFull, Photo } from "../../../layer";
import Button from "../../button"; import Button from "../../button";
import ButtonIcon from "../../buttonIcon"; import ButtonIcon from "../../buttonIcon";
import I18n, { i18n, LangPackKey } from "../../../lib/langPack"; import I18n, { i18n, LangPackKey } from "../../../lib/langPack";
import { SettingSection } from "../../sidebarLeft"; import { generateDelimiter, SettingSection } from "../../sidebarLeft";
import Row from "../../row"; import Row from "../../row";
import { copyTextToClipboard } from "../../../helpers/clipboard"; import { copyTextToClipboard } from "../../../helpers/clipboard";
import { toast, toastNew } from "../../toast"; import { toast, toastNew } from "../../toast";
@ -572,10 +572,7 @@ class PeerProfile {
this.section.content.append(this.phone.container, this.username.container, this.bio.container, this.notifications.container); this.section.content.append(this.phone.container, this.username.container, this.bio.container, this.notifications.container);
const delimiter = document.createElement('div'); this.element.append(this.section.container, generateDelimiter());
delimiter.classList.add('gradient-delimiter');
this.element.append(this.section.container, delimiter);
this.notifications.checkboxField.input.addEventListener('change', (e) => { this.notifications.checkboxField.input.addEventListener('change', (e) => {
if(!e.isTrusted) { if(!e.isTrusted) {

View File

@ -36,7 +36,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
div.classList.add('chatlist-container'); div.classList.add('chatlist-container');
section.content.insertBefore(div, section.title); section.content.insertBefore(div, section.title);
const list = appDialogsManager.createChatList(); const list = appDialogsManager.createChatList({new: true});
div.append(list); div.append(list);
const {dom} = appDialogsManager.addDialogNew({ const {dom} = appDialogsManager.addDialogNew({

View File

@ -12,6 +12,7 @@ import { insertInDescendSortedArray } from "../helpers/array";
import isInDOM from "../helpers/dom/isInDOM"; import isInDOM from "../helpers/dom/isInDOM";
import positionElementByIndex from "../helpers/dom/positionElementByIndex"; import positionElementByIndex from "../helpers/dom/positionElementByIndex";
import replaceContent from "../helpers/dom/replaceContent"; import replaceContent from "../helpers/dom/replaceContent";
import { safeAssign } from "../helpers/object";
type SortedUser = { type SortedUser = {
peerId: number, peerId: number,
@ -19,14 +20,24 @@ type SortedUser = {
dom: DialogDom dom: DialogDom
}; };
export default class SortedUserList { export default class SortedUserList {
public static SORT_INTERVAL = 30e3; protected static SORT_INTERVAL = 30e3;
protected users: Map<number, SortedUser>;
protected sorted: Array<SortedUser>;
public list: HTMLUListElement; public list: HTMLUListElement;
public users: Map<number, SortedUser>;
public sorted: Array<SortedUser>;
public lazyLoadQueue: LazyLoadQueueIntersector;
constructor() { protected lazyLoadQueue: LazyLoadQueueIntersector;
this.list = appDialogsManager.createChatList(); protected avatarSize = 48;
protected rippleEnabled = true;
constructor(options: Partial<{
lazyLoadQueue: SortedUserList['lazyLoadQueue'],
avatarSize: SortedUserList['avatarSize'],
rippleEnabled: SortedUserList['rippleEnabled'],
new: boolean
}> = {}) {
safeAssign(this, options);
this.list = appDialogsManager.createChatList({new: options.new});
this.users = new Map(); this.users = new Map();
this.sorted = []; this.sorted = [];
@ -76,10 +87,10 @@ export default class SortedUserList {
dialog: peerId, dialog: peerId,
container: false, container: false,
drawStatus: false, drawStatus: false,
avatarSize: 48, avatarSize: this.avatarSize,
autonomous: true, autonomous: true,
meAsSaved: false, meAsSaved: false,
rippleEnabled: false, rippleEnabled: this.rippleEnabled,
lazyLoadQueue: this.lazyLoadQueue lazyLoadQueue: this.lazyLoadQueue
}); });

View File

@ -507,7 +507,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
} }
//let fileName = stringMiddleOverflow(doc.file_name || 'Unknown.file', 26); //let fileName = stringMiddleOverflow(doc.file_name || 'Unknown.file', 26);
let fileName = doc.file_name || 'Unknown.file'; let fileName = doc.fileName || 'Unknown.file';
let size = formatBytes(doc.size); let size = formatBytes(doc.size);
if(withTime) { if(withTime) {

1
src/layer.d.ts vendored
View File

@ -3133,6 +3133,7 @@ export namespace Document {
h?: number, h?: number,
w?: number, w?: number,
file_name?: string, file_name?: string,
fileName?: string,
file?: File, file?: File,
duration?: number, duration?: number,
audioTitle?: string, audioTitle?: string,

View File

@ -35,7 +35,7 @@ import { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
import lottieLoader from "../lottieLoader"; import lottieLoader from "../lottieLoader";
import { wrapLocalSticker } from "../../components/wrappers"; import { wrapLocalSticker } from "../../components/wrappers";
import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder"; import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder";
import appSidebarLeft from "../../components/sidebarLeft"; import appSidebarLeft, { SettingSection } from "../../components/sidebarLeft";
import { attachClickEvent } from "../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../helpers/dom/clickEvent";
import positionElementByIndex from "../../helpers/dom/positionElementByIndex"; import positionElementByIndex from "../../helpers/dom/positionElementByIndex";
import replaceContent from "../../helpers/dom/replaceContent"; import replaceContent from "../../helpers/dom/replaceContent";
@ -43,6 +43,8 @@ import ConnectionStatusComponent from "../../components/connectionStatus";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl"; import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import { fastRafPromise } from "../../helpers/schedulers"; import { fastRafPromise } from "../../helpers/schedulers";
import appPhotosManager from "./appPhotosManager";
import SortedUserList from "../../components/sortedUserList";
export type DialogDom = { export type DialogDom = {
avatarEl: AvatarElement, avatarEl: AvatarElement,
@ -106,6 +108,7 @@ export class AppDialogsManager {
private lastActiveElements: Set<HTMLElement> = new Set(); private lastActiveElements: Set<HTMLElement> = new Set();
private offsets: {top: number, bottom: number} = {top: 0, bottom: 0}; private offsets: {top: number, bottom: number} = {top: 0, bottom: 0};
loadContacts: () => void;
constructor() { constructor() {
this.chatsPreloader = putPreloader(null, true); this.chatsPreloader = putPreloader(null, true);
@ -436,7 +439,7 @@ export class AppDialogsManager {
appDraftsManager.addMissedDialogs(); appDraftsManager.addMissedDialogs();
} }
return this.loadDialogs(); return this.onChatsScroll();
} }
/* private getOffset(side: 'top' | 'bottom'): {index: number, pos: number} { /* private getOffset(side: 'top' | 'bottom'): {index: number, pos: number} {
@ -533,7 +536,7 @@ export class AppDialogsManager {
this.offsets.top = this.offsets.bottom = 0; this.offsets.top = this.offsets.bottom = 0;
this.loadDialogsPromise = undefined; this.loadDialogsPromise = undefined;
this.chatList = this.chatLists[this.filterId]; this.chatList = this.chatLists[this.filterId];
this.loadDialogs(); this.onChatsScroll();
}; };
private setFilterUnreadCount(filterId: number, folder?: Dialog[]) { private setFilterUnreadCount(filterId: number, folder?: Dialog[]) {
@ -593,7 +596,6 @@ export class AppDialogsManager {
const scrollable = new Scrollable(null, 'CL', 500); const scrollable = new Scrollable(null, 'CL', 500);
scrollable.container.addEventListener('scroll', this.onChatsRegularScroll); scrollable.container.addEventListener('scroll', this.onChatsRegularScroll);
scrollable.container.dataset.filterId = '' + filterId; scrollable.container.dataset.filterId = '' + filterId;
scrollable.container.append(list);
scrollable.onScrolledTop = this.onChatsScrollTop; scrollable.onScrolledTop = this.onChatsScrollTop;
scrollable.onScrolledBottom = this.onChatsScroll; scrollable.onScrolledBottom = this.onChatsScroll;
scrollable.setVirtualContainer(list); scrollable.setVirtualContainer(list);
@ -627,6 +629,23 @@ export class AppDialogsManager {
const ul = this.createChatList(); const ul = this.createChatList();
const scrollable = this.generateScrollable(ul, filter.id); const scrollable = this.generateScrollable(ul, filter.id);
scrollable.container.classList.add('chatlist-parts');
/* const parts = document.createElement('div');
parts.classList.add('chatlist-parts'); */
const top = document.createElement('div');
top.classList.add('chatlist-top');
const bottom = document.createElement('div');
bottom.classList.add('chatlist-bottom');
top.append(ul);
scrollable.container.append(top, bottom);
/* parts.append(top, bottom);
scrollable.container.append(parts); */
const div = scrollable.container; const div = scrollable.container;
//this.folders.container.append(div); //this.folders.container.append(div);
positionElementByIndex(scrollable.container, this.folders.container, filter.orderIndex); positionElementByIndex(scrollable.container, this.folders.container, filter.orderIndex);
@ -654,7 +673,7 @@ export class AppDialogsManager {
} }
} }
private loadDialogs(side: SliceSides = 'bottom') { private loadDialogs(side: SliceSides) {
/* if(testScroll) { /* if(testScroll) {
return; return;
} */ } */
@ -779,13 +798,13 @@ export class AppDialogsManager {
return {container, header, subtitle}; return {container, header, subtitle};
} }
private onListLengthChange = () => { private checkIfPlaceholderNeeded() {
//return;
if(this.filterId === 1) { if(this.filterId === 1) {
return; return;
} }
let placeholderContainer = (Array.from(this.chatList.parentElement.children) as HTMLElement[]).find(el => el.matches('.empty-placeholder')); const part = this.chatList.parentElement as HTMLElement;
let placeholderContainer = (Array.from(part.children) as HTMLElement[]).find(el => el.matches('.empty-placeholder'));
const needPlaceholder = this.scroll.loadedAll.bottom && !this.chatList.childElementCount/* || true */; const needPlaceholder = this.scroll.loadedAll.bottom && !this.chatList.childElementCount/* || true */;
// this.chatList.style.display = 'none'; // this.chatList.style.display = 'none';
@ -793,6 +812,7 @@ export class AppDialogsManager {
return; return;
} else if(!needPlaceholder) { } else if(!needPlaceholder) {
if(placeholderContainer) { if(placeholderContainer) {
part.classList.remove('with-placeholder');
placeholderContainer.remove(); placeholderContainer.remove();
} }
@ -863,7 +883,55 @@ export class AppDialogsManager {
placeholderContainer.append(button); placeholderContainer.append(button);
} }
this.chatList.parentElement.append(placeholderContainer); part.append(placeholderContainer);
part.classList.add('with-placeholder');
}
private onListLengthChange = () => {
return;
this.checkIfPlaceholderNeeded();
if(this.filterId > 0) return;
const bottom = this.chatList.parentElement.nextElementSibling as HTMLElement;
if(bottom.childElementCount) return;
bottom.parentElement.classList.add('with-contacts');
const section = new SettingSection({
name: 'Contacts',
noDelimiter: true,
fakeGradientDelimiter: true
});
section.container.classList.add('hide');
appUsersManager.getContacts(undefined, undefined, 'online').then(contacts => {
const sortedUserList = new SortedUserList({avatarSize: 42, new: true});
this.loadContacts = () => {
const pageCount = appPhotosManager.windowH / 60 | 0;
const arr = contacts.splice(0, pageCount);
arr.forEach((peerId) => {
sortedUserList.add(peerId);
});
if(!contacts.length) {
this.loadContacts = undefined;
}
};
this.loadContacts();
const list = sortedUserList.list;
list.classList.add('chatlist-new');
this.setListClickListener(list);
section.content.append(list);
section.container.classList.remove('hide');
});
bottom.append(section.container);
}; };
public onChatsRegularScroll = () => { public onChatsRegularScroll = () => {
@ -987,9 +1055,16 @@ export class AppDialogsManager {
}; };
public onChatsScroll = (side: SliceSides = 'bottom') => { public onChatsScroll = (side: SliceSides = 'bottom') => {
if(this.scroll.loadedAll[side] || this.loadDialogsPromise) return; if(this.scroll.loadedAll[side]) {
if(this.loadContacts) {
this.loadContacts();
}
return;
} else if(this.loadDialogsPromise) return this.loadDialogsPromise;
this.log('onChatsScroll', side); this.log('onChatsScroll', side);
this.loadDialogs(side); return this.loadDialogs(side);
}; };
public setListClickListener(list: HTMLUListElement, onFound?: () => void, withContext = false, autonomous = false, openInner = false) { public setListClickListener(list: HTMLUListElement, onFound?: () => void, withContext = false, autonomous = false, openInner = false) {
@ -1050,15 +1125,20 @@ export class AppDialogsManager {
} }
} }
public createChatList(/* options: { public createChatList(options: {
avatarSize?: number, // avatarSize?: number,
handheldsSize?: number, // handheldsSize?: number,
//size?: number, // size?: number,
} = {} */) { new?: boolean
} = {}) {
const list = document.createElement('ul'); const list = document.createElement('ul');
list.classList.add('chatlist'/* , list.classList.add('chatlist'/* ,
'chatlist-avatar-' + (options.avatarSize || 54) *//* , 'chatlist-' + (options.size || 72) */); 'chatlist-avatar-' + (options.avatarSize || 54) *//* , 'chatlist-' + (options.size || 72) */);
if(options.new) {
list.classList.add('chatlist-new');
}
/* if(options.handheldsSize) { /* if(options.handheldsSize) {
list.classList.add('chatlist-handhelds-' + options.handheldsSize); list.classList.add('chatlist-handhelds-' + options.handheldsSize);
} */ } */

View File

@ -93,12 +93,13 @@ export class AppDocsManager {
switch(attribute._) { switch(attribute._) {
case 'documentAttributeFilename': case 'documentAttributeFilename':
doc.file_name = RichTextProcessor.wrapPlainText(attribute.file_name); doc.file_name = RichTextProcessor.wrapPlainText(attribute.file_name);
doc.fileName = RichTextProcessor.wrapEmojiText(attribute.file_name);
break; break;
case 'documentAttributeAudio': case 'documentAttributeAudio':
doc.duration = attribute.duration; doc.duration = attribute.duration;
doc.audioTitle = attribute.title; doc.audioTitle = RichTextProcessor.wrapEmojiText(attribute.title);
doc.audioPerformer = attribute.performer; doc.audioPerformer = RichTextProcessor.wrapEmojiText(attribute.performer);
doc.type = attribute.pFlags.voice && doc.mime_type === 'audio/ogg' ? 'voice' : 'audio'; doc.type = attribute.pFlags.voice && doc.mime_type === 'audio/ogg' ? 'voice' : 'audio';
/* if(apiDoc.type === 'audio') { /* if(apiDoc.type === 'audio') {
apiDoc.supportsStreaming = true; apiDoc.supportsStreaming = true;
@ -182,7 +183,7 @@ export class AppDocsManager {
if(doc.type === 'voice' || doc.type === 'round') { if(doc.type === 'voice' || doc.type === 'round') {
// browser will identify extension // browser will identify extension
doc.file_name = doc.type + '_' + getFullDate(new Date(doc.date * 1000), {monthAsNumber: true, leadingZero: true}).replace(/[:\.]/g, '-').replace(', ', '_'); doc.file_name = doc.fileName = doc.type + '_' + getFullDate(new Date(doc.date * 1000), {monthAsNumber: true, leadingZero: true}).replace(/[:\.]/g, '-').replace(', ', '_');
} }
if(apiManager.isServiceWorkerOnline()) { if(apiManager.isServiceWorkerOnline()) {
@ -201,7 +202,7 @@ export class AppDocsManager {
// doc.url = ''; // * this will break upload urls // doc.url = ''; // * this will break upload urls
if(!doc.file_name) { if(!doc.file_name) {
doc.file_name = ''; doc.file_name = doc.fileName = '';
} }
if(doc.mime_type === 'application/x-tgsticker' && doc.file_name === 'AnimatedSticker.tgs') { if(doc.mime_type === 'application/x-tgsticker' && doc.file_name === 'AnimatedSticker.tgs') {

View File

@ -2626,7 +2626,7 @@ export class AppMessagesManager {
addPart(undefined, ((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker'); addPart(undefined, ((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker');
text = ''; text = '';
} else { } else {
addPart(document.file_name, undefined, message.message); addPart(undefined, plain ? document.file_name : RichTextProcessor.wrapEmojiText(document.file_name), message.message);
} }
break; break;

View File

@ -261,7 +261,7 @@ export class AppUsersManager {
return arr.filter(Boolean).join(' '); return arr.filter(Boolean).join(' ');
} }
public getContacts(query?: string, includeSaved = false) { public getContacts(query?: string, includeSaved = false, sortBy: 'name' | 'online' | 'none' = 'name') {
return this.fillContacts().then(_contactsList => { return this.fillContacts().then(_contactsList => {
let contactsList = [..._contactsList]; let contactsList = [..._contactsList];
if(query) { if(query) {
@ -271,30 +271,27 @@ export class AppUsersManager {
contactsList = filteredContactsList; contactsList = filteredContactsList;
} }
contactsList.sort((userId1: number, userId2: number) => { if(sortBy === 'name') {
const sortName1 = (this.users[userId1] || {}).sortName || ''; contactsList.sort((userId1, userId2) => {
const sortName2 = (this.users[userId2] || {}).sortName || ''; const sortName1 = (this.users[userId1] || {}).sortName || '';
const sortName2 = (this.users[userId2] || {}).sortName || '';
return sortName1.localeCompare(sortName2); return sortName1.localeCompare(sortName2);
}); });
} else if(sortBy === 'online') {
contactsList.sort((userId1, userId2) => {
const status1 = appUsersManager.getUserStatusForSort(appUsersManager.getUser(userId1).status);
const status2 = appUsersManager.getUserStatusForSort(appUsersManager.getUser(userId2).status);
return status2 - status1;
});
}
contactsList.findAndSplice(p => p === rootScope.myId);
if(includeSaved) { if(includeSaved) {
if(this.testSelfSearch(query)) { if(this.testSelfSearch(query)) {
contactsList.findAndSplice(p => p === rootScope.myId);
contactsList.unshift(rootScope.myId); contactsList.unshift(rootScope.myId);
} }
} }
/* contactsList.sort((userId1: number, userId2: number) => {
const sortName1 = (this.users[userId1] || {}).sortName || '';
const sortName2 = (this.users[userId2] || {}).sortName || '';
if(sortName1 === sortName2) {
return 0;
}
return sortName1 > sortName2 ? 1 : -1;
}); */
return contactsList; return contactsList;
}); });
} }

View File

@ -748,7 +748,7 @@ namespace RichTextProcessor {
}); });
} }
export function wrapPlainText(text: any) { export function wrapPlainText(text: string) {
if(emojiSupported) { if(emojiSupported) {
return text; return text;
} }
@ -757,32 +757,32 @@ namespace RichTextProcessor {
return ''; return '';
} }
text = text.replace(/\ufe0f/g, '', text); text = text.replace(/\ufe0f/g, '');
var match; var match;
var raw = text; var raw = text;
var text: any = [], const arr: string[] = [];
emojiTitle; let emojiTitle;
fullRegExp.lastIndex = 0; fullRegExp.lastIndex = 0;
while((match = raw.match(fullRegExp))) { while((match = raw.match(fullRegExp))) {
text.push(raw.substr(0, match.index)) arr.push(raw.substr(0, match.index))
if(match[8]) { if(match[8]) {
// @ts-ignore // @ts-ignore
const emojiCode = EmojiHelper.emojiMap[match[8]]; const emojiCode = EmojiHelper.emojiMap[match[8]];
if(emojiCode && if(emojiCode &&
// @ts-ignore // @ts-ignore
(emojiTitle = emojiData[emojiCode][1][0])) { (emojiTitle = emojiData[emojiCode][1][0])) {
text.push(':' + emojiTitle + ':'); arr.push(':' + emojiTitle + ':');
} else { } else {
text.push(match[0]); arr.push(match[0]);
} }
} else { } else {
text.push(match[0]); arr.push(match[0]);
} }
raw = raw.substr(match.index + match[0].length); raw = raw.substr(match.index + match[0].length);
} }
text.push(raw); arr.push(raw);
return text.join(''); return arr.join('');
} }
export function wrapEmojiText(text: string) { export function wrapEmojiText(text: string) {

View File

@ -6,6 +6,7 @@
{"name": "h", "type": "number"}, {"name": "h", "type": "number"},
{"name": "w", "type": "number"}, {"name": "w", "type": "number"},
{"name": "file_name", "type": "string"}, {"name": "file_name", "type": "string"},
{"name": "fileName", "type": "string"},
{"name": "file", "type": "File"}, {"name": "file", "type": "File"},
{"name": "duration", "type": "number"}, {"name": "duration", "type": "number"},
{"name": "audioTitle", "type": "string"}, {"name": "audioTitle", "type": "string"},

View File

@ -399,6 +399,89 @@ ul.chatlist {
li.is-muted .unread { li.is-muted .unread {
background-color: var(--secondary-color); background-color: var(--secondary-color);
} }
&-parts {
/* width: 100%;
min-height: 100%;
display: flex;
flex-direction: column; */
&.with-contacts {
.chatlist-top:not(.with-placeholder) {
height: auto;
padding-bottom: .5rem;
}
.chatlist-top.with-placeholder {
height: 24.125rem;
.empty-placeholder-dialogs {
top: 50%;
}
}
}
}
// do not set position: relative here. will break chatlist slicing.
&-top {
// flex: 0 0 auto;
height: 100%;
}
&-bottom {
// flex: 1 1 auto;
max-height: 36.375rem;
.sidebar-left-section {
padding-bottom: 0;
}
.chatlist-new {
padding: 0;
margin-top: -.5rem;
li {
height: 3.5rem;
}
.user-caption {
padding-left: 1.125rem;
}
.dialog-subtitle {
margin-top: .0625rem;
}
}
}
// * supernew correct layout
&-new {
li {
height: 4.5rem;
padding: 0 .75rem;
align-items: center;
}
.user-caption {
padding-left: .75rem;
}
p {
height: auto !important;
}
span {
line-height: var(--line-height) !important;
}
.dialog-subtitle {
margin-top: .125rem;
}
.user-last-message {
font-size: .875rem;
}
}
} }
// use together like class="chatlist-container contacts-container" // use together like class="chatlist-container contacts-container"

View File

@ -830,6 +830,10 @@
user-select: none; user-select: none;
padding: .5rem 0 1rem; padding: .5rem 0 1rem;
&.with-fake-delimiter {
padding-top: 0;
}
@include respond-to(handhelds) { @include respond-to(handhelds) {
padding-bottom: .5rem; padding-bottom: .5rem;
} }
@ -1182,6 +1186,9 @@
} }
.empty-placeholder { .empty-placeholder {
// left: 50%;
// transform: translate(-50%, -50%);
// position: absolute;
top: 40%; top: 40%;
transform: translateY(-50%); transform: translateY(-50%);
text-align: center; text-align: center;
@ -1190,6 +1197,7 @@
width: 21rem !important; width: 21rem !important;
margin: 0 auto; margin: 0 auto;
padding: 0 1rem; padding: 0 1rem;
position: relative;
.media-sticker-wrapper { .media-sticker-wrapper {
width: 128px; width: 128px;

View File

@ -708,35 +708,8 @@
padding: 0 0 .5rem; padding: 0 0 .5rem;
} }
// * supernew and correct layout .chatlist-new {
.chatlist {
padding: 0; padding: 0;
li {
height: 72px;
padding: 0 .75rem;
align-items: center;
}
.user-caption {
padding-left: .75rem;
}
p {
height: auto;
}
span {
line-height: 1.3125;
}
.dialog-subtitle {
margin-top: .125rem;
}
.user-last-message {
font-size: .875rem;
}
} }
} }

View File

@ -147,12 +147,12 @@
display: flex; display: flex;
} }
> div:not(.scroll-padding) { /* > div:not(.scroll-padding) {
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
//overflow: hidden; //overflow: hidden;
position: relative; position: relative;
} } */
} }
/* &[data-animation="tabs"] { /* &[data-animation="tabs"] {

View File

@ -1212,7 +1212,6 @@ middle-ellipsis-element {
.gradient-delimiter { .gradient-delimiter {
width: 100%; width: 100%;
height: .75rem; height: .75rem;
display: flex;
background-color: var(--background-color-true); background-color: var(--background-color-true);
position: relative; position: relative;