Search multiselect
Fix keyboard inputs on iOS Alt+ArrowUp/ArrowDown shortcut
This commit is contained in:
parent
30e923f119
commit
c2604ec14d
@ -1684,7 +1684,9 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
onForwardClick = () => {
|
||||
if(this.currentMessageId) {
|
||||
//appSidebarRight.forwardTab.open([this.currentMessageId]);
|
||||
new PopupForward(this.currentPeerId, [this.currentMessageId], () => {
|
||||
new PopupForward({
|
||||
[this.currentPeerId]: [this.currentMessageId]
|
||||
}, () => {
|
||||
return this.close();
|
||||
});
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import AppMediaViewer from "./appMediaViewer";
|
||||
import { SearchGroup, SearchGroupType } from "./appSearch";
|
||||
import { horizontalMenu } from "./horizontalMenu";
|
||||
import LazyLoadQueue from "./lazyLoadQueue";
|
||||
import { putPreloader } from "./misc";
|
||||
import { attachContextMenuListener, openBtnMenu, positionMenu, putPreloader } from "./misc";
|
||||
import { ripple } from "./ripple";
|
||||
import Scrollable, { ScrollableX } from "./scrollable";
|
||||
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
|
||||
@ -42,6 +42,14 @@ import { isTouchSupported } from "../helpers/touchSupport";
|
||||
import handleTabSwipe from "../helpers/dom/handleTabSwipe";
|
||||
import windowSize from "../helpers/windowSize";
|
||||
import { formatPhoneNumber } from "../helpers/formatPhoneNumber";
|
||||
import ButtonMenu, { ButtonMenuItemOptions } from "./buttonMenu";
|
||||
import PopupForward from "./popups/forward";
|
||||
import PopupDeleteMessages from "./popups/deleteMessages";
|
||||
import Row from "./row";
|
||||
import htmlToDocumentFragment from "../helpers/dom/htmlToDocumentFragment";
|
||||
import { SearchSelection } from "./chat/selection";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
import { attachClickEvent, simulateClickEvent } from "../helpers/dom/clickEvent";
|
||||
|
||||
//const testScroll = false;
|
||||
|
||||
@ -69,6 +77,149 @@ export type SearchSuperMediaTab = {
|
||||
scroll?: {scrollTop: number, scrollHeight: number}
|
||||
};
|
||||
|
||||
class SearchContextMenu {
|
||||
private buttons: (ButtonMenuItemOptions & {verify?: () => boolean, withSelection?: true})[];
|
||||
private element: HTMLElement;
|
||||
private target: HTMLElement;
|
||||
private peerId: number;
|
||||
private mid: number;
|
||||
private isSelected: boolean;
|
||||
|
||||
constructor(
|
||||
private attachTo: HTMLElement,
|
||||
private searchSuper: AppSearchSuper
|
||||
) {
|
||||
const onContextMenu = (e: MouseEvent) => {
|
||||
if(this.init) {
|
||||
this.init();
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
let item: HTMLElement;
|
||||
try {
|
||||
item = findUpClassName(e.target, 'search-super-item');
|
||||
} catch(e) {}
|
||||
|
||||
if(!item) return;
|
||||
|
||||
if(e instanceof MouseEvent) e.preventDefault();
|
||||
if(this.element.classList.contains('active')) {
|
||||
return false;
|
||||
}
|
||||
if(e instanceof MouseEvent) e.cancelBubble = true;
|
||||
|
||||
this.target = item;
|
||||
this.peerId = +item.dataset.peerId;
|
||||
this.mid = +item.dataset.mid;
|
||||
this.isSelected = searchSuper.selection.isMidSelected(this.peerId, this.mid);
|
||||
|
||||
this.buttons.forEach(button => {
|
||||
let good: boolean;
|
||||
|
||||
if(this.isSelected && !button.withSelection) {
|
||||
good = false;
|
||||
} else {
|
||||
good = button.verify ? button.verify() : true;
|
||||
}
|
||||
|
||||
button.element.classList.toggle('hide', !good);
|
||||
});
|
||||
|
||||
item.classList.add('menu-open');
|
||||
|
||||
positionMenu(e, this.element);
|
||||
openBtnMenu(this.element, () => {
|
||||
item.classList.remove('menu-open');
|
||||
});
|
||||
};
|
||||
|
||||
if(isTouchSupported) {
|
||||
|
||||
} else {
|
||||
attachContextMenuListener(attachTo, onContextMenu as any);
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
this.buttons = [{
|
||||
icon: 'forward',
|
||||
text: 'Forward',
|
||||
onClick: this.onForwardClick
|
||||
}, {
|
||||
icon: 'forward',
|
||||
text: 'Message.Context.Selection.Forward',
|
||||
onClick: this.onForwardClick,
|
||||
verify: () => this.isSelected &&
|
||||
!this.searchSuper.selection.selectionForwardBtn.classList.contains('hide'),
|
||||
withSelection: true
|
||||
}, {
|
||||
icon: 'message',
|
||||
text: 'Message.Context.Goto',
|
||||
onClick: this.onGotoClick,
|
||||
withSelection: true
|
||||
}, {
|
||||
icon: 'select',
|
||||
text: 'Message.Context.Select',
|
||||
onClick: this.onSelectClick
|
||||
}, {
|
||||
icon: 'select',
|
||||
text: 'Message.Context.Selection.Clear',
|
||||
onClick: this.onClearSelectionClick,
|
||||
verify: () => this.isSelected,
|
||||
withSelection: true
|
||||
}, {
|
||||
icon: 'delete danger',
|
||||
text: 'Delete',
|
||||
onClick: this.onDeleteClick,
|
||||
verify: () => appMessagesManager.canDeleteMessage(appMessagesManager.getMessageByPeer(this.peerId, this.mid))
|
||||
}, {
|
||||
icon: 'delete danger',
|
||||
text: 'Message.Context.Selection.Delete',
|
||||
onClick: this.onDeleteClick,
|
||||
verify: () => this.isSelected && !this.searchSuper.selection.selectionDeleteBtn.classList.contains('hide'),
|
||||
withSelection: true
|
||||
}];
|
||||
|
||||
this.element = ButtonMenu(this.buttons);
|
||||
this.element.classList.add('search-contextmenu', 'contextmenu');
|
||||
document.getElementById('page-chats').append(this.element);
|
||||
}
|
||||
|
||||
private onGotoClick = () => {
|
||||
rootScope.dispatchEvent('history_focus', {
|
||||
peerId: this.peerId,
|
||||
mid: this.mid,
|
||||
threadId: this.searchSuper.searchContext.threadId
|
||||
});
|
||||
};
|
||||
|
||||
private onForwardClick = () => {
|
||||
if(this.searchSuper.selection.isSelecting) {
|
||||
simulateClickEvent(this.searchSuper.selection.selectionForwardBtn);
|
||||
} else {
|
||||
new PopupForward({
|
||||
[this.peerId]: [this.mid]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private onSelectClick = () => {
|
||||
this.searchSuper.selection.toggleByElement(this.target);
|
||||
};
|
||||
|
||||
private onClearSelectionClick = () => {
|
||||
this.searchSuper.selection.cancelSelection();
|
||||
};
|
||||
|
||||
private onDeleteClick = () => {
|
||||
if(this.searchSuper.selection.isSelecting) {
|
||||
simulateClickEvent(this.searchSuper.selection.selectionDeleteBtn);
|
||||
} else {
|
||||
new PopupDeleteMessages(this.peerId, [this.mid], 'chat');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default class AppSearchSuper {
|
||||
public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any;
|
||||
|
||||
@ -76,8 +227,9 @@ export default class AppSearchSuper {
|
||||
|
||||
public container: HTMLElement;
|
||||
public nav: HTMLElement;
|
||||
private navScrollableContainer: HTMLDivElement;
|
||||
private tabsContainer: HTMLElement;
|
||||
public navScrollableContainer: HTMLDivElement;
|
||||
public tabsContainer: HTMLElement;
|
||||
public navScrollable: ScrollableX;
|
||||
private tabsMenu: HTMLElement;
|
||||
private prevTabId = -1;
|
||||
|
||||
@ -88,7 +240,7 @@ export default class AppSearchSuper {
|
||||
public usedFromHistory: Partial<{[type in SearchSuperType]: number}> = {};
|
||||
public urlsToRevoke: string[] = [];
|
||||
|
||||
private searchContext: SearchSuperContext;
|
||||
public searchContext: SearchSuperContext;
|
||||
public loadMutex: Promise<any> = Promise.resolve();
|
||||
|
||||
private nextRates: Partial<{[type in SearchSuperType]: number}> = {};
|
||||
@ -127,16 +279,23 @@ export default class AppSearchSuper {
|
||||
public onChangeTab?: (mediaTab: SearchSuperMediaTab) => void;
|
||||
public showSender? = false;
|
||||
|
||||
private searchContextMenu: SearchContextMenu;
|
||||
public selection: SearchSelection;
|
||||
|
||||
constructor(options: Pick<AppSearchSuper, 'mediaTabs' | 'scrollable' | 'searchGroups' | 'asChatList' | 'groupByMonth' | 'hideEmptyTabs' | 'onChangeTab' | 'showSender'>) {
|
||||
safeAssign(this, options);
|
||||
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('search-super');
|
||||
|
||||
this.searchContextMenu = new SearchContextMenu(this.container, this);
|
||||
this.selection = new SearchSelection(this, appMessagesManager);
|
||||
|
||||
const navScrollableContainer = this.navScrollableContainer = document.createElement('div');
|
||||
navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable', 'sticky');
|
||||
|
||||
const navScrollable = new ScrollableX(navScrollableContainer);
|
||||
const navScrollable = this.navScrollable = new ScrollableX(navScrollableContainer);
|
||||
navScrollable.container.classList.add('search-super-nav-scrollable');
|
||||
|
||||
const nav = this.nav = document.createElement('nav');
|
||||
nav.classList.add('search-super-tabs', 'menu-horizontal-div');
|
||||
@ -284,6 +443,13 @@ export default class AppSearchSuper {
|
||||
|
||||
this.onTransitionEnd();
|
||||
}, undefined, navScrollable);
|
||||
|
||||
attachClickEvent(this.tabsContainer, (e) => {
|
||||
if(this.selection.isSelecting) {
|
||||
cancelEvent(e);
|
||||
this.selection.toggleByElement(findUpClassName(e.target, 'search-super-item'));
|
||||
}
|
||||
}, {capture: true, passive: false});
|
||||
|
||||
const onMediaClick = (className: string, targetClassName: string, inputFilter: MyInputMessagesFilter, e: MouseEvent) => {
|
||||
const target = findUpClassName(e.target as HTMLDivElement, className);
|
||||
@ -314,8 +480,20 @@ export default class AppSearchSuper {
|
||||
.openMedia(message, targets[idx].element, 0, false, targets.slice(0, idx), targets.slice(idx + 1));
|
||||
};
|
||||
|
||||
this.tabs.inputMessagesFilterPhotoVideo.addEventListener('click', onMediaClick.bind(null, 'grid-item', 'grid-item', 'inputMessagesFilterPhotoVideo'));
|
||||
this.tabs.inputMessagesFilterDocument.addEventListener('click', onMediaClick.bind(null, 'document-with-thumb', 'media-container', 'inputMessagesFilterDocument'));
|
||||
attachClickEvent(this.tabs.inputMessagesFilterPhotoVideo, onMediaClick.bind(null, 'grid-item', 'grid-item', 'inputMessagesFilterPhotoVideo'));
|
||||
attachClickEvent(this.tabs.inputMessagesFilterDocument, onMediaClick.bind(null, 'document-with-thumb', 'media-container', 'inputMessagesFilterDocument'));
|
||||
|
||||
attachClickEvent(this.tabs.inputMessagesFilterUrl, (e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if(target.tagName === 'A') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const a = findUpClassName(target, 'row').querySelector('.anchor-url:last-child') as HTMLAnchorElement;
|
||||
a.click();
|
||||
} catch(err) {}
|
||||
});
|
||||
|
||||
this.mediaTab = this.mediaTabs[0];
|
||||
|
||||
@ -593,7 +771,7 @@ export default class AppSearchSuper {
|
||||
let div = document.createElement('div');
|
||||
|
||||
let previewDiv = document.createElement('div');
|
||||
previewDiv.classList.add('preview');
|
||||
previewDiv.classList.add('preview', 'row-media');
|
||||
|
||||
//this.log('wrapping webpage', webpage);
|
||||
|
||||
@ -618,7 +796,19 @@ export default class AppSearchSuper {
|
||||
|
||||
let title = webpage.rTitle || '';
|
||||
let subtitle = webpage.rDescription || '';
|
||||
let url = RichTextProcessor.wrapRichText(webpage.url || '');
|
||||
|
||||
const subtitleFragment = htmlToDocumentFragment(subtitle);
|
||||
const aFragment = htmlToDocumentFragment(RichTextProcessor.wrapRichText(webpage.url || ''));
|
||||
const a = aFragment.firstElementChild;
|
||||
if(a instanceof HTMLAnchorElement) {
|
||||
a.innerText = decodeURIComponent(a.href);
|
||||
}
|
||||
|
||||
if(subtitleFragment.firstChild) {
|
||||
subtitleFragment.append('\n');
|
||||
}
|
||||
|
||||
subtitleFragment.append(a);
|
||||
|
||||
if(!title) {
|
||||
//title = new URL(webpage.url).hostname;
|
||||
@ -632,18 +822,32 @@ export default class AppSearchSuper {
|
||||
titleAdditionHTML = `<div class="sent-time">${formatDateAccordingToToday(new Date(message.date * 1000))}</div>`;
|
||||
}
|
||||
|
||||
const row = new Row({
|
||||
title,
|
||||
titleRight: titleAdditionHTML,
|
||||
subtitle: subtitleFragment,
|
||||
havePadding: true,
|
||||
clickable: true,
|
||||
noRipple: true
|
||||
});
|
||||
|
||||
/* const mediaDiv = document.createElement('div');
|
||||
mediaDiv.classList.add('row-media'); */
|
||||
|
||||
row.container.append(previewDiv);
|
||||
|
||||
/* ripple(div);
|
||||
div.append(previewDiv);
|
||||
div.insertAdjacentHTML('beforeend', `
|
||||
<div class="title">${title}${titleAdditionHTML}</div>
|
||||
<div class="subtitle">${subtitle}</div>
|
||||
<div class="url">${url}</div>
|
||||
${sender}
|
||||
`);
|
||||
`); */
|
||||
|
||||
if(div.innerText.trim().length) {
|
||||
elemsToAppend.push({element: div, message});
|
||||
if(row.container.innerText.trim().length) {
|
||||
elemsToAppend.push({element: row.container, message});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
@ -675,6 +879,10 @@ export default class AppSearchSuper {
|
||||
element.dataset.mid = '' + message.mid;
|
||||
element.dataset.peerId = '' + message.peerId;
|
||||
monthContainer.items[method](element);
|
||||
|
||||
if(this.selection.isSelecting) {
|
||||
this.selection.toggleElementCheckbox(element, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1245,6 +1453,10 @@ export default class AppSearchSuper {
|
||||
this.usedFromHistory[mediaTab.inputFilter] = -1;
|
||||
});
|
||||
|
||||
if(this.selection.isSelecting) {
|
||||
this.selection.cancelSelection();
|
||||
}
|
||||
|
||||
// * must go to first tab (это костыль)
|
||||
/* const membersTab = this.mediaTabsMap.get('members');
|
||||
if(membersTab) {
|
||||
|
@ -439,6 +439,7 @@ export default class ChatBubbles {
|
||||
});
|
||||
});
|
||||
|
||||
// attachClickEvent(this.bubblesContainer, this.onBubblesClick, {listenerSetter: this.listenerSetter});
|
||||
this.listenerSetter.add(this.bubblesContainer)('click', this.onBubblesClick/* , {capture: true, passive: false} */);
|
||||
|
||||
if(DEBUG) {
|
||||
@ -865,7 +866,7 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
if(!isTouchSupported && findUpClassName(target, 'time')) {
|
||||
this.chat.selection.toggleByBubble(bubble);
|
||||
this.chat.selection.toggleByElement(bubble);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -884,7 +885,7 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
//this.chatSelection.toggleByBubble(bubble);
|
||||
this.chat.selection.toggleByBubble(findUpClassName(target, 'grouped-item') || bubble);
|
||||
this.chat.selection.toggleByElement(findUpClassName(target, 'grouped-item') || bubble);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1076,7 +1077,9 @@ export default class ChatBubbles {
|
||||
} else if(target.classList.contains('forward')) {
|
||||
const mid = +bubble.dataset.mid;
|
||||
const message = this.appMessagesManager.getMessageByPeer(this.peerId, mid);
|
||||
new PopupForward(this.peerId, this.appMessagesManager.getMidsByMessage(message));
|
||||
new PopupForward({
|
||||
[this.peerId]: this.appMessagesManager.getMidsByMessage(message)
|
||||
});
|
||||
//appSidebarRight.forwardTab.open([mid]);
|
||||
return;
|
||||
}
|
||||
@ -1390,7 +1393,7 @@ export default class ChatBubbles {
|
||||
});
|
||||
|
||||
if(permanent && this.chat.selection.isSelecting) {
|
||||
this.chat.selection.deleteSelectedMids(mids);
|
||||
this.chat.selection.deleteSelectedMids(this.peerId, mids);
|
||||
}
|
||||
|
||||
animationIntersector.checkAnimations(false, CHAT_ANIMATION_GROUP);
|
||||
@ -2213,6 +2216,7 @@ export default class ChatBubbles {
|
||||
// ! reset due to album edit or delete item
|
||||
this.bubbles[+message.mid] = bubble;
|
||||
bubble.dataset.mid = message.mid;
|
||||
bubble.dataset.peerId = '' + message.peerId;
|
||||
bubble.dataset.timestamp = message.date;
|
||||
|
||||
const loadPromises: Promise<any>[] = [];
|
||||
@ -2281,7 +2285,7 @@ export default class ChatBubbles {
|
||||
});
|
||||
|
||||
let canHaveTail = true;
|
||||
|
||||
let isStandaloneMedia = false;
|
||||
let needToSetHTML = true;
|
||||
if(totalEntities && !messageMedia) {
|
||||
let emojiEntities = totalEntities.filter((e) => e._ === 'messageEntityEmoji');
|
||||
@ -2308,6 +2312,7 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
bubble.classList.add('is-message-empty', 'emoji-big');
|
||||
isStandaloneMedia = true;
|
||||
canHaveTail = false;
|
||||
needToSetHTML = false;
|
||||
}
|
||||
@ -2391,7 +2396,9 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
new PopupForward(this.peerId, [], (peerId) => {
|
||||
new PopupForward({
|
||||
[this.peerId]: []
|
||||
}, (peerId) => {
|
||||
resolve(peerId);
|
||||
}, () => {
|
||||
reject();
|
||||
@ -2468,8 +2475,6 @@ export default class ChatBubbles {
|
||||
const isOut = our && (!message.fwd_from || this.peerId !== rootScope.myId);
|
||||
let nameContainer: HTMLElement = bubbleContainer;
|
||||
|
||||
let isStandaloneMedia = false;
|
||||
|
||||
// media
|
||||
if(messageMedia/* && messageMedia._ === 'messageMediaPhoto' */) {
|
||||
let attachmentDiv = document.createElement('div');
|
||||
@ -2856,7 +2861,7 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
if(this.chat.selection.isSelecting) {
|
||||
this.chat.selection.toggleBubbleCheckbox(bubble, true);
|
||||
this.chat.selection.toggleElementCheckbox(bubble, true);
|
||||
}
|
||||
|
||||
let savedFrom = '';
|
||||
|
@ -19,11 +19,10 @@ import PopupPinMessage from "../popups/unpinMessage";
|
||||
import { copyTextToClipboard } from "../../helpers/clipboard";
|
||||
import PopupSendNow from "../popups/sendNow";
|
||||
import { toast } from "../toast";
|
||||
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
||||
import I18n, { LangPackKey } from "../../lib/langPack";
|
||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
import cancelSelection from "../../helpers/dom/cancelSelection";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import isSelectionEmpty from "../../helpers/dom/isSelectionEmpty";
|
||||
import { Message } from "../../layer";
|
||||
import PopupReportMessages from "../popups/reportMessages";
|
||||
@ -33,6 +32,7 @@ export default class ChatContextMenu {
|
||||
private element: HTMLElement;
|
||||
|
||||
private isSelectable: boolean;
|
||||
private isSelected: boolean;
|
||||
private target: HTMLElement;
|
||||
private isTargetAGroupedItem: boolean;
|
||||
private isTextSelected: boolean;
|
||||
@ -75,17 +75,6 @@ export default class ChatContextMenu {
|
||||
let mid = +bubble.dataset.mid;
|
||||
if(!mid) return;
|
||||
|
||||
// * если открыть контекстное меню для альбома не по бабблу, и последний элемент не выбран, чтобы показать остальные пункты
|
||||
if(chat.selection.isSelecting && !contentWrapper) {
|
||||
const mids = this.chat.getMidsByMid(mid);
|
||||
if(mids.length > 1) {
|
||||
const selectedMid = chat.selection.selectedMids.has(mid) ? mid : mids.find(mid => chat.selection.selectedMids.has(mid));
|
||||
if(selectedMid) {
|
||||
mid = selectedMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.isSelectable = this.chat.selection.canSelectBubble(bubble);
|
||||
this.peerId = this.chat.peerId;
|
||||
//this.msgID = msgID;
|
||||
@ -97,6 +86,19 @@ export default class ChatContextMenu {
|
||||
);
|
||||
this.isUsernameTarget = this.target.tagName === 'A' && this.target.classList.contains('mention');
|
||||
|
||||
// * если открыть контекстное меню для альбома не по бабблу, и последний элемент не выбран, чтобы показать остальные пункты
|
||||
if(chat.selection.isSelecting && !contentWrapper) {
|
||||
const mids = this.chat.getMidsByMid(mid);
|
||||
if(mids.length > 1) {
|
||||
const selectedMid = this.chat.selection.isMidSelected(this.peerId, mid) ?
|
||||
mid :
|
||||
mids.find(mid => this.chat.selection.isMidSelected(this.peerId, mid));
|
||||
if(selectedMid) {
|
||||
mid = selectedMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const groupedItem = findUpClassName(this.target, 'grouped-item');
|
||||
this.isTargetAGroupedItem = !!groupedItem;
|
||||
if(groupedItem) {
|
||||
@ -105,6 +107,7 @@ export default class ChatContextMenu {
|
||||
this.mid = mid;
|
||||
}
|
||||
|
||||
this.isSelected = this.chat.selection.isMidSelected(this.peerId, this.mid);
|
||||
this.message = this.chat.getMessage(this.mid);
|
||||
|
||||
this.buttons.forEach(button => {
|
||||
@ -147,29 +150,10 @@ export default class ChatContextMenu {
|
||||
if(good) {
|
||||
cancelEvent(e);
|
||||
//onContextMenu((e as TouchEvent).changedTouches[0]);
|
||||
onContextMenu((e as TouchEvent).changedTouches ? (e as TouchEvent).changedTouches[0] : e as MouseEvent);
|
||||
// onContextMenu((e as TouchEvent).changedTouches ? (e as TouchEvent).changedTouches[0] : e as MouseEvent);
|
||||
onContextMenu(e);
|
||||
}
|
||||
}, {listenerSetter: this.chat.bubbles.listenerSetter});
|
||||
|
||||
attachContextMenuListener(attachTo, (e) => {
|
||||
if(chat.selection.isSelecting) return;
|
||||
|
||||
// * these two lines will fix instant text selection on iOS Safari
|
||||
document.body.classList.add('no-select'); // * need no-select on body because chat-input transforms in channels
|
||||
attachTo.addEventListener('touchend', (e) => {
|
||||
cancelEvent(e); // ! this one will fix propagation to document loader button, etc
|
||||
document.body.classList.remove('no-select');
|
||||
|
||||
//this.chat.bubbles.onBubblesClick(e);
|
||||
}, {once: true, capture: true});
|
||||
|
||||
cancelSelection();
|
||||
//cancelEvent(e as any);
|
||||
const bubble = findUpClassName(e.target, 'grouped-item') || findUpClassName(e.target, 'bubble');
|
||||
if(bubble) {
|
||||
chat.selection.toggleByBubble(bubble);
|
||||
}
|
||||
}, this.chat.bubbles.listenerSetter);
|
||||
} else attachContextMenuListener(attachTo, onContextMenu, this.chat.bubbles.listenerSetter);
|
||||
}
|
||||
|
||||
@ -183,7 +167,7 @@ export default class ChatContextMenu {
|
||||
icon: 'send2',
|
||||
text: 'Message.Context.Selection.SendNow',
|
||||
onClick: this.onSendScheduledClick,
|
||||
verify: () => this.chat.type === 'scheduled' && this.chat.selection.selectedMids.has(this.mid) && !this.chat.selection.selectionSendNowBtn.hasAttribute('disabled'),
|
||||
verify: () => this.chat.type === 'scheduled' && this.isSelected && !this.chat.selection.selectionSendNowBtn.hasAttribute('disabled'),
|
||||
notDirect: () => true,
|
||||
withSelection: true
|
||||
}, {
|
||||
@ -228,7 +212,21 @@ export default class ChatContextMenu {
|
||||
icon: 'copy',
|
||||
text: 'Message.Context.Selection.Copy',
|
||||
onClick: this.onCopyClick,
|
||||
verify: () => this.chat.selection.selectedMids.has(this.mid) && !![...this.chat.selection.selectedMids].find(mid => !!this.chat.getMessage(mid).message),
|
||||
verify: () => {
|
||||
if(!this.isSelected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const [peerId, mids] of this.chat.selection.selectedMids) {
|
||||
for(const mid of mids) {
|
||||
if(!!this.appMessagesManager.getMessageByPeer(peerId, mid).message) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
notDirect: () => true,
|
||||
withSelection: true
|
||||
}, {
|
||||
@ -319,7 +317,7 @@ export default class ChatContextMenu {
|
||||
text: 'Message.Context.Selection.Forward',
|
||||
onClick: this.onForwardClick,
|
||||
verify: () => this.chat.selection.selectionForwardBtn &&
|
||||
this.chat.selection.selectedMids.has(this.mid) &&
|
||||
this.isSelected &&
|
||||
!this.chat.selection.selectionForwardBtn.hasAttribute('disabled'),
|
||||
notDirect: () => true,
|
||||
withSelection: true
|
||||
@ -336,14 +334,14 @@ export default class ChatContextMenu {
|
||||
icon: 'select',
|
||||
text: 'Message.Context.Select',
|
||||
onClick: this.onSelectClick,
|
||||
verify: () => !this.message.action && !this.chat.selection.selectedMids.has(this.mid) && this.isSelectable,
|
||||
verify: () => !this.message.action && !this.isSelected && this.isSelectable,
|
||||
notDirect: () => true,
|
||||
withSelection: true
|
||||
}, {
|
||||
icon: 'select',
|
||||
text: 'Message.Context.Selection.Clear',
|
||||
onClick: this.onClearSelectionClick,
|
||||
verify: () => this.chat.selection.selectedMids.has(this.mid),
|
||||
verify: () => this.isSelected,
|
||||
notDirect: () => true,
|
||||
withSelection: true
|
||||
}, {
|
||||
@ -355,7 +353,7 @@ export default class ChatContextMenu {
|
||||
icon: 'delete danger',
|
||||
text: 'Message.Context.Selection.Delete',
|
||||
onClick: this.onDeleteClick,
|
||||
verify: () => this.chat.selection.selectedMids.has(this.mid) && !this.chat.selection.selectionDeleteBtn.hasAttribute('disabled'),
|
||||
verify: () => this.isSelected && !this.chat.selection.selectionDeleteBtn.hasAttribute('disabled'),
|
||||
notDirect: () => true,
|
||||
withSelection: true
|
||||
}];
|
||||
@ -364,11 +362,11 @@ export default class ChatContextMenu {
|
||||
this.element.id = 'bubble-contextmenu';
|
||||
this.element.classList.add('contextmenu');
|
||||
this.chat.container.append(this.element);
|
||||
};
|
||||
}
|
||||
|
||||
private onSendScheduledClick = () => {
|
||||
if(this.chat.selection.isSelecting) {
|
||||
this.chat.selection.selectionSendNowBtn.click();
|
||||
simulateClickEvent(this.chat.selection.selectionSendNowBtn);
|
||||
} else {
|
||||
new PopupSendNow(this.peerId, this.chat.getMidsByMid(this.mid));
|
||||
}
|
||||
@ -384,11 +382,15 @@ export default class ChatContextMenu {
|
||||
|
||||
private onCopyClick = () => {
|
||||
if(isSelectionEmpty()) {
|
||||
const mids = this.chat.selection.isSelecting ? [...this.chat.selection.selectedMids].sort((a, b) => a - b) : [this.mid];
|
||||
const mids = this.chat.selection.isSelecting ?
|
||||
[...this.chat.selection.selectedMids.get(this.peerId)].sort((a, b) => a - b) :
|
||||
[this.mid];
|
||||
|
||||
const str = mids.reduce((acc, mid) => {
|
||||
const message = this.chat.getMessage(mid);
|
||||
return acc + (message?.message ? message.message + '\n' : '');
|
||||
}, '').trim();
|
||||
|
||||
copyTextToClipboard(str);
|
||||
} else {
|
||||
document.execCommand('copy');
|
||||
@ -443,14 +445,17 @@ export default class ChatContextMenu {
|
||||
|
||||
private onForwardClick = () => {
|
||||
if(this.chat.selection.isSelecting) {
|
||||
this.chat.selection.selectionForwardBtn.click();
|
||||
simulateClickEvent(this.chat.selection.selectionForwardBtn);
|
||||
} else {
|
||||
new PopupForward(this.peerId, this.isTargetAGroupedItem ? [this.mid] : this.chat.getMidsByMid(this.mid));
|
||||
const mids = this.isTargetAGroupedItem ? [this.mid] : this.chat.getMidsByMid(this.mid);
|
||||
new PopupForward({
|
||||
[this.peerId]: mids
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private onSelectClick = () => {
|
||||
this.chat.selection.toggleByBubble(findUpClassName(this.target, 'grouped-item') || findUpClassName(this.target, 'bubble'));
|
||||
this.chat.selection.toggleByElement(findUpClassName(this.target, 'grouped-item') || findUpClassName(this.target, 'bubble'));
|
||||
};
|
||||
|
||||
private onClearSelectionClick = () => {
|
||||
@ -459,7 +464,7 @@ export default class ChatContextMenu {
|
||||
|
||||
private onDeleteClick = () => {
|
||||
if(this.chat.selection.isSelecting) {
|
||||
this.chat.selection.selectionDeleteBtn.click();
|
||||
simulateClickEvent(this.chat.selection.selectionDeleteBtn);
|
||||
} else {
|
||||
new PopupDeleteMessages(this.peerId, this.isTargetAGroupedItem ? [this.mid] : this.chat.getMidsByMid(this.mid), this.chat.type);
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ import PeerTitle from '../peerTitle';
|
||||
import { fastRaf } from '../../helpers/schedulers';
|
||||
import PopupDeleteMessages from '../popups/deleteMessages';
|
||||
import fixSafariStickyInputFocusing, { IS_STICKY_INPUT_BUGGED } from '../../helpers/dom/fixSafariStickyInputFocusing';
|
||||
import { copy } from '../../helpers/object';
|
||||
|
||||
const RECORD_MIN_TIME = 500;
|
||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||
@ -117,8 +118,7 @@ export default class ChatInput {
|
||||
|
||||
private getWebPagePromise: Promise<void>;
|
||||
private willSendWebPage: WebPage = null;
|
||||
private forwardingMids: number[] = [];
|
||||
private forwardingFromPeerId: number = 0;
|
||||
private forwarding: {[fromPeerId: number]: number[]};
|
||||
public replyToMsgId: number;
|
||||
public editMsgId: number;
|
||||
private noWebPage: true;
|
||||
@ -1402,7 +1402,7 @@ export default class ChatInput {
|
||||
private onBtnSendClick = (e: Event) => {
|
||||
cancelEvent(e);
|
||||
|
||||
if(!this.recorder || this.recording || !this.isInputEmpty() || this.forwardingMids.length || this.editMsgId) {
|
||||
if(!this.recorder || this.recording || !this.isInputEmpty() || this.forwarding || this.editMsgId) {
|
||||
if(this.recording) {
|
||||
if((Date.now() - this.recordStartTime) < RECORD_MIN_TIME) {
|
||||
this.onCancelRecordClick();
|
||||
@ -1526,13 +1526,11 @@ export default class ChatInput {
|
||||
if(this.helperWaitingForward) return;
|
||||
this.helperWaitingForward = true;
|
||||
|
||||
const fromId = this.forwardingFromPeerId;
|
||||
const mids = this.forwardingMids.slice();
|
||||
const helperFunc = this.helperFunc;
|
||||
this.clearHelper();
|
||||
this.updateSendBtn();
|
||||
let selected = false;
|
||||
new PopupForward(fromId, mids, () => {
|
||||
new PopupForward(copy(this.forwarding), () => {
|
||||
selected = true;
|
||||
}, () => {
|
||||
this.helperWaitingForward = false;
|
||||
@ -1593,7 +1591,7 @@ export default class ChatInput {
|
||||
const isInputEmpty = this.isInputEmpty();
|
||||
|
||||
if(this.editMsgId) icon = 'edit';
|
||||
else if(!this.recorder || this.recording || !isInputEmpty || this.forwardingMids.length) icon = this.chat.type === 'scheduled' ? 'schedule' : 'send';
|
||||
else if(!this.recorder || this.recording || !isInputEmpty || this.forwarding) icon = this.chat.type === 'scheduled' ? 'schedule' : 'send';
|
||||
else icon = 'record';
|
||||
|
||||
['send', 'record', 'edit', 'schedule'].forEach(i => {
|
||||
@ -1673,17 +1671,18 @@ export default class ChatInput {
|
||||
}
|
||||
|
||||
// * wait for sendText set messageId for invokeAfterMsg
|
||||
if(this.forwardingMids.length) {
|
||||
const mids = this.forwardingMids.slice();
|
||||
const fromPeerId = this.forwardingFromPeerId;
|
||||
if(this.forwarding) {
|
||||
const forwarding = copy(this.forwarding);
|
||||
const peerId = this.chat.peerId;
|
||||
const silent = this.sendSilent;
|
||||
const scheduleDate = this.scheduleDate;
|
||||
setTimeout(() => {
|
||||
this.appMessagesManager.forwardMessages(peerId, fromPeerId, mids, {
|
||||
silent,
|
||||
scheduleDate: scheduleDate
|
||||
});
|
||||
for(const fromPeerId in forwarding) {
|
||||
this.appMessagesManager.forwardMessages(peerId, +fromPeerId, forwarding[fromPeerId], {
|
||||
silent,
|
||||
scheduleDate: scheduleDate
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@ -1751,17 +1750,26 @@ export default class ChatInput {
|
||||
f();
|
||||
}
|
||||
|
||||
public initMessagesForward(fromPeerId: number, mids: number[]) {
|
||||
public initMessagesForward(fromPeerIdsMids: {[fromPeerId: number]: number[]}) {
|
||||
const f = () => {
|
||||
//const peerTitles: string[]
|
||||
const smth: Set<string | number> = new Set(mids.map(mid => {
|
||||
const message = this.appMessagesManager.getMessageByPeer(fromPeerId, mid);
|
||||
if(message.fwd_from && message.fwd_from.from_name && !message.fromId && !message.fwdFromId) {
|
||||
return message.fwd_from.from_name;
|
||||
} else {
|
||||
return message.fromId;
|
||||
}
|
||||
}));
|
||||
const fromPeerIds = Object.keys(fromPeerIdsMids).map(str => +str);
|
||||
const smth: Set<string | number> = new Set();
|
||||
let length = 0;
|
||||
|
||||
fromPeerIds.forEach(fromPeerId => {
|
||||
const mids = fromPeerIdsMids[fromPeerId];
|
||||
mids.forEach(mid => {
|
||||
const message = this.appMessagesManager.getMessageByPeer(fromPeerId, mid);
|
||||
if(message.fwd_from?.from_name && !message.fromId && !message.fwdFromId) {
|
||||
smth.add(message.fwd_from.from_name);
|
||||
} else {
|
||||
smth.add(message.fromId);
|
||||
}
|
||||
});
|
||||
|
||||
length += mids.length;
|
||||
});
|
||||
|
||||
const onlyFirstName = smth.size > 2;
|
||||
const peerTitles = [...smth].map(smth => {
|
||||
@ -1776,26 +1784,31 @@ export default class ChatInput {
|
||||
} else {
|
||||
title.append(peerTitles[0], i18n('AndOther', [peerTitles.length - 1]));
|
||||
}
|
||||
|
||||
const firstMessage = this.appMessagesManager.getMessageByPeer(fromPeerId, mids[0]);
|
||||
|
||||
let usingFullAlbum = !!firstMessage.grouped_id;
|
||||
if(firstMessage.grouped_id) {
|
||||
const albumMids = this.appMessagesManager.getMidsByMessage(firstMessage);
|
||||
if(albumMids.length !== mids.length || albumMids.find(mid => !mids.includes(mid))) {
|
||||
usingFullAlbum = false;
|
||||
|
||||
let firstMessage: any, usingFullAlbum: boolean;
|
||||
if(fromPeerIds.length === 1) {
|
||||
const fromPeerId = fromPeerIds[0];
|
||||
const mids = fromPeerIdsMids[fromPeerId];
|
||||
firstMessage = this.appMessagesManager.getMessageByPeer(fromPeerId, mids[0]);
|
||||
|
||||
usingFullAlbum = !!firstMessage.grouped_id;
|
||||
if(usingFullAlbum) {
|
||||
const albumMids = this.appMessagesManager.getMidsByMessage(firstMessage);
|
||||
if(albumMids.length !== length || albumMids.find(mid => !mids.includes(mid))) {
|
||||
usingFullAlbum = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const replyFragment = this.appMessagesManager.wrapMessageForReply(firstMessage, undefined, mids);
|
||||
if(usingFullAlbum || mids.length === 1) {
|
||||
|
||||
if(usingFullAlbum || length === 1) {
|
||||
const mids = fromPeerIdsMids[fromPeerIds[0]];
|
||||
const replyFragment = this.appMessagesManager.wrapMessageForReply(firstMessage, undefined, mids);
|
||||
this.setTopInfo('forward', f, title, replyFragment);
|
||||
} else {
|
||||
this.setTopInfo('forward', f, title, i18n('ForwardedMessageCount', [mids.length]));
|
||||
this.setTopInfo('forward', f, title, i18n('ForwardedMessageCount', [length]));
|
||||
}
|
||||
|
||||
this.forwardingMids = mids.slice();
|
||||
this.forwardingFromPeerId = fromPeerId;
|
||||
this.forwarding = fromPeerIdsMids;
|
||||
};
|
||||
|
||||
f();
|
||||
@ -1845,8 +1858,7 @@ export default class ChatInput {
|
||||
}
|
||||
|
||||
this.replyToMsgId = undefined;
|
||||
this.forwardingMids.length = 0;
|
||||
this.forwardingFromPeerId = 0;
|
||||
this.forwarding = undefined;
|
||||
this.editMsgId = undefined;
|
||||
this.helperType = this.helperFunc = undefined;
|
||||
|
||||
|
@ -52,7 +52,7 @@ export function wrapReplyDivAndCaption(options: {
|
||||
media = media.webpage;
|
||||
}
|
||||
|
||||
if(media.photo || (media.document && ['video', 'sticker', 'gif', 'round'].indexOf(media.document.type) !== -1)) {
|
||||
if(media.photo || (media.document && ['video', 'sticker', 'gif', 'round', 'photo'].indexOf(media.document.type) !== -1)) {
|
||||
middleware = appImManager.chat.bubbles.getMiddleware();
|
||||
const lazyLoadQueue = appImManager.chat.bubbles.lazyLoadQueue;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -249,13 +249,13 @@ export default class ChatTopbar {
|
||||
return;
|
||||
}
|
||||
|
||||
const original = selection.toggleByBubble.bind(selection);
|
||||
selection.toggleByBubble = (bubble) => {
|
||||
const original = selection.toggleByElement.bind(selection);
|
||||
selection.toggleByElement = (bubble) => {
|
||||
appStateManager.pushToState('chatContextMenuHintWasShown', true);
|
||||
toast(i18n('Chat.Menu.Hint'));
|
||||
|
||||
selection.toggleByBubble = original;
|
||||
selection.toggleByBubble(bubble);
|
||||
selection.toggleByElement = original;
|
||||
selection.toggleByElement(bubble);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -8,7 +8,12 @@ import appImManager from "../../lib/appManagers/appImManager";
|
||||
import PopupPickUser from "./pickUser";
|
||||
|
||||
export default class PopupForward extends PopupPickUser {
|
||||
constructor(fromPeerId: number, mids: number[], onSelect?: (peerId: number) => Promise<void> | void, onClose?: () => void, overrideOnSelect = false) {
|
||||
constructor(
|
||||
peerIdMids: {[fromPeerId: number]: number[]},
|
||||
onSelect?: (peerId: number) => Promise<void> | void,
|
||||
onClose?: () => void,
|
||||
overrideOnSelect = false
|
||||
) {
|
||||
super({
|
||||
peerTypes: ['dialogs', 'contacts'],
|
||||
onSelect: overrideOnSelect ? onSelect : async(peerId) => {
|
||||
@ -20,7 +25,7 @@ export default class PopupForward extends PopupPickUser {
|
||||
}
|
||||
|
||||
appImManager.setInnerPeer(peerId);
|
||||
appImManager.chat.input.initMessagesForward(fromPeerId, mids.slice());
|
||||
appImManager.chat.input.initMessagesForward(peerIdMids);
|
||||
},
|
||||
onClose,
|
||||
placeholder: 'ShareModal.Search.ForwardPlaceholder',
|
||||
|
@ -73,11 +73,13 @@ export default class RangeSelector {
|
||||
this.rect = this.container.getBoundingClientRect();
|
||||
this.mousedown = true;
|
||||
this.scrub(event);
|
||||
this.container.classList.add('is-focused');
|
||||
this.events?.onMouseDown && this.events.onMouseDown(event);
|
||||
};
|
||||
|
||||
protected onMouseUp = (event: GrabEvent) => {
|
||||
this.mousedown = false;
|
||||
this.container.classList.remove('is-focused');
|
||||
this.events?.onMouseUp && this.events.onMouseUp(event);
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,7 @@ import { SliderSuperTab } from "./slider";
|
||||
import RadioForm from "./radioForm";
|
||||
import { i18n, LangPackKey } from "../lib/langPack";
|
||||
import replaceContent from "../helpers/dom/replaceContent";
|
||||
import setInnerHTML from "../helpers/dom/setInnerHTML";
|
||||
|
||||
export default class Row {
|
||||
public container: HTMLElement;
|
||||
@ -24,7 +25,7 @@ export default class Row {
|
||||
|
||||
constructor(options: Partial<{
|
||||
icon: string,
|
||||
subtitle: string,
|
||||
subtitle: string | HTMLElement | DocumentFragment,
|
||||
subtitleLangKey: LangPackKey,
|
||||
subtitleLangArgs: any[],
|
||||
radioField: Row['radioField'],
|
||||
@ -35,7 +36,8 @@ export default class Row {
|
||||
titleRight: string | HTMLElement,
|
||||
clickable: boolean | ((e: Event) => void),
|
||||
navigationTab: SliderSuperTab,
|
||||
havePadding: boolean
|
||||
havePadding: boolean,
|
||||
noRipple: boolean
|
||||
}> = {}) {
|
||||
this.container = document.createElement(options.radioField || options.checkboxField ? 'label' : 'div');
|
||||
this.container.classList.add('row');
|
||||
@ -44,7 +46,11 @@ export default class Row {
|
||||
this.subtitle.classList.add('row-subtitle');
|
||||
this.subtitle.setAttribute('dir', 'auto');
|
||||
if(options.subtitle) {
|
||||
this.subtitle.innerHTML = options.subtitle;
|
||||
if(typeof(options.subtitle) === 'string') {
|
||||
setInnerHTML(this.subtitle, options.subtitle);
|
||||
} else {
|
||||
this.subtitle.append(options.subtitle);
|
||||
}
|
||||
} else if(options.subtitleLangKey) {
|
||||
this.subtitle.append(i18n(options.subtitleLangKey, options.subtitleLangArgs));
|
||||
}
|
||||
@ -137,7 +143,10 @@ export default class Row {
|
||||
}
|
||||
|
||||
this.container.classList.add('row-clickable', 'hover-effect');
|
||||
ripple(this.container, undefined, undefined, true);
|
||||
|
||||
if(!options.noRipple) {
|
||||
ripple(this.container, undefined, undefined, true);
|
||||
}
|
||||
|
||||
/* if(options.radioField || options.checkboxField) {
|
||||
this.container.prepend(this.container.lastElementChild);
|
||||
|
86
src/components/sendingStatus.ts
Normal file
86
src/components/sendingStatus.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { Message } from "../layer";
|
||||
/* import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import Transition from "./transition"; */
|
||||
|
||||
export enum SENDING_STATUS {
|
||||
Error = -1,
|
||||
Pending,
|
||||
Sent,
|
||||
Read
|
||||
}
|
||||
|
||||
export function getSendingStatus(message: Message.message | Message.messageService) {
|
||||
return message.pFlags.is_outgoing ?
|
||||
SENDING_STATUS.Pending : (
|
||||
message.pFlags.unread ?
|
||||
SENDING_STATUS.Sent :
|
||||
SENDING_STATUS.Read
|
||||
);
|
||||
}
|
||||
|
||||
export function setSendingStatus(
|
||||
container: HTMLElement,
|
||||
message?: Message.message | Message.messageService,
|
||||
disableAnimationIfRippleFound?: boolean
|
||||
) {
|
||||
let className: 'check' | 'checks' | 'sending';
|
||||
if(message?.pFlags.out) {
|
||||
if(message.pFlags.is_outgoing) {
|
||||
className = 'sending';
|
||||
} else if(message.pFlags.unread) {
|
||||
className = 'check';
|
||||
} else {
|
||||
className = 'checks';
|
||||
}
|
||||
}
|
||||
|
||||
if(!className) {
|
||||
container.textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const iconClassName = 'tgico-' + className;
|
||||
const lastElement = container.lastElementChild as HTMLElement;
|
||||
if(lastElement && lastElement.classList.contains(iconClassName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = document.createElement('i');
|
||||
element.classList.add('sending-status-icon', /* 'transition-item', */ iconClassName);
|
||||
container.append(element);
|
||||
|
||||
if(lastElement) {
|
||||
lastElement.remove();
|
||||
}
|
||||
|
||||
/* if(!lastElement) {
|
||||
element.classList.add('active');
|
||||
return;
|
||||
}
|
||||
|
||||
const select = Transition(container, undefined, 350, () => {
|
||||
lastElement.remove();
|
||||
}, false, true, false);
|
||||
|
||||
let animate = rootScope.settings.animationsEnabled && className !== 'sending' && !lastElement.classList.contains('tgico-sending');
|
||||
if(disableAnimationIfRippleFound && animate) {
|
||||
const parent = findUpClassName(container, 'rp');
|
||||
if(parent.querySelector('.c-ripple__circle') || parent.matches(':hover')) {
|
||||
animate = false;
|
||||
}
|
||||
}
|
||||
|
||||
select(element, animate, lastElement); */
|
||||
|
||||
/* SetTransition(lastElement, 'is-visible', false, 350, () => {
|
||||
// lastElement.remove();
|
||||
}, 2);
|
||||
SetTransition(element, 'is-visible', true, 350, undefined, 2); */
|
||||
}
|
@ -245,7 +245,8 @@ class PeerProfileAvatars {
|
||||
|
||||
const rect = this.container.getBoundingClientRect();
|
||||
|
||||
const e = (_e as TouchEvent).touches ? (_e as TouchEvent).touches[0] : _e as MouseEvent;
|
||||
// const e = (_e as TouchEvent).touches ? (_e as TouchEvent).touches[0] : _e as MouseEvent;
|
||||
const e = _e;
|
||||
const x = e.pageX;
|
||||
|
||||
const clickX = x - rect.left;
|
||||
@ -1055,16 +1056,15 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
const inputFilter = mediaTab.inputFilter;
|
||||
const filtered = this.searchSuper.filterMessagesByType(mids.map(mid => appMessagesManager.getMessageByPeer(peerId, mid)), inputFilter);
|
||||
if(filtered.length) {
|
||||
if(this.historiesStorage[peerId][inputFilter]) {
|
||||
this.historiesStorage[peerId][inputFilter].unshift(...filtered.map(message => ({mid: message.mid, peerId: message.peerId})));
|
||||
const history = this.historiesStorage[peerId][inputFilter];
|
||||
if(history) {
|
||||
history.unshift(...filtered.map(message => ({mid: message.mid, peerId: message.peerId})));
|
||||
}
|
||||
|
||||
if(this.peerId === peerId && this.searchSuper.usedFromHistory[inputFilter] !== -1) {
|
||||
this.searchSuper.usedFromHistory[inputFilter] += filtered.length;
|
||||
this.searchSuper.performSearchResult(filtered, mediaTab, false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1078,17 +1078,21 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
for(const type of this.searchSuper.mediaTabs) {
|
||||
const inputFilter = type.inputFilter;
|
||||
|
||||
if(!this.historiesStorage[peerId][inputFilter]) continue;
|
||||
|
||||
const history = this.historiesStorage[peerId][inputFilter];
|
||||
if(!history) continue;
|
||||
|
||||
const idx = history.findIndex(m => m.mid === mid);
|
||||
if(idx !== -1) {
|
||||
history.splice(idx, 1);
|
||||
|
||||
if(this.peerId === peerId) {
|
||||
const container = this.searchSuper.tabs[inputFilter];
|
||||
const div = container.querySelector(`div[data-mid="${mid}"][data-peer-id="${peerId}"]`);
|
||||
const div = container.querySelector(`div[data-mid="${mid}"][data-peer-id="${peerId}"]`) as HTMLElement;
|
||||
if(div) {
|
||||
if(this.searchSuper.selection.isSelecting) {
|
||||
this.searchSuper.selection.toggleByElement(div);
|
||||
}
|
||||
|
||||
div.remove();
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,10 @@ const SetTransition = (
|
||||
}
|
||||
}
|
||||
|
||||
// if(forwards && className && element.classList.contains(className) && !element.classList.contains('animating')) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if(useRafs && rootScope.settings.animationsEnabled && duration) {
|
||||
element.dataset.raf = '' + window.requestAnimationFrame(() => {
|
||||
delete element.dataset.raf;
|
||||
|
@ -10,6 +10,7 @@ import { dispatchHeavyAnimationEvent } from "../hooks/useHeavyAnimationCheck";
|
||||
import whichChild from "../helpers/dom/whichChild";
|
||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import { isSafari } from "../helpers/userAgent";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
|
||||
function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
||||
const width = prevTabContent.getBoundingClientRect().width;
|
||||
@ -80,7 +81,13 @@ function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight
|
||||
};
|
||||
}
|
||||
|
||||
export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fade' | 'slide-fade' | 'none'/* | 'counter' */, transitionTime: number, onTransitionEnd?: (id: number) => void, isHeavy = true) => {
|
||||
export const TransitionSlider = (
|
||||
content: HTMLElement,
|
||||
type: 'tabs' | 'navigation' | 'zoom-fade' | 'slide-fade' | 'none'/* | 'counter' */,
|
||||
transitionTime: number,
|
||||
onTransitionEnd?: (id: number) => void,
|
||||
isHeavy = true
|
||||
) => {
|
||||
let animationFunction: TransitionFunction = null;
|
||||
|
||||
switch(type) {
|
||||
@ -101,62 +108,94 @@ export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigatio
|
||||
|
||||
type TransitionFunction = (tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) => void | (() => void);
|
||||
|
||||
const Transition = (content: HTMLElement, animationFunction: TransitionFunction, transitionTime: number, onTransitionEnd?: (id: number) => void, isHeavy = true) => {
|
||||
const Transition = (
|
||||
content: HTMLElement,
|
||||
animationFunction: TransitionFunction,
|
||||
transitionTime: number,
|
||||
onTransitionEnd?: (id: number) => void,
|
||||
isHeavy = true,
|
||||
once = false,
|
||||
withAnimationListener = true
|
||||
) => {
|
||||
const onTransitionEndCallbacks: Map<HTMLElement, Function> = new Map();
|
||||
let animationDeferred: CancellablePromise<void>;
|
||||
let animationStarted = 0;
|
||||
// let animationStarted = 0;
|
||||
let from: HTMLElement = null;
|
||||
|
||||
// TODO: check for transition type (transform, etc) using by animationFunction
|
||||
content.addEventListener(animationFunction ? 'transitionend' : 'animationend', (e) => {
|
||||
if((e.target as HTMLElement).parentElement !== content) {
|
||||
return;
|
||||
if(withAnimationListener) {
|
||||
const listenerName = animationFunction ? 'transitionend' : 'animationend';
|
||||
|
||||
const onEndEvent = (e: TransitionEvent | AnimationEvent) => {
|
||||
cancelEvent(e);
|
||||
|
||||
if((e.target as HTMLElement).parentElement !== content) {
|
||||
return;
|
||||
}
|
||||
|
||||
//console.log('Transition: transitionend', /* content, */ e, selectTab.prevId, performance.now() - animationStarted);
|
||||
|
||||
const callback = onTransitionEndCallbacks.get(e.target as HTMLElement);
|
||||
if(callback) callback();
|
||||
|
||||
if(e.target !== from) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!animationDeferred && isHeavy) return;
|
||||
|
||||
if(animationDeferred) {
|
||||
animationDeferred.resolve();
|
||||
animationDeferred = undefined;
|
||||
}
|
||||
|
||||
if(onTransitionEnd) {
|
||||
onTransitionEnd(selectTab.prevId());
|
||||
}
|
||||
|
||||
content.classList.remove('animating', 'backwards', 'disable-hover');
|
||||
|
||||
if(once) {
|
||||
content.removeEventListener(listenerName, onEndEvent/* , {capture: false} */);
|
||||
from = animationDeferred = undefined;
|
||||
onTransitionEndCallbacks.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: check for transition type (transform, etc) using by animationFunction
|
||||
content.addEventListener(listenerName, onEndEvent/* , {passive: true, capture: false} */);
|
||||
}
|
||||
|
||||
function selectTab(id: number | HTMLElement, animate = true, overrideFrom?: typeof from) {
|
||||
if(overrideFrom) {
|
||||
from = overrideFrom;
|
||||
}
|
||||
|
||||
//console.log('Transition: transitionend', /* content, */ e, selectTab.prevId, performance.now() - animationStarted);
|
||||
|
||||
const callback = onTransitionEndCallbacks.get(e.target as HTMLElement);
|
||||
if(callback) callback();
|
||||
|
||||
if(e.target !== from) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!animationDeferred && isHeavy) return;
|
||||
|
||||
if(animationDeferred) {
|
||||
animationDeferred.resolve();
|
||||
animationDeferred = undefined;
|
||||
}
|
||||
|
||||
if(onTransitionEnd) {
|
||||
onTransitionEnd(selectTab.prevId());
|
||||
}
|
||||
|
||||
content.classList.remove('animating', 'backwards', 'disable-hover');
|
||||
});
|
||||
|
||||
function selectTab(id: number | HTMLElement, animate = true) {
|
||||
const self = selectTab;
|
||||
|
||||
if(id instanceof HTMLElement) {
|
||||
id = whichChild(id);
|
||||
}
|
||||
|
||||
const prevId = self.prevId();
|
||||
const prevId = selectTab.prevId();
|
||||
if(id === prevId) return false;
|
||||
|
||||
//console.log('selectTab id:', id);
|
||||
|
||||
const _from = from;
|
||||
const to = content.children[id] as HTMLElement;
|
||||
|
||||
if(!rootScope.settings.animationsEnabled || prevId === -1) {
|
||||
animate = false;
|
||||
}
|
||||
|
||||
if(!withAnimationListener) {
|
||||
const timeout = content.dataset.timeout;
|
||||
if(timeout !== undefined) {
|
||||
clearTimeout(+timeout);
|
||||
}
|
||||
|
||||
delete content.dataset.timeout;
|
||||
}
|
||||
|
||||
if(!animate) {
|
||||
if(_from) _from.classList.remove('active', 'to', 'from');
|
||||
if(from) from.classList.remove('active', 'to', 'from');
|
||||
if(to) {
|
||||
to.classList.remove('to', 'from');
|
||||
to.classList.add('active');
|
||||
@ -170,12 +209,21 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
|
||||
return;
|
||||
}
|
||||
|
||||
if(!withAnimationListener) {
|
||||
content.dataset.timeout = '' + window.setTimeout(() => {
|
||||
to.classList.remove('to');
|
||||
from && from.classList.remove('from');
|
||||
content.classList.remove('animating', 'backwards', 'disable-hover');
|
||||
delete content.dataset.timeout;
|
||||
}, transitionTime);
|
||||
}
|
||||
|
||||
if(from) {
|
||||
from.classList.remove('to');
|
||||
from.classList.add('from');
|
||||
}
|
||||
|
||||
content.classList.add('animating', 'disable-hover');
|
||||
content.classList.add('animating'/* , 'disable-hover' */);
|
||||
const toRight = prevId < id;
|
||||
content.classList.toggle('backwards', !toRight);
|
||||
|
||||
@ -200,7 +248,8 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
|
||||
});
|
||||
}
|
||||
|
||||
if(_from/* && false */) {
|
||||
if(from/* && false */) {
|
||||
const _from = from;
|
||||
const callback = () => {
|
||||
_from.classList.remove('active', 'from');
|
||||
|
||||
@ -223,7 +272,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
|
||||
if(isHeavy) {
|
||||
if(!animationDeferred) {
|
||||
animationDeferred = deferredPromise<void>();
|
||||
animationStarted = performance.now();
|
||||
// animationStarted = performance.now();
|
||||
}
|
||||
|
||||
dispatchHeavyAnimationEvent(animationDeferred, transitionTime * 2);
|
||||
|
@ -550,7 +550,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
icoDiv.classList.add('document-ico');
|
||||
|
||||
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||
if(doc.thumbs?.length || (message.pFlags.is_outgoing && cacheContext.url && doc.type === 'photo')) {
|
||||
if((doc.thumbs?.length || (message.pFlags.is_outgoing && cacheContext.url && doc.type === 'photo')) && doc.mime_type !== 'image/gif') {
|
||||
docDiv.classList.add('document-with-thumb');
|
||||
|
||||
let imgs: HTMLImageElement[] = [];
|
||||
@ -1564,6 +1564,7 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble,
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('document-container');
|
||||
container.dataset.mid = '' + mid;
|
||||
container.dataset.peerId = '' + message.peerId;
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.classList.add('document-wrapper');
|
||||
|
@ -16,7 +16,7 @@ export const MAIN_DOMAIN = 'web.telegram.org';
|
||||
const App = {
|
||||
id: 1025907,
|
||||
hash: '452b0359b988148995f22ff0f4229750',
|
||||
version: '0.8.2',
|
||||
version: '0.8.3',
|
||||
langPackVersion: '0.3.3',
|
||||
langPack: 'macos',
|
||||
langPackCode: 'en',
|
||||
|
@ -8,9 +8,9 @@ import type ListenerSetter from "../listenerSetter";
|
||||
import { isTouchSupported } from "../touchSupport";
|
||||
import simulateEvent from "./dispatchEvent";
|
||||
|
||||
export const CLICK_EVENT_NAME: 'mousedown' | 'touchend' | 'click' = (isTouchSupported ? 'mousedown' : 'click') as any;
|
||||
export const CLICK_EVENT_NAME: 'mousedown' /* | 'touchend' */ | 'click' = (isTouchSupported ? 'mousedown' : 'click') as any;
|
||||
export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>;
|
||||
export function attachClickEvent(elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options: AttachClickOptions = {}) {
|
||||
export function attachClickEvent(elem: HTMLElement | Window, callback: (e: /* TouchEvent | */MouseEvent) => void, options: AttachClickOptions = {}) {
|
||||
const add = options.listenerSetter ? options.listenerSetter.add(elem) : elem.addEventListener.bind(elem);
|
||||
// const remove = options.listenerSetter ? options.listenerSetter.removeManual.bind(options.listenerSetter, elem) : elem.removeEventListener.bind(elem);
|
||||
|
||||
@ -46,11 +46,11 @@ export function attachClickEvent(elem: HTMLElement, callback: (e: TouchEvent | M
|
||||
}
|
||||
|
||||
export function detachClickEvent(elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: AddEventListenerOptions) {
|
||||
if(CLICK_EVENT_NAME === 'touchend') {
|
||||
elem.removeEventListener('touchstart', callback, options);
|
||||
} else {
|
||||
// if(CLICK_EVENT_NAME === 'touchend') {
|
||||
// elem.removeEventListener('touchstart', callback, options);
|
||||
// } else {
|
||||
elem.removeEventListener(CLICK_EVENT_NAME, callback, options);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
export function simulateClickEvent(elem: HTMLElement) {
|
||||
|
@ -56,7 +56,7 @@ if(IS_STICKY_INPUT_BUGGED) {
|
||||
// let hasFocus = false;
|
||||
let lastFocusOutTimeStamp = 0;
|
||||
document.addEventListener('focusin', (e) => {
|
||||
if((e.timeStamp - lastFocusOutTimeStamp) < 50/* && document.activeElement === input */) {
|
||||
if(!(e.target as HTMLElement).classList.contains('is-sticky-input-bugged') || (e.timeStamp - lastFocusOutTimeStamp) < 50/* && document.activeElement === input */) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -793,6 +793,7 @@ const lang = {
|
||||
"Message.Context.Select": "Select",
|
||||
"Message.Context.Pin": "Pin",
|
||||
"Message.Context.Unpin": "Unpin",
|
||||
"Message.Context.Goto": "Show Message",
|
||||
"MessageContext.CopyMessageLink1": "Copy Message Link",
|
||||
"NewPoll.Anonymous": "Anonymous Voting",
|
||||
"NewPoll.Explanation.Placeholder": "Add a Comment (Optional)",
|
||||
|
@ -22,7 +22,7 @@ import rootScope from "../rootScope";
|
||||
import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appPeersManager from './appPeersManager';
|
||||
import appImManager from "./appImManager";
|
||||
import appMessagesManager, { Dialog } from "./appMessagesManager";
|
||||
import appMessagesManager, { Dialog, MyMessage } from "./appMessagesManager";
|
||||
import appStateManager, { State } from "./appStateManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import Button from "../../components/button";
|
||||
@ -51,6 +51,7 @@ import windowSize from "../../helpers/windowSize";
|
||||
import isInDOM from "../../helpers/dom/isInDOM";
|
||||
import appPhotosManager, { MyPhoto } from "./appPhotosManager";
|
||||
import { MyDocument } from "./appDocsManager";
|
||||
import { setSendingStatus } from "../../components/sendingStatus";
|
||||
|
||||
export type DialogDom = {
|
||||
avatarEl: AvatarElement,
|
||||
@ -440,6 +441,7 @@ export class AppDialogsManager {
|
||||
public setFilterId(filterId: number) {
|
||||
this.filterId = filterId;
|
||||
this.indexKey = appMessagesManager.dialogsStorage ? appMessagesManager.dialogsStorage.getDialogIndexKey(this.filterId) : 'index';
|
||||
rootScope.filterId = filterId;
|
||||
}
|
||||
|
||||
private async onStateLoaded(state: State) {
|
||||
@ -1433,22 +1435,15 @@ export class AppDialogsManager {
|
||||
}
|
||||
}
|
||||
|
||||
const lastMessage = dialog.draft?._ === 'draftMessage' ?
|
||||
dialog.draft :
|
||||
appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);
|
||||
if(!lastMessage.deleted && lastMessage.pFlags.out && lastMessage.peerId !== rootScope.myId/* &&
|
||||
dialog.read_outbox_max_id */) { // maybe comment, 06.20.2020
|
||||
const isUnread = !!lastMessage.pFlags?.unread
|
||||
/* && dialog.read_outbox_max_id !== 0 */; // maybe uncomment, 31.01.2020
|
||||
|
||||
if(isUnread) {
|
||||
dom.statusSpan.classList.remove('tgico-checks');
|
||||
dom.statusSpan.classList.add('tgico-check');
|
||||
} else {
|
||||
dom.statusSpan.classList.remove('tgico-check');
|
||||
dom.statusSpan.classList.add('tgico-checks');
|
||||
let setStatusMessage: MyMessage;
|
||||
if(dialog.draft?._ !== 'draftMessage') {
|
||||
const lastMessage: MyMessage = appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);
|
||||
if(!lastMessage.deleted && lastMessage.pFlags.out && lastMessage.peerId !== rootScope.myId) {
|
||||
setStatusMessage = lastMessage;
|
||||
}
|
||||
} else dom.statusSpan.classList.remove('tgico-check', 'tgico-checks');
|
||||
}
|
||||
|
||||
setSendingStatus(dom.statusSpan, setStatusMessage, true);
|
||||
|
||||
const filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
|
||||
let isPinned: boolean;
|
||||
@ -1672,7 +1667,7 @@ export class AppDialogsManager {
|
||||
li.dataset.peerId = '' + peerId;
|
||||
|
||||
const statusSpan = document.createElement('span');
|
||||
statusSpan.classList.add('message-status');
|
||||
statusSpan.classList.add('message-status', 'sending-status'/* , 'transition', 'reveal' */);
|
||||
|
||||
const lastTimeSpan = document.createElement('span');
|
||||
lastTimeSpan.classList.add('message-time');
|
||||
|
@ -806,6 +806,16 @@ export class AppImManager {
|
||||
|
||||
if(e.code === 'KeyC' && (e.ctrlKey || e.metaKey) && target.tagName !== 'INPUT') {
|
||||
return;
|
||||
} else if(e.altKey && (e.code === 'ArrowUp' || e.code === 'ArrowDown') && rootScope.peerId) {
|
||||
const folder = appMessagesManager.dialogsStorage.getFolder(rootScope.filterId, true);
|
||||
const idx = folder.findIndex(dialog => dialog.peerId === rootScope.peerId);
|
||||
if(idx !== -1) {
|
||||
const nextIndex = e.code === 'ArrowUp' ? idx - 1 : idx + 1;
|
||||
const nextDialog = folder[nextIndex];
|
||||
if(nextDialog) {
|
||||
this.setPeer(nextDialog.peerId);
|
||||
}
|
||||
}
|
||||
} else if(e.code === 'ArrowUp') {
|
||||
if(!chat.input.editMsgId && chat.input.isInputEmpty()) {
|
||||
const historyStorage = appMessagesManager.getHistoryStorage(chat.peerId, chat.threadId);
|
||||
@ -854,23 +864,18 @@ export class AppImManager {
|
||||
|
||||
document.body.addEventListener('keydown', onKeyDown);
|
||||
|
||||
rootScope.addEventListener('history_multiappend', (e) => {
|
||||
const msgIdsByPeer = e;
|
||||
|
||||
rootScope.addEventListener('history_multiappend', (msgIdsByPeer) => {
|
||||
for(const peerId in msgIdsByPeer) {
|
||||
appSidebarRight.sharedMediaTab.renderNewMessages(+peerId, Array.from(msgIdsByPeer[peerId]));
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.addEventListener('history_delete', (e) => {
|
||||
const {peerId, msgs} = e;
|
||||
|
||||
rootScope.addEventListener('history_delete', ({peerId, msgs}) => {
|
||||
appSidebarRight.sharedMediaTab.deleteDeletedMessages(peerId, Array.from(msgs));
|
||||
});
|
||||
|
||||
// Calls when message successfully sent and we have an id
|
||||
rootScope.addEventListener('message_sent', (e) => {
|
||||
const {storage, tempId, mid} = e;
|
||||
rootScope.addEventListener('message_sent', ({storage, tempId, mid}) => {
|
||||
const message = appMessagesManager.getMessageFromStorage(storage, mid);
|
||||
appSidebarRight.sharedMediaTab.renderNewMessages(message.peerId, [mid]);
|
||||
});
|
||||
|
@ -2171,7 +2171,7 @@ export class AppMessagesManager {
|
||||
//return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
public getMidsByMessage(message: any) {
|
||||
public getMidsByMessage(message: Message.message) {
|
||||
if(message?.grouped_id) return this.getMidsByAlbum(message.grouped_id);
|
||||
else return [message.mid];
|
||||
}
|
||||
|
@ -147,6 +147,7 @@ export class RootScope extends EventListenerBase<{
|
||||
public connectionStatus: {[name: string]: ConnectionStatusChange} = {};
|
||||
public settings: State['settings'];
|
||||
public peerId = 0;
|
||||
public filterId = 0;
|
||||
public systemTheme: Theme['name'];
|
||||
public config: Partial<Config.config> = {
|
||||
forwarded_count_max: 100,
|
||||
|
@ -155,6 +155,11 @@ Utility Classes
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.disable-hover/* ,
|
||||
.disable-hover * */ {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/* .flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
@ -212,7 +212,7 @@
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
z-index: 3;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
//background-color: rgba(0, 0, 0, .2);
|
||||
|
@ -1147,11 +1147,6 @@ $chat-helper-size: 36px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 0 $chat-padding-handhelds;
|
||||
|
||||
html.is-ios & {
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-chat {
|
||||
|
@ -221,7 +221,8 @@ $bubble-margin: .25rem;
|
||||
// ! hide context menu for media on android
|
||||
.bubbles.is-selecting & {
|
||||
img,
|
||||
video {
|
||||
video,
|
||||
a {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
@ -1031,7 +1032,7 @@ $bubble-margin: .25rem;
|
||||
&-title,
|
||||
&-subtitle,
|
||||
i {
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&-border {
|
||||
@ -1412,31 +1413,31 @@ $bubble-margin: .25rem;
|
||||
position: absolute;
|
||||
/* position: relative;
|
||||
width: max-content; */
|
||||
bottom: .1rem;
|
||||
right: .2rem;
|
||||
border-radius: 12px;
|
||||
bottom: .1875rem;
|
||||
right: .1875rem;
|
||||
border-radius: .75rem;
|
||||
background-color: var(--message-time-background);
|
||||
padding: 0 .2rem;
|
||||
padding: 0 .3125rem;
|
||||
z-index: 2;
|
||||
|
||||
.time {
|
||||
margin-left: 0;
|
||||
color: #fff;
|
||||
visibility: visible;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 2.5px;
|
||||
line-height: 18px;
|
||||
pointer-events: all; // show title
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
height: 18px;
|
||||
|
||||
&:after {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.inner {
|
||||
display: none;
|
||||
right: unset;
|
||||
bottom: unset;
|
||||
color: #fff;
|
||||
margin: inherit;
|
||||
|
||||
&:after {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2102,6 +2103,10 @@ $bubble-margin: .25rem;
|
||||
.reply, .name {
|
||||
right: calc(100% + 10px);
|
||||
}
|
||||
|
||||
.message {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.just-media) {
|
||||
@ -2117,14 +2122,6 @@ $bubble-margin: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.sticker,
|
||||
&.round,
|
||||
&.emoji-big {
|
||||
.message {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.quote:before {
|
||||
background-color: var(--message-out-primary-color);
|
||||
}
|
||||
@ -2152,10 +2149,6 @@ $bubble-margin: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-message-empty .time .inner {
|
||||
color: var(--message-out-primary-color);
|
||||
}
|
||||
|
||||
/* &.is-message-empty .time:after {
|
||||
margin-bottom: 1px;
|
||||
} */
|
||||
|
@ -109,6 +109,7 @@ ul.chatlist {
|
||||
} */
|
||||
|
||||
li {
|
||||
--background: unset;
|
||||
//height: var(--height);
|
||||
height: 72px;
|
||||
//max-height: var(--height);
|
||||
@ -124,12 +125,14 @@ ul.chatlist {
|
||||
padding-right: 8.5px;
|
||||
padding-left: 8.5px; */
|
||||
overflow: hidden;
|
||||
background: var(--background);
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@include hover-background-effect();
|
||||
//@include hover-background-effect();
|
||||
@include hover(gray, --background, false);
|
||||
|
||||
&.is-muted {
|
||||
.user-title {
|
||||
@ -183,13 +186,13 @@ ul.chatlist {
|
||||
} */
|
||||
|
||||
&.menu-open {
|
||||
background: var(--light-secondary-text-color);
|
||||
--background: var(--light-secondary-text-color);
|
||||
}
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
&.active {
|
||||
--background: var(--primary-color) !important;
|
||||
//background: var(--light-secondary-text-color);
|
||||
background: var(--primary-color) !important;
|
||||
|
||||
.user-caption,
|
||||
.tgico-chatspinned:before,
|
||||
@ -405,25 +408,25 @@ ul.chatlist {
|
||||
}
|
||||
|
||||
.message-status {
|
||||
margin-right: .1rem;
|
||||
//margin-top: .3rem;
|
||||
margin-top: -.3rem;
|
||||
margin-right: 0.125rem;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
&[class*=" tgico-"] {
|
||||
color: var(--chatlist-status-color);
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
color: var(--chatlist-status-color);
|
||||
line-height: 1;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 1.25rem;
|
||||
position: relative;
|
||||
margin-top: -.0625rem;
|
||||
|
||||
&:before {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.message-time {
|
||||
/* .message-time {
|
||||
vertical-align: middle;
|
||||
}
|
||||
} */
|
||||
|
||||
.tgico-chatspinned {
|
||||
background: transparent;
|
||||
|
@ -56,10 +56,12 @@
|
||||
}
|
||||
|
||||
&-background {
|
||||
top: -15%;
|
||||
// it is needed for circle scale animation
|
||||
top: -15%;
|
||||
right: -15%;
|
||||
bottom: -15%;
|
||||
left: -15%;
|
||||
|
||||
background-color: var(--primary-color);
|
||||
transform: scale(1);
|
||||
border-radius: 50%;
|
||||
@ -138,6 +140,14 @@
|
||||
|
||||
.checkbox-box {
|
||||
border-radius: 50%;
|
||||
overflow: auto;
|
||||
|
||||
&-background {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&-border {
|
||||
border: 2px solid var(--secondary-color);
|
||||
@ -147,6 +157,10 @@
|
||||
&-check {
|
||||
--offset: calc(var(--size) - (var(--size) / 2 + .125rem));
|
||||
}
|
||||
|
||||
html.is-safari & {
|
||||
-webkit-mask-image: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,6 +287,9 @@ video::-webkit-media-controls-enclosure {
|
||||
margin: 0;
|
||||
outline: none;
|
||||
caret-color: var(--color);
|
||||
position: absolute;
|
||||
top: -.5rem;
|
||||
bottom: -.5rem;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
@ -348,17 +351,24 @@ video::-webkit-media-controls-enclosure {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translate(calc(var(--thumb-size) / 2), -50%);
|
||||
transform: translate(calc(var(--thumb-size) / 2), -50%) scale(1);
|
||||
|
||||
@include animation-level(2) {
|
||||
transition: transform .125s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-focused .progress-line__filled:not(.progress-line__loaded):after {
|
||||
transform: translate(calc(var(--thumb-size) / 2), -50%) scale(1.25);
|
||||
}
|
||||
|
||||
&__loaded, &:before {
|
||||
opacity: .3;
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
&__seek,
|
||||
&__filled,
|
||||
&__loaded {
|
||||
border-radius: var(--border-radius);
|
||||
|
@ -10,6 +10,10 @@
|
||||
padding-left: 4.25rem;
|
||||
height: 70px;
|
||||
|
||||
.media-photo {
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
&-ico {
|
||||
background-color: var(--background-color);
|
||||
border-radius: $border-radius;
|
||||
|
@ -194,7 +194,7 @@
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.checkbox-field {
|
||||
/* .checkbox-field {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: -54px;
|
||||
@ -202,7 +202,7 @@
|
||||
|
||||
.checkbox-caption {
|
||||
padding-left: 54px;
|
||||
}
|
||||
} */
|
||||
|
||||
&-wrapper {
|
||||
flex: 1 1 auto;
|
||||
|
@ -310,7 +310,9 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document-name, .audio-title, .title {
|
||||
.document-name,
|
||||
.audio-title,
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@ -347,6 +349,11 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
right: .25rem;
|
||||
top: .25rem;
|
||||
}
|
||||
|
||||
/* span.video-play {
|
||||
background-color: var(--message-time-background);
|
||||
color: #fff;
|
||||
@ -357,6 +364,37 @@
|
||||
} */
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
&-box {
|
||||
box-shadow: 0px 0px 3px 0px rgb(0 0 0 / 40%);
|
||||
|
||||
&-border {
|
||||
border-color: var(--message-checkbox-border-color);
|
||||
}
|
||||
|
||||
&-background {
|
||||
background-color: var(--message-checkbox-color);
|
||||
}
|
||||
}
|
||||
|
||||
&-field {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.document,
|
||||
.audio {
|
||||
.checkbox-field {
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-left: 2rem;
|
||||
margin-top: 1rem;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
&-content-media &-month {
|
||||
&-items {
|
||||
width: 100%;
|
||||
@ -385,7 +423,8 @@
|
||||
//height: 54px;
|
||||
height: calc(48px + 1.5rem);
|
||||
|
||||
&-ico, &-download {
|
||||
&-ico,
|
||||
&-download {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 5px !important;
|
||||
@ -412,73 +451,87 @@
|
||||
.search-super-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 20px;
|
||||
margin-left: 5px;
|
||||
padding-bottom: 2px;
|
||||
//padding-bottom: 10px;
|
||||
padding-left: 4.4375rem;
|
||||
position: relative;
|
||||
padding-left: 60px;
|
||||
overflow: hidden;
|
||||
//min-height: 48px;
|
||||
min-height: 58px;
|
||||
min-height: 4.375rem;
|
||||
cursor: pointer;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.preview {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
border-radius: .375rem;
|
||||
overflow: hidden;
|
||||
.row-media {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
border-radius: .375rem;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: .6875rem;
|
||||
|
||||
&.empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
/* .anchor-url {
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
&.empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.media-photo {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
content: " ";
|
||||
cursor: pointer;
|
||||
}
|
||||
} */
|
||||
|
||||
.url {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.media-photo {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
margin-top: 2px;
|
||||
.row-title {
|
||||
margin-top: .1875rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
max-width: 310px;
|
||||
.row-subtitle {
|
||||
overflow: hidden;
|
||||
white-space: pre-wrap;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-word;
|
||||
|
||||
&.sender {
|
||||
margin-top: 2px;
|
||||
margin-top: .125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.search-super-month-items {
|
||||
padding: 0 24px 15px 15px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 0 16px 15px 7px;
|
||||
.sent-time {
|
||||
margin: 1px 0 0;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
padding: 0 !important;
|
||||
margin: 2rem 0 0 -1.75rem !important;
|
||||
}
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
.search-super-month-items {
|
||||
margin: .5625rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
.search-super-month-name {
|
||||
padding: .875rem 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-content-music, &-content-voice {
|
||||
@ -528,6 +581,76 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-tabs-scrollable {
|
||||
.search-super-nav-scrollable {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.search-super-nav-scrollable,
|
||||
.search-super-selection-container {
|
||||
@include animation-level(2) {
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-selecting {
|
||||
&:not(.backwards) {
|
||||
.search-super-nav-scrollable {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.search-super-selection-container {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-selecting {
|
||||
a {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.row {
|
||||
&:not(.menu-open) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-selection {
|
||||
&-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 1rem;
|
||||
opacity: 0;
|
||||
|
||||
.btn-icon + .btn-icon {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-count {
|
||||
flex-grow: 1;
|
||||
font-weight: 500;
|
||||
color: var(--primary-text-color);
|
||||
white-space: nowrap;
|
||||
text-transform: capitalize;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#search-container {
|
||||
|
@ -115,4 +115,8 @@
|
||||
margin: 0 !important;
|
||||
left: .5rem;
|
||||
}
|
||||
|
||||
&.menu-open {
|
||||
background-color: var(--light-secondary-text-color);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,7 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
// * Jolly Cobra's transition
|
||||
|
||||
.transition {
|
||||
--easeOutSine: cubic-bezier(.39, .575, .565, 1);
|
||||
--easeInSine: cubic-bezier(.47, 0, .745, .715);
|
||||
|
||||
.transition-item {
|
||||
> .transition-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -71,6 +64,8 @@
|
||||
* slide-fade
|
||||
*/
|
||||
&.slide-fade {
|
||||
--easeOutSine: cubic-bezier(.39, .575, .565, 1);
|
||||
--easeInSine: cubic-bezier(.47, 0, .745, .715);
|
||||
position: relative;
|
||||
|
||||
> .from {
|
||||
@ -117,6 +112,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* &.reveal {
|
||||
> .to {
|
||||
clip-path: inset(0 100% 0 0);
|
||||
}
|
||||
|
||||
&.animating {
|
||||
> .to {
|
||||
animation: reveal-in 350ms ease-in;
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -188,6 +195,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* @keyframes reveal-in {
|
||||
0% {
|
||||
clip-path: inset(0 100% 0 0);
|
||||
}
|
||||
100% {
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
} */
|
||||
|
||||
/* .zoom-fade {
|
||||
transition: .15s ease-in-out opacity, .15s ease-in-out transform;
|
||||
transform: scale3d(1.1, 1.1, 1);
|
||||
|
@ -406,6 +406,10 @@ html.is-ios {
|
||||
//&, body {
|
||||
position: fixed; // fix iOS fullscreen scroll
|
||||
//}
|
||||
|
||||
// disable image longtapping
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
@supports(padding: unquote('max(0px)')) {
|
||||
@ -490,11 +494,6 @@ input, textarea, button, select, a, div {
|
||||
//}
|
||||
}
|
||||
|
||||
.disable-hover/* ,
|
||||
.disable-hover * */ {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
.only-handhelds {
|
||||
display: none !important;
|
||||
@ -1359,3 +1358,20 @@ middle-ellipsis-element {
|
||||
content: "W";
|
||||
}
|
||||
}
|
||||
|
||||
.sending-status {
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* &.animating {
|
||||
.sending-status-icon {
|
||||
background: var(--background);
|
||||
}
|
||||
} */
|
||||
|
||||
&-icon {
|
||||
position: absolute;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user