Browse Source

fix mediaviewer

mediaviewer forward
chat contextmenu with rights
support manually attached stickers (not upload)
right sidebar fix blink & preloader
minor fixes

Signed-off-by: morethanwords <thanwords24@gmail.com>
master
morethanwords 4 years ago
parent
commit
842f69216d
  1. 44
      package-lock.json
  2. 28
      src/components/lazyLoadQueue.ts
  3. 7
      src/components/misc.ts
  4. 92
      src/components/popup.ts
  5. 5
      src/components/scrollable_new.ts
  6. 2
      src/components/wrappers.ts
  7. 9
      src/lib/appManagers/apiUpdatesManager.ts
  8. 47
      src/lib/appManagers/appChatsManager.ts
  9. 101
      src/lib/appManagers/appDialogsManager.ts
  10. 10
      src/lib/appManagers/appDocsManager.ts
  11. 407
      src/lib/appManagers/appImManager.ts
  12. 308
      src/lib/appManagers/appMediaViewer.ts
  13. 104
      src/lib/appManagers/appMessagesManager.ts
  14. 57
      src/lib/appManagers/appSidebarLeft.ts
  15. 7
      src/lib/appManagers/appSidebarRight.ts
  16. 8
      src/lib/appManagers/appStickersManager.ts
  17. 6
      src/lib/lottieLoader.ts
  18. 2
      src/pages/pageSignIn.ts
  19. 75
      src/scss/partials/_mediaViewer.scss
  20. 31
      src/scss/partials/_rightSIdebar.scss
  21. 4
      src/scss/partials/_scrollable.scss
  22. 1
      src/scss/partials/popups/_peer.scss
  23. 2
      src/scss/style.scss
  24. 2
      webpack.common.js

44
package-lock.json generated

@ -4558,8 +4558,7 @@ @@ -4558,8 +4558,7 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"optional": true
"bundled": true
},
"aproba": {
"version": "1.2.0",
@ -4577,13 +4576,11 @@ @@ -4577,13 +4576,11 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"optional": true
"bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -4596,18 +4593,15 @@ @@ -4596,18 +4593,15 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@ -4710,8 +4704,7 @@ @@ -4710,8 +4704,7 @@
},
"inherits": {
"version": "2.0.4",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@ -4721,7 +4714,6 @@ @@ -4721,7 +4714,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -4734,20 +4726,17 @@ @@ -4734,20 +4726,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.5",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -4764,7 +4753,6 @@ @@ -4764,7 +4753,6 @@
"mkdirp": {
"version": "0.5.3",
"bundled": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
@ -4820,8 +4808,7 @@ @@ -4820,8 +4808,7 @@
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"npm-packlist": {
"version": "1.4.8",
@ -4846,8 +4833,7 @@ @@ -4846,8 +4833,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@ -4857,7 +4843,6 @@ @@ -4857,7 +4843,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -4926,8 +4911,7 @@ @@ -4926,8 +4911,7 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"optional": true
"bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@ -4957,7 +4941,6 @@ @@ -4957,7 +4941,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -4975,7 +4958,6 @@ @@ -4975,7 +4958,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -5014,13 +4996,11 @@ @@ -5014,13 +4996,11 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"optional": true
"bundled": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
"optional": true
"bundled": true
}
}
},

28
src/components/lazyLoadQueue.ts

@ -17,7 +17,9 @@ export default class LazyLoadQueue { @@ -17,7 +17,9 @@ export default class LazyLoadQueue {
private observer: IntersectionObserver;
constructor(private parallelLimit = 5) {
constructor(private parallelLimit = 5, withObserver = true) {
if(!withObserver) return;
this.observer = new IntersectionObserver(entries => {
if(this.lockPromise) return;
@ -41,7 +43,10 @@ export default class LazyLoadQueue { @@ -41,7 +43,10 @@ export default class LazyLoadQueue {
this.tempID--;
this.lazyLoadMedia.length = 0;
this.loadingMedia = 0;
this.observer.disconnect();
if(this.observer) {
this.observer.disconnect();
}
}
public length() {
@ -103,15 +108,26 @@ export default class LazyLoadQueue { @@ -103,15 +108,26 @@ export default class LazyLoadQueue {
}
}
}
public push(el: LazyLoadElement) {
this.lazyLoadMedia.push(el);
public addElement(el: LazyLoadElement) {
if(el.wasSeen) {
this.processQueue(el);
} else {
el.wasSeen = false;
this.observer.observe(el.div);
if(this.observer) {
this.observer.observe(el.div);
}
}
}
public push(el: LazyLoadElement) {
this.lazyLoadMedia.push(el);
this.addElement(el);
}
public unshift(el: LazyLoadElement) {
this.lazyLoadMedia.unshift(el);
this.addElement(el);
}
}

7
src/components/misc.ts

@ -341,6 +341,13 @@ export function formatPhoneNumber(str: string) { @@ -341,6 +341,13 @@ export function formatPhoneNumber(str: string) {
return {formatted: str, country};
}
export function parseMenuButtonsTo(to: {[name: string]: HTMLButtonElement}, elements: HTMLCollection) {
Array.from(elements).forEach(el => {
let name = el.className.match(/ menu-(.+?) /)[1];
to[name] = el as HTMLButtonElement;
});
}
let onMouseMove = (e: MouseEvent) => {
let rect = openedMenu.getBoundingClientRect();
let {clientX, clientY} = e;

92
src/components/popup.ts

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
import AvatarElement from "./avatar";
import { ripple } from "./misc";
export class PopupElement {
protected element = document.createElement('div');
protected container = document.createElement('div');
protected header = document.createElement('div');
protected title = document.createElement('div');
constructor(className: string) {
this.element.classList.add('popup');
this.element.className = 'popup' + (className ? ' ' + className : '');
this.container.classList.add('popup-container', 'z-depth-1');
this.header.classList.add('popup-header');
this.title.classList.add('popup-title');
this.header.append(this.title);
this.container.append(this.header);
this.element.append(this.container);
}
public show() {
document.body.append(this.element);
void this.element.offsetWidth; // reflow
this.element.classList.add('active');
}
public destroy() {
this.element.classList.remove('active');
setTimeout(() => {
this.element.remove();
}, 1000);
}
}
export type PopupPeerButton = {
text: string,
callback?: () => void,
isDanger?: true,
isCancel?: true
};
export class PopupPeer extends PopupElement {
constructor(private className: string, options: Partial<{
peerID: number,
title: string,
description: string,
buttons: Array<PopupPeerButton>
}> = {}) {
super('popup-peer' + (className ? ' ' + className : ''));
let avatarEl = new AvatarElement();
avatarEl.setAttribute('dialog', '1');
avatarEl.setAttribute('peer', '' + options.peerID);
avatarEl.classList.add('peer-avatar');
this.title.innerText = options.title || '';
this.header.prepend(avatarEl);
let p = document.createElement('p');
p.classList.add('popup-description');
p.innerHTML = options.description;
let buttonsDiv = document.createElement('div');
buttonsDiv.classList.add('popup-buttons');
let buttons = options.buttons.map(b => {
let button = document.createElement('button');
ripple(button);
button.className = 'btn' + (b.isDanger ? ' danger' : '');
button.innerHTML = b.text;
if(b.callback) {
button.addEventListener('click', () => {
b.callback();
this.destroy();
});
} else if(b.isCancel) {
button.addEventListener('click', () => {
this.destroy();
});
}
return button;
});
buttonsDiv.append(...buttons);
this.container.append(p, buttonsDiv);
}
}

5
src/components/scrollable_new.ts

@ -364,7 +364,10 @@ export default class Scrollable { @@ -364,7 +364,10 @@ export default class Scrollable {
public scrollIntoView(element: HTMLElement, smooth = true) {
if(element.parentElement && !this.scrollLocked) {
let isFirstUnread = element.classList.contains('is-first-unread');
let offsetTop = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
offsetTop = this.container.scrollTop + offsetTop;
if(!smooth && isFirstUnread) {
this.scrollTo(offsetTop, false);
return;
@ -374,7 +377,7 @@ export default class Scrollable { @@ -374,7 +377,7 @@ export default class Scrollable {
let height = element.scrollHeight;
let d = (clientHeight - height) / 2;
offsetTop = this.container.scrollTop + offsetTop - d;
offsetTop -= d;
this.scrollTo(offsetTop, smooth);
}

2
src/components/wrappers.ts

@ -796,8 +796,6 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -796,8 +796,6 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
//console.timeEnd('decompress sticker' + doc.id);
console.log('sticker json:', json);
let animation = await LottieLoader.loadAnimation({
container: div,
loop: false,

9
src/lib/appManagers/apiUpdatesManager.ts

@ -176,8 +176,8 @@ export class ApiUpdatesManager { @@ -176,8 +176,8 @@ export class ApiUpdatesManager {
public getDifference() {
// console.trace(dT(), 'Get full diff')
let updatesState = this.updatesState;
if (!updatesState.syncLoading) {
const updatesState = this.updatesState;
if(!updatesState.syncLoading) {
updatesState.syncLoading = true;
updatesState.pendingSeqUpdates = {};
updatesState.pendingPtsUpdates = [];
@ -188,7 +188,7 @@ export class ApiUpdatesManager { @@ -188,7 +188,7 @@ export class ApiUpdatesManager {
updatesState.syncPending = false;
}
apiManager.invokeApi('updates.getDifference', {
return apiManager.invokeApi('updates.getDifference', {
pts: updatesState.pts,
date: updatesState.date,
qts: -1
@ -232,7 +232,7 @@ export class ApiUpdatesManager { @@ -232,7 +232,7 @@ export class ApiUpdatesManager {
});
});
var nextState = differenceResult.intermediate_state || differenceResult.state;
const nextState = differenceResult.intermediate_state || differenceResult.state;
updatesState.seq = nextState.seq;
updatesState.pts = nextState.pts;
updatesState.date = nextState.date;
@ -515,7 +515,6 @@ export class ApiUpdatesManager { @@ -515,7 +515,6 @@ export class ApiUpdatesManager {
});
} else {
Object.assign(this.updatesState, state);
this.updatesState.syncLoading = false;
this.getDifference();
}
}

47
src/lib/appManagers/appChatsManager.ts

@ -128,12 +128,10 @@ export class AppChatsManager { @@ -128,12 +128,10 @@ export class AppChatsManager {
return this.chats[id] || {id: id, deleted: true, access_hash: this.channelAccess[id]};
}
public hasRights(id: number, action: 'send' | 'edit_title' | 'edit_photo' | 'invite') {
if(!(id in this.chats)) {
return false;
}
public hasRights(id: number, action: 'send' | 'edit_title' | 'edit_photo' | 'invite' | 'pin' | 'deleteRevoke') {
const chat = this.getChat(id);
if(!chat) return false;
let chat = this.getChat(id);
if(chat._ == 'chatForbidden' ||
chat._ == 'channelForbidden' ||
chat.pFlags.kicked ||
@ -145,24 +143,50 @@ export class AppChatsManager { @@ -145,24 +143,50 @@ export class AppChatsManager {
return true;
}
let myFlags = (chat.admin_rights || chat.banned_rights || chat.default_banned_rights)?.pFlags ?? {};
switch(action) {
// good
case 'send': {
if(chat._ == 'channel' &&
!chat.pFlags.megagroup &&
!chat.pFlags.editor) {
!chat.pFlags.megagroup &&
!myFlags.post_messages) {
return false;
}
break;
}
// good
case 'deleteRevoke': {
if(chat._ == 'channel') {
return !!myFlags.delete_messages;
} else if(!chat.pFlags.admin) {
return false;
}
break;
}
// good
case 'pin': {
if(chat._ == 'channel') {
return chat.admin_rights ? !!myFlags.pin_messages || !!myFlags.post_messages : !myFlags.pin_messages;
} else {
if(myFlags.pin_messages && !chat.pFlags.admin) {
return false;
}
}
break;
}
case 'edit_title':
case 'edit_photo':
case 'invite': {
if(chat._ == 'channel') {
if(chat.pFlags.megagroup) {
if(!chat.pFlags.editor &&
!(action == 'invite' && chat.pFlags.democracy)) {
if(!(action == 'invite' && chat.pFlags.democracy)) {
return false;
}
} else {
@ -174,6 +198,7 @@ export class AppChatsManager { @@ -174,6 +198,7 @@ export class AppChatsManager {
return false;
}
}
break;
}
}
@ -306,7 +331,7 @@ export class AppChatsManager { @@ -306,7 +331,7 @@ export class AppChatsManager {
let chat = this.getChat(id);
let myID = appUsersManager.getSelf().id;
if(this.isChannel(id)) {
let isAdmin = chat.pFlags.creator || chat.pFlags.editor || chat.pFlags.moderator;
let isAdmin = chat.pFlags.creator;
participants.forEach((participant) => {
participant.canLeave = myID == participant.user_id;
participant.canKick = isAdmin && participant._ == 'channelParticipant';

101
src/lib/appManagers/appDialogsManager.ts

@ -4,12 +4,13 @@ import appPeersManager from './appPeersManager'; @@ -4,12 +4,13 @@ import appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager, Dialog } from "./appMessagesManager";
import appUsersManager, { User } from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor";
import { ripple, putPreloader, positionMenu, openBtnMenu } from "../../components/misc";
import { ripple, putPreloader, positionMenu, openBtnMenu, parseMenuButtonsTo } from "../../components/misc";
//import Scrollable from "../../components/scrollable";
import Scrollable from "../../components/scrollable_new";
import { logger } from "../polyfill";
import appChatsManager from "./appChatsManager";
import AvatarElement from "../../components/avatar";
import { PopupPeerButton, PopupPeer } from "../../components/popup";
type DialogDom = {
avatarEl: AvatarElement,
@ -25,95 +26,7 @@ type DialogDom = { @@ -25,95 +26,7 @@ type DialogDom = {
let testScroll = false;
class PopupElement {
protected element = document.createElement('div');
protected container = document.createElement('div');
protected header = document.createElement('div');
protected title = document.createElement('div');
constructor(className: string) {
this.element.classList.add('popup');
this.element.className = 'popup' + (className ? ' ' + className : '');
this.container.classList.add('popup-container', 'z-depth-1');
this.header.classList.add('popup-header');
this.title.classList.add('popup-title');
this.header.append(this.title);
this.container.append(this.header);
this.element.append(this.container);
}
public show() {
document.body.append(this.element);
void this.element.offsetWidth; // reflow
this.element.classList.add('active');
}
public destroy() {
this.element.classList.remove('active');
setTimeout(() => {
this.element.remove();
}, 1000);
}
}
type PopupPeerButton = {
text: string,
callback?: () => void,
isDanger?: true,
isCancel?: true
};
class PopupPeer extends PopupElement {
constructor(private className: string, options: Partial<{
peerID: number,
title: string,
description: string,
buttons: Array<PopupPeerButton>
}> = {}) {
super('popup-peer' + (className ? ' ' + className : ''));
let avatarEl = new AvatarElement();
avatarEl.setAttribute('dialog', '1');
avatarEl.setAttribute('peer', '' + options.peerID);
avatarEl.classList.add('peer-avatar');
this.title.innerText = options.title || '';
this.header.prepend(avatarEl);
let p = document.createElement('p');
p.classList.add('popup-description');
p.innerHTML = options.description;
let buttonsDiv = document.createElement('div');
buttonsDiv.classList.add('popup-buttons');
let buttons = options.buttons.map(b => {
let button = document.createElement('button');
ripple(button);
button.className = 'btn' + (b.isDanger ? ' danger' : '');
button.innerHTML = b.text;
if(b.callback) {
button.addEventListener('click', () => {
b.callback();
this.destroy();
});
} else if(b.isCancel) {
button.addEventListener('click', () => {
this.destroy();
});
}
return button;
});
buttonsDiv.append(...buttons);
this.container.append(p, buttonsDiv);
}
}
class DialogsContextMenu {
private element = document.getElementById('dialogs-contextmenu') as HTMLDivElement;
@ -129,11 +42,7 @@ class DialogsContextMenu { @@ -129,11 +42,7 @@ class DialogsContextMenu {
private peerType: 'channel' | 'chat' | 'megagroup' | 'group' | 'saved';
constructor(private attachTo: HTMLElement[]) {
(Array.from(this.element.querySelectorAll('.btn-menu-item')) as HTMLElement[]).forEach(el => {
let name = el.className.match(/ menu-(.+?) /)[1];
// @ts-ignore
this.buttons[name] = el;
});
parseMenuButtonsTo(this.buttons, this.element.children);
const onContextMenu = (e: MouseEvent) => {
let li: HTMLDivElement = null;
@ -145,13 +54,9 @@ class DialogsContextMenu { @@ -145,13 +54,9 @@ class DialogsContextMenu {
if(!li) return;
e.preventDefault();
if(this.element.classList.contains('active')) {
/* this.element.classList.remove('active');
this.element.parentElement.classList.remove('menu-open'); */
return false;
}
e.cancelBubble = true;
this.selectedID = +li.getAttribute('data-peerID');

10
src/lib/appManagers/appDocsManager.ts

@ -85,10 +85,6 @@ class AppDocsManager { @@ -85,10 +85,6 @@ class AppDocsManager {
if(/* apiDoc.thumbs && */apiDoc.mime_type == 'image/webp') {
apiDoc.type = 'sticker';
apiDoc.sticker = 1;
} else if(apiDoc.mime_type == 'application/x-tgsticker') {
apiDoc.type = 'sticker';
apiDoc.animated = true;
apiDoc.sticker = 2;
}
break;
@ -134,6 +130,12 @@ class AppDocsManager { @@ -134,6 +130,12 @@ class AppDocsManager {
if(!apiDoc.file_name) {
apiDoc.file_name = '';
}
if(apiDoc.mime_type == 'application/x-tgsticker' && apiDoc.file_name == "AnimatedSticker.tgs") {
apiDoc.type = 'sticker';
apiDoc.animated = true;
apiDoc.sticker = 2;
}
if(apiDoc._ == 'documentEmpty') {
apiDoc.size = 0;

407
src/lib/appManagers/appImManager.ts

@ -15,11 +15,10 @@ import lottieLoader from "../lottieLoader"; @@ -15,11 +15,10 @@ import lottieLoader from "../lottieLoader";
import appMediaViewer from "./appMediaViewer";
import appSidebarLeft from "./appSidebarLeft";
import appChatsManager from "./appChatsManager";
import appMessagesIDsManager from "./appMessagesIDsManager";
import apiUpdatesManager from './apiUpdatesManager';
import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply, wrapAlbum, wrapPoll } from '../../components/wrappers';
import ProgressivePreloader from '../../components/preloader';
import { openBtnMenu, formatPhoneNumber, positionMenu, ripple } from '../../components/misc';
import { openBtnMenu, formatPhoneNumber, positionMenu, ripple, parseMenuButtonsTo } from '../../components/misc';
import { ChatInput } from '../../components/chatInput';
//import Scrollable from '../../components/scrollable';
import Scrollable from '../../components/scrollable_new';
@ -31,6 +30,7 @@ import appStickersManager from './appStickersManager'; @@ -31,6 +30,7 @@ import appStickersManager from './appStickersManager';
import AvatarElement from '../../components/avatar';
import appInlineBotsManager from './AppInlineBotsManager';
import StickyIntersector from '../../components/stickyIntersector';
import { PopupPeerButton, PopupPeer } from '../../components/popup';
console.log('appImManager included!');
@ -40,6 +40,172 @@ let testScroll = false; @@ -40,6 +40,172 @@ let testScroll = false;
const IGNOREACTIONS = ['messageActionChannelMigrateFrom'];
class ChatContextMenu {
private element = document.getElementById('bubble-contextmenu') as HTMLDivElement;
private buttons: {
reply: HTMLButtonElement,
edit: HTMLButtonElement,
copy: HTMLButtonElement,
pin: HTMLButtonElement,
forward: HTMLButtonElement,
delete: HTMLButtonElement
} = {} as any;
public msgID: number;
constructor(private attachTo: HTMLElement) {
parseMenuButtonsTo(this.buttons, this.element.children);
attachTo.addEventListener('contextmenu', e => {
let bubble: HTMLDivElement = null;
try {
bubble = findUpClassName(e.target, 'bubble__container');
} catch(e) {}
if(!bubble) return;
e.preventDefault();
if(this.element.classList.contains('active')) {
return false;
}
e.cancelBubble = true;
bubble = bubble.parentElement as HTMLDivElement; // bc container
let msgID = +bubble.dataset.mid;
if(!msgID) return;
let peerID = $rootScope.selectedPeerID;
this.msgID = msgID;
const message = appMessagesManager.getMessage(msgID);
this.buttons.copy.style.display = message.message ? '' : 'none';
if($rootScope.myID == peerID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin'))) {
this.buttons.pin.style.display = '';
} else {
this.buttons.pin.style.display = 'none';
}
this.buttons.edit.style.display = appMessagesManager.canEditMessage(msgID) ? '' : 'none';
let side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right';
positionMenu(e, this.element, side);
openBtnMenu(this.element);
/////this.log('contextmenu', e, bubble, msgID, side);
});
this.buttons.copy.addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.msgID);
let str = message ? message.message : '';
var textArea = document.createElement("textarea");
textArea.value = str;
textArea.style.position = "fixed"; //avoid scrolling to bottom
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Oops, unable to copy', err);
}
document.body.removeChild(textArea);
});
this.buttons.delete.addEventListener('click', () => {
let peerID = $rootScope.selectedPeerID;
let firstName = appPeersManager.getPeerTitle(peerID, false, true);
let callback = (revoke: boolean) => {
appMessagesManager.deleteMessages([this.msgID], revoke);
};
let title: string, description: string, buttons: PopupPeerButton[];
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
});
let popup = new PopupPeer('popup-delete-chat', {
peerID: peerID,
title: title,
description: description,
buttons: buttons
});
popup.show();
});
this.buttons.reply.addEventListener('click', () => {
const message = appMessagesManager.getMessage(this.msgID);
const chatInputC = appImManager.chatInputC;
chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message);
chatInputC.replyToMsgID = this.msgID;
chatInputC.editMsgID = 0;
});
this.buttons.forward.addEventListener('click', () => {
appForward.init([this.msgID]);
});
this.buttons.edit.addEventListener('click', () => {
const message = appMessagesManager.getMessage(this.msgID);
const chatInputC = appImManager.chatInputC;
chatInputC.setTopInfo('Editing', message.message, message.message, message);
chatInputC.replyToMsgID = 0;
chatInputC.editMsgID = this.msgID;
});
this.buttons.pin.addEventListener('click', () => {
apiManager.invokeApi('messages.updatePinnedMessage', {
flags: 0,
peer: appPeersManager.getInputPeerByID($rootScope.selectedPeerID),
id: this.msgID
}).then(updates => {
/////this.log('pinned updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
});
});
}
}
export class AppImManager {
public pageEl = document.getElementById('page-chats') as HTMLDivElement;
public btnMute = this.pageEl.querySelector('.tool-mute') as HTMLButtonElement;
@ -58,9 +224,8 @@ export class AppImManager { @@ -58,9 +224,8 @@ export class AppImManager {
public myID = 0;
public peerID = 0;
public muted = false;
public bubbles: {[mid: number]: HTMLDivElement} = {};
public bubbles: {[mid: string]: HTMLDivElement} = {};
public dateMessages: {[timestamp: number]: {
div: HTMLDivElement,
firstTimestamp: number,
@ -94,11 +259,8 @@ export class AppImManager { @@ -94,11 +259,8 @@ export class AppImManager {
private scrolledAll: boolean;
private scrolledAllDown: boolean;
public contextMenu = document.getElementById('bubble-contextmenu') as HTMLDivElement;
private contextMenuPin = this.contextMenu.querySelector('.menu-pin') as HTMLDivElement;
private contextMenuEdit = this.contextMenu.querySelector('.menu-edit') as HTMLDivElement;
private contextMenuMsgID: number;
public contextMenu = new ChatContextMenu(this.bubblesContainer);
private popupDeleteMessage: {
popupEl?: HTMLDivElement,
deleteBothBtn?: HTMLButtonElement,
@ -510,123 +672,14 @@ export class AppImManager { @@ -510,123 +672,14 @@ export class AppImManager {
};
document.body.addEventListener('keydown', onKeyDown);
this.bubblesContainer.addEventListener('contextmenu', e => {
let bubble: HTMLDivElement = null;
try {
bubble = findUpClassName(e.target, 'bubble__container');
} catch(e) {}
if(bubble) {
bubble = bubble.parentElement as HTMLDivElement; // bc container
e.preventDefault();
e.cancelBubble = true;
let msgID = 0;
for(let id in this.bubbles) {
if(this.bubbles[id] === bubble) {
msgID = +id;
break;
}
}
if(!msgID) return;
if(this.myID == this.peerID || (this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
this.contextMenuPin.style.display = '';
} else this.contextMenuPin.style.display = 'none';
this.contextMenuMsgID = msgID;
let side = bubble.classList.contains('is-in') ? 'left' : 'right';
this.contextMenuEdit.style.display = side == 'right' ? '' : 'none';
positionMenu(e, this.contextMenu, side as any);
openBtnMenu(this.contextMenu);
/////this.log('contextmenu', e, bubble, msgID, side);
}
});
this.contextMenu.querySelector('.menu-copy').addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
let str = message ? message.message : '';
var textArea = document.createElement("textarea");
textArea.value = str;
textArea.style.position = "fixed"; //avoid scrolling to bottom
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Oops, unable to copy', err);
}
document.body.removeChild(textArea);
});
this.contextMenu.querySelector('.menu-delete').addEventListener('click', () => {
if(this.peerID == this.myID) {
this.popupDeleteMessage.deleteBothBtn.style.display = 'none';
this.popupDeleteMessage.deleteMeBtn.innerText = 'DELETE';
} else {
this.popupDeleteMessage.deleteBothBtn.style.display = '';
this.popupDeleteMessage.deleteMeBtn.innerText = 'DELETE JUST FOR ME';
if(this.peerID > 0) {
let title = appPeersManager.getPeerTitle(this.peerID);
this.popupDeleteMessage.deleteBothBtn.innerHTML = 'DELETE FOR ME AND ' + title;
} else {
this.popupDeleteMessage.deleteBothBtn.innerText = 'DELETE FOR ALL';
}
}
this.popupDeleteMessage.popupEl.classList.add('active');
});
this.contextMenu.querySelector('.menu-reply').addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
this.chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message);
this.chatInputC.replyToMsgID = this.contextMenuMsgID;
this.chatInputC.editMsgID = 0;
});
this.contextMenu.querySelector('.menu-forward').addEventListener('click', () => {
appForward.init([this.contextMenuMsgID]);
});
this.contextMenuEdit.addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
this.chatInputC.setTopInfo('Editing', message.message, message.message, message);
this.chatInputC.replyToMsgID = 0;
this.chatInputC.editMsgID = this.contextMenuMsgID;
});
this.contextMenuPin.addEventListener('click', () => {
apiManager.invokeApi('messages.updatePinnedMessage', {
flags: 0,
peer: appPeersManager.getInputPeerByID(this.peerID),
id: this.contextMenuMsgID
}).then(updates => {
/////this.log('pinned updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
});
});
this.popupDeleteMessage.deleteBothBtn.addEventListener('click', () => {
this.deleteMessages(true);
appMessagesManager.deleteMessages([this.contextMenu.msgID], true);
this.popupDeleteMessage.cancelBtn.click();
});
this.popupDeleteMessage.deleteMeBtn.addEventListener('click', () => {
this.deleteMessages(false);
appMessagesManager.deleteMessages([this.contextMenu.msgID], false);
this.popupDeleteMessage.cancelBtn.click();
});
@ -690,7 +743,7 @@ export class AppImManager { @@ -690,7 +743,7 @@ export class AppImManager {
} */
//appMessagesManager.readMessages(readed);
/* false && */appMessagesManager.readHistory(this.peerID, max, length).catch((err: any) => {
/* false && */ appMessagesManager.readHistory(this.peerID, max, length).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, length);
});
@ -698,36 +751,6 @@ export class AppImManager { @@ -698,36 +751,6 @@ export class AppImManager {
});
}
public deleteMessages(revoke = false) {
let flags = revoke ? 1 : 0;
let ids = [this.contextMenuMsgID];
apiManager.invokeApi('messages.deleteMessages', {
flags: flags,
revoke: revoke,
id: ids
}).then((affectedMessages: any) => {
/////this.log('deleted messages:', affectedMessages);
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: ids
}
});
});
}
public updateStatus() {
if(!this.myID) return Promise.resolve();
@ -897,8 +920,7 @@ export class AppImManager { @@ -897,8 +920,7 @@ export class AppImManager {
////console.time('appImManager cleanup');
this.scrolledAll = false;
this.scrolledAllDown = false;
this.muted = false;
this.bubbles = {};
this.dateMessages = {};
this.bubbleGroups.cleanup();
@ -951,15 +973,12 @@ export class AppImManager { @@ -951,15 +973,12 @@ export class AppImManager {
const samePeer = this.peerID == peerID;
if(this.setPeerPromise && samePeer) return this.setPeerPromise;
/* if(lastMsgID) {
appMessagesManager.readHistory(peerID, lastMsgID); // lol
} */
const dialog = appMessagesManager.getDialogByPeerID(peerID)[0] || null;
const topMessage = lastMsgID <= 0 ? lastMsgID : dialog?.top_message ?? 0;
if(lastMsgID === undefined && dialog) {
if(dialog.unread_count) {
const isTarget = lastMsgID !== undefined;
if(!isTarget && dialog) {
if(dialog.unread_count && !samePeer) {
lastMsgID = dialog.read_inbox_max_id;
} else {
lastMsgID = dialog.top_message;
@ -971,7 +990,7 @@ export class AppImManager { @@ -971,7 +990,7 @@ export class AppImManager {
if(dialog && lastMsgID == topMessage) {
this.log('will scroll down', this.scroll.scrollTop, this.scroll.scrollHeight);
this.scroll.scrollTop = this.scroll.scrollHeight;
} else {
} else if(isTarget) {
this.scrollable.scrollIntoView(this.bubbles[lastMsgID]);
this.highlightBubble(this.bubbles[lastMsgID]);
}
@ -1053,11 +1072,11 @@ export class AppImManager { @@ -1053,11 +1072,11 @@ export class AppImManager {
}
const fromUp = maxBubbleID > 0 && (maxBubbleID < lastMsgID || lastMsgID < 0);
if(!fromUp && samePeer) {
const forwardingUnread = dialog.read_inbox_max_id == lastMsgID;
if(!fromUp && (samePeer || forwardingUnread)) {
this.scrollable.scrollTop = this.scrollable.scrollHeight;
}
const forwardingUnread = dialog.read_inbox_max_id == lastMsgID;
const bubble = forwardingUnread ? (this.firstUnreadBubble || this.bubbles[lastMsgID]) : this.bubbles[lastMsgID];
this.scrollable.scrollIntoView(bubble, samePeer/* , fromUp */);
@ -1075,6 +1094,12 @@ export class AppImManager { @@ -1075,6 +1094,12 @@ export class AppImManager {
this.log('scrolledAllDown:', this.scrolledAllDown);
if(!this.unreaded.length && dialog) { // lol
appMessagesManager.readHistory(peerID, dialog.top_message);
}
this.chatInner.classList.remove('disable-hover', 'is-scrolling'); // warning, performance!
//console.timeEnd('appImManager setPeer');
return true;
@ -1126,6 +1151,8 @@ export class AppImManager { @@ -1126,6 +1151,8 @@ export class AppImManager {
this.pinnedMessageContainer.style.display = 'none';
this.btnMute.style.display = appPeersManager.isBroadcast(peerID) ? '' : 'none';
window.requestAnimationFrame(() => {
let title = '';
if(this.peerID == this.myID) title = 'Saved Messages';
@ -1642,6 +1669,8 @@ export class AppImManager { @@ -1642,6 +1669,8 @@ export class AppImManager {
}
}
}
const isOut = our && (!message.fwd_from || this.peerID != this.myID);
// media
if(messageMedia/* && messageMedia._ == 'messageMediaPhoto' */) {
@ -1696,7 +1725,7 @@ export class AppImManager { @@ -1696,7 +1725,7 @@ export class AppImManager {
boxWidth: 380,
boxHeight: 380,
withTail: doc.type != 'round',
isOut: our,
isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue,
middleware: null
});
@ -1744,7 +1773,7 @@ export class AppImManager { @@ -1744,7 +1773,7 @@ export class AppImManager {
lazyLoadQueue: this.lazyLoadQueue
});
} else {
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, our, this.lazyLoadQueue, () => {
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, isOut, this.lazyLoadQueue, () => {
return this.peerID == peerID;
});
}
@ -1800,7 +1829,8 @@ export class AppImManager { @@ -1800,7 +1829,8 @@ export class AppImManager {
lazyLoadQueue: this.lazyLoadQueue,
middleware: () => {
return this.peerID == peerID;
}
},
isOut
});
//}
} else {
@ -1907,7 +1937,7 @@ export class AppImManager { @@ -1907,7 +1937,7 @@ export class AppImManager {
boxWidth: 380,
boxHeight: 380,
withTail: doc.type != 'round',
isOut: our,
isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue,
middleware: () => {
return this.peerID == peerID;
@ -2097,7 +2127,7 @@ export class AppImManager { @@ -2097,7 +2127,7 @@ export class AppImManager {
bubble.classList.add('hide-name');
}
bubble.classList.add(our && (!message.fwd_from || this.peerID != this.myID) ? 'is-out' : 'is-in');
bubble.classList.add(isOut ? 'is-out' : 'is-in');
if(updatePosition) {
this.bubbleGroups.addBubble(bubble, message, reverse);
@ -2151,10 +2181,16 @@ export class AppImManager { @@ -2151,10 +2181,16 @@ export class AppImManager {
let realLength = this.scrollable.length;
let previousScrollHeightMinusTop: number;
if(realLength > 0 && reverse) {
if(realLength > 0 && reverse) { // for safari need set when scrolling bottom too
this.messagesQueueOnRender = () => {
let scrollTop = this.scrollable.scrollTop;
previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop;
/* if(reverse) {
previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop;
} else {
previousScrollHeightMinusTop = scrollTop;
} */
this.log('performHistoryResult: messagesQueueOnRender, scrollTop:', scrollTop, previousScrollHeightMinusTop);
this.messagesQueueOnRender = undefined;
@ -2168,8 +2204,9 @@ export class AppImManager { @@ -2168,8 +2204,9 @@ export class AppImManager {
(this.messagesQueuePromise || Promise.resolve()).then(() => {
if(previousScrollHeightMinusTop !== undefined) {
this.log('performHistoryResult: will set scrollTop', this.scrollable.scrollHeight, previousScrollHeightMinusTop);
this.scrollable.scrollTop = this.scrollable.scrollHeight - previousScrollHeightMinusTop;
const newScrollTop = reverse ? this.scrollable.scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
this.log('performHistoryResult: will set scrollTop', this.scrollable.scrollHeight, newScrollTop, this.scrollable.container.clientHeight);
this.scrollable.scrollTop = newScrollTop;
}
resolve(true);
@ -2186,7 +2223,7 @@ export class AppImManager { @@ -2186,7 +2223,7 @@ export class AppImManager {
let peerID = this.peerID;
//console.time('appImManager call getHistory');
let pageCount = this.bubblesContainer.clientHeight / 38/* * 1.25 */ | 0;
let pageCount = appPhotosManager.windowH / 38/* * 1.25 */ | 0;
//let loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
let realLoadCount = Object.keys(this.bubbles).length > 0 ? Math.max(40, pageCount) : pageCount;//let realLoadCount = 50;
let loadCount = realLoadCount;
@ -2215,7 +2252,7 @@ export class AppImManager { @@ -2215,7 +2252,7 @@ export class AppImManager {
if(result instanceof Promise) {
cached = false;
promise = result.then((result) => {
this.log('getHistory result by maxID:', maxID, reverse, isBackLimit, result);
this.log('getHistory not cached result by maxID:', maxID, reverse, isBackLimit, result, peerID);
//console.timeEnd('appImManager call getHistory');
@ -2234,8 +2271,8 @@ export class AppImManager { @@ -2234,8 +2271,8 @@ export class AppImManager {
return false;
});
} else {
this.log('getHistory result by maxID:', maxID, reverse, isBackLimit, result);
cached = true;
this.log('getHistory cached result by maxID:', maxID, reverse, isBackLimit, result, peerID);
promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID);
//return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
//return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
@ -2292,7 +2329,7 @@ export class AppImManager { @@ -2292,7 +2329,7 @@ export class AppImManager {
if(!dialog?.unread_count) return;
let maxID = dialog.read_inbox_max_id;
maxID = Object.keys(this.bubbles).map(i => +i).sort((a, b) => a - b).find(i => i > maxID);
maxID = Object.keys(this.bubbles).filter(mid => !this.bubbles[mid].classList.contains('is-out')).map(i => +i).sort((a, b) => a - b).find(i => i > maxID);
if(maxID && this.bubbles[maxID]) {
let bubble = this.bubbles[maxID];
@ -2313,7 +2350,7 @@ export class AppImManager { @@ -2313,7 +2350,7 @@ export class AppImManager {
for(let i in this.dateMessages) {
let dateMessage = this.dateMessages[i];
if(dateMessage.container.childElementCount == 1) { // only date div
if(dateMessage.container.childElementCount == 2) { // only date div + sentinel div
dateMessage.container.remove();
this.stickyIntersector.unobserve(dateMessage.container, dateMessage.div);
delete this.dateMessages[i];
@ -2324,19 +2361,11 @@ export class AppImManager { @@ -2324,19 +2361,11 @@ export class AppImManager {
public setMutedState(muted = false) {
appSidebarRight.profileElements.notificationsCheckbox.checked = !muted;
appSidebarRight.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
let peerID = this.peerID;
this.muted = muted;
if(peerID < 0) { // not human
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
if(isChannel) {
this.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
this.btnMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
this.btnMute.style.display = '';
} else {
this.btnMute.style.display = 'none';
}
if(appPeersManager.isBroadcast(this.peerID)) { // not human
this.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
this.btnMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
this.btnMute.style.display = '';
} else {
this.btnMute.style.display = 'none';
}

308
src/lib/appManagers/appMediaViewer.ts

@ -9,6 +9,8 @@ import appDocsManager from "./appDocsManager"; @@ -9,6 +9,8 @@ import appDocsManager from "./appDocsManager";
import VideoPlayer from "../mediaPlayer";
import { renderImageFromUrl } from "../../components/misc";
import AvatarElement from "../../components/avatar";
import LazyLoadQueue from "../../components/lazyLoadQueue";
import appForward from "../../components/appForward";
export class AppMediaViewer {
private overlaysDiv = document.querySelector('.overlays') as HTMLDivElement;
@ -59,10 +61,15 @@ export class AppMediaViewer { @@ -59,10 +61,15 @@ export class AppMediaViewer {
private needLoadMore = true;
private pageEl = document.getElementById('page-chats') as HTMLDivElement;
private setMoverPromise: Promise<void>;
private lazyLoadQueue: LazyLoadQueue;
constructor() {
this.log = logger('AMV');
this.preloader = new ProgressivePreloader();
this.lazyLoadQueue = new LazyLoadQueue(5, false);
this.onKeyDownBinded = this.onKeyDown.bind(this);
@ -75,6 +82,7 @@ export class AppMediaViewer { @@ -75,6 +82,7 @@ export class AppMediaViewer {
this.peerID = 0;
this.currentMessageID = 0;
this.lazyLoadQueue.clear();
this.setMoverToTarget(this.lastTarget, true);
@ -88,6 +96,8 @@ export class AppMediaViewer { @@ -88,6 +96,8 @@ export class AppMediaViewer {
});
this.buttons.prev.addEventListener('click', () => {
if(this.setMoverPromise) return;
let target = this.prevTargets.pop();
if(target) {
this.nextTargets.unshift({element: this.lastTarget, mid: this.currentMessageID});
@ -98,6 +108,8 @@ export class AppMediaViewer { @@ -98,6 +108,8 @@ export class AppMediaViewer {
});
this.buttons.next.addEventListener('click', () => {
if(this.setMoverPromise) return;
let target = this.nextTargets.shift();
if(target) {
this.prevTargets.push({element: this.lastTarget, mid: this.currentMessageID});
@ -124,6 +136,10 @@ export class AppMediaViewer { @@ -124,6 +136,10 @@ export class AppMediaViewer {
}
});
this.buttons.forward.addEventListener('click', () => {
appForward.init([this.currentMessageID]);
});
this.onClickBinded = (e: MouseEvent) => {
let target = e.target as HTMLElement;
@ -155,7 +171,7 @@ export class AppMediaViewer { @@ -155,7 +171,7 @@ export class AppMediaViewer {
}
}
public setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
public async setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
let mover = this.content.mover;
if(!closing) {
@ -222,7 +238,7 @@ export class AppMediaViewer { @@ -222,7 +238,7 @@ export class AppMediaViewer {
}
} else {
aspecter = document.createElement('div');
aspecter.classList.add('media-viewer-aspecter');
aspecter.classList.add('media-viewer-aspecter', 'disable-hover');
mover.prepend(aspecter);
}
@ -257,19 +273,25 @@ export class AppMediaViewer { @@ -257,19 +273,25 @@ export class AppMediaViewer {
let isOut = target.classList.contains('is-out');
if(!closing) {
let img: HTMLImageElement;
let video: HTMLVideoElement;
if(target.tagName == 'DIV') { // means backgrounded with cover
img = new Image();
img.src = target.style.backgroundImage.slice(5, -2);
let mediaElement: HTMLImageElement | HTMLVideoElement;
let src: string;
if(target.tagName == 'DIV') { // useContainerAsTarget
if(target.firstElementChild) {
mediaElement = new Image();
src = (target.firstElementChild as HTMLImageElement).src;
mover.append(mediaElement);
}
/* mediaElement = new Image();
src = target.style.backgroundImage.slice(5, -2); */
} else if(target instanceof HTMLImageElement) {
img = new Image();
img.src = target.src;
mediaElement = new Image();
src = target.src;
} else if(target instanceof HTMLVideoElement) {
video = document.createElement('video');
let video = mediaElement = document.createElement('video');
let source = document.createElement('source');
source.src = target.querySelector('source')?.src;
src = target.querySelector('source')?.src;
video.append(source);
} else if(target instanceof SVGSVGElement) {
let clipID = target.dataset.clipID;
@ -314,23 +336,42 @@ export class AppMediaViewer { @@ -314,23 +336,42 @@ export class AppMediaViewer {
path.setAttributeNS(null, 'd', d);
}
let mediaEl = newSvg.lastElementChild;
mediaEl.setAttributeNS(null, 'width', '' + containerRect.width);
mediaEl.setAttributeNS(null, 'height', '' + containerRect.height);
let foreignObject = newSvg.lastElementChild;
foreignObject.setAttributeNS(null, 'width', '' + containerRect.width);
foreignObject.setAttributeNS(null, 'height', '' + containerRect.height);
mover.prepend(newSvg);
}
if(aspecter) {
aspecter.style.borderRadius = borderRadius;
aspecter.append(img || video);
aspecter.append(mediaElement);
}
mediaElement = mover.querySelector('video, img');
if(mediaElement instanceof HTMLImageElement) {
await new Promise((resolve, reject) => {
mediaElement.addEventListener('load', resolve);
if(src) {
mediaElement.src = src;
}
});
} else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && (mediaElement.firstElementChild as HTMLSourceElement).src) {
await new Promise((resolve, reject) => {
mediaElement.addEventListener('loadeddata', resolve);
if(src) {
(mediaElement.firstElementChild as HTMLSourceElement).src = src;
}
});
}
mover.style.display = '';
setTimeout(() => {
window.requestAnimationFrame(() => {
mover.classList.add(wasActive ? 'moving' : 'active');
}, 0);
});
} else {
if(target instanceof SVGSVGElement) {
path = mover.querySelector('path');
@ -340,6 +381,10 @@ export class AppMediaViewer { @@ -340,6 +381,10 @@ export class AppMediaViewer {
}
}
if(target.classList.contains('media-viewer-media')) {
mover.classList.add('hiding');
}
setTimeout(() => {
this.overlaysDiv.classList.remove('active');
}, 0);
@ -354,46 +399,48 @@ export class AppMediaViewer { @@ -354,46 +399,48 @@ export class AppMediaViewer {
setTimeout(() => {
mover.innerHTML = '';
mover.classList.remove('moving', 'active');
mover.classList.remove('moving', 'active', 'hiding');
mover.style.display = 'none';
}, delay);
}
return () => {
mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`;
return;
}
if(aspecter) {
this.setFullAspect(aspecter, containerRect, rect);
aspecter.classList.add('disable-hover');
}
//await new Promise((resolve) => setTimeout(resolve, 0));
await new Promise((resolve) => window.requestAnimationFrame(resolve));
setTimeout(() => {
mover.style.borderRadius = '';
mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`;
if(mover.firstElementChild) {
(mover.firstElementChild as HTMLElement).style.borderRadius = '';
}
}, delay / 2);
if(aspecter) {
this.setFullAspect(aspecter, containerRect, rect);
}
mover.dataset.timeout = '' + setTimeout(() => {
mover.classList.remove('moving');
setTimeout(() => {
mover.style.borderRadius = '';
if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
mover.classList.remove('active');
aspecter.style.cssText = '';
void mover.offsetLeft; // reflow
if(mover.firstElementChild) {
(mover.firstElementChild as HTMLElement).style.borderRadius = '';
}
}, delay / 2);
aspecter.classList.remove('disable-hover');
}
mover.dataset.timeout = '' + setTimeout(() => {
mover.classList.remove('moving');
mover.classList.add('active');
delete mover.dataset.timeout;
}, delay);
if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
mover.classList.remove('active');
//aspecter.style.cssText = '';
void mover.offsetLeft; // reflow
if(path) {
this.sizeTailPath(path, containerRect, scaleX, delay, true, isOut, borderRadius);
aspecter.classList.remove('disable-hover');
}
};
mover.classList.add('active');
delete mover.dataset.timeout;
}, delay);
if(path) {
this.sizeTailPath(path, containerRect, scaleX, delay, true, isOut, borderRadius);
}
}
private setFullAspect(aspecter: HTMLDivElement, containerRect: DOMRect, rect: DOMRect) {
@ -415,6 +462,8 @@ export class AppMediaViewer { @@ -415,6 +462,8 @@ export class AppMediaViewer {
height = width * proportion;
}
//this.log('will set style aspecter:', `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`);
aspecter.style.cssText = `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`;
}
}
@ -581,7 +630,8 @@ export class AppMediaViewer { @@ -581,7 +630,8 @@ export class AppMediaViewer {
public openMedia(message: any, target?: HTMLElement, reverse = false, targetContainer?: HTMLElement,
prevTargets: AppMediaViewer['prevTargets'] = [], nextTargets: AppMediaViewer['prevTargets'] = [], needLoadMore = true) {
////////this.log('openMedia doc:', message, prevTarget, nextTarget);
if(this.setMoverPromise) return this.setMoverPromise;
this.log('openMedia doc:', message);
const media = message.media.photo || message.media.document || message.media.webpage.document || message.media.webpage.photo;
const isVideo = media.mime_type == 'video/mp4';
@ -652,7 +702,11 @@ export class AppMediaViewer { @@ -652,7 +702,11 @@ export class AppMediaViewer {
this.content.caption.innerHTML = '';
}
let oldAvatar = this.author.avatarEl;
// @ts-ignore
this.author.avatarEl = this.author.avatarEl.cloneNode();
this.author.avatarEl.setAttribute('peer', '' + message.fromID);
oldAvatar.parentElement.replaceChild(this.author.avatarEl, oldAvatar);
// ok set
@ -675,18 +729,17 @@ export class AppMediaViewer { @@ -675,18 +729,17 @@ export class AppMediaViewer {
const size = appPhotosManager.setAttachmentSize(isVideo ? media : media.id, container, maxWidth, maxHeight);
// need after setAttachmentSize
if(useContainerAsTarget) {
/* if(useContainerAsTarget) {
target = target.querySelector('img, video') || target;
}
} */
let setMoverPromise: Promise<void>;
if(isVideo) {
////////this.log('will wrap video', media, size);
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
setMoverPromise = this.setMoverToTarget(target, false, fromRight).then(() => {
//return; // set and don't move
//if(wasActive) return;
setTimeout(() => {
afterTimeout();
//return;
let video = mover.querySelector('video') || document.createElement('video');
@ -694,6 +747,7 @@ export class AppMediaViewer { @@ -694,6 +747,7 @@ export class AppMediaViewer {
if(media.type == 'gif') {
video.autoplay = true;
video.loop = true;
}
let createPlayer = () => {
@ -704,88 +758,112 @@ export class AppMediaViewer { @@ -704,88 +758,112 @@ export class AppMediaViewer {
let player = new VideoPlayer(video, true);
/* player.wrapper.parentElement.append(video);
mover.append(player.wrapper); */
} else {
video.play();
}
};
if(!source || !source.src) {
let promise = appDocsManager.downloadDoc(media);
this.preloader.attach(mover, true, promise);
promise.then(() => {
if(this.currentMessageID != message.mid) {
this.log.warn('media viewer changed video');
return;
}
let url = media.url;
if(target instanceof SVGSVGElement) {
this.updateMediaSource(mover, url, 'source');
this.updateMediaSource(target, url, 'source');
} else {
let div = mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement;
if(image instanceof HTMLImageElement) {
image.remove();
let load = () => {
let promise = appDocsManager.downloadDoc(media);
this.preloader.attach(mover, true, promise);
promise.then(() => {
if(this.currentMessageID != message.mid) {
this.log.warn('media viewer changed video');
return;
}
renderImageFromUrl(source, url);
source.type = media.mime_type;
if(!source.parentElement) {
video.append(source);
let url = media.url;
if(target instanceof SVGSVGElement) {
this.updateMediaSource(mover, url, 'source');
this.updateMediaSource(target, url, 'source');
} else {
let div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement;
if(image instanceof HTMLImageElement) {
image.remove();
}
renderImageFromUrl(source, url);
source.type = media.mime_type;
if(!source.parentElement) {
video.append(source);
}
if(!video.parentElement) {
div.prepend(video);
}
}
createPlayer();
});
if(!video.parentElement) {
div.prepend(video);
}
}
return promise;
};
createPlayer();
this.lazyLoadQueue.unshift({
div: null,
load,
wasSeen: true
});
} else createPlayer();
}, 0);
});
} else {
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
setMoverPromise = this.setMoverToTarget(target, false, fromRight).then(() => {
//return; // set and don't move
//if(wasActive) return;
setTimeout(() => {
afterTimeout();
//return;
this.preloader.attach(mover);
let cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
cancellablePromise.then(() => {
if(this.currentMessageID != message.mid) {
this.log.warn('media viewer changed photo');
return;
}
///////this.log('indochina', blob);
let url = media.url;
if(target instanceof SVGSVGElement) {
this.updateMediaSource(target, url, 'img');
this.updateMediaSource(mover, url, 'img');
} else {
let div = mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement;
if(!image || image.tagName != 'IMG') {
image = new Image();
let load = () => {
let cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
this.preloader.attach(mover, true, cancellablePromise);
cancellablePromise.then(() => {
if(this.currentMessageID != message.mid) {
this.log.warn('media viewer changed photo');
return;
}
///////this.log('indochina', blob);
let url = media.url;
if(target instanceof SVGSVGElement) {
this.updateMediaSource(target, url, 'img');
this.updateMediaSource(mover, url, 'img');
} else {
let div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement;
if(!image || image.tagName != 'IMG') {
image = new Image();
}
//this.log('will renderImageFromUrl:', image, div, target);
renderImageFromUrl(image, url).then(() => {
div.append(image);
});
}
this.preloader.detach();
}).catch(err => {
this.log.error(err);
});
//this.log('will renderImageFromUrl:', image, div, target);
renderImageFromUrl(image, url).then(() => {
div.append(image);
});
}
return cancellablePromise;
};
this.preloader.detach();
}).catch(err => {
this.log.error(err);
this.lazyLoadQueue.unshift({
div: null,
load,
wasSeen: true
});
}, 0);
});
}
return this.setMoverPromise = setMoverPromise.then(() => {
this.setMoverPromise = null;
});
}
}

104
src/lib/appManagers/appMessagesManager.ts

@ -1844,42 +1844,21 @@ export class AppMessagesManager { @@ -1844,42 +1844,21 @@ export class AppMessagesManager {
case 'messageMediaDocument':
let document = message.media.document;
let found = false;
for(let attribute of document.attributes) {
if(found) break;
switch(attribute._) {
case 'documentAttributeSticker':
messageText += RichTextProcessor.wrapRichText(attribute.alt) + '<i>Sticker</i>';
found = true;
break;
case 'documentAttributeFilename':
messageText += '<i>' + attribute.file_name + '</i>';
found = true;
break;
/* default:
console.warn('Got unknown document type!', message);
break; */
}
}
if(document.type == 'video') {
messageText = '<i>Video' + (message.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'voice') {
messageText = '<i>Voice message</i>';
found = true;
} else if(document.type == 'gif') {
messageText = '<i>GIF' + (message.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'round') {
messageText = '<i>Video message' + (message.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'sticker') {
messageText = (document.stickerEmoji || '') + '<i>Sticker</i>';
} else {
messageText = '<i>' + document.file_name + '</i>';
}
if(found) {
break;
}
break;
default:
///////console.warn('Got unknown message.media type!', message);
@ -2537,6 +2516,77 @@ export class AppMessagesManager { @@ -2537,6 +2516,77 @@ export class AppMessagesManager {
}
}
public deleteMessages(messageIDs: number[], revoke: boolean) {
const splitted = appMessagesIDsManager.splitMessageIDsByChannels(messageIDs);
const promises: Promise<any>[] = [];
for(const channelIDStr in splitted.msgIDs) {
const channelID = +channelIDStr;
let msgIDs = splitted.msgIDs[channelID];
let promise: Promise<any>;
if(channelID > 0) {
const channel = appChatsManager.getChat(channelID);
if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) {
const goodMsgIDs: number[] = [];
if (channel.pFlags.editor || channel.pFlags.megagroup) {
msgIDs.forEach((msgID, i) => {
const message = this.getMessage(splitted.mids[channelID][i]);
if(message.pFlags.out) {
goodMsgIDs.push(msgID);
}
});
}
if(!goodMsgIDs.length) {
return;
}
msgIDs = goodMsgIDs;
}
promise = apiManager.invokeApi('channels.deleteMessages', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
}).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteChannelMessages',
channel_id: channelID,
messages: msgIDs,
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
} else {
let flags = 0;
if(revoke) {
flags |= 1;
}
promise = apiManager.invokeApi('messages.deleteMessages', {
flags: flags,
id: msgIDs
}).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: msgIDs,
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
}
promises.push(promise);
}
return Promise.all(promises);
}
public readHistory(peerID: number, maxID = 0, readLength = 0): Promise<boolean> {
// console.trace('start read')
const isChannel = appPeersManager.isChannel(peerID);
@ -2590,7 +2640,7 @@ export class AppMessagesManager { @@ -2590,7 +2640,7 @@ export class AppMessagesManager {
index = historyStorage.history.indexOf(maxID);
}
let readedLength = 0;
let readedLength = 1;
if(historyStorage.history.length && maxID) {
for(let i = index == -1 ? 0 : index, length = historyStorage.history.length; i < length; i++) {

57
src/lib/appManagers/appSidebarLeft.ts

@ -5,7 +5,7 @@ import appImManager from "./appImManager"; @@ -5,7 +5,7 @@ import appImManager from "./appImManager";
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import AppSearch, { SearchGroup } from "../../components/appSearch";
import { horizontalMenu, putPreloader } from "../../components/misc";
import { horizontalMenu, putPreloader, parseMenuButtonsTo } from "../../components/misc";
import appUsersManager from "./appUsersManager";
import Scrollable from "../../components/scrollable_new";
import appPhotosManager from "./appPhotosManager";
@ -273,10 +273,14 @@ class AppContactsTab implements SliderTab { @@ -273,10 +273,14 @@ class AppContactsTab implements SliderTab {
public onCloseAfterTimeout() {
this.list.innerHTML = '';
this.input.value = '';
}
public openContacts(query?: string) {
appSidebarLeft.selectTab(SLIDERITEMSIDS.contacts);
if(appSidebarLeft.historyTabIDs.indexOf(SLIDERITEMSIDS.contacts) === -1) {
appSidebarLeft.selectTab(SLIDERITEMSIDS.contacts);
}
if(this.promise) return this.promise;
this.scrollable.onScrolledBottom = null;
@ -288,6 +292,9 @@ class AppContactsTab implements SliderTab { @@ -288,6 +292,9 @@ class AppContactsTab implements SliderTab {
return;
}
contacts = contacts.slice();
contacts.findAndSplice(u => u == $rootScope.myID);
let sorted = contacts
.map(userID => {
let user = appUsersManager.getUser(userID);
@ -340,11 +347,7 @@ class AppSettingsTab implements SliderTab { @@ -340,11 +347,7 @@ class AppSettingsTab implements SliderTab {
} = {} as any;
constructor() {
(Array.from(this.container.querySelector('.profile-buttons').children) as HTMLButtonElement[]).forEach(el => {
let name = el.className.match(/ menu-(.+?) /)[1];
// @ts-ignore
this.buttons[name] = el;
});
parseMenuButtonsTo(this.buttons, this.container.querySelector('.profile-buttons').children);
$rootScope.$on('user_auth', (e: CustomEvent) => {
this.fillElements();
@ -569,19 +572,22 @@ class AppSidebarLeft { @@ -569,19 +572,22 @@ class AppSidebarLeft {
private searchInput = document.getElementById('global-search') as HTMLInputElement;
private menuEl = this.toolsBtn.querySelector('.btn-menu');
private newGroupBtn = this.menuEl.querySelector('.menu-new-group');
private contactsBtn = this.menuEl.querySelector('.menu-contacts');
private archivedBtn = this.menuEl.querySelector('.menu-archive');
private savedBtn = this.menuEl.querySelector('.menu-saved');
private settingsBtn = this.menuEl.querySelector('.menu-settings');
public archivedCount = this.archivedBtn.querySelector('.archived-count') as HTMLSpanElement;
private buttons: {
newGroup: HTMLButtonElement,
contacts: HTMLButtonElement,
archived: HTMLButtonElement,
saved: HTMLButtonElement,
settings: HTMLButtonElement,
help: HTMLButtonElement
} = {} as any;
public archivedCount: HTMLSpanElement;
private newBtnMenu = this.sidebarEl.querySelector('#new-menu');
private newButtons = {
channel: this.newBtnMenu.querySelector('.menu-channel'),
group: this.newBtnMenu.querySelector('.menu-group'),
privateChat: this.newBtnMenu.querySelector('.menu-private-chat'),
};
private newButtons: {
channel: HTMLButtonElement,
group: HTMLButtonElement,
privateChat: HTMLButtonElement,
} = {} as any;
public newChannelTab = new AppNewChannelTab();
public addMembersTab = new AppAddMembersTab();
@ -620,7 +626,12 @@ class AppSidebarLeft { @@ -620,7 +626,12 @@ class AppSidebarLeft {
this.searchGroups.people.container.append(peopleContainer);
let peopleScrollable = new Scrollable(peopleContainer, 'x');
this.savedBtn.addEventListener('click', (e) => {
parseMenuButtonsTo(this.buttons, this.menuEl.children);
parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
this.archivedCount = this.buttons.archived.querySelector('.archived-count') as HTMLSpanElement;
this.buttons.saved.addEventListener('click', (e) => {
///////this.log('savedbtn click');
setTimeout(() => { // menu doesn't close if no timeout (lol)
let dom = appDialogsManager.getDialogDom(appImManager.myID);
@ -628,15 +639,15 @@ class AppSidebarLeft { @@ -628,15 +639,15 @@ class AppSidebarLeft {
}, 0);
});
this.archivedBtn.addEventListener('click', (e) => {
this.buttons.archived.addEventListener('click', (e) => {
this.selectTab(SLIDERITEMSIDS.archived);
});
this.contactsBtn.addEventListener('click', (e) => {
this.buttons.contacts.addEventListener('click', (e) => {
this.contactsTab.openContacts();
});
this.settingsBtn.addEventListener('click', () => {
this.buttons.settings.addEventListener('click', () => {
this.settingsTab.fillElements();
this.selectTab(SLIDERITEMSIDS.settings);
});
@ -676,7 +687,7 @@ class AppSidebarLeft { @@ -676,7 +687,7 @@ class AppSidebarLeft {
this.selectTab(SLIDERITEMSIDS.newChannel);
});
[this.newButtons.group, this.newGroupBtn].forEach(btn => {
[this.newButtons.group, this.buttons.newGroup].forEach(btn => {
btn.addEventListener('click', (e) => {
this.addMembersTab.init(0, 'chat', false, (peerIDs) => {
this.newGroupTab.init(peerIDs);

7
src/lib/appManagers/appSidebarRight.ts

@ -175,7 +175,7 @@ class AppSidebarRight { @@ -175,7 +175,7 @@ class AppSidebarRight {
appImManager.mutePeer(this.peerID);
});
if(testScroll) {
if(testScroll && false) {
let div = document.createElement('div');
for(let i = 0; i < 500; ++i) {
//div.insertAdjacentHTML('beforeend', `<div style="background-image: url(assets/img/camomile.jpg);"></div>`);
@ -713,9 +713,8 @@ class AppSidebarRight { @@ -713,9 +713,8 @@ class AppSidebarRight {
setText(appPeersManager.getPeerUsername(peerID), this.profileElements.username);
}
let dialog: any = appMessagesManager.getDialogByPeerID(peerID);
if(dialog.length) {
dialog = dialog[0];
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) {
let muted = false;
if(dialog.notify_settings && dialog.notify_settings.mute_until) {
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();

8
src/lib/appManagers/appStickersManager.ts

@ -68,7 +68,7 @@ class AppStickersManager { @@ -68,7 +68,7 @@ class AppStickersManager {
}
//if(!this.stickerSets['emoji']) {
this.getStickerSet({id: 'emoji', access_hash: ''});
this.getStickerSet({id: 'emoji', access_hash: ''}, {overwrite: true});
//}
});
}
@ -95,8 +95,10 @@ class AppStickersManager { @@ -95,8 +95,10 @@ class AppStickersManager {
public async getStickerSet(set: {
id: string,
access_hash: string
}) {
if(this.stickerSets[set.id]) return this.stickerSets[set.id];
}, params: Partial<{
overwrite: boolean
}> = {}) {
if(this.stickerSets[set.id] && !params.overwrite) return this.stickerSets[set.id];
let promise = apiManager.invokeApi('messages.getStickerSet', {
stickerset: set.id == 'emoji' ? {

6
src/lib/lottieLoader.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { isInDOM } from "./utils";
//import { isInDOM } from "./utils";
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d";
let convert = (value: number) => {
@ -85,7 +85,7 @@ class LottieLoader { @@ -85,7 +85,7 @@ class LottieLoader {
for(let i = length - 1; i >= 0; --i) {
let {animation, container, paused, autoplay, canvas} = animations[i];
if(destroy && !isInDOM(container)) {
if(destroy && !container.parentElement/* !isInDOM(container) */) {
this.debug && console.log('destroy animation');
animation.destroy();
animations.splice(i, 1);
@ -140,7 +140,7 @@ class LottieLoader { @@ -140,7 +140,7 @@ class LottieLoader {
k[2] = (foundReplacement[1] & 255) / 255;
}
console.log('foundReplacement!', foundReplacement, color.toString(16), k);
//console.log('foundReplacement!', foundReplacement, color.toString(16), k);
break;
}

2
src/pages/pageSignIn.ts

@ -206,7 +206,7 @@ let onFirstMount = () => { @@ -206,7 +206,7 @@ let onFirstMount = () => {
putPreloader(this);
//this.innerHTML = 'PLEASE WAIT...';
return;
//return;
let phone_number = telEl.value;
apiManager.invokeApi('auth.sendCode', {

75
src/scss/partials/_mediaViewer.scss

@ -74,49 +74,39 @@ $move-duration: .35s; @@ -74,49 +74,39 @@ $move-duration: .35s;
max-height: 100%;
max-width: 100%;
overflow: hidden;
}
.media-viewer-stub {
flex: 1;
}
.media-viewer-container {
align-self: center;
position: relative;
max-width: 100%;
max-height: 100%;
overflow: hidden;
flex: 1 1 auto;
display: flex;
align-items: center;
}
&-stub {
flex: 1;
}
.media-viewer-media {
display: flex;
align-items: center;
justify-content: center;
visibility: hidden;
}
&-container {
align-self: center;
position: relative;
max-width: 100%;
max-height: 100%;
overflow: hidden;
flex: 1 1 auto;
display: flex;
align-items: center;
}
img, video {
max-height: calc(100vh - 100px);
max-width: calc(100vw - 16px);
/* max-height: 720px;
max-width: 1280px; */
}
&-media {
visibility: hidden;
}
.media-viewer-caption {
flex: 1;
text-align: center;
color: $color-gray;
transition: $open-duration;
max-width: 50vw;
word-break: break-word;
overflow: hidden;
text-overflow: ellipsis;
&-caption {
flex: 1;
text-align: center;
color: $color-gray;
transition: $open-duration;
max-width: 50vw;
word-break: break-word;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
color: #fff;
}
&:hover {
color: #fff;
}
}
@ -188,6 +178,7 @@ $move-duration: .35s; @@ -188,6 +178,7 @@ $move-duration: .35s;
height: 100%;
user-select: none;
object-fit: cover;
opacity: 1;
}
&.active {
@ -197,6 +188,13 @@ $move-duration: .35s; @@ -197,6 +188,13 @@ $move-duration: .35s;
&.moving {
transition: $move-duration transform ease;
}
&.hiding {
img, video {
transition: $open-duration opacity;
opacity: 0;
}
}
}
// возможно тут это вообще не нужно
@ -205,6 +203,7 @@ $move-duration: .35s; @@ -205,6 +203,7 @@ $move-duration: .35s;
height: 100%;
transform: scale(1);
overflow: hidden;
position: absolute;
}
&-mover.active &-aspecter {

31
src/scss/partials/_rightSIdebar.scss

@ -30,6 +30,10 @@ @@ -30,6 +30,10 @@
}
}
#forward-container {
z-index: 5;
}
.sidebar-search {
display: none;
@ -41,7 +45,7 @@ @@ -41,7 +45,7 @@
.profile {
&-content {
flex: 1 1 auto;
/* flex: 1 1 auto; */
display: flex;
flex-direction: column;
/* height: 100%; */
@ -54,7 +58,7 @@ @@ -54,7 +58,7 @@
}
&-wrapper {
flex: 0 0 auto;
flex: 1 1 auto;
display: flex;
flex-direction: column;
margin-bottom: 36px;
@ -64,16 +68,18 @@ @@ -64,16 +68,18 @@
width: 100%;
max-width: 100%;
//overflow: hidden;
flex: 1 1 auto;
position: relative;
//height: 1%; // fix safari
position: absolute;
top: 100%;
min-height: calc(100vh - 100% - 60px);
display: flex;
flex-direction: column;
}
}
/* &.loaded { // warning
.profile-tabs-content {
position: relative;
min-height: auto;
}
} */
&-container {
> .scrollable {
display: flex;
flex-direction: column;
}
}
@ -159,12 +165,13 @@ @@ -159,12 +165,13 @@
position: -webkit-sticky !important;
position: sticky !important;
top: 0;
z-index: 3;
z-index: 2;
background-color: #fff;
&-content {
//min-height: 100%;
min-height: calc(100% - 49px);
flex: 1 1 auto;
//position: absolute; // FIX THE SAFARI!
//position: relative;
/* width: 500%;

4
src/scss/partials/_scrollable.scss

@ -26,8 +26,8 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -26,8 +26,8 @@ div.scrollable::-webkit-scrollbar-thumb {
//position: relative;
//will-change: transform;
transform: translateZ(0);
-webkit-transform: translateZ(0);
/* transform: translateZ(0);
-webkit-transform: translateZ(0); */
position: absolute;
top: 0px;

1
src/scss/partials/popups/_peer.scss

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
font-size: 1.25rem;
font-weight: 500;
margin-bottom: 0.125rem;
text-transform: capitalize;
}
&-description {

2
src/scss/style.scss

@ -534,7 +534,7 @@ avatar-element { @@ -534,7 +534,7 @@ avatar-element {
}
&-download {
z-index: 2;
z-index: 1;
align-items: center;
font-size: 24px;
cursor: pointer;

2
webpack.common.js

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '192.168.0.111'];
let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '192.168.0.111', '192.168.0.105'];
const opts = {
MTPROTO_WORKER: true,

Loading…
Cancel
Save