Multiselect: forward and delete
Chat input helper is now clickable Popup stickers lower resolution for handhelds
This commit is contained in:
parent
f135565529
commit
baf1f78618
@ -36,13 +36,22 @@ export default class AppSelectPeers {
|
||||
private cachedContacts: number[];
|
||||
|
||||
private loadedWhat: Partial<{[k in 'dialogs' | 'archived' | 'contacts']: true}> = {};
|
||||
|
||||
private renderedPeerIDs: Set<number> = new Set();
|
||||
|
||||
constructor(private appendTo: HTMLElement, private onChange?: (length: number) => void, private peerType: PeerType[] = ['dialogs'], onFirstRender?: () => void, private renderResultsFunc?: (peerIDs: number[]) => void, private chatRightsAction?: ChatRights, private multiSelect = true) {
|
||||
this.container.classList.add('selector');
|
||||
|
||||
if(!this.renderResultsFunc) {
|
||||
this.renderResultsFunc = this.renderResults;
|
||||
}
|
||||
const f = (renderResultsFunc || this.renderResults).bind(this);
|
||||
this.renderResultsFunc = (peerIDs: number[]) => {
|
||||
peerIDs = peerIDs.filter(peerID => {
|
||||
const notRendered = !this.renderedPeerIDs.has(peerID);
|
||||
if(notRendered) this.renderedPeerIDs.add(peerID);
|
||||
return notRendered;
|
||||
});
|
||||
|
||||
return f(peerIDs);
|
||||
};
|
||||
|
||||
this.input = document.createElement('input');
|
||||
this.input.classList.add('selector-search-input');
|
||||
@ -130,6 +139,7 @@ export default class AppSelectPeers {
|
||||
this.promise = null;
|
||||
this.list.innerHTML = '';
|
||||
this.query = value;
|
||||
this.renderedPeerIDs.clear();
|
||||
|
||||
//console.log('selectPeers input:', this.query);
|
||||
this.getMoreResults();
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
import appChatsManager from "../../lib/appManagers/appChatsManager";
|
||||
import appImManager from "../../lib/appManagers/appImManager";
|
||||
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
||||
import appPeersManager from "../../lib/appManagers/appPeersManager";
|
||||
import appPollsManager, { Poll } from "../../lib/appManagers/appPollsManager";
|
||||
import $rootScope from "../../lib/rootScope";
|
||||
import { findUpClassName } from "../../lib/utils";
|
||||
import { cancelEvent, cancelSelection, findUpClassName } from "../../lib/utils";
|
||||
import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu";
|
||||
import { attachContextMenuListener, openBtnMenu, positionMenu } from "../misc";
|
||||
import { PopupButton } from "../popup";
|
||||
import PopupDeleteMessages from "../popupDeleteMessages";
|
||||
import PopupForward from "../popupForward";
|
||||
import PopupPeer from "../popupPeer";
|
||||
import appSidebarRight from "../sidebarRight";
|
||||
@ -21,7 +23,7 @@ export default class ChatContextMenu {
|
||||
public msgID: number;
|
||||
|
||||
constructor(private attachTo: HTMLElement) {
|
||||
attachContextMenuListener(attachTo, (e) => {
|
||||
const onContextMenu = (e: MouseEvent | Touch) => {
|
||||
if(this.init) {
|
||||
this.init();
|
||||
this.init = null;
|
||||
@ -69,7 +71,29 @@ export default class ChatContextMenu {
|
||||
});
|
||||
|
||||
/////this.log('contextmenu', e, bubble, msgID, side);
|
||||
});
|
||||
};
|
||||
|
||||
if(isTouchSupported) {
|
||||
attachTo.addEventListener('click', (e) => {
|
||||
//const good = !!findUpClassName(e.target, 'message') || !!findUpClassName(e.target, 'bubble__container');
|
||||
const className = (e.target as HTMLElement).className;
|
||||
const good = ['bubble', 'bubble__container', 'message', 'time', 'inner'].find(c => className.includes(c));
|
||||
if(good) {
|
||||
onContextMenu(e);
|
||||
}
|
||||
});
|
||||
|
||||
attachContextMenuListener(attachTo, (e) => {
|
||||
if(appImManager.chatSelection.isSelecting) return;
|
||||
|
||||
cancelSelection();
|
||||
cancelEvent(e as any);
|
||||
let bubble = findUpClassName(e.target, 'bubble');
|
||||
if(bubble) {
|
||||
appImManager.chatSelection.toggleByBubble(bubble);
|
||||
}
|
||||
});
|
||||
} else attachContextMenuListener(attachTo, onContextMenu);
|
||||
}
|
||||
|
||||
private init = () => {
|
||||
@ -145,7 +169,7 @@ export default class ChatContextMenu {
|
||||
icon: 'delete danger',
|
||||
text: 'Delete',
|
||||
onClick: this.onDeleteClick,
|
||||
verify: () => this.peerID > 0 || appMessagesManager.getMessage(this.msgID).fromID == $rootScope.myID || appChatsManager.hasRights(-this.peerID, 'deleteRevoke')
|
||||
verify: () => appMessagesManager.canDeleteMessage(this.msgID)
|
||||
}];
|
||||
|
||||
this.element = ButtonMenu(this.buttons);
|
||||
@ -217,58 +241,6 @@ export default class ChatContextMenu {
|
||||
};
|
||||
|
||||
private onDeleteClick = () => {
|
||||
const peerID = $rootScope.selectedPeerID;
|
||||
const firstName = appPeersManager.getPeerTitle(peerID, false, true);
|
||||
|
||||
const msgID = this.msgID;
|
||||
const callback = (revoke: boolean) => {
|
||||
appMessagesManager.deleteMessages([msgID], revoke);
|
||||
};
|
||||
|
||||
let title: string, description: string, buttons: PopupButton[];
|
||||
title = 'Delete Message?';
|
||||
description = `Are you sure you want to delete this message?`;
|
||||
|
||||
if(peerID == $rootScope.myID) {
|
||||
buttons = [{
|
||||
text: 'DELETE',
|
||||
isDanger: true,
|
||||
callback: () => callback(false)
|
||||
}];
|
||||
} else {
|
||||
buttons = [{
|
||||
text: 'DELETE JUST FOR ME',
|
||||
isDanger: true,
|
||||
callback: () => callback(false)
|
||||
}];
|
||||
|
||||
if(peerID > 0) {
|
||||
buttons.push({
|
||||
text: 'DELETE FOR ME AND ' + firstName,
|
||||
isDanger: true,
|
||||
callback: () => callback(true)
|
||||
});
|
||||
} else if(appChatsManager.hasRights(-peerID, 'deleteRevoke')) {
|
||||
buttons.push({
|
||||
text: 'DELETE FOR ALL',
|
||||
isDanger: true,
|
||||
callback: () => callback(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
text: 'CANCEL',
|
||||
isCancel: true
|
||||
});
|
||||
|
||||
const popup = new PopupPeer('popup-delete-chat', {
|
||||
peerID: peerID,
|
||||
title: title,
|
||||
description: description,
|
||||
buttons: buttons
|
||||
});
|
||||
|
||||
popup.show();
|
||||
new PopupDeleteMessages([this.msgID]);
|
||||
};
|
||||
}
|
@ -11,10 +11,11 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
|
||||
import opusDecodeController from "../../lib/opusDecodeController";
|
||||
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
||||
import $rootScope from '../../lib/rootScope';
|
||||
import { cancelEvent, getRichValue } from "../../lib/utils";
|
||||
import { cancelEvent, findUpClassName, getRichValue } from "../../lib/utils";
|
||||
import ButtonMenu, { ButtonMenuItemOptions } from '../buttonMenu';
|
||||
import emoticonsDropdown from "../emoticonsDropdown";
|
||||
import PopupCreatePoll from "../popupCreatePoll";
|
||||
import PopupForward from '../popupForward';
|
||||
import PopupNewMedia from '../popupNewMedia';
|
||||
import { ripple } from '../ripple';
|
||||
import Scrollable from "../scrollable";
|
||||
@ -205,7 +206,7 @@ export class ChatInput {
|
||||
if(this.lastUrl != url) return;
|
||||
//console.log('got webpage: ', webpage);
|
||||
|
||||
this.setTopInfo('webpage', () => {}, webpage.site_name || webpage.title, webpage.description || webpage.url);
|
||||
this.setTopInfo('webpage', () => {}, webpage.site_name || webpage.title || 'Webpage', webpage.description || webpage.url || '');
|
||||
|
||||
delete this.noWebPage;
|
||||
this.willSendWebPage = webpage;
|
||||
@ -267,7 +268,7 @@ export class ChatInput {
|
||||
//console.log('messageInput paste', text, entities);
|
||||
entities = entities.filter(e => e._ == 'messageEntityEmoji' || e._ == 'messageEntityLinebreak');
|
||||
//text = RichTextProcessor.wrapEmojiText(text);
|
||||
text = RichTextProcessor.wrapRichText(text, {entities});
|
||||
text = RichTextProcessor.wrapRichText(text, {entities, noLinks: true});
|
||||
|
||||
// console.log('messageInput paste after', text);
|
||||
|
||||
@ -453,7 +454,7 @@ export class ChatInput {
|
||||
|
||||
return; */
|
||||
|
||||
let perf = performance.now();
|
||||
//let perf = performance.now();
|
||||
opusDecodeController.decode(typedArray, true).then(result => {
|
||||
//console.log('WAVEFORM!:', /* waveform, */performance.now() - perf);
|
||||
|
||||
@ -509,6 +510,33 @@ export class ChatInput {
|
||||
this.clearHelper();
|
||||
this.updateSendBtn();
|
||||
});
|
||||
|
||||
let d = false;
|
||||
this.replyElements.container.addEventListener('click', (e) => {
|
||||
if(!findUpClassName(e.target, 'reply-wrapper')) return;
|
||||
if(this.helperType == 'forward') {
|
||||
if(d) return;
|
||||
d = true;
|
||||
|
||||
const mids = this.forwardingMids.slice();
|
||||
const helperFunc = this.helperFunc;
|
||||
this.clearHelper();
|
||||
let selected = false;
|
||||
new PopupForward(mids, () => {
|
||||
selected = true;
|
||||
}, () => {
|
||||
d = false;
|
||||
|
||||
if(!selected) {
|
||||
helperFunc();
|
||||
}
|
||||
});
|
||||
} else if(this.helperType == 'reply') {
|
||||
appImManager.setPeer($rootScope.selectedPeerID, this.replyToMsgID);
|
||||
} else if(this.helperType == 'edit') {
|
||||
appImManager.setPeer($rootScope.selectedPeerID, this.editMsgID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private isInputEmpty() {
|
||||
@ -579,8 +607,12 @@ export class ChatInput {
|
||||
});
|
||||
}
|
||||
|
||||
// * wait for sendText set messageID for invokeAfterMsg
|
||||
if(this.forwardingMids.length) {
|
||||
appMessagesManager.forwardMessages(appImManager.peerID, this.forwardingMids);
|
||||
const mids = this.forwardingMids.slice();
|
||||
setTimeout(() => {
|
||||
appMessagesManager.forwardMessages(appImManager.peerID, mids);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
this.onMessageSent();
|
||||
@ -629,7 +661,7 @@ export class ChatInput {
|
||||
this.setTopInfo('forward', f, title, mids.length + ' forwarded messages');
|
||||
}
|
||||
|
||||
this.forwardingMids = mids;
|
||||
this.forwardingMids = mids.slice();
|
||||
};
|
||||
|
||||
f();
|
||||
@ -640,6 +672,12 @@ export class ChatInput {
|
||||
this.messageInput.innerText = '';
|
||||
}
|
||||
|
||||
if(type) {
|
||||
this.lastUrl = '';
|
||||
delete this.noWebPage;
|
||||
this.willSendWebPage = null;
|
||||
}
|
||||
|
||||
this.replyToMsgID = 0;
|
||||
this.forwardingMids.length = 0;
|
||||
this.editMsgID = 0;
|
||||
@ -660,9 +698,13 @@ export class ChatInput {
|
||||
}
|
||||
|
||||
this.chatInput.parentElement.classList.add('is-helper-active');
|
||||
/* const scroll = appImManager.scrollable;
|
||||
if(scroll.isScrolledDown && !scroll.scrollLocked && !appImManager.messagesQueuePromise && !appImManager.setPeerPromise) {
|
||||
scroll.scrollTo(scroll.scrollHeight, 'top', true, true, 200);
|
||||
} */
|
||||
|
||||
if(input !== undefined) {
|
||||
this.messageInput.innerHTML = input ? RichTextProcessor.wrapRichText(input) : '';
|
||||
this.messageInput.innerHTML = input ? RichTextProcessor.wrapRichText(input, {noLinks: true}) : '';
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
import type { AppImManager } from "../../lib/appManagers/appImManager";
|
||||
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager";
|
||||
import { cancelEvent, cancelSelection, findUpClassName } from "../../lib/utils";
|
||||
import { cancelEvent, cancelSelection, findUpClassName, getSelectedText } from "../../lib/utils";
|
||||
import Button from "../button";
|
||||
import ButtonIcon from "../buttonIcon";
|
||||
import CheckboxField from "../checkbox";
|
||||
import PopupDeleteMessages from "../popupDeleteMessages";
|
||||
import PopupForward from "../popupForward";
|
||||
import { toast } from "../toast";
|
||||
|
||||
@ -34,7 +35,7 @@ const SetTransition = (element: HTMLElement, className: string, forwards: boolea
|
||||
};
|
||||
|
||||
const MAX_SELECTION_LENGTH = 100;
|
||||
const MIN_CLICK_MOVE = 32; // minimum bubble height
|
||||
//const MIN_CLICK_MOVE = 32; // minimum bubble height
|
||||
|
||||
export default class ChatSelection {
|
||||
public selectedMids: Set<number> = new Set();
|
||||
@ -42,14 +43,25 @@ export default class ChatSelection {
|
||||
|
||||
private selectionContainer: HTMLElement;
|
||||
private selectionCountEl: HTMLElement;
|
||||
private selectionForwardBtn: HTMLElement;
|
||||
private selectionDeleteBtn: HTMLElement;
|
||||
|
||||
public selectedText: string;
|
||||
|
||||
constructor(private appImManager: AppImManager, private appMessagesManager: AppMessagesManager) {
|
||||
if(isTouchSupported) return;
|
||||
|
||||
const bubblesContainer = appImManager.bubblesContainer;
|
||||
|
||||
if(isTouchSupported) {
|
||||
bubblesContainer.addEventListener('touchend', (e) => {
|
||||
if(!this.isSelecting) return;
|
||||
this.selectedText = getSelectedText();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
bubblesContainer.addEventListener('mousedown', (e) => {
|
||||
//console.log('selection mousedown', e);
|
||||
if(e.button != 0) { // LEFT BUTTON
|
||||
if(e.button != 0 || (!this.selectedMids.size && !(e.target as HTMLElement).classList.contains('bubble'))) { // LEFT BUTTON
|
||||
return;
|
||||
}
|
||||
|
||||
@ -169,6 +181,28 @@ export default class ChatSelection {
|
||||
if(!this.selectedMids.size) return;
|
||||
this.selectionCountEl.innerText = this.selectedMids.size + ' Message' + (this.selectedMids.size == 1 ? '' : 's');
|
||||
|
||||
let cantForward = false, cantDelete = false;
|
||||
for(const mid of this.selectedMids.values()) {
|
||||
const message = this.appMessagesManager.getMessage(mid);
|
||||
if(!cantForward) {
|
||||
if(message.action) {
|
||||
cantForward = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!cantDelete) {
|
||||
const canDelete = this.appMessagesManager.canDeleteMessage(mid);
|
||||
if(!canDelete) {
|
||||
cantDelete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(cantForward && cantDelete) break;
|
||||
}
|
||||
|
||||
this.selectionForwardBtn.toggleAttribute('disabled', cantForward);
|
||||
this.selectionDeleteBtn.toggleAttribute('disabled', cantDelete);
|
||||
}
|
||||
|
||||
public toggleSelection(toggleCheckboxes = true) {
|
||||
@ -180,11 +214,23 @@ export default class ChatSelection {
|
||||
const bubblesContainer = this.appImManager.bubblesContainer;
|
||||
//bubblesContainer.classList.toggle('is-selecting', !!this.selectedMids.size);
|
||||
|
||||
/* if(bubblesContainer.classList.contains('is-chat-input-hidden')) {
|
||||
const scrollable = this.appImManager.scrollable;
|
||||
if(scrollable.isScrolledDown) {
|
||||
scrollable.scrollTo(scrollable.scrollHeight, 'top', true, true, 200);
|
||||
}
|
||||
} */
|
||||
|
||||
SetTransition(bubblesContainer, 'is-selecting', !!this.selectedMids.size, 200, () => {
|
||||
if(!this.isSelecting) {
|
||||
this.selectionContainer.remove();
|
||||
this.selectionContainer = null;
|
||||
this.selectionContainer = this.selectionForwardBtn = this.selectionDeleteBtn = null;
|
||||
this.selectedText = undefined;
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
this.appImManager.onScroll();
|
||||
});
|
||||
});
|
||||
|
||||
//const chatInput = this.appImManager.chatInput;
|
||||
@ -201,19 +247,23 @@ export default class ChatSelection {
|
||||
this.selectionCountEl = document.createElement('div');
|
||||
this.selectionCountEl.classList.add('selection-container-count');
|
||||
|
||||
const btnForward = Button('btn-primary btn-transparent', {icon: 'forward'});
|
||||
btnForward.append('Forward');
|
||||
|
||||
btnForward.addEventListener('click', () => {
|
||||
this.selectionForwardBtn = Button('btn-primary btn-transparent selection-container-forward', {icon: 'forward'});
|
||||
this.selectionForwardBtn.append('Forward');
|
||||
this.selectionForwardBtn.addEventListener('click', () => {
|
||||
new PopupForward([...this.selectedMids], () => {
|
||||
this.cancelSelection();
|
||||
});
|
||||
});
|
||||
|
||||
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete'});
|
||||
btnDelete.append('Delete');
|
||||
this.selectionDeleteBtn = Button('btn-primary btn-transparent danger selection-container-delete', {icon: 'delete'});
|
||||
this.selectionDeleteBtn.append('Delete');
|
||||
this.selectionDeleteBtn.addEventListener('click', () => {
|
||||
new PopupDeleteMessages([...this.selectedMids], () => {
|
||||
this.cancelSelection();
|
||||
});
|
||||
});
|
||||
|
||||
this.selectionContainer.append(btnCancel, this.selectionCountEl, btnForward, btnDelete);
|
||||
this.selectionContainer.append(btnCancel, this.selectionCountEl, this.selectionForwardBtn, this.selectionDeleteBtn);
|
||||
|
||||
inputMessageDiv.append(this.selectionContainer);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import $rootScope from "../lib/rootScope";
|
||||
import { cancelEvent } from "../lib/utils";
|
||||
import AvatarElement from "./avatar";
|
||||
import { cancelEvent, findUpClassName } from "../lib/utils";
|
||||
import { ripple } from "./ripple";
|
||||
|
||||
export class PopupElement {
|
||||
@ -16,7 +15,7 @@ export class PopupElement {
|
||||
protected onCloseAfterTimeout: () => void;
|
||||
protected onEscape: () => boolean = () => true;
|
||||
|
||||
constructor(className: string, buttons?: Array<PopupButton>, options: Partial<{closable: boolean, withConfirm: string, body: boolean}> = {}) {
|
||||
constructor(className: string, buttons?: Array<PopupButton>, options: Partial<{closable: true, overlayClosable: true, withConfirm: string, body: true}> = {}) {
|
||||
this.element.classList.add('popup');
|
||||
this.element.className = 'popup' + (className ? ' ' + className : '');
|
||||
this.container.classList.add('popup-container', 'z-depth-1');
|
||||
@ -33,6 +32,16 @@ export class PopupElement {
|
||||
this.header.prepend(this.closeBtn);
|
||||
|
||||
this.closeBtn.addEventListener('click', this.destroy, {once: true});
|
||||
|
||||
if(options.overlayClosable) {
|
||||
const onOverlayClick = (e: MouseEvent) => {
|
||||
if(!findUpClassName(e.target, 'popup-container')) {
|
||||
this.closeBtn.click();
|
||||
}
|
||||
};
|
||||
|
||||
this.element.addEventListener('click', onOverlayClick, {once: true});
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', this._onKeyDown, {capture: true});
|
||||
|
65
src/components/popupDeleteMessages.ts
Normal file
65
src/components/popupDeleteMessages.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import appPeersManager from "../lib/appManagers/appPeersManager";
|
||||
import $rootScope from "../lib/rootScope";
|
||||
import { PopupButton } from "./popup";
|
||||
import PopupPeer from "./popupPeer";
|
||||
|
||||
export default class PopupDeleteMessages {
|
||||
constructor(mids: number[], onConfirm?: () => void) {
|
||||
const peerID = $rootScope.selectedPeerID;
|
||||
const firstName = appPeersManager.getPeerTitle(peerID, false, true);
|
||||
|
||||
mids = mids.slice();
|
||||
const callback = (revoke: boolean) => {
|
||||
onConfirm && onConfirm();
|
||||
appMessagesManager.deleteMessages(mids, revoke);
|
||||
};
|
||||
|
||||
let title: string, description: string, buttons: PopupButton[];
|
||||
title = `Delete Message${mids.length == 1 ? '' : 's'}?`;
|
||||
description = `Are you sure you want to delete ${mids.length == 1 ? 'this message' : 'these messages'}?`;
|
||||
|
||||
if(peerID == $rootScope.myID) {
|
||||
buttons = [{
|
||||
text: 'DELETE',
|
||||
isDanger: true,
|
||||
callback: () => callback(false)
|
||||
}];
|
||||
} else {
|
||||
buttons = [{
|
||||
text: 'DELETE JUST FOR ME',
|
||||
isDanger: true,
|
||||
callback: () => callback(false)
|
||||
}];
|
||||
|
||||
if(peerID > 0) {
|
||||
buttons.push({
|
||||
text: 'DELETE FOR ME AND ' + firstName,
|
||||
isDanger: true,
|
||||
callback: () => callback(true)
|
||||
});
|
||||
} else if(appChatsManager.hasRights(-peerID, 'deleteRevoke')) {
|
||||
buttons.push({
|
||||
text: 'DELETE FOR ALL',
|
||||
isDanger: true,
|
||||
callback: () => callback(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
text: 'CANCEL',
|
||||
isCancel: true
|
||||
});
|
||||
|
||||
const popup = new PopupPeer('popup-delete-chat', {
|
||||
peerID: peerID,
|
||||
title: title,
|
||||
description: description,
|
||||
buttons: buttons
|
||||
});
|
||||
|
||||
popup.show();
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import { isTouchSupported } from "../helpers/touchSupport";
|
||||
import appImManager from "../lib/appManagers/appImManager";
|
||||
import AppSelectPeers from "./appSelectPeers";
|
||||
import { PopupElement } from "./popup";
|
||||
@ -6,8 +7,10 @@ export default class PopupForward extends PopupElement {
|
||||
private selector: AppSelectPeers;
|
||||
//private scrollable: Scrollable;
|
||||
|
||||
constructor(mids: number[], onSelect?: () => Promise<void> | void) {
|
||||
super('popup-forward', null, {closable: true, body: true});
|
||||
constructor(mids: number[], onSelect?: () => Promise<void> | void, onClose?: () => void) {
|
||||
super('popup-forward', null, {closable: true, overlayClosable: true, body: true});
|
||||
|
||||
if(onClose) this.onClose = onClose;
|
||||
|
||||
this.selector = new AppSelectPeers(this.body, async() => {
|
||||
const peerID = this.selector.getSelected()[0];
|
||||
@ -21,6 +24,10 @@ export default class PopupForward extends PopupElement {
|
||||
appImManager.chatInputC.initMessagesForward(mids.slice());
|
||||
}, ['dialogs', 'contacts'], () => {
|
||||
this.show();
|
||||
|
||||
if(!isTouchSupported) {
|
||||
this.selector.input.focus();
|
||||
}
|
||||
}, null, 'send', false);
|
||||
|
||||
//this.scrollable = new Scrollable(this.body);
|
||||
|
@ -9,6 +9,7 @@ import animationIntersector from "./animationIntersector";
|
||||
import { findUpClassName } from "../lib/utils";
|
||||
import appImManager from "../lib/appManagers/appImManager";
|
||||
import { StickerSet } from "../layer";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
|
||||
const ANIMATION_GROUP = 'STICKERS-POPUP';
|
||||
|
||||
@ -24,7 +25,7 @@ export default class PopupStickers extends PopupElement {
|
||||
id: string,
|
||||
access_hash: string
|
||||
}) {
|
||||
super('popup-stickers', null, {closable: true, body: true});
|
||||
super('popup-stickers', null, {closable: true, overlayClosable: true, body: true});
|
||||
|
||||
this.h6 = document.createElement('h6');
|
||||
this.h6.innerText = 'Loading...';
|
||||
@ -36,21 +37,12 @@ export default class PopupStickers extends PopupElement {
|
||||
animationIntersector.checkAnimations(false);
|
||||
this.stickersFooter.removeEventListener('click', this.onFooterClick);
|
||||
this.stickersDiv.removeEventListener('click', this.onStickersClick);
|
||||
this.element.removeEventListener('click', onOverlayClick);
|
||||
};
|
||||
|
||||
this.onCloseAfterTimeout = () => {
|
||||
animationIntersector.checkAnimations(undefined, ANIMATION_GROUP);
|
||||
};
|
||||
|
||||
const onOverlayClick = (e: MouseEvent) => {
|
||||
if(!findUpClassName(e.target, 'popup-container')) {
|
||||
this.closeBtn.click();
|
||||
}
|
||||
};
|
||||
|
||||
this.element.addEventListener('click', onOverlayClick);
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('sticker-set');
|
||||
|
||||
@ -129,6 +121,8 @@ export default class PopupStickers extends PopupElement {
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('sticker-set-sticker');
|
||||
|
||||
const size = mediaSizes.active.esgSticker.width;
|
||||
|
||||
wrapSticker({
|
||||
doc,
|
||||
@ -137,8 +131,8 @@ export default class PopupStickers extends PopupElement {
|
||||
group: ANIMATION_GROUP,
|
||||
play: true,
|
||||
loop: true,
|
||||
width: 80,
|
||||
height: 80
|
||||
width: size,
|
||||
height: size
|
||||
});
|
||||
|
||||
this.stickersDiv.append(div);
|
||||
|
@ -10,7 +10,7 @@ import appMessagesManager from '../lib/appManagers/appMessagesManager';
|
||||
import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager';
|
||||
import LottieLoader from '../lib/lottieLoader';
|
||||
import VideoPlayer from '../lib/mediaPlayer';
|
||||
import { formatBytes, getEmojiToneIndex, isInDOM } from "../lib/utils";
|
||||
import { cancelEvent, formatBytes, getEmojiToneIndex, isInDOM } from "../lib/utils";
|
||||
import webpWorkerController from '../lib/webp/webpWorkerController';
|
||||
import animationIntersector from './animationIntersector';
|
||||
import appMediaPlaybackController from './appMediaPlaybackController';
|
||||
@ -353,7 +353,7 @@ export function wrapDocument(doc: MyDocument, withTime = false, uploading = fals
|
||||
let preloader: ProgressivePreloader;
|
||||
let download: DownloadBlob;
|
||||
|
||||
docDiv.addEventListener('click', () => {
|
||||
docDiv.addEventListener('click', (e) => {
|
||||
if(!download) {
|
||||
if(downloadDiv.classList.contains('downloading')) {
|
||||
return; // means not ready yet
|
||||
|
@ -1,2 +1,2 @@
|
||||
// @ts-ignore
|
||||
export const isTouchSupported = ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch);
|
||||
export const isTouchSupported = ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch)/* || true */;
|
@ -440,8 +440,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="bubbles" class="scrolled-down">
|
||||
<div id="bubbles-inner"></div>
|
||||
<div id="bubbles-go-down" class="tgico-down btn-corner z-depth-1 rp hide"></div>
|
||||
{{!-- <div id="bubbles-transform-helper"> --}}
|
||||
<div id="bubbles-inner"></div>
|
||||
<div id="bubbles-go-down" class="tgico-down btn-corner z-depth-1 rp hide"></div>
|
||||
{{!-- </div> --}}
|
||||
</div>
|
||||
<div id="chat-input" style="display: none;">
|
||||
<div class="chat-input-container">
|
||||
|
@ -63,6 +63,7 @@ const ANIMATION_GROUP = 'chat';
|
||||
|
||||
export class AppImManager {
|
||||
public columnEl = document.getElementById('column-center') as HTMLDivElement;
|
||||
public backgroundEl = this.columnEl.firstElementChild as HTMLDivElement;
|
||||
public btnJoin = this.columnEl.querySelector('.chat-join') as HTMLButtonElement;
|
||||
public btnMute = this.columnEl.querySelector('.chat-mute-button') as HTMLButtonElement;
|
||||
public avatarEl = document.getElementById('im-avatar') as AvatarElement;
|
||||
@ -123,7 +124,7 @@ export class AppImManager {
|
||||
|
||||
public contextMenu = new ChatContextMenu(this.bubblesContainer);
|
||||
|
||||
private setPeerPromise: Promise<boolean> = null;
|
||||
public setPeerPromise: Promise<boolean> = null;
|
||||
|
||||
public bubbleGroups = new BubbleGroups();
|
||||
|
||||
@ -137,7 +138,7 @@ export class AppImManager {
|
||||
private loadedTopTimes = 0;
|
||||
private loadedBottomTimes = 0;
|
||||
|
||||
private messagesQueuePromise: Promise<void> = null;
|
||||
public messagesQueuePromise: Promise<void> = null;
|
||||
private messagesQueue: {message: any, bubble: HTMLDivElement, reverse: boolean, promises: Promise<void>[]}[] = [];
|
||||
private messagesQueueOnRender: () => void = null;
|
||||
|
||||
@ -437,9 +438,19 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
// ! Trusted - due to audio autoclick
|
||||
if(this.chatSelection.isSelecting && !bubble.classList.contains('service') && e.isTrusted) {
|
||||
if(this.chatSelection.isSelecting && e.isTrusted) {
|
||||
if(bubble.classList.contains('service') && bubble.dataset.mid === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelEvent(e);
|
||||
//console.log('bubble click', e);
|
||||
|
||||
if(isTouchSupported && this.chatSelection.selectedText) {
|
||||
this.chatSelection.selectedText = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this.chatSelection.toggleByBubble(bubble);
|
||||
return;
|
||||
}
|
||||
@ -526,6 +537,7 @@ export class AppImManager {
|
||||
new AppMediaViewer().openMedia(message, targets[idx].element, true,
|
||||
targets.slice(0, idx), targets.slice(idx + 1)/* , !message.grouped_id */);
|
||||
|
||||
cancelEvent(e);
|
||||
//appMediaViewer.openMedia(message, target as HTMLImageElement);
|
||||
return;
|
||||
}
|
||||
@ -552,7 +564,7 @@ export class AppImManager {
|
||||
if(!isNaN(peerID)) {
|
||||
this.setPeer(peerID);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
} else if(target.tagName == "AVATAR-ELEMENT") {
|
||||
let peerID = +target.getAttribute('peer');
|
||||
@ -560,7 +572,7 @@ export class AppImManager {
|
||||
if(!isNaN(peerID)) {
|
||||
this.setPeer(peerID);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -583,7 +595,7 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
//console.log('chatInner click', e);
|
||||
});
|
||||
}, {capture: true, passive: false});
|
||||
|
||||
this.closeBtn.addEventListener('click', (e) => {
|
||||
cancelEvent(e);
|
||||
@ -831,7 +843,7 @@ export class AppImManager {
|
||||
}
|
||||
}
|
||||
|
||||
public onScroll(e: Event) {
|
||||
public onScroll() {
|
||||
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF);
|
||||
|
||||
// * В таком случае, кнопка не будет моргать если чат в самом низу, и правильно отработает случай написания нового сообщения и проскролла вниз
|
||||
@ -854,10 +866,10 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
if(this.scrollable.isScrolledDown) {
|
||||
this.scroll.parentElement.classList.add('scrolled-down');
|
||||
this.bubblesContainer.classList.add('scrolled-down');
|
||||
this.scrolledDown = true;
|
||||
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
|
||||
this.scroll.parentElement.classList.remove('scrolled-down');
|
||||
} else if(this.bubblesContainer.classList.contains('scrolled-down')) {
|
||||
this.bubblesContainer.classList.remove('scrolled-down');
|
||||
this.scrolledDown = false;
|
||||
}
|
||||
|
||||
@ -866,7 +878,7 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
public setScroll() {
|
||||
this.scrollable = new Scrollable(this.bubblesContainer, 'IM', this.chatInner, 300);
|
||||
this.scrollable = new Scrollable(this.bubblesContainer/* .firstElementChild */ as HTMLElement, 'IM', this.chatInner, 300);
|
||||
|
||||
/* const getScrollOffset = () => {
|
||||
//return Math.round(Math.max(300, appPhotosManager.windowH / 1.5));
|
||||
@ -880,14 +892,14 @@ export class AppImManager {
|
||||
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, getScrollOffset()); */
|
||||
this.scroll = this.scrollable.container;
|
||||
|
||||
this.bubblesContainer.append(this.goDownBtn);
|
||||
this.bubblesContainer/* .firstElementChild */.append(this.goDownBtn);
|
||||
|
||||
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
|
||||
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
|
||||
//this.scrollable.attachSentinels(undefined, 300);
|
||||
|
||||
this.scroll.addEventListener('scroll', this.onScroll.bind(this));
|
||||
this.scroll.parentElement.classList.add('scrolled-down');
|
||||
this.bubblesContainer.classList.add('scrolled-down');
|
||||
|
||||
if(isTouchSupported) {
|
||||
this.scroll.addEventListener('touchmove', () => {
|
||||
@ -1289,9 +1301,18 @@ export class AppImManager {
|
||||
this.chatInner.classList.toggle('has-rights', hasRights);
|
||||
|
||||
const canWrite = (!isChannel || hasRights) && (peerID < 0 || appUsersManager.canSendToUser(peerID));
|
||||
this.chatInput.style.display = canWrite ? '' : 'none';
|
||||
//const needToChangeInputDisplay = !(!this.chatInput.classList.contains('is-hidden') && canWrite);
|
||||
//this.chatInput.style.display = needToChangeInputDisplay ? 'none' : '';
|
||||
this.chatInput.style.display = '';
|
||||
this.chatInput.classList.toggle('is-hidden', !canWrite);
|
||||
this.bubblesContainer.classList.toggle('is-chat-input-hidden', !canWrite);
|
||||
|
||||
this.chatInner.classList.toggle('is-chat-input-hidden', !canWrite);
|
||||
// const noTransition = [this.columnEl/* appSidebarRight.sidebarEl, this.backgroundEl,
|
||||
// this.bubblesContainer, this.chatInput, this.chatInner,
|
||||
// this.chatInputC.replyElements.container */];
|
||||
// noTransition.forEach(el => {
|
||||
// el.classList.add('no-transition-all');
|
||||
// });
|
||||
|
||||
this.topbar.classList.remove('is-pinned-shown');
|
||||
this.topbar.style.display = '';
|
||||
@ -1308,6 +1329,13 @@ export class AppImManager {
|
||||
this.setPinnedMessage();
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
/* noTransition.forEach(el => {
|
||||
el.classList.remove('no-transition-all');
|
||||
}); */
|
||||
/* if(needToChangeInputDisplay) {
|
||||
this.chatInput.style.display = '';
|
||||
} */
|
||||
|
||||
let title = '';
|
||||
if(this.peerID == this.myID) title = 'Saved Messages';
|
||||
else title = appPeersManager.getPeerTitle(this.peerID);
|
||||
|
@ -2018,7 +2018,7 @@ export class AppMessagesManager {
|
||||
withMyScore: true
|
||||
}> = {}) {
|
||||
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
|
||||
mids = mids.sort((a, b) => a - b);
|
||||
mids = mids.slice().sort((a, b) => a - b);
|
||||
|
||||
const splitted = appMessagesIDsManager.splitMessageIDsByChannels(mids);
|
||||
const promises: Promise<void>[] = [];
|
||||
@ -2442,6 +2442,9 @@ export class AppMessagesManager {
|
||||
case 'messageMediaPhoto':
|
||||
messageText += '<i>Photo' + (message.message ? ', ' : '') + '</i>';
|
||||
break;
|
||||
case 'messageMediaDice':
|
||||
messageText += RichTextProcessor.wrapEmojiText(message.media.emoticon);
|
||||
break;
|
||||
case 'messageMediaGeo':
|
||||
messageText += '<i>Geolocation</i>';
|
||||
break;
|
||||
@ -2471,6 +2474,7 @@ export class AppMessagesManager {
|
||||
break;
|
||||
|
||||
default:
|
||||
//messageText += message.media._;
|
||||
///////this.log.warn('Got unknown message.media type!', message);
|
||||
break;
|
||||
}
|
||||
@ -2660,6 +2664,15 @@ export class AppMessagesManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
public canDeleteMessage(messageID: number) {
|
||||
const message = this.messagesStorage[messageID];
|
||||
if(message) {
|
||||
return message.peerID > 0 || message.fromID == $rootScope.myID || appChatsManager.hasRights(message.peerID, 'deleteRevoke');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) {
|
||||
// * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages
|
||||
|
||||
|
@ -349,12 +349,12 @@ namespace RichTextProcessor {
|
||||
entities: MessageEntity[],
|
||||
contextSite: string,
|
||||
highlightUsername: string,
|
||||
noLinks: boolean,
|
||||
noLinebreaks: boolean,
|
||||
noCommands: boolean,
|
||||
noLinks: true,
|
||||
noLinebreaks: true,
|
||||
noCommands: true,
|
||||
fromBot: boolean,
|
||||
noTextFormat: boolean,
|
||||
nested?: boolean,
|
||||
noTextFormat: true,
|
||||
nested?: true,
|
||||
contextHashtag?: string
|
||||
}> = {}) {
|
||||
if(!text || !text.length) {
|
||||
|
@ -607,4 +607,15 @@ export function cancelSelection() {
|
||||
}
|
||||
}
|
||||
|
||||
export function getSelectedText() {
|
||||
if(window.getSelection) {
|
||||
return window.getSelection().toString();
|
||||
// @ts-ignore
|
||||
} else if(document.selection) {
|
||||
// @ts-ignore
|
||||
return document.selection.createRange().text;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
//(window as any).splitStringByLength = splitStringByLength;
|
||||
|
@ -88,6 +88,14 @@ Utility Classes
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* .no-transition {
|
||||
transition: none !important;
|
||||
|
||||
&-all, &-all * {
|
||||
transition: none !important;
|
||||
}
|
||||
} */
|
||||
|
||||
.center-align, .text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -214,6 +214,7 @@ $chat-helper-size: 39px;
|
||||
}
|
||||
|
||||
#chat-input {
|
||||
--translateY: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
@ -222,6 +223,8 @@ $chat-helper-size: 39px;
|
||||
flex: 0 0 auto; /* Forces side columns to stay same width */
|
||||
position: relative;
|
||||
//overflow: hidden;
|
||||
transition: transform var(--layer-transition);
|
||||
transform: translateY(var(--translateY));
|
||||
|
||||
/* // * for no ESG top
|
||||
flex: 1 1 auto;
|
||||
@ -235,10 +238,10 @@ $chat-helper-size: 39px;
|
||||
|
||||
@include respond-to(medium-screens) {
|
||||
width: calc(100% - var(--right-column-width));
|
||||
transition: transform var(--layer-transition);
|
||||
//transition: transform var(--layer-transition);
|
||||
|
||||
body.is-right-column-shown & {
|
||||
transform: translate3d(calc(var(--right-column-width) / -2), 0, 0);
|
||||
transform: translate3d(calc(var(--right-column-width) / -2), var(--translateY), 0);
|
||||
}
|
||||
|
||||
body.animation-level-0 & {
|
||||
@ -246,20 +249,31 @@ $chat-helper-size: 39px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-hidden {
|
||||
--translateY: 100%;
|
||||
transform: translate3d(0, var(--translateY), 0);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
||||
#bubbles.is-selecting:not(.backwards) ~ & {
|
||||
--translateY: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
--padding-horizontal: #{$chat-padding-handhelds};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: var(--messages-container-width);
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 0 $chat-padding-handhelds;
|
||||
padding: 0 var(--padding-horizontal);
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
padding-left: $chat-padding;
|
||||
padding-right: $chat-padding;
|
||||
--padding-horizontal: #{$chat-padding};
|
||||
padding-bottom: 21px;
|
||||
}
|
||||
|
||||
@ -327,17 +341,16 @@ $chat-helper-size: 39px;
|
||||
top: 0;
|
||||
|
||||
// here percents can be used since there are no other transforms
|
||||
transform: translateX(calc(-100% + #{-1rem + -$btn-send-margin}));
|
||||
transform: translateX(calc(-100% + var(--padding-horizontal) * -1 + #{-$btn-send-margin}));
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
/* @include respond-to(handhelds) {
|
||||
transform: translateX(calc(-100% + #{-.5rem + -$btn-send-margin}));
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
.btn-send-container {
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
align-self: flex-end;
|
||||
position: absolute;
|
||||
right: var(--padding-horizontal);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@ -625,6 +638,10 @@ $chat-helper-size: 39px;
|
||||
.chat-background {
|
||||
overflow: hidden;
|
||||
|
||||
&.no-transition:before {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
&, &:before {
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
@ -884,7 +901,7 @@ $chat-helper-size: 39px;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: calc(100% - #{$chat-input-size + $btn-send-margin});
|
||||
max-width: 100%;
|
||||
max-width: calc(100% - #{$chat-input-size + $btn-send-margin});
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
@ -920,8 +937,13 @@ $chat-helper-size: 39px;
|
||||
@include respond-to(handhelds) {
|
||||
--padding: .5px .5rem;
|
||||
width: calc(100% - #{$chat-input-handhelds-size + $btn-send-margin});
|
||||
max-width: calc(100% - #{$chat-input-handhelds-size + $btn-send-margin});
|
||||
min-height: $chat-input-handhelds-size;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 420px) {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@include respond-to(esg-bottom) {
|
||||
--padding: .5px .5rem;
|
||||
@ -1031,21 +1053,54 @@ $chat-helper-size: 39px;
|
||||
border-radius: inherit;
|
||||
padding: inherit;
|
||||
user-select: none;
|
||||
font-size: 15px;
|
||||
|
||||
&-count {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
flex-grow: 1;
|
||||
padding-left: .5rem;
|
||||
white-space: nowrap;
|
||||
//padding-left: .5rem;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
margin-left: 6px;
|
||||
maRgin-top: 6px;
|
||||
color: #3f454a;
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
height: 2.5rem;
|
||||
width: 7.5rem;
|
||||
width: auto;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 380px) {
|
||||
font-size: 0;
|
||||
|
||||
&:before {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary + .btn-primary {
|
||||
margin-left: .75rem;
|
||||
&-forward {
|
||||
&:before {
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&-delete {
|
||||
margin-right: .625rem;
|
||||
margin-left: .375rem;
|
||||
|
||||
&:before {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1086,6 +1141,7 @@ $chat-helper-size: 39px;
|
||||
flex: 1 1 auto; /* Lets middle column shrink/grow to available width */
|
||||
//overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
transform: translateY(var(--translateY));
|
||||
transition: transform var(--layer-transition);
|
||||
|
||||
@ -1096,9 +1152,54 @@ $chat-helper-size: 39px;
|
||||
.chat-container.is-helper-active & {
|
||||
&:not(.is-selecting), &.is-selecting.backwards {
|
||||
--translateY: -#{$chat-helper-size};
|
||||
|
||||
#bubbles-inner {
|
||||
transform: translateY(calc(var(--translateY) * -1));
|
||||
//margin-top: $chat-helper-size;
|
||||
//transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-chat-input-hidden.is-selecting:not(.backwards) {
|
||||
--translateY: -79px;
|
||||
|
||||
#bubbles-inner {
|
||||
transform: translateY(calc(var(--translateY) * -1));
|
||||
//margin-top: $chat-helper-size;
|
||||
//transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* #bubbles-transform-helper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
position: relative;
|
||||
transform: translateY(var(--translateY));
|
||||
transition: transform var(--layer-transition); */
|
||||
|
||||
> .scrollable {
|
||||
height: auto;
|
||||
/* position: absolute;
|
||||
bottom: 0;
|
||||
left: 0; */
|
||||
//position: relative; // неизвестно зачем это было
|
||||
|
||||
//display: flex; // for end
|
||||
//flex-direction: unset;
|
||||
display: block;
|
||||
|
||||
/* display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end; */
|
||||
// * scrollbar takes some width, don't need to set padding for iOS
|
||||
html.is-safari:not(.is-ios) & {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
// ! WARNING, НЕЛЬЗЯ СТАВИТЬ ТРАНСФОРМ КРОМЕ TRANSLATEZ(0) НА БЛОК С OVERFLOW, ОН БУДЕТ ПРЫГАТЬ ВВЕРХ ПРИ ВКЛЮЧЕННОМ ПРАВИЛЕ И ЭТО НЕ ИСПРАВИТЬ JS'ОМ!
|
||||
@include respond-to(medium-screens) {
|
||||
//transition: transform var(--layer-transition);
|
||||
@ -1113,9 +1214,6 @@ $chat-helper-size: 39px;
|
||||
}
|
||||
|
||||
&.is-selecting {
|
||||
cursor: default !important;
|
||||
user-select: none;
|
||||
|
||||
&:not(.backwards) .is-in .bubble__container {
|
||||
transform: translateX(2.5rem);
|
||||
}
|
||||
@ -1132,42 +1230,25 @@ $chat-helper-size: 39px;
|
||||
/* &.is-selecting > .scrollable::-webkit-scrollbar {
|
||||
display: none;
|
||||
} */
|
||||
|
||||
> .scrollable {
|
||||
height: auto;
|
||||
/* position: absolute;
|
||||
bottom: 0;
|
||||
left: 0; */
|
||||
//position: relative; // неизвестно зачем это было
|
||||
|
||||
//display: flex; // for end
|
||||
//flex-direction: unset;
|
||||
display: block;
|
||||
|
||||
/* display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end; */
|
||||
// * scrollbar takes some width, don't need to set padding for iOS
|
||||
html.is-safari:not(.is-ios) & {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.scrolled-down):not(.search-results-active) {
|
||||
> .scrollable {
|
||||
-webkit-mask-image: -webkit-linear-gradient(bottom, transparent, #000 20px);
|
||||
mask-image: linear-gradient(0deg, transparent 0, #000 20px);
|
||||
}
|
||||
//> #bubbles-transform-helper {
|
||||
// ! these lines will blur messages if chat input helper is active
|
||||
> .scrollable {
|
||||
-webkit-mask-image: -webkit-linear-gradient(bottom, transparent, #000 20px);
|
||||
mask-image: linear-gradient(0deg, transparent 0, #000 20px);
|
||||
}
|
||||
|
||||
> #bubbles-go-down {
|
||||
cursor: pointer;
|
||||
--translateY: 0;
|
||||
opacity: 1;
|
||||
> #bubbles-go-down {
|
||||
cursor: pointer;
|
||||
--translateY: 0;
|
||||
opacity: 1;
|
||||
|
||||
/* &.is-broadcast {
|
||||
--translateY: 79px !important;
|
||||
} */
|
||||
}
|
||||
/* &.is-broadcast {
|
||||
--translateY: 79px !important;
|
||||
} */
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
.preloader {
|
||||
@ -1201,6 +1282,11 @@ $chat-helper-size: 39px;
|
||||
padding: 0 1rem;
|
||||
max-width: var(--messages-container-width);
|
||||
|
||||
transition: transform var(--layer-transition);
|
||||
transform: translateY(0);
|
||||
/* transition: margin-top var(--layer-transition);
|
||||
transition-delay: .2s; */
|
||||
|
||||
@include respond-to(medium-screens) {
|
||||
width: calc(100% - var(--right-column-width));
|
||||
}
|
||||
@ -1237,9 +1323,9 @@ $chat-helper-size: 39px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-chat-input-hidden {
|
||||
/* #bubbles.is-chat-input-hidden & {
|
||||
padding-bottom: 55px;
|
||||
}
|
||||
} */
|
||||
|
||||
&:not(.is-channel), &.is-chat {
|
||||
.message {
|
||||
|
@ -466,7 +466,6 @@
|
||||
}
|
||||
|
||||
.tgico-add:before {
|
||||
content: "\e903";
|
||||
font-size: 24px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
@ -619,17 +618,7 @@
|
||||
color: #50a2e9;
|
||||
}
|
||||
|
||||
.included-chats-container {
|
||||
.sidebar-left-h2 {
|
||||
color: #707579;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
padding: 6px 24px 8px 24px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 6px 16px 8px 16px;
|
||||
}
|
||||
}
|
||||
.popup-forward, .included-chats-container {
|
||||
.selector {
|
||||
ul {
|
||||
li > .rp {
|
||||
@ -647,10 +636,6 @@
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
span.user-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
padding: 0px 0px 0 14px;
|
||||
margin-top: -2px;
|
||||
@ -660,7 +645,24 @@
|
||||
font-size: 15px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.included-chats-container {
|
||||
.sidebar-left-h2 {
|
||||
color: #707579;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
padding: 6px 24px 8px 24px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 6px 16px 8px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.selector {
|
||||
ul {
|
||||
.checkbox {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
@ -129,10 +129,6 @@
|
||||
p {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
span.user-title {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
span.user-last-message {
|
||||
font-size: 14px;
|
||||
|
@ -6,15 +6,15 @@
|
||||
width: 420px;
|
||||
max-width: 420px;
|
||||
//padding: 12px 20px 32.5px;
|
||||
padding: .75rem 0 0 0;
|
||||
padding: 9px 0 0 0;
|
||||
max-height: unquote('min(40.625rem, 100%)');
|
||||
height: 40.625rem;
|
||||
}
|
||||
|
||||
&-header {
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: 9px;
|
||||
padding: 0 .75rem;
|
||||
margin-bottom: 4px;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
&-title {
|
||||
|
@ -60,12 +60,16 @@
|
||||
}
|
||||
|
||||
&-sticker {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
width: var(--esg-sticker-size);
|
||||
height: var(--esg-sticker-size);
|
||||
margin-bottom: 2px;
|
||||
justify-self: center;
|
||||
cursor: pointer;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-radius: 12px;
|
||||
background-color: var(--color-gray-hover);
|
||||
|
@ -840,9 +840,10 @@ input:focus, button:focus {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0; // new
|
||||
transition: .2s opacity;
|
||||
|
||||
html.no-touch &:hover {
|
||||
transition: .2s background-color;
|
||||
transition: .2s background-color, .2s opacity;
|
||||
background: darken($color-blue, 8%);
|
||||
}
|
||||
|
||||
@ -852,24 +853,31 @@ input:focus, button:focus {
|
||||
left: auto;
|
||||
}
|
||||
|
||||
// * tgico
|
||||
&:before {
|
||||
color: #707579;
|
||||
font-size: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
&:disabled {
|
||||
pointer-events: none !important;
|
||||
opacity: .25;
|
||||
}
|
||||
}
|
||||
|
||||
// ! example: multiselect input
|
||||
.btn-transparent {
|
||||
color: #000;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 .875rem;
|
||||
//width: auto;
|
||||
|
||||
html.no-touch &:hover {
|
||||
background-color: var(--color-gray-hover);
|
||||
}
|
||||
|
||||
// * tgico
|
||||
&:before {
|
||||
color: #707579;
|
||||
font-size: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary.btn-circle {
|
||||
|
Loading…
x
Reference in New Issue
Block a user