diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index 8379c552..915017ed 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -106,7 +106,7 @@ export default class LazyLoadQueue { let {div} = this.lazyLoadMedia[i]; if(isElementInViewport(div)) { - console.log('will load div:', div); + //console.log('will load div:', div); this.lazyLoadMedia[i].wasSeen = true; this.processQueue(i); //this.lazyLoadMedia.splice(i, 1); diff --git a/src/components/pageIm.ts b/src/components/pageIm.ts index d726b98e..5b845af8 100644 --- a/src/components/pageIm.ts +++ b/src/components/pageIm.ts @@ -70,18 +70,20 @@ export default () => import('../lib/services').then(services => { console.log('updating dialog:', dialog); + ++performed; + if(!(dialog.peerID in appDialogsManager.doms)) { appDialogsManager.addDialog(dialog); continue; } appDialogsManager.setLastMessage(dialog); - - ++performed; } if(performed) { + console.log('will sortDom'); appDialogsManager.sortDom(); + appDialogsManager.sortDom(true); } }); diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index ae71ba41..5292a6d4 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -108,10 +108,17 @@ export function wrapVideo(this: any, doc: MTDocument, container: HTMLDivElement, container.append(video); - if(!justLoader) { + if(!justLoader || round) { video.dataset.ckin = round ? 'circle' : 'default'; video.dataset.overlay = '1'; - wrapPlayer(video); + let wrapper = wrapPlayer(video); + + if(!round) { + (wrapper.querySelector('.toggle') as HTMLButtonElement).click(); + } + } else if(doc.type == 'gif') { + video.autoplay = true; + video.loop = true; } //container.style.width = ''; diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index a522e4c9..d7940250 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -61,9 +61,9 @@ export class AppDialogsManager { return; } - if(this.lastActiveListElement) { + /* if(this.lastActiveListElement) { this.lastActiveListElement.classList.remove('active'); - } + } */ if(elem) { /* if(chatClosedDiv) { diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 54673bef..8fe0c1b5 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -1,5 +1,5 @@ import apiManager from '../mtproto/apiManager'; -import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, calcImageInBox } from "../utils"; +import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, calcImageInBox, findUpTag } from "../utils"; import appUsersManager from "./appUsersManager"; import appMessagesManager from "./appMessagesManager"; import appPeersManager from "./appPeersManager"; @@ -435,6 +435,7 @@ export class AppImManager { public subtitleEl = document.getElementById('im-subtitle') as HTMLDivElement; public chatInner = document.getElementById('bubbles-inner') as HTMLDivElement; public searchBtn = this.pageEl.querySelector('.chat-search-button') as HTMLButtonElement; + public goDownBtn = this.pageEl.querySelector('#bubbles-go-down') as HTMLButtonElement; public firstContainerDiv: HTMLDivElement; public lastContainerDiv: HTMLDivElement; private getHistoryPromise: Promise; @@ -664,6 +665,8 @@ export class AppImManager { if(!bubble) return; + if(['IMG', 'VIDEO', 'SVG', 'DIV'].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV'); + /* if(target.tagName == 'VIDEO' && bubble.classList.contains('round')) { let video = target as HTMLVideoElement; video.currentTime = 0; @@ -678,6 +681,24 @@ export class AppImManager { } */ if(target.tagName == 'DIV') { + if(target.classList.contains('forward')) { + let savedFrom = bubble.dataset.savedFrom; + let splitted = savedFrom.split('_'); + let peerID = +splitted[0]; + let msgID = +splitted[1]; + this.log('savedFrom', peerID, msgID); + this.setPeer(peerID, msgID, true); + return; + } else if(target.classList.contains('user-avatar') || target.classList.contains('name')) { + let peerID = +target.dataset.peerID; + + if(!isNaN(peerID)) { + this.setPeer(peerID); + } + + return; + } + let isReplyClick = false; try { @@ -690,6 +711,12 @@ export class AppImManager { } } else if(bubble.classList.contains('round')) { + } else if(target.tagName == 'IMG' && target.parentElement.classList.contains('user-avatar')) { + let peerID = +target.parentElement.dataset.peerID; + + if(!isNaN(peerID)) { + this.setPeer(peerID); + } } else if((target.tagName == 'IMG' && !target.classList.contains('emoji')) || target.tagName == 'VIDEO') { let messageID = +target.getAttribute('message-id'); let message = appMessagesManager.getMessage(messageID); @@ -888,6 +915,14 @@ export class AppImManager { this.deleteMessages(false); this.popupDeleteMessage.cancelBtn.click(); }); + + this.goDownBtn.addEventListener('click', () => { + if(this.lastDialog) { + this.setPeer(this.peerID, this.lastDialog.top_message); + } else { + this.scroll.scrollTop = this.scroll.scrollHeight; + } + }); this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3); this.updateStatus(); @@ -1056,7 +1091,8 @@ export class AppImManager { let willLoad = false; if(!this.scrolledAll) { - for(let i = 0; i < 10; ++i) { + let length = history.length < 10 ? history.length : 10; + for(let i = 0; i < length; ++i) { let msgID = history[i]; let bubble = this.bubbles[msgID]; @@ -1117,6 +1153,7 @@ export class AppImManager { this.scroll = scroll; this.scrollPosition = new ScrollPosition(this.chatInner); this.scroll.addEventListener('scroll', this.onScroll.bind(this)); + this.scroll.parentElement.classList.add('scrolled-down'); } public setPeerStatus() { @@ -1242,10 +1279,10 @@ export class AppImManager { //appSidebarRight.minMediaID = {}; } - public setPeer(peerID: number, lastMsgID = 0) { + public setPeer(peerID: number, lastMsgID = 0, forwarding = false) { if(peerID == 0) { appSidebarRight.toggleSidebar(false); - this.topbar.style.display = this.chatInput.style.display = 'none'; + this.topbar.style.display = this.chatInput.style.display = this.goDownBtn.style.display = 'none'; this.cleanup(); return Promise.resolve(false); } @@ -1285,9 +1322,12 @@ export class AppImManager { this.preloader.attach(this.chatInner); let dialog = this.lastDialog = appMessagesManager.getDialogByPeerID(this.peerID)[0] || null; - this.log('setPeer peerID:', this.peerID, dialog); + this.log('setPeer peerID:', this.peerID, dialog, lastMsgID); appDialogsManager.loadDialogPhoto(this.avatarEl, this.peerID); appDialogsManager.loadDialogPhoto(appSidebarRight.profileElements.avatar, this.peerID); + if(appDialogsManager.lastActiveListElement) { + appDialogsManager.lastActiveListElement.classList.remove('active'); + } this.firstTopMsgID = dialog ? dialog.top_message : 0; @@ -1305,7 +1345,7 @@ export class AppImManager { //this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = dom.titleSpan.innerHTML; this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = appPeersManager.getPeerTitle(this.peerID); - this.topbar.style.display = ''; + this.topbar.style.display = this.goDownBtn.style.display = ''; appSidebarRight.toggleSidebar(true); this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : ''; @@ -1317,14 +1357,21 @@ export class AppImManager { } return Promise.all([ - this.getHistory(lastMsgID).then(() => { + this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID).then(() => { this.log('setPeer removing preloader'); if(lastMsgID) { - this.renderMessage(appMessagesManager.getMessage(lastMsgID)); + if(!forwarding) { + let message = appMessagesManager.getMessage(lastMsgID); + this.log('setPeer render last message:', message, lastMsgID); + this.renderMessage(message); + } if(!dialog || lastMsgID != dialog.top_message) { - this.bubbles[lastMsgID].scrollIntoView(); + let bubble = this.bubbles[lastMsgID]; + + if(bubble) this.bubbles[lastMsgID].scrollIntoView(); + else this.log.warn('no bubble by lastMsgID:', lastMsgID); } else { this.scroll.scrollTop = this.scroll.scrollHeight; } @@ -1418,6 +1465,8 @@ export class AppImManager { } public renderMessage(message: any, reverse = false, multipleRender?: boolean, bubble: HTMLDivElement = null) { + if(message.deleted) return; + let peerID = this.peerID; let our = message.fromID == this.myID; @@ -1694,7 +1743,7 @@ export class AppImManager { } bubble.classList.add('video'); - wrapVideo.call(this, doc, attachmentDiv, message, doc.type != 'round', null, false, doc.type == 'round'); + wrapVideo.call(this, doc, attachmentDiv, message, true, null, false, doc.type == 'round'); break; } else { @@ -1736,10 +1785,25 @@ export class AppImManager { if(message.fwdFromID || message.fwd_from) { bubble.classList.add('forwarded'); + if(message.savedFrom) { + let fwd = document.createElement('div'); + fwd.classList.add('forward'/* , 'tgico-forward' */); + fwd.innerHTML = ` + + + + + + `; + bubble.append(fwd); + bubble.dataset.savedFrom = message.savedFrom; + } + if(!bubble.classList.contains('sticker')) { let nameDiv = document.createElement('div'); nameDiv.classList.add('name'); nameDiv.innerHTML = 'Forwarded from ' + title; + nameDiv.dataset.peerID = message.fwdFromID; //nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false); bubble.append(nameDiv); } @@ -1789,7 +1853,7 @@ export class AppImManager { if(originalMessage.mid) { bubble.setAttribute('data-original-mid', originalMessage.mid); - } + } bubble.append(box); bubble.classList.add('is-reply'); @@ -1812,6 +1876,7 @@ export class AppImManager { nameDiv.classList.add('name'); nameDiv.innerHTML = title; nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false); + nameDiv.dataset.peerID = message.fromID; bubble.append(nameDiv); } else if(!message.reply_to_mid) { bubble.classList.add('hide-name'); @@ -1838,6 +1903,8 @@ export class AppImManager { appDialogsManager.loadDialogPhoto(avatarDiv, title); } + + avatarDiv.dataset.peerID = message.fromID; bubble.append(avatarDiv); } diff --git a/src/lib/appManagers/appMediaViewer.ts b/src/lib/appManagers/appMediaViewer.ts index 33034ab4..80afc642 100644 --- a/src/lib/appManagers/appMediaViewer.ts +++ b/src/lib/appManagers/appMediaViewer.ts @@ -225,7 +225,9 @@ export class AppMediaViewer { let index = history.findIndex(m => m == message.mid); let comparer = (mid: number) => { let _message = appMessagesManager.getMessage(mid); - if(_message.media && _message.media.photo) return true; + let media = _message.media; + + if(media && (media.photo || (media.document && ['video', 'gif'].indexOf(media.document.type) !== -1))) return true; return false; }; diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 346293aa..8ebfe99e 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -1034,10 +1034,11 @@ export class AppMessagesManager { apiMessage.fromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id; } else { - apiMessage.fwdFromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id; apiMessage.fwdPostID = fwdHeader.channel_post; } + apiMessage.fwdFromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id; + fwdHeader.date -= serverTimeManager.serverTimeOffset; } diff --git a/src/lib/appManagers/appWebpManager.ts b/src/lib/appManagers/appWebpManager.ts index a36bd35f..83165b20 100644 --- a/src/lib/appManagers/appWebpManager.ts +++ b/src/lib/appManagers/appWebpManager.ts @@ -7,6 +7,7 @@ class AppWebpManager { public busyPromise: Promise; public queue: {bytes: Uint8Array, img: HTMLImageElement}[] = []; //public worker: any; + public webpSupport: Promise = null; constructor() { //let canvas = document.createElement('canvas'); @@ -16,21 +17,25 @@ class AppWebpManager { console.log('got message from worker:', e, canvas.toDataURL()); }); */ - this.loaded = new Promise((resolve, reject) => { - (window as any).webpLoaded = () => { - console.log('webpHero loaded'); - this.webpMachine = new (window as any).WebpMachine(); - resolve(); - }; - - let sc = document.createElement('script'); - sc.src = 'webp.bundle.js'; - sc.async = true; - sc.onload = (window as any).webpLoaded; - - document.body.appendChild(sc); - - resolve(); + this.webpSupported().then(res => { + this.loaded = new Promise((resolve, reject) => { + if(!res) { + (window as any).webpLoaded = () => { + console.log('webpHero loaded'); + this.webpMachine = new (window as any).WebpMachine(); + resolve(); + }; + + let sc = document.createElement('script'); + sc.src = 'webp.bundle.js'; + sc.async = true; + sc.onload = (window as any).webpLoaded; + + document.body.appendChild(sc); + } else { + resolve(); + } + }); }); } @@ -56,11 +61,23 @@ class AppWebpManager { } } + webpSupported() { + return this.webpSupport = new Promise((resolve, reject) => { + var webP = new Image(); + webP.src = 'data:image/webp;base64,UklGRi4AAABXRUJQVlA4TCEAAAAvAUAAEB8wAiMw' + + 'AgSSNtse/cXjxyCCmrYNWPwmHRH9jwMA'; + webP.onload = webP.onerror = () => { + resolve(webP.height === 2); + }; + }); + } + async polyfillImage(img: HTMLImageElement, blob: Blob) { /* console.log('polyfillImage', this); return this.webpMachine.polyfillImage(image); */ - if(await this.webpMachine.webpSupport) { + //if(await this.webpMachine.webpSupport) { + if(await this.webpSupport) { img.src = URL.createObjectURL(blob); return; } diff --git a/src/lib/ckin.js b/src/lib/ckin.js index 6e600c35..070239c6 100644 --- a/src/lib/ckin.js +++ b/src/lib/ckin.js @@ -173,6 +173,8 @@ export function wrapPlayer(video) { wrapper.appendChild(video); stylePlayer(wrapper, video); + + return wrapper; } function buildControls(skin) { diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index a53b3435..a18cb806 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -110,10 +110,18 @@ flex: 1 1 auto; /* Lets middle column shrink/grow to available width */ overflow: hidden; position: relative; + + > .scrollable { + position: unset; + } &:not(.scrolled-down) { -webkit-mask-image: -webkit-linear-gradient(bottom, transparent, #000 20px); mask-image: linear-gradient(0deg, transparent 0, #000 20px); + + #bubbles-go-down { + opacity: 1; + } } .preloader { @@ -144,6 +152,30 @@ } } } + + #bubbles-go-down { + position: absolute; + //opacity: 0; + background-color: #fff; + border-radius: 50%; + width: 3.25rem; + height: 3.25rem; + color: $placeholder-color; + font-size: 30px; + display: flex; + align-items: center; + justify-content: center; + right: 17.5px; + bottom: 17.5px; + cursor: pointer; + opacity: 0; + transition: .2s opacity; + user-select: none; + + &:before { + margin-left: .75px; + } + } .service { justify-content: center; @@ -172,6 +204,37 @@ font-size: 0; width: max-content; height: fit-content; + + &.forwarded { + .forward { + opacity: 0; + position: absolute; + right: -46px; + bottom: 0; + width: 38px; + height: 38px; + font-size: 1.5rem; + align-items: center; + display: flex; + justify-content: center; + color: #fff; + border-radius: 50%; + background: rgba(0, 0, 0, 0.16); + cursor: pointer; + transition: .2s opacity; + + svg { + width: 20px; + height: 20px; + } + } + } + + &:hover { + .forward { + opacity: 1; + } + } &.photo, &.video { width: min-content; @@ -426,12 +489,14 @@ } .message { - font-size: 1rem; - padding: 0 .6rem .2675rem .6rem; + font-size: 16px; + //padding: 0 .6rem .2675rem .6rem; + padding: 0 .6rem 6px .6rem; overflow: hidden; text-overflow: ellipsis; max-width: 100%; color: #000; + line-height: 21px; * { overflow: hidden; @@ -439,7 +504,8 @@ } &:last-child { - padding-top: .2675rem; + //padding-top: .2675rem; + padding-top: 6px; } &.message-empty { @@ -498,13 +564,13 @@ width: calc(5rem + 42px); } - &.is-edited .time { - width: 90px; - } - &.channel-post .time { width: 5rem; } + + &.is-edited .time { + width: 90px; + } .user-avatar { position: absolute; @@ -514,6 +580,7 @@ line-height: 40px; bottom: 0; font-size: 1rem; + cursor: pointer; } &:not(.forwarded).hide-name, &.emoji-big { @@ -543,17 +610,20 @@ &:not(.webpage) { &.photo, &.video { .name { - padding-bottom: .2675rem; + //padding-bottom: .2675rem; + padding-bottom: 6px; } .message:not(.message-empty) { - padding-top: .2675rem; + //padding-top: .2675rem; + padding-top: 6px; } } } &.hide-name .message:not(.message-empty) { - padding-top: .2675rem; + //padding-top: .2675rem; + padding-top: 6px; } &:not(.sticker):not(.emoji-big):not(.round):last-child:after { @@ -622,6 +692,7 @@ &:last-child { border-radius: 6px 12px 12px 0px; + //border-radius: 12px 12px 12px 0px; &:after { left: -8.4px; @@ -707,6 +778,10 @@ .bubble.is-reply .name { display: none; } + + .bubble.is-edited .time { + width: 85px; + } .bubble { background-color: #eeffde; diff --git a/src/scss/partials/_ckin.scss b/src/scss/partials/_ckin.scss index df057ecd..8fcf33f7 100644 --- a/src/scss/partials/_ckin.scss +++ b/src/scss/partials/_ckin.scss @@ -42,6 +42,11 @@ border-radius: 0 !important; display: -ms-flexbox; display: flex; + + video { + max-height: none; + max-width: none; + } } .default { @@ -290,7 +295,7 @@ video::-webkit-media-controls-enclosure { } .bottom-controls { - padding: 3px 4px 5px 4px; + padding: 3px 4px 0px 4px; //padding: 5px 4px 5px 4px; display: flex; justify-content: space-between; @@ -308,8 +313,8 @@ video::-webkit-media-controls-enclosure { color: #fff; font-size: 13px; float: left; - margin-top: 1px; - } + //margin-top: 1px; +} .circle .circle-time-left { position: absolute; top: 3px;