Context menu unpin
Refactored pinned container Pin rights fixed again
This commit is contained in:
parent
28b6653e65
commit
076e648300
@ -1,65 +1,43 @@
|
|||||||
import appImManager from "../../lib/appManagers/appImManager";
|
|
||||||
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
||||||
import appPeersManager from "../../lib/appManagers/appPeersManager";
|
import appPeersManager from "../../lib/appManagers/appPeersManager";
|
||||||
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
||||||
import $rootScope from "../../lib/rootScope";
|
import $rootScope from "../../lib/rootScope";
|
||||||
import { cancelEvent } from "../../lib/utils";
|
import { cancelEvent } from "../../lib/utils";
|
||||||
import appMediaPlaybackController from "../appMediaPlaybackController";
|
import appMediaPlaybackController from "../appMediaPlaybackController";
|
||||||
|
import DivAndCaption from "../divAndCaption";
|
||||||
import { formatDate } from "../wrappers";
|
import { formatDate } from "../wrappers";
|
||||||
|
import PinnedContainer from "./pinnedContainer";
|
||||||
|
|
||||||
export class ChatAudio {
|
export class ChatAudio extends PinnedContainer {
|
||||||
public container: HTMLElement;
|
private toggleEl: HTMLElement;
|
||||||
private toggle: HTMLElement;
|
|
||||||
private title: HTMLElement;
|
|
||||||
private subtitle: HTMLElement;
|
|
||||||
private close: HTMLElement;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.container = document.createElement('div');
|
super('audio', new DivAndCaption('pinned-audio', (title: string, subtitle: string) => {
|
||||||
this.container.classList.add('pinned-audio', 'pinned-container');
|
this.divAndCaption.title.innerHTML = title;
|
||||||
this.container.style.display = 'none';
|
this.divAndCaption.subtitle.innerHTML = subtitle;
|
||||||
|
}), () => {
|
||||||
this.toggle = document.createElement('div');
|
if(this.toggleEl.classList.contains('flip-icon')) {
|
||||||
this.toggle.classList.add('pinned-audio-ico', 'tgico');
|
|
||||||
|
|
||||||
this.title = document.createElement('div');
|
|
||||||
this.title.classList.add('pinned-audio-title');
|
|
||||||
|
|
||||||
this.subtitle = document.createElement('div');
|
|
||||||
this.subtitle.classList.add('pinned-audio-subtitle');
|
|
||||||
|
|
||||||
this.close = document.createElement('button');
|
|
||||||
this.close.classList.add('pinned-audio-close', 'btn-icon', 'tgico-close');
|
|
||||||
|
|
||||||
this.container.append(this.toggle, this.title, this.subtitle, this.close);
|
|
||||||
|
|
||||||
this.close.addEventListener('click', (e) => {
|
|
||||||
cancelEvent(e);
|
|
||||||
const scrollTop = appImManager.scrollable.scrollTop;
|
|
||||||
this.container.style.display = 'none';
|
|
||||||
appImManager.topbar.classList.remove('is-audio-shown');
|
|
||||||
if(this.toggle.classList.contains('flip-icon')) {
|
|
||||||
appMediaPlaybackController.toggle();
|
appMediaPlaybackController.toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!appImManager.topbar.classList.contains('is-pinned-shown')) {
|
|
||||||
appImManager.scrollable.scrollTop = scrollTop - height;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.toggle.addEventListener('click', (e) => {
|
this.divAndCaption.border.remove();
|
||||||
|
|
||||||
|
this.toggleEl = document.createElement('div');
|
||||||
|
this.toggleEl.classList.add('pinned-audio-ico', 'tgico');
|
||||||
|
this.toggleEl.addEventListener('click', (e) => {
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
appMediaPlaybackController.toggle();
|
appMediaPlaybackController.toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
const height = 52;
|
this.divAndCaption.container.prepend(this.toggleEl);
|
||||||
|
|
||||||
$rootScope.$on('audio_play', (e) => {
|
$rootScope.$on('audio_play', (e) => {
|
||||||
const {doc, mid} = e.detail;
|
const {doc, mid} = e.detail;
|
||||||
|
|
||||||
let title: string, subtitle: string;
|
let title: string, subtitle: string;
|
||||||
|
const message = appMessagesManager.getMessage(mid);
|
||||||
if(doc.type == 'voice' || doc.type == 'round') {
|
if(doc.type == 'voice' || doc.type == 'round') {
|
||||||
const message = appMessagesManager.getMessage(mid);
|
|
||||||
title = appPeersManager.getPeerTitle(message.fromID, false, true);
|
title = appPeersManager.getPeerTitle(message.fromID, false, true);
|
||||||
//subtitle = 'Voice message';
|
//subtitle = 'Voice message';
|
||||||
subtitle = formatDate(message.date, false, false);
|
subtitle = formatDate(message.date, false, false);
|
||||||
@ -68,24 +46,13 @@ export class ChatAudio {
|
|||||||
subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : 'Unknown Artist';
|
subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : 'Unknown Artist';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.title.innerHTML = title;
|
this.fill(title, subtitle, message);
|
||||||
this.subtitle.innerHTML = subtitle;
|
this.toggleEl.classList.add('flip-icon');
|
||||||
this.toggle.classList.add('flip-icon');
|
this.toggle(false);
|
||||||
|
|
||||||
this.container.dataset.mid = '' + mid;
|
|
||||||
if(this.container.style.display) {
|
|
||||||
const scrollTop = appImManager.scrollable.scrollTop;
|
|
||||||
this.container.style.display = '';
|
|
||||||
appImManager.topbar.classList.add('is-audio-shown');
|
|
||||||
|
|
||||||
if(!appImManager.topbar.classList.contains('is-pinned-shown')) {
|
|
||||||
appImManager.scrollable.scrollTop = scrollTop + height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on('audio_pause', () => {
|
$rootScope.$on('audio_pause', () => {
|
||||||
this.toggle.classList.remove('flip-icon');
|
this.toggleEl.classList.remove('flip-icon');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -77,7 +77,15 @@ export class ChatContextMenu {
|
|||||||
icon: 'pin',
|
icon: 'pin',
|
||||||
text: 'Pin',
|
text: 'Pin',
|
||||||
onClick: this.onPinClick,
|
onClick: this.onPinClick,
|
||||||
verify: (peerID: number) => peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin'))
|
verify: (peerID: number, msgID: number) => {
|
||||||
|
const message = appMessagesManager.getMessage(msgID);
|
||||||
|
return message._ != 'messageService' && appImManager.pinnedMsgID != msgID && (peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin')));
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
icon: 'unpin',
|
||||||
|
text: 'Unpin',
|
||||||
|
onClick: this.onUnpinClick,
|
||||||
|
verify: (peerID: number, msgID: number) => appImManager.pinnedMsgID == msgID && (peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin')))
|
||||||
}, {
|
}, {
|
||||||
icon: 'revote',
|
icon: 'revote',
|
||||||
text: 'Revote',
|
text: 'Revote',
|
||||||
@ -154,6 +162,10 @@ export class ChatContextMenu {
|
|||||||
appMessagesManager.updatePinnedMessage($rootScope.selectedPeerID, this.msgID);
|
appMessagesManager.updatePinnedMessage($rootScope.selectedPeerID, this.msgID);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onUnpinClick = () => {
|
||||||
|
appMessagesManager.updatePinnedMessage($rootScope.selectedPeerID, 0);
|
||||||
|
};
|
||||||
|
|
||||||
private onRetractVote = () => {
|
private onRetractVote = () => {
|
||||||
appPollsManager.sendVote(this.msgID, []);
|
appPollsManager.sendVote(this.msgID, []);
|
||||||
};
|
};
|
||||||
|
61
src/components/chat/pinnedContainer.ts
Normal file
61
src/components/chat/pinnedContainer.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import mediaSizes from "../../helpers/mediaSizes";
|
||||||
|
import appImManager from "../../lib/appManagers/appImManager";
|
||||||
|
import { cancelEvent } from "../../lib/utils";
|
||||||
|
import DivAndCaption from "../divAndCaption";
|
||||||
|
|
||||||
|
const CLASSNAME_SHOWN_MAIN = 'is-pinned-message-shown';
|
||||||
|
const CLASSNAME_BASE = 'pinned-container';
|
||||||
|
const HEIGHT = 52;
|
||||||
|
|
||||||
|
export default class PinnedContainer {
|
||||||
|
private close: HTMLElement;
|
||||||
|
|
||||||
|
constructor(protected className: string, public divAndCaption: DivAndCaption<(title: string, subtitle: string, message?: any) => void>, onClose?: () => void | Promise<boolean>) {
|
||||||
|
/* const prev = this.divAndCaption.fill;
|
||||||
|
this.divAndCaption.fill = (mid, title, subtitle) => {
|
||||||
|
this.divAndCaption.container.dataset.mid = '' + mid;
|
||||||
|
prev(mid, title, subtitle);
|
||||||
|
}; */
|
||||||
|
|
||||||
|
divAndCaption.container.classList.add(CLASSNAME_BASE, 'hide');
|
||||||
|
divAndCaption.title.classList.add(CLASSNAME_BASE + '-title');
|
||||||
|
divAndCaption.subtitle.classList.add(CLASSNAME_BASE + '-subtitle');
|
||||||
|
|
||||||
|
this.close = document.createElement('button');
|
||||||
|
this.close.classList.add(CLASSNAME_BASE + '-close', `pinned-${className}-close`, 'btn-icon', 'tgico-close');
|
||||||
|
|
||||||
|
divAndCaption.container.append(this.close);
|
||||||
|
|
||||||
|
this.close.addEventListener('click', (e) => {
|
||||||
|
cancelEvent(e);
|
||||||
|
|
||||||
|
((onClose ? onClose() : null) || Promise.resolve(true)).then(needClose => {
|
||||||
|
if(needClose) {
|
||||||
|
this.toggle(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggle(hide?: boolean) {
|
||||||
|
const isHidden = this.divAndCaption.container.classList.contains('hide');
|
||||||
|
if(hide === undefined) {
|
||||||
|
hide = !isHidden;
|
||||||
|
} else if(hide == isHidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollTop = mediaSizes.isMobile ? appImManager.scrollable.scrollTop : undefined;
|
||||||
|
this.divAndCaption.container.classList.toggle('hide', hide);
|
||||||
|
appImManager.topbar.classList.toggle(`is-pinned-${this.className}-shown`, !hide);
|
||||||
|
|
||||||
|
if(scrollTop !== undefined && !appImManager.topbar.classList.contains(CLASSNAME_SHOWN_MAIN)) {
|
||||||
|
appImManager.scrollable.scrollTop = scrollTop + ((hide ? -1 : 1) * HEIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fill(title: string, subtitle: string, message: any) {
|
||||||
|
this.divAndCaption.container.dataset.mid = '' + message.mid;
|
||||||
|
this.divAndCaption.fill(title, subtitle, message);
|
||||||
|
}
|
||||||
|
}
|
59
src/components/chat/replyContainer.ts
Normal file
59
src/components/chat/replyContainer.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import appPhotosManager from "../../lib/appManagers/appPhotosManager";
|
||||||
|
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
||||||
|
import DivAndCaption from "../divAndCaption";
|
||||||
|
import { renderImageFromUrl } from "../misc";
|
||||||
|
|
||||||
|
export default class ReplyContainer extends DivAndCaption<(title: string, subtitle: string, message?: any) => void> {
|
||||||
|
private mediaEl: HTMLElement;
|
||||||
|
|
||||||
|
constructor(protected className: string) {
|
||||||
|
super(className, (title: string, subtitle: string = '', message?: any) => {
|
||||||
|
if(title.length > 150) {
|
||||||
|
title = title.substr(0, 140) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(subtitle.length > 150) {
|
||||||
|
subtitle = subtitle.substr(0, 140) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
title = title ? RichTextProcessor.wrapEmojiText(title) : '';
|
||||||
|
|
||||||
|
if(this.mediaEl) {
|
||||||
|
this.mediaEl.remove();
|
||||||
|
this.container.classList.remove('is-media');
|
||||||
|
}
|
||||||
|
|
||||||
|
const media = message && message.media;
|
||||||
|
if(media) {
|
||||||
|
subtitle = message.rReply;
|
||||||
|
|
||||||
|
//console.log('wrap reply', media);
|
||||||
|
|
||||||
|
if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) {
|
||||||
|
let replyMedia = document.createElement('div');
|
||||||
|
replyMedia.classList.add(this.className + '-media');
|
||||||
|
|
||||||
|
let photo = media.photo || media.document;
|
||||||
|
|
||||||
|
let sizes = photo.sizes || photo.thumbs;
|
||||||
|
if(sizes && sizes[0].bytes) {
|
||||||
|
appPhotosManager.setAttachmentPreview(sizes[0].bytes, replyMedia, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
appPhotosManager.preloadPhoto(photo, appPhotosManager.choosePhotoSize(photo, 32, 32))
|
||||||
|
.then(() => {
|
||||||
|
renderImageFromUrl(replyMedia, photo._ == 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.content.prepend(this.mediaEl = replyMedia);
|
||||||
|
this.container.classList.add('is-media');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subtitle = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.title.innerHTML = title;
|
||||||
|
this.subtitle.innerHTML = subtitle;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
27
src/components/divAndCaption.ts
Normal file
27
src/components/divAndCaption.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export default class DivAndCaption<T> {
|
||||||
|
public container: HTMLElement;
|
||||||
|
public border: HTMLElement;
|
||||||
|
public content: HTMLElement;
|
||||||
|
public title: HTMLElement;
|
||||||
|
public subtitle: HTMLElement;
|
||||||
|
|
||||||
|
constructor(protected className: string, public fill: T) {
|
||||||
|
this.container = document.createElement('div');
|
||||||
|
this.container.className = className;
|
||||||
|
|
||||||
|
this.border = document.createElement('div');
|
||||||
|
this.border.classList.add(className + '-border');
|
||||||
|
|
||||||
|
this.content = document.createElement('div');
|
||||||
|
this.content.classList.add(className + '-content');
|
||||||
|
|
||||||
|
this.title = document.createElement('div');
|
||||||
|
this.title.classList.add(className + '-title');
|
||||||
|
|
||||||
|
this.subtitle = document.createElement('div');
|
||||||
|
this.subtitle.classList.add(className + '-subtitle');
|
||||||
|
|
||||||
|
this.content.append(this.title, this.subtitle);
|
||||||
|
this.container.append(this.border, this.content);
|
||||||
|
}
|
||||||
|
}
|
@ -104,17 +104,22 @@ export class ScrollableBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.scrollLocked) clearTimeout(this.scrollLocked);
|
const wasLocked = !!this.scrollLocked;
|
||||||
else {
|
if(wasLocked) clearTimeout(this.scrollLocked);
|
||||||
this.scrollLockedPromise = deferredPromise<void>();
|
if(smooth) {
|
||||||
}
|
if(!wasLocked) {
|
||||||
|
this.scrollLockedPromise = deferredPromise<void>();
|
||||||
this.scrollLocked = window.setTimeout(() => {
|
}
|
||||||
this.scrollLocked = 0;
|
|
||||||
|
this.scrollLocked = window.setTimeout(() => {
|
||||||
|
this.scrollLocked = 0;
|
||||||
|
this.scrollLockedPromise.resolve();
|
||||||
|
//this.onScroll();
|
||||||
|
this.container.dispatchEvent(new CustomEvent('scroll'));
|
||||||
|
}, scrollTime);
|
||||||
|
} else if(wasLocked) {
|
||||||
this.scrollLockedPromise.resolve();
|
this.scrollLockedPromise.resolve();
|
||||||
//this.onScroll();
|
}
|
||||||
this.container.dispatchEvent(new CustomEvent('scroll'));
|
|
||||||
}, scrollTime);
|
|
||||||
|
|
||||||
const options: SmoothScrollToOptions = {
|
const options: SmoothScrollToOptions = {
|
||||||
behavior: smooth ? 'smooth' : 'auto',
|
behavior: smooth ? 'smooth' : 'auto',
|
||||||
@ -124,6 +129,10 @@ export class ScrollableBase {
|
|||||||
options[side] = value;
|
options[side] = value;
|
||||||
|
|
||||||
this.container.scrollTo(options as any);
|
this.container.scrollTo(options as any);
|
||||||
|
|
||||||
|
if(!smooth) {
|
||||||
|
this.container.dispatchEvent(new CustomEvent('scroll'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length() {
|
||||||
@ -490,6 +499,10 @@ export default class Scrollable extends ScrollableBase {
|
|||||||
return this.scrollTop;
|
return this.scrollTop;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
get isScrolledDown() {
|
||||||
|
return this.scrollHeight - Math.round(this.scrollTop + this.container.offsetHeight) <= 1;
|
||||||
|
}
|
||||||
|
|
||||||
set scrollTop(y: number) {
|
set scrollTop(y: number) {
|
||||||
this.container.scrollTop = y;
|
this.container.scrollTop = y;
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import appPhotosManager, {MyPhoto} from '../lib/appManagers/appPhotosManager';
|
|
||||||
import LottieLoader from '../lib/lottieLoader';
|
|
||||||
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
|
|
||||||
import { formatBytes, getEmojiToneIndex, isInDOM } from "../lib/utils";
|
|
||||||
import ProgressivePreloader from './preloader';
|
|
||||||
import LazyLoadQueue from './lazyLoadQueue';
|
|
||||||
import VideoPlayer from '../lib/mediaPlayer';
|
|
||||||
import { RichTextProcessor } from '../lib/richtextprocessor';
|
|
||||||
import { renderImageFromUrl } from './misc';
|
|
||||||
import appMessagesManager from '../lib/appManagers/appMessagesManager';
|
|
||||||
import { Layouter, RectPart } from './groupedLayout';
|
|
||||||
import PollElement from './poll';
|
|
||||||
import animationIntersector from './animationIntersector';
|
|
||||||
import AudioElement from './audio';
|
|
||||||
import { DownloadBlob } from '../lib/appManagers/appDownloadManager';
|
|
||||||
import webpWorkerController from '../lib/webp/webpWorkerController';
|
|
||||||
import { readBlobAsText } from '../helpers/blob';
|
import { readBlobAsText } from '../helpers/blob';
|
||||||
import appMediaPlaybackController from './appMediaPlaybackController';
|
|
||||||
import { PhotoSize } from '../layer';
|
|
||||||
import { deferredPromise } from '../helpers/cancellablePromise';
|
import { deferredPromise } from '../helpers/cancellablePromise';
|
||||||
|
import { months } from '../helpers/date';
|
||||||
import mediaSizes from '../helpers/mediaSizes';
|
import mediaSizes from '../helpers/mediaSizes';
|
||||||
import { isSafari } from '../helpers/userAgent';
|
import { isSafari } from '../helpers/userAgent';
|
||||||
import { months } from '../helpers/date';
|
import { PhotoSize } from '../layer';
|
||||||
|
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
|
||||||
|
import { DownloadBlob } from '../lib/appManagers/appDownloadManager';
|
||||||
|
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 webpWorkerController from '../lib/webp/webpWorkerController';
|
||||||
|
import animationIntersector from './animationIntersector';
|
||||||
|
import appMediaPlaybackController from './appMediaPlaybackController';
|
||||||
|
import AudioElement from './audio';
|
||||||
|
import ReplyContainer from './chat/replyContainer';
|
||||||
|
import { Layouter, RectPart } from './groupedLayout';
|
||||||
|
import LazyLoadQueue from './lazyLoadQueue';
|
||||||
|
import { renderImageFromUrl } from './misc';
|
||||||
|
import PollElement from './poll';
|
||||||
|
import ProgressivePreloader from './preloader';
|
||||||
|
|
||||||
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
||||||
doc: MyDocument,
|
doc: MyDocument,
|
||||||
@ -697,68 +697,11 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load();
|
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapReply(title: string, subtitle: string, message?: any, isPinned?: boolean) {
|
export function wrapReply(title: string, subtitle: string, message?: any) {
|
||||||
const prefix = isPinned ? 'pinned-message' : 'reply';
|
const replyContainer = new ReplyContainer('reply');
|
||||||
const div = document.createElement('div');
|
replyContainer.fill(title, subtitle, message);
|
||||||
div.classList.add(prefix);
|
|
||||||
|
|
||||||
const replyBorder = document.createElement('div');
|
|
||||||
replyBorder.classList.add(prefix + '-border');
|
|
||||||
|
|
||||||
const replyContent = document.createElement('div');
|
|
||||||
replyContent.classList.add(prefix + '-content');
|
|
||||||
|
|
||||||
const replyTitle = document.createElement('div');
|
|
||||||
replyTitle.classList.add(prefix + '-title');
|
|
||||||
|
|
||||||
const replySubtitle = document.createElement('div');
|
|
||||||
replySubtitle.classList.add(prefix + '-subtitle');
|
|
||||||
|
|
||||||
if(title.length > 150) {
|
|
||||||
title = title.substr(0, 140) + '...';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subtitle.length > 150) {
|
|
||||||
subtitle = subtitle.substr(0, 140) + '...';
|
|
||||||
}
|
|
||||||
|
|
||||||
replyTitle.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : '';
|
|
||||||
|
|
||||||
const media = message && message.media;
|
|
||||||
if(media) {
|
|
||||||
replySubtitle.innerHTML = message.rReply;
|
|
||||||
|
|
||||||
//console.log('wrap reply', media);
|
|
||||||
|
|
||||||
if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) {
|
|
||||||
let replyMedia = document.createElement('div');
|
|
||||||
replyMedia.classList.add(prefix + '-media');
|
|
||||||
|
|
||||||
let photo = media.photo || media.document;
|
|
||||||
|
|
||||||
let sizes = photo.sizes || photo.thumbs;
|
|
||||||
if(sizes && sizes[0].bytes) {
|
|
||||||
appPhotosManager.setAttachmentPreview(sizes[0].bytes, replyMedia, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
appPhotosManager.preloadPhoto(photo, appPhotosManager.choosePhotoSize(photo, 32, 32))
|
|
||||||
.then(() => {
|
|
||||||
renderImageFromUrl(replyMedia, photo._ == 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url);
|
|
||||||
});
|
|
||||||
|
|
||||||
replyContent.append(replyMedia);
|
|
||||||
div.classList.add('is-media');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
replySubtitle.innerHTML = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
replyContent.append(replyTitle, replySubtitle);
|
|
||||||
div.append(replyBorder, replyContent);
|
|
||||||
|
|
||||||
/////////console.log('wrapReply', title, subtitle, media);
|
/////////console.log('wrapReply', title, subtitle, media);
|
||||||
|
return replyContainer.container;
|
||||||
return div;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut}: {
|
export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut}: {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ChatBannedRights, InputChannel, InputChatPhoto, InputPeer, Updates } from "../../layer";
|
import { ChatAdminRights, ChatBannedRights, InputChannel, InputChatPhoto, InputPeer, Updates } from "../../layer";
|
||||||
import apiManager from '../mtproto/mtprotoworker';
|
import apiManager from '../mtproto/mtprotoworker';
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import $rootScope from "../rootScope";
|
import $rootScope from "../rootScope";
|
||||||
@ -166,17 +166,19 @@ export class AppChatsManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let myFlags = (chat.admin_rights || chat.banned_rights || chat.default_banned_rights)?.pFlags ?? {};
|
const rights = chat.admin_rights || chat.banned_rights || chat.default_banned_rights;
|
||||||
|
let myFlags: {[flag in keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags']]: true};
|
||||||
|
if(rights) myFlags = rights.pFlags;
|
||||||
|
|
||||||
switch(action) {
|
switch(action) {
|
||||||
// good
|
// good
|
||||||
case 'send': {
|
case 'send': {
|
||||||
if(flag && myFlags[flag]) {
|
if(flag && myFlags && myFlags[flag]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chat._ == 'channel') {
|
if(chat._ == 'channel') {
|
||||||
if((!chat.pFlags.megagroup && !myFlags.post_messages)) {
|
if((!chat.pFlags.megagroup && !myFlags?.post_messages)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,7 +189,7 @@ export class AppChatsManager {
|
|||||||
// good
|
// good
|
||||||
case 'deleteRevoke': {
|
case 'deleteRevoke': {
|
||||||
if(chat._ == 'channel') {
|
if(chat._ == 'channel') {
|
||||||
return !!myFlags.delete_messages;
|
return !!myFlags?.delete_messages;
|
||||||
} else if(!chat.pFlags.admin) {
|
} else if(!chat.pFlags.admin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -198,9 +200,9 @@ export class AppChatsManager {
|
|||||||
// good
|
// good
|
||||||
case 'pin': {
|
case 'pin': {
|
||||||
if(chat._ == 'channel') {
|
if(chat._ == 'channel') {
|
||||||
return chat.admin_rights ? !!myFlags.pin_messages || !!myFlags.post_messages : !!myFlags.pin_messages;
|
return chat.admin_rights ? !!myFlags.pin_messages || !!myFlags.post_messages : myFlags && !myFlags.pin_messages;
|
||||||
} else {
|
} else {
|
||||||
if(myFlags.pin_messages && !chat.pFlags.admin) {
|
if(myFlags?.pin_messages && !chat.pFlags.admin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import BubbleGroups from '../../components/bubbleGroups';
|
|||||||
import { ChatAudio } from '../../components/chat/audio';
|
import { ChatAudio } from '../../components/chat/audio';
|
||||||
import { ChatContextMenu } from '../../components/chat/contextMenu';
|
import { ChatContextMenu } from '../../components/chat/contextMenu';
|
||||||
import { ChatInput } from '../../components/chat/input';
|
import { ChatInput } from '../../components/chat/input';
|
||||||
|
import PinnedContainer from '../../components/chat/pinnedContainer';
|
||||||
|
import ReplyContainer from '../../components/chat/replyContainer';
|
||||||
import { ChatSearch } from '../../components/chat/search';
|
import { ChatSearch } from '../../components/chat/search';
|
||||||
import { horizontalMenu } from '../../components/horizontalMenu';
|
import { horizontalMenu } from '../../components/horizontalMenu';
|
||||||
import LazyLoadQueue from '../../components/lazyLoadQueue';
|
import LazyLoadQueue from '../../components/lazyLoadQueue';
|
||||||
@ -94,7 +96,7 @@ export class AppImManager {
|
|||||||
public updateStatusInterval = 0;
|
public updateStatusInterval = 0;
|
||||||
|
|
||||||
public pinnedMsgID = 0;
|
public pinnedMsgID = 0;
|
||||||
private pinnedMessageContainer: HTMLDivElement = null;
|
private pinnedMessageContainer: PinnedContainer = null;
|
||||||
|
|
||||||
public lazyLoadQueue = new LazyLoadQueue();
|
public lazyLoadQueue = new LazyLoadQueue();
|
||||||
|
|
||||||
@ -165,7 +167,10 @@ export class AppImManager {
|
|||||||
parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children);
|
parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children);
|
||||||
|
|
||||||
this.chatAudio = new ChatAudio();
|
this.chatAudio = new ChatAudio();
|
||||||
this.chatInfo.nextElementSibling.prepend(this.chatAudio.container);
|
this.chatInfo.nextElementSibling.prepend(this.chatAudio.divAndCaption.container);
|
||||||
|
|
||||||
|
this.pinnedMessageContainer = new PinnedContainer('message', new ReplyContainer('pinned-message'));
|
||||||
|
this.btnJoin.parentElement.insertBefore(this.pinnedMessageContainer.divAndCaption.container, this.btnJoin);
|
||||||
|
|
||||||
// will call when message is sent (only 1)
|
// will call when message is sent (only 1)
|
||||||
$rootScope.$on('history_append', (e) => {
|
$rootScope.$on('history_append', (e) => {
|
||||||
@ -323,17 +328,20 @@ export class AppImManager {
|
|||||||
this.renderMessage(message, true, false, bubble, false);
|
this.renderMessage(message, true, false, bubble, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$rootScope.$on('peer_pinned_message', (e) => {
|
||||||
|
const peerID = e.detail;
|
||||||
|
|
||||||
|
if(peerID == this.peerID) {
|
||||||
|
(this.messagesQueuePromise || Promise.resolve()).then(() => {
|
||||||
|
this.setPinnedMessage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$rootScope.$on('messages_downloaded', (e) => {
|
$rootScope.$on('messages_downloaded', (e) => {
|
||||||
const mids: number[] = e.detail;
|
const mids: number[] = e.detail;
|
||||||
|
|
||||||
const pinnedMessage = appMessagesManager.getPinnedMessage(this.peerID);
|
|
||||||
mids.forEach(mid => {
|
mids.forEach(mid => {
|
||||||
if(pinnedMessage.mid == mid) {
|
|
||||||
(this.messagesQueuePromise || Promise.resolve()).then(() => {
|
|
||||||
this.setPinnedMessage(pinnedMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* const promise = (this.scrollable.scrollLocked && this.scrollable.scrollLockedPromise) || Promise.resolve();
|
/* const promise = (this.scrollable.scrollLocked && this.scrollable.scrollLockedPromise) || Promise.resolve();
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
|
|
||||||
@ -747,38 +755,18 @@ export class AppImManager {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
public setPinnedMessage(message: any) {
|
public setPinnedMessage() {
|
||||||
/////this.log('setting pinned message', message);
|
/////this.log('setting pinned message', message);
|
||||||
//return;
|
//return;
|
||||||
const height = 52;
|
const message = appMessagesManager.getPinnedMessage(this.peerID);
|
||||||
const scrollTop = this.scrollable.container.scrollTop;
|
if(message && !message.deleted) {
|
||||||
const newPinned = wrapReply('Pinned Message', message.message, message, true);
|
this.pinnedMessageContainer.fill('Pinned Message', message.message, message);
|
||||||
newPinned.dataset.mid = '' + message.mid;
|
this.pinnedMessageContainer.toggle(false);
|
||||||
newPinned.classList.add('pinned-container');
|
this.pinnedMsgID = message.mid;
|
||||||
|
} else if(this.pinnedMsgID) {
|
||||||
const close = document.createElement('button');
|
this.pinnedMsgID = 0;
|
||||||
close.classList.add('pinned-message-close', 'btn-icon', 'tgico-close');
|
this.pinnedMessageContainer.toggle(true);
|
||||||
close.addEventListener('click', (e) => {
|
|
||||||
cancelEvent(e);
|
|
||||||
const scrollTop = this.scrollable.scrollTop;
|
|
||||||
newPinned.remove();
|
|
||||||
this.topbar.classList.remove('is-pinned-shown');
|
|
||||||
|
|
||||||
this.pinnedMessageContainer = null;
|
|
||||||
this.scrollable.scrollTop = scrollTop - height;
|
|
||||||
}, {once: true});
|
|
||||||
newPinned.append(close);
|
|
||||||
|
|
||||||
this.btnJoin.parentElement.insertBefore(newPinned, this.btnJoin);
|
|
||||||
this.topbar.classList.add('is-pinned-shown');
|
|
||||||
|
|
||||||
if(this.pinnedMessageContainer) {
|
|
||||||
this.pinnedMessageContainer.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pinnedMessageContainer = newPinned;
|
|
||||||
//this.pinnedMessageContent.innerHTML = message.rReply;
|
|
||||||
this.scrollable.scrollTop = scrollTop + height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateStatus() {
|
public updateStatus() {
|
||||||
@ -851,7 +839,7 @@ export class AppImManager {
|
|||||||
}, 1350);
|
}, 1350);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.scroll.scrollHeight - Math.round(this.scroll.scrollTop + this.scroll.offsetHeight) <= 1/* <= 5 */) {
|
if(this.scrollable.isScrolledDown) {
|
||||||
this.scroll.parentElement.classList.add('scrolled-down');
|
this.scroll.parentElement.classList.add('scrolled-down');
|
||||||
this.scrolledDown = true;
|
this.scrolledDown = true;
|
||||||
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
|
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
|
||||||
@ -1289,13 +1277,7 @@ export class AppImManager {
|
|||||||
|
|
||||||
this.menuButtons.mute.style.display = this.myID == this.peerID ? 'none' : '';
|
this.menuButtons.mute.style.display = this.myID == this.peerID ? 'none' : '';
|
||||||
|
|
||||||
const pinned = appMessagesManager.getPinnedMessage(peerID);
|
this.setPinnedMessage();
|
||||||
if(pinned && !pinned.deleted) {
|
|
||||||
this.setPinnedMessage(pinned);
|
|
||||||
} else if(this.pinnedMessageContainer) {
|
|
||||||
this.pinnedMessageContainer.remove();
|
|
||||||
this.pinnedMessageContainer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
let title = '';
|
let title = '';
|
||||||
@ -1370,9 +1352,13 @@ export class AppImManager {
|
|||||||
|
|
||||||
//if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight;
|
//if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight;
|
||||||
if(this.messagesQueuePromise && scrolledDown) {
|
if(this.messagesQueuePromise && scrolledDown) {
|
||||||
this.scrollable.scrollTo(this.scrollable.scrollHeight - 1, 'top', false, true);
|
if(this.scrollable.isScrolledDown && !this.scrollable.scrollLocked) {
|
||||||
|
//this.log('renderNewMessagesByIDs: messagesQueuePromise before will set prev max');
|
||||||
|
this.scrollable.scrollTo(this.scrollable.scrollHeight - 1, 'top', false, true);
|
||||||
|
}
|
||||||
|
|
||||||
this.messagesQueuePromise.then(() => {
|
this.messagesQueuePromise.then(() => {
|
||||||
//this.log('messagesQueuePromise after:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
|
//this.log('renderNewMessagesByIDs: messagesQueuePromise after', this.scrollable.isScrolledDown);
|
||||||
this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
|
this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
|
||||||
|
|
||||||
/* setTimeout(() => {
|
/* setTimeout(() => {
|
||||||
|
@ -2162,11 +2162,19 @@ export class AppMessagesManager {
|
|||||||
public savePinnedMessage(peerID: number, mid: number) {
|
public savePinnedMessage(peerID: number, mid: number) {
|
||||||
if(!mid) {
|
if(!mid) {
|
||||||
delete this.pinnedMessages[peerID];
|
delete this.pinnedMessages[peerID];
|
||||||
return;
|
} else {
|
||||||
|
this.pinnedMessages[peerID] = mid;
|
||||||
|
|
||||||
|
if(!this.messagesStorage.hasOwnProperty(mid)) {
|
||||||
|
this.wrapSingleMessage(mid).then(() => {
|
||||||
|
$rootScope.$broadcast('peer_pinned_message', peerID);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pinnedMessages[peerID] = mid;
|
$rootScope.$broadcast('peer_pinned_message', peerID);
|
||||||
this.wrapSingleMessage(mid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPinnedMessage(peerID: number) {
|
public getPinnedMessage(peerID: number) {
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
// border-bottom: 1px solid #DADCE0;
|
// border-bottom: 1px solid #DADCE0;
|
||||||
|
|
||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
&.is-audio-shown, &.is-pinned-shown:not(.hide-pinned) {
|
&.is-pinned-audio-shown, &.is-pinned-message-shown:not(.hide-pinned) {
|
||||||
& + #bubbles {
|
& + #bubbles {
|
||||||
margin-top: 52px;
|
margin-top: 52px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-pinned-shown:not(.hide-pinned):not(.is-audio-shown) {
|
&.is-pinned-message-shown:not(.hide-pinned):not(.is-pinned-audio-shown) {
|
||||||
.pinned-message {
|
.pinned-message {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-pinned-shown:not(.hide-pinned) {
|
&.is-pinned-message-shown:not(.hide-pinned) {
|
||||||
.pinned-message {
|
.pinned-message {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -102,18 +102,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to(medium-screens) {
|
@include respond-to(medium-screens) {
|
||||||
&.is-pinned-shown {
|
&.is-pinned-message-shown {
|
||||||
body.is-right-column-shown & {
|
body.is-right-column-shown & {
|
||||||
.chat-info {
|
.chat-info {
|
||||||
max-width: calc(100% - var(--right-column-width) * 1.75);
|
max-width: calc(100% - var(--right-column-width) * 1.75);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&.is-pinned-shown.is-audio-shown {
|
&.is-pinned-audio-shown {
|
||||||
body.is-right-column-shown & {
|
body.is-right-column-shown & {
|
||||||
.chat-info {
|
.chat-info {
|
||||||
max-width: calc(100% - var(--right-column-width) * 2.25);
|
max-width: calc(100% - var(--right-column-width) * 2.25);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -792,6 +792,7 @@
|
|||||||
|
|
||||||
.pinned-container {
|
.pinned-container {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.15);
|
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.15);
|
||||||
@ -808,21 +809,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pinned-message-close, .pinned-audio-close, .pinned-audio-ico {
|
&-close, .pinned-audio-ico {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.tgico-close {
|
&-close {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
//left: -3rem;
|
||||||
|
|
||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
right: 9px;
|
right: 9px;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user