diff --git a/src/components/appSearch.ts b/src/components/appSearch.ts index b956a2e4..7f3e7ad1 100644 --- a/src/components/appSearch.ts +++ b/src/components/appSearch.ts @@ -19,7 +19,7 @@ export class SearchGroup { this.nameEl.classList.add('search-group__name'); this.nameEl.innerText = name; - this.container.classList.add('search-group'); + this.container.classList.add('search-group', 'search-group-' + type); this.container.append(this.nameEl, this.list); this.container.style.display = 'none'; diff --git a/src/components/chatInput.ts b/src/components/chatInput.ts index 2001cdfc..0300ffd3 100644 --- a/src/components/chatInput.ts +++ b/src/components/chatInput.ts @@ -187,10 +187,11 @@ export class ChatInput { }); let attachFile = (file: File) => { - console.log('selected file:', file, typeof(file)); - - willAttachFile = file; - willAttachObjectURL = ''; + willAttach.file = file; + delete willAttach.objectURL; + delete willAttach.duration; + delete willAttach.width; + delete willAttach.height; this.fileInput.value = ''; @@ -200,29 +201,55 @@ export class ChatInput { this.attachMediaPopUp.mediaContainer.style.height = ''; this.attachMediaPopUp.mediaContainer.classList.remove('is-document'); - if(file.type.indexOf('video/') === 0) { - willAttach = 'document'; - } else if(file.type.indexOf('image/') === -1 && willAttach == 'media') { - willAttach = 'document'; + if(willAttach.type == 'media' && !['image/', 'video/'].find(s => file.type.indexOf(s) === 0)) { + willAttach.type = 'document'; } - switch(willAttach) { - case 'media': { - let img = new Image(); - img.src = willAttachObjectURL = URL.createObjectURL(file); - img.onload = () => { - willAttachWidth = img.naturalWidth; - willAttachHeight = img.naturalHeight; - - let {w, h} = calcImageInBox(willAttachWidth, willAttachHeight, 378, 256); - this.attachMediaPopUp.mediaContainer.style.width = w + 'px'; - this.attachMediaPopUp.mediaContainer.style.height = h + 'px'; - this.attachMediaPopUp.mediaContainer.append(img); - }; - - this.attachMediaPopUp.titleEl.innerText = 'Send Photo'; - this.attachMediaPopUp.container.classList.add('active'); + console.log('selected file:', file, typeof(file), willAttach); + switch(willAttach.type) { + case 'media': { + let isVideo = file.type.indexOf('video/') === 0; + + if(isVideo) { + let video = document.createElement('video'); + let source = document.createElement('source'); + source.src = willAttach.objectURL = URL.createObjectURL(file); + video.autoplay = false; + video.controls = false; + + video.onloadeddata = () => { + willAttach.width = video.videoWidth; + willAttach.height = video.videoHeight; + willAttach.duration = Math.floor(video.duration); + + let {w, h} = calcImageInBox(willAttach.width, willAttach.height, 378, 256); + this.attachMediaPopUp.mediaContainer.style.width = w + 'px'; + this.attachMediaPopUp.mediaContainer.style.height = h + 'px'; + this.attachMediaPopUp.mediaContainer.append(video); + this.attachMediaPopUp.container.classList.add('active'); + }; + + video.append(source); + + this.attachMediaPopUp.titleEl.innerText = 'Send Video'; + } else { + let img = new Image(); + img.src = willAttach.objectURL = URL.createObjectURL(file); + img.onload = () => { + willAttach.width = img.naturalWidth; + willAttach.height = img.naturalHeight; + + let {w, h} = calcImageInBox(willAttach.width, willAttach.height, 378, 256); + this.attachMediaPopUp.mediaContainer.style.width = w + 'px'; + this.attachMediaPopUp.mediaContainer.style.height = h + 'px'; + this.attachMediaPopUp.mediaContainer.append(img); + this.attachMediaPopUp.container.classList.add('active'); + }; + + this.attachMediaPopUp.titleEl.innerText = 'Send Photo'; + } + break; } @@ -231,11 +258,7 @@ export class ChatInput { file: file, file_name: file.name || '', size: file.size, - type: ['image/jpeg', - 'image/png', - 'image/gif', - 'image/webp', - 'image/bmp'].indexOf(file.type) !== -1 ? 'photo' : 'doc' + type: file.type.indexOf('image/') !== -1 ? 'photo' : 'doc' } as any, false, true); this.attachMediaPopUp.titleEl.innerText = 'Send File'; @@ -248,10 +271,17 @@ export class ChatInput { } }; - let willAttach = ''; - let willAttachFile: File = null; - let willAttachObjectURL = ''; - let willAttachWidth = 0, willAttachHeight = 0; + let willAttach: Partial<{ + type: 'media' | 'document', + isMedia: boolean, + file: File, + caption: string, + objectURL: string, + width: number, + height: number, + duration: number + }> = {}; + this.fileInput.addEventListener('change', (e) => { var file = (e.target as HTMLInputElement & EventTarget).files[0]; if(!file) { @@ -262,12 +292,12 @@ export class ChatInput { }, false); this.attachMenu.media.addEventListener('click', () => { - willAttach = 'media'; + willAttach.type = 'media'; this.fileInput.click(); }); this.attachMenu.document.addEventListener('click', () => { - willAttach = 'document'; + willAttach.type = 'document'; this.fileInput.click(); }); @@ -291,7 +321,7 @@ export class ChatInput { //console.log(items[i], file); if(!file) continue; - willAttach = file.type.indexOf('image/') === 0 ? 'media' : "document"; + willAttach.type = file.type.indexOf('image/') === 0 ? 'media' : "document"; attachFile(file); } } @@ -299,15 +329,10 @@ export class ChatInput { this.attachMediaPopUp.sendBtn.addEventListener('click', () => { this.attachMediaPopUp.container.classList.remove('active'); - let caption = this.attachMediaPopUp.captionInput.value; - - appMessagesManager.sendFile(appImManager.peerID, willAttachFile, { - isMedia: true, - caption, - width: willAttachWidth, - height: willAttachHeight, - objectURL: willAttachObjectURL - }); + willAttach.caption = this.attachMediaPopUp.captionInput.value; + willAttach.isMedia = willAttach.type == 'media'; + + appMessagesManager.sendFile(appImManager.peerID, willAttach.file, willAttach); this.onMessageSent(); }); diff --git a/src/components/emoticonsDropdown.ts b/src/components/emoticonsDropdown.ts index ddc99d10..29f08715 100644 --- a/src/components/emoticonsDropdown.ts +++ b/src/components/emoticonsDropdown.ts @@ -118,7 +118,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, //console.log('emoticons sorted:', sorted); - Object.keys(sorted).forEach(c => sorted[c].sort()); + Object.keys(sorted).forEach(c => sorted[c].sort((a, b) => a - b)); categories.pop(); delete sorted["Skin Tones"]; diff --git a/src/components/misc.ts b/src/components/misc.ts index b9a1a1ce..69bc2d75 100644 --- a/src/components/misc.ts +++ b/src/components/misc.ts @@ -151,7 +151,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLDivElement, onCli let tabsChildren = tabs ? Array.from(tabs.firstElementChild.children) : []; let activeInSlide: Set = new Set(); - let selectTab = (id: number) => { + let selectTab = async(id: number) => { if(id == prevId) return false; let p = prevTabContent; @@ -172,10 +172,25 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLDivElement, onCli //content.style.marginLeft = id > 0 ? (-id * 100) + '%' : ''; let toRight = prevId < id; if(prevId != -1) { - content.style.cssText = `width: ${activeInSlide.size * 100}%; will-change: width, transform; transform: translateX(-${100 - 100 / activeInSlide.size}%);`; - - //////console.log('mambo rap setting', toRight); - + //content.classList.remove('animated'); + await new Promise((resolve) => window.requestAnimationFrame(() => { + content.style.cssText = `will-change: width, transform; width: ${activeInSlide.size * 100}%; transform: translateX(-${100 - 100 / activeInSlide.size}%);`; + + content.classList.remove('animated'); + if(toRight) { + content.classList.add('animated'); + } else { + window.requestAnimationFrame(() => { + content.classList.add('animated'); + content.style.transform = ''; + }); + } + + resolve(); + })); + + /* content.style.cssText = `will-change: width, transform; width: ${activeInSlide.size * 100}%; transform: translateX(-${100 - 100 / activeInSlide.size}%);`; + content.classList.remove('animated'); if(toRight) { content.classList.add('animated'); @@ -184,7 +199,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLDivElement, onCli content.classList.add('animated'); content.style.transform = ''; }); - } + } */ } if(hideTimeout) clearTimeout(hideTimeout); diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 99484c66..49bc194e 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -60,13 +60,10 @@ export type MTPhotoSize = { bytes?: Uint8Array // if type == 'i' }; -export function wrapVideo({doc, container, message, justLoader, preloader, round, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue}: { +export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue}: { doc: MTDocument, container: HTMLDivElement, message: any, - justLoader: boolean, - preloader?: ProgressivePreloader, - round: boolean, boxWidth: number, boxHeight: number, withTail?: boolean, @@ -89,10 +86,6 @@ export function wrapVideo({doc, container, message, justLoader, preloader, round container.append(img); } } - - if(!preloader) { - preloader = new ProgressivePreloader(container, true); - } let video = document.createElement('video'); if(withTail) { @@ -112,11 +105,14 @@ export function wrapVideo({doc, container, message, justLoader, preloader, round let loadVideo = () => { let promise = appDocsManager.downloadDoc(doc); - - preloader.attach(container, true, promise); + + if(!doc.downloaded) { + let preloader = new ProgressivePreloader(container, true); + preloader.attach(container, true, promise); + } return promise.then(blob => { - if(!middleware()) { + if(middleware && !middleware()) { return; } @@ -136,57 +132,60 @@ export function wrapVideo({doc, container, message, justLoader, preloader, round container.append(video); } - if(!justLoader || round) { - video.dataset.ckin = round ? 'circle' : 'default'; - video.dataset.overlay = '1'; - let player = new VideoPlayer(video, !round); - } else if(doc.type == 'gif') { + if(doc.type == 'gif') { video.autoplay = true; video.loop = true; + } else if(doc.type == 'round') { + //video.dataset.ckin = doc.type == 'round' ? 'circle' : 'default'; + video.dataset.ckin = 'circle'; + video.dataset.overlay = '1'; + let player = new VideoPlayer(video/* , doc.type != 'round' */); } }); }; - - if(doc.type == 'gif' || true) { // extra fix - return doc.downloaded ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo, wasSeen: true}); - } /* else { // if video - let load = () => appPhotosManager.preloadPhoto(doc).then((blob) => { - if((this.peerID ? this.peerID : this.currentMessageID) != peerID) { - this.log.warn('peer changed'); - return; - } - - img.src = URL.createObjectURL(blob); - - if(!justLoader) { - return loadVideo(); - } else { - container.style.width = ''; - container.style.height = ''; - preloader.detach(); - } + + if(doc.size >= 20e6 && !doc.downloaded) { + let downloadDiv = document.createElement('div'); + downloadDiv.classList.add('download'); + + let span = document.createElement('span'); + span.classList.add('tgico-download'); + downloadDiv.append(span); + + downloadDiv.addEventListener('click', () => { + downloadDiv.remove(); + loadVideo(); }); - - return this.peerID ? this.loadMediaQueuePush(load) : load(); - } */ + + container.append(downloadDiv); + + return; + } + + return doc.downloaded ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo, wasSeen: true}); } +let formatDate = (timestamp: number) => { + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + const date = new Date(timestamp * 1000); + + return months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear() + + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2); +}; + export function wrapDocument(doc: MTDocument, withTime = false, uploading = false): HTMLDivElement { if(doc.type == 'voice') { return wrapVoiceMessage(doc, withTime); } else if(doc.type == 'audio') { - return wrapAudio(doc); + return wrapAudio(doc, withTime); } - - let docDiv = document.createElement('div'); - docDiv.classList.add('document'); - - let iconDiv = document.createElement('div'); - iconDiv.classList.add('tgico-document'); - + let extSplitted = doc.file_name ? doc.file_name.split('.') : ''; let ext = ''; ext = extSplitted.length > 1 && Array.isArray(extSplitted) ? extSplitted.pop().toLowerCase() : 'file'; + + let docDiv = document.createElement('div'); + docDiv.classList.add('document', `ext-${ext}`); let ext2 = ext; if(doc.type == 'photo') { @@ -198,15 +197,11 @@ export function wrapDocument(doc: MTDocument, withTime = false, uploading = fals let size = formatBytes(doc.size); if(withTime) { - let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - let date = new Date(doc.date * 1000); - - size += ' · ' + months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear() - + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2); + size += ' · ' + formatDate(doc.date); } docDiv.innerHTML = ` -
${ext2}
+
${ext2}
${!uploading ? `
` : ''}
${fileName}
${size}
@@ -255,20 +250,30 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { console.log('wrapAudio doc:', doc); - /* let durationStr = String(doc.duration | 0).toHHMMSS(true); + let durationStr = String(doc.duration | 0).toHHMMSS(true); let title = doc.audioTitle || doc.file_name; - let subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : ''; */ - let durationStr = '3:24'; + let subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : ''; + /* let durationStr = '3:24'; let title = 'Million Telegrams'; - let subtitle = 'Best Artist'; + let subtitle = 'Best Artist'; */ + + if(withTime) { + subtitle += (subtitle ? ' · ' : '') + formatDate(doc.date); + } div.innerHTML = ` -
${title}
-
${subtitle}
-
-
${durationStr}
+
+
+
${title}
+
${subtitle}
+
${durationStr}
+
`; + + /* if(!subtitle) { + div.classList.add('audio-no-subtitle'); + } */ //////console.log('wrapping audio', doc, doc.attributes[0].waveform); @@ -281,15 +286,11 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { let onClick = () => { if(!promise) { - if(downloadDiv.classList.contains('downloading')) { - return; // means not ready yet - } - if(!preloader) { preloader = new ProgressivePreloader(null, true); } - let promise = appDocsManager.downloadDoc(doc.id); + promise = appDocsManager.downloadDoc(doc.id); preloader.attach(downloadDiv, true, promise); promise.then(blob => { @@ -298,7 +299,7 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { let audio = document.createElement('audio'); let source = document.createElement('source'); - source.src = URL.createObjectURL(blob); + source.src = doc.url; source.type = doc.mime_type; audio.volume = 1; @@ -343,18 +344,19 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { audio.style.display = 'none'; audio.append(source); + div.classList.add('audio-show-progress'); div.append(audio); }); downloadDiv.classList.add('downloading'); } else { downloadDiv.classList.remove('downloading'); + promise.cancel(); promise = null; } }; div.addEventListener('click', onClick); - div.click(); return div; @@ -418,15 +420,11 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem let onClick = () => { if(!promise) { - if(downloadDiv.classList.contains('downloading')) { - return; // means not ready yet - } - if(!preloader) { preloader = new ProgressivePreloader(null, true); } - let promise = appDocsManager.downloadDoc(doc.id); + promise = appDocsManager.downloadDoc(doc.id); preloader.attach(downloadDiv, true, promise); promise.then(blob => { @@ -435,7 +433,7 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem let audio = document.createElement('audio'); let source = document.createElement('source'); - source.src = URL.createObjectURL(blob); + source.src = doc.url; source.type = doc.mime_type; audio.volume = 1; @@ -544,12 +542,12 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem downloadDiv.classList.add('downloading'); } else { downloadDiv.classList.remove('downloading'); + promise.cancel(); promise = null; } }; div.addEventListener('click', onClick); - div.click(); return div; diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 0836fb85..92d1c84b 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -445,7 +445,7 @@ export class AppDialogsManager { if(lastMessage.media) { switch(lastMessage.media._) { case 'messageMediaPhoto': - lastMessageText += 'Photo' + (lastMessage.message ? ', ' : '') + ''; + lastMessageText += '' + (lastMessage.grouped_id ? 'Album' : 'Photo') + (lastMessage.message ? ', ' : '') + ''; break; case 'messageMediaGeo': lastMessageText += 'Geolocation'; @@ -512,7 +512,7 @@ export class AppDialogsManager { d.push(duration % 60 + ' s'); if(duration > 60) d.push((duration / 60 | 0) + ' min'); - if(duration > 3600) d.push((duration / 3600 | 0) + ' h'); + //if(duration > 3600) d.push((duration / 3600 | 0) + ' h'); suffix = ' (' + d.reverse().join(' ') + ')'; } } diff --git a/src/lib/appManagers/appDocsManager.ts b/src/lib/appManagers/appDocsManager.ts index b1c8a73c..9c54b9b9 100644 --- a/src/lib/appManagers/appDocsManager.ts +++ b/src/lib/appManagers/appDocsManager.ts @@ -3,6 +3,7 @@ import FileManager from '../filemanager'; import {RichTextProcessor} from '../richtextprocessor'; import { CancellablePromise } from '../polyfill'; import { MTDocument } from '../../components/wrappers'; +import { isObject } from '../utils'; class AppDocsManager { private docs: {[docID: string]: MTDocument} = {}; @@ -140,8 +141,8 @@ class AppDocsManager { return apiDoc; } - public getDoc(docID: string) { - return this.docs[docID] || {_: 'documentEmpty'}; + public getDoc(docID: any): MTDocument { + return isObject(docID) ? docID : this.docs[docID]; } public getFileName(doc: MTDocument) { @@ -199,6 +200,8 @@ class AppDocsManager { } if(doc.downloaded && !toFileEntry) { + if(doc.url) return Promise.resolve(null); + var cachedBlob = apiFileManager.getCachedFile(inputFileLocation); if(cachedBlob) { return Promise.resolve(cachedBlob); @@ -217,7 +220,7 @@ class AppDocsManager { if(blob) { doc.downloaded = true; - if(/* !doc.animated || */doc.type != 'sticker') { + if(/* !doc.animated || */doc.type && doc.type != 'sticker') { doc.url = FileManager.getFileCorrectUrl(blob, doc.mime_type); } } diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 747689a4..83177b02 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -24,6 +24,7 @@ import { ChatInput } from '../../components/chatInput'; import Scrollable from '../../components/scrollable'; import BubbleGroups from '../../components/bubbleGroups'; import LazyLoadQueue from '../../components/lazyLoadQueue'; +import appDocsManager from './appDocsManager'; console.log('appImManager included!'); @@ -200,12 +201,21 @@ export class AppImManager { // set cached url to media let message = appMessagesManager.getMessage(mid); - if(message.media && message.media.photo) { - let photo = appPhotosManager.getPhoto(tempID); - if(photo) { - let newPhoto = message.media.photo; - newPhoto.downloaded = photo.downloaded; - newPhoto.url = photo.url; + if(message.media) { + if(message.media.photo) { + let photo = appPhotosManager.getPhoto(tempID); + if(photo) { + let newPhoto = message.media.photo; + newPhoto.downloaded = photo.downloaded; + newPhoto.url = photo.url; + } + } else if(message.media.document) { + let doc = appDocsManager.getDoc(tempID); + if(doc && doc.type && doc.type != 'sticker') { + let newDoc = message.media.document; + newDoc.downloaded = doc.downloaded; + newDoc.url = doc.url; + } } } @@ -368,7 +378,7 @@ export class AppImManager { let message = appMessagesManager.getMessage(id); return message.media && (message.media.photo || (message.media.document && (message.media.document.type == 'video' || message.media.document.type == 'gif')) || (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo))); - }).sort(); + }).sort((a, b) => a - b); let idx = ids.findIndex(i => i == messageID); let targets = ids.map(id => ({ @@ -377,7 +387,7 @@ export class AppImManager { mid: id })); - /////this.log('ids', ids, idx, this.bubbles[prev], this.bubbles[next]); + this.log('open mediaViewer with ids:', ids, idx, targets); appMediaViewer.openMedia(message, targets[idx].element, true, this.scroll.parentElement, targets.slice(0, idx), targets.slice(idx + 1)); @@ -649,7 +659,7 @@ export class AppImManager { this.log('loadMoreHistory', top); if(!this.peerID || testScroll || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return; - let history = Object.keys(this.bubbles).map(id => +id).sort(); + let history = Object.keys(this.bubbles).map(id => +id).sort((a, b) => a - b); if(!history.length) return; /* let history = appMessagesManager.historiesStorage[this.peerID].history; @@ -962,6 +972,9 @@ export class AppImManager { this.topbar.style.display = ''; if(appPeersManager.isAnyGroup(peerID)) this.chatInner.classList.add('is-chat'); else this.chatInner.classList.remove('is-chat'); + if(appPeersManager.isChannel(peerID)) this.chatInner.classList.add('is-channel'); + else this.chatInner.classList.remove('is-channel'); + this.pinnedMessageContainer.style.display = 'none'; window.requestAnimationFrame(() => { //this.chatInner.style.visibility = 'hidden'; @@ -970,7 +983,6 @@ export class AppImManager { else title = appPeersManager.getPeerTitle(this.peerID); //this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = dom.titleSpan.innerHTML; this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title; - this.pinnedMessageContainer.style.display = 'none'; this.goDownBtn.style.display = ''; //this.topbar.style.display = this.goDownBtn.style.display = ''; //this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : ''; @@ -1340,15 +1352,37 @@ export class AppImManager { switch(pending.type) { case 'photo': { - if(pending.size < 5e6) { + //if(pending.size < 5e6) { this.log('will wrap pending photo:', pending, message, appPhotosManager.getPhoto(message.id)); wrapPhoto(message.id, message, attachmentDiv, undefined, undefined, true, true, this.lazyLoadQueue, null); preloader.attach(attachmentDiv, false); bubble.classList.add('hide-name', 'photo'); - - break; - } + //} + + break; + } + + case 'video': { + //if(pending.size < 5e6) { + let doc = appDocsManager.getDoc(message.id); + this.log('will wrap pending video:', pending, message, doc); + wrapVideo({ + doc, + container: attachmentDiv, + message, + boxWidth: 380, + boxHeight: 380, + withTail: doc.type != 'round', + isOut: our, + lazyLoadQueue: this.lazyLoadQueue, + middleware: null + }); + + preloader.attach(attachmentDiv, false); + bubble.classList.add('hide-name', 'video'); + //} + break; } case 'audio': @@ -1424,9 +1458,6 @@ export class AppImManager { doc, container: preview, message, - justLoader: true, - preloader: null, - round: false, boxWidth: 380, boxHeight: 300, lazyLoadQueue: this.lazyLoadQueue, @@ -1502,7 +1533,7 @@ export class AppImManager { }, this.lazyLoadQueue, 'chat', false, !!message.pending || !multipleRender); break; - } else if((doc.type == 'video' || doc.type == 'gif' || doc.type == 'round') && doc.size <= 20e6) { + } else if(doc.type == 'video' || doc.type == 'gif' || doc.type == 'round'/* && doc.size <= 20e6 */) { this.log('never get free 2', doc); if(doc.type == 'round') { @@ -1510,14 +1541,10 @@ export class AppImManager { } bubble.classList.add('hide-name', 'video'); - //wrapVideo.call(this, doc, attachmentDiv, message, true, null, false, doc.type == 'round', 380, 380, doc.type != 'round', our); wrapVideo({ doc, container: attachmentDiv, message, - justLoader: true, - preloader: null, - round: doc.type == 'round', boxWidth: 380, boxHeight: 380, withTail: doc.type != 'round', @@ -1861,11 +1888,11 @@ export class AppImManager { let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.innerHeight / 38/* * 1.25 */ | 0; - /* if(testScroll) { - loadCount = 5; + if(testScroll) { + loadCount = 1; if(Object.keys(this.bubbles).length > 0) return Promise.resolve(true); - } */ + } ////console.time('render history total'); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 7dbddc31..08a8abf7 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -39,6 +39,7 @@ export type HistoryResult = { export class AppMessagesManager { public messagesStorage: any = {}; public messagesForDialogs: any = {}; + public groupedMessagesStorage: {[groupID: string]: any} = {}; // will be used for albums public historiesStorage: { [peerID: string]: HistoryStorage } = {}; @@ -455,13 +456,15 @@ export class AppMessagesManager { entities?: any[], width?: number, height?: number, - objectURL?: string + objectURL?: string, + isRoundMessage?: boolean, + duration?: number } = {}) { peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID; var messageID = this.tempID--; var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]; var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(); - var historyStorage = this.historiesStorage[peerID]; + var historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {count: null, history: [], pending: []}); var flags = 0; var pFlags: any = {}; var replyToMsgID = options.replyToMsgID; @@ -482,6 +485,8 @@ export class AppMessagesManager { caption = RichTextProcessor.parseMarkdown(caption, entities); } + let attributes: any[] = []; + let actionName = ''; if(!options.isMedia) { attachType = 'document'; @@ -512,27 +517,55 @@ export class AppMessagesManager { }; appPhotosManager.savePhoto(photo); - } else if(fileType.substr(0, 6) == 'audio/' || ['video/ogg'].indexOf(fileType) >= 0) { + } else if(fileType.indexOf('audio/') === 0 || ['video/ogg'].indexOf(fileType) >= 0) { attachType = 'audio'; apiFileName = 'audio.' + (fileType.split('/')[1] == 'ogg' ? 'ogg' : 'mp3'); actionName = 'sendMessageUploadAudioAction'; - } else if(fileType.substr(0, 6) == 'video/') { - //attachType = 'video'; - //apiFileName = 'video.mp4'; - attachType = 'document'; // last minute fix + } else if(fileType.indexOf('video/') === 0) { + attachType = 'video'; apiFileName = 'video.mp4'; actionName = 'sendMessageUploadVideoAction'; + + let flags = 1; + if(options.isRoundMessage) flags |= 2; + let videoAttribute = { + _: 'documentAttributeVideo', + flags: flags, + pFlags: { // that's only for client, not going to telegram + supports_streaming: true, + round_message: options.isRoundMessage + }, + round_message: options.isRoundMessage, + supports_streaming: true, + duration: options.duration, + w: options.width, + h: options.height + }; + + attributes.push(videoAttribute); + + let doc: any = { + _: 'document', + id: '' + messageID, + duration: options.duration, + attributes: attributes, + w: options.width, + h: options.height, + downloaded: file.size, + thumbs: [], + mime_type: fileType, + url: options.objectURL || '', + size: file.size + }; + + appDocsManager.saveDoc(doc); } else { attachType = 'document'; apiFileName = 'document.' + fileType.split('/')[1]; actionName = 'sendMessageUploadDocumentAction'; } - // console.log(attachType, apiFileName, file.type) - - if(historyStorage === undefined) { - historyStorage = this.historiesStorage[peerID] = {count: null, history: [], pending: []}; - } + console.log('AMM: sendFile', attachType, apiFileName, file.type, options); var fromID = appUsersManager.getSelf().id; if(peerID != fromID) { @@ -576,6 +609,8 @@ export class AppMessagesManager { } }; + attributes.push({_: 'documentAttributeFilename', file_name: media.file_name}); + preloader.preloader.onclick = () => { console.log('cancelling upload', media); appImManager.setTyping('sendMessageCancelAction'); @@ -690,17 +725,13 @@ export class AppMessagesManager { file: inputFile }; break; - - case 'document': + default: inputMedia = { _: 'inputMediaUploadedDocument', file: inputFile, mime_type: fileType, - caption: '', - attributes: [ - {_: 'documentAttributeFilename', file_name: fileName} - ] + attributes: attributes }; } @@ -1079,6 +1110,11 @@ export class AppMessagesManager { var mid = appMessagesIDsManager.getFullMessageID(apiMessage.id, channelID); apiMessage.mid = mid; + if(apiMessage.grouped_id) { + let storage = this.groupedMessagesStorage[apiMessage.grouped_id] ?? (this.groupedMessagesStorage[apiMessage.grouped_id] = {}); + storage[mid] = apiMessage; + } + var dialog = this.getDialogByPeerID(peerID)[0]; if(dialog && mid > 0) { let dialogKey = apiMessage.pFlags.out @@ -3106,7 +3142,7 @@ export class AppMessagesManager { //return Promise.resolve(result); } - public requestHistory(peerID: number, maxID: number, limit: number, offset = 0) { + public requestHistory(peerID: number, maxID: number, limit: number, offset = 0): Promise { var isChannel = AppPeersManager.isChannel(peerID); //console.trace('requestHistory', peerID, maxID, limit, offset); @@ -3126,7 +3162,7 @@ export class AppMessagesManager { timeout: 300, noErrorBox: true }).then((historyResult: any) => { - ///console.log('requestHistory result:', historyResult); + console.log('requestHistory result:', historyResult, maxID, limit, offset); appUsersManager.saveApiUsers(historyResult.users); appChatsManager.saveApiChats(historyResult.chats); @@ -3143,6 +3179,15 @@ export class AppMessagesManager { historyResult.count--; } + // will load more history if last message is album grouped (because it can be not last item) + let historyStorage = this.historiesStorage[peerID]; + // historyResult.messages: desc sorted + if(historyResult.messages[length - 1].grouped_id && (historyStorage.history.length + historyResult.messages.length) < historyResult.count) { + return this.requestHistory(peerID, historyResult.messages[length - 1].mid, 10, 0).then((_historyResult: any) => { + return historyResult; + }); + } + // don't need the intro now /* if(peerID < 0 || !appUsersManager.isBot(peerID) || (length == limit && limit < historyResult.count)) { return historyResult; diff --git a/src/lib/appManagers/appPhotosManager.ts b/src/lib/appManagers/appPhotosManager.ts index 0c3e80ea..145e39da 100644 --- a/src/lib/appManagers/appPhotosManager.ts +++ b/src/lib/appManagers/appPhotosManager.ts @@ -109,14 +109,16 @@ export class AppPhotosManager { let bestPhotoSize: MTPhotoSize = {_: 'photoSizeEmpty'}; let sizes = (photo.sizes || photo.thumbs) as typeof bestPhotoSize[]; - for(let photoSize of sizes) { - if(!photoSize.w || !photoSize.h) continue; - - bestPhotoSize = photoSize; - - let {w, h} = calcImageInBox(photoSize.w, photoSize.h, width, height); - if(w == width || h == height) { - break; + if(sizes) { + for(let photoSize of sizes) { + if(!photoSize.w || !photoSize.h) continue; + + bestPhotoSize = photoSize; + + let {w, h} = calcImageInBox(photoSize.w, photoSize.h, width, height); + if(w == width || h == height) { + break; + } } } diff --git a/src/lib/appManagers/appSidebarRight.ts b/src/lib/appManagers/appSidebarRight.ts index 5c139b85..e143b966 100644 --- a/src/lib/appManagers/appSidebarRight.ts +++ b/src/lib/appManagers/appSidebarRight.ts @@ -11,7 +11,7 @@ import { logger } from "../polyfill"; import appImManager from "./appImManager"; import appMediaViewer from "./appMediaViewer"; import LazyLoadQueue from "../../components/lazyLoadQueue"; -import { wrapDocument } from "../../components/wrappers"; +import { wrapDocument, wrapAudio } from "../../components/wrappers"; import AppSearch, { SearchGroup } from "../../components/appSearch"; const testScroll = false; @@ -20,6 +20,7 @@ class AppSidebarRight { public sidebarEl = document.getElementById('column-right') as HTMLDivElement; public profileContainer = this.sidebarEl.querySelector('.profile-container') as HTMLDivElement; public profileContentEl = this.sidebarEl.querySelector('.profile-content') as HTMLDivElement; + public contentContainer = this.sidebarEl.querySelector('.content-container') as HTMLDivElement; public profileElements = { avatar: this.profileContentEl.querySelector('.profile-avatar') as HTMLDivElement, name: this.profileContentEl.querySelector('.profile-name') as HTMLDivElement, @@ -99,10 +100,11 @@ class AppSidebarRight { }); constructor() { - let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement; + let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement; this.profileTabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement; - this.scroll = new Scrollable(this.profileContainer, 'y', 1200, 'SR'); + this.scroll = new Scrollable(this.profileContainer, 'y', 1200, 'SR', undefined, 400); + //this.scroll = new Scrollable(this.profileContentEl, 'y', 1200, 'SR', undefined, 400); this.scroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this)); this.scroll.onScrolledBottom = () => { if(this.sharedMediaSelected && !this.scroll.hiddenElements.down.length && this.sharedMediaSelected.childElementCount/* && false */) { @@ -116,17 +118,15 @@ class AppSidebarRight { this.sharedMediaType = this.sharedMediaTypes[id]; this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement; - - if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix - this.loadSidebarMedia(true); + + if(this.profileTabs.offsetTop) { + this.scroll.scrollTop -= this.profileTabs.offsetTop; } - + if(this.prevTabID != -1) { this.savedVirtualStates[this.prevTabID] = this.scroll.state; } - this.prevTabID = id; - this.log('setVirtualContainer', id, this.sharedMediaSelected); this.scroll.setVirtualContainer(this.sharedMediaSelected); @@ -134,6 +134,15 @@ class AppSidebarRight { this.log(this.savedVirtualStates[id]); this.scroll.state = this.savedVirtualStates[id]; } + + if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix + this.contentContainer.classList.remove('loaded'); + this.loadSidebarMedia(true); + } + + this.prevTabID = id; + + this.scroll.onScroll(); }, this.onSidebarScroll.bind(this)); let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement; @@ -157,7 +166,7 @@ class AppSidebarRight { let message = appMessagesManager.getMessage(messageID); - let ids = Object.keys(this.mediaDivsByIDs).map(k => +k).sort(); + let ids = Object.keys(this.mediaDivsByIDs).map(k => +k).sort((a, b) => a - b); let idx = ids.findIndex(i => i == messageID); let targets = ids.map(id => ({element: this.mediaDivsByIDs[id], mid: id})); @@ -397,7 +406,7 @@ class AppSidebarRight { continue; } - let div = wrapDocument(message.media.document, true); + let div = wrapAudio(message.media.document, true); elemsToAppend.push(div); } break; @@ -422,6 +431,7 @@ class AppSidebarRight { let parent = sharedMediaDiv.parentElement; if(parent.lastElementChild.classList.contains('preloader')) { parent.lastElementChild.remove(); + this.contentContainer.classList.add('loaded'); } } @@ -433,7 +443,7 @@ class AppSidebarRight { return; } - this.log('loadSidebarMedia', single, this.peerID); + this.log('loadSidebarMedia', single, this.peerID, this.loadSidebarMediaPromises); let peerID = this.peerID; @@ -455,10 +465,10 @@ class AppSidebarRight { // render from cache if(history.length && this.usedFromHistory[type] < history.length && this.cleared[type]) { let ids = history.slice(this.usedFromHistory[type], this.usedFromHistory[type] + loadCount); - this.log('will render from cache', this.usedFromHistory[type], history, ids, loadCount); + this.log('loadSidebarMedia: will render from cache', this.usedFromHistory[type], history, ids, loadCount); this.usedFromHistory[type] += ids.length; this.performSearchResult(ids, type); - return; + return Promise.resolve(); } // заливать новую картинку сюда только после полной отправки! @@ -469,7 +479,7 @@ class AppSidebarRight { ? appMessagesManager.historiesStorage[peerID].history.slice() : []; maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID; - this.log('search house of glass pre', type, ids, maxID); + this.log('loadSidebarMedia: search house of glass pre', type, ids, maxID); //let loadCount = history.length ? 50 : 15; return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, loadCount) @@ -477,7 +487,7 @@ class AppSidebarRight { ids = ids.concat(value.history); history.push(...ids); - this.log('search house of glass', type, value, ids, this.cleared); + this.log('loadSidebarMedia: search house of glass', type, value, ids, this.cleared); if($rootScope.selectedPeerID != peerID) { this.log.warn('peer changed'); @@ -515,6 +525,8 @@ class AppSidebarRight { this.lastSharedMediaDiv = document.createElement('div'); //this.log('fillProfileElements'); + + this.contentContainer.classList.remove('loaded'); window.requestAnimationFrame(() => { this.profileContentEl.parentElement.scrollTop = 0; diff --git a/src/lib/mtproto/apiManager.ts b/src/lib/mtproto/apiManager.ts index 05d46f23..62fe082c 100644 --- a/src/lib/mtproto/apiManager.ts +++ b/src/lib/mtproto/apiManager.ts @@ -12,7 +12,7 @@ import HTTP from './transports/http'; import { logger } from '../polyfill'; import passwordManager from './passwordManager'; -console.error('apiManager included!'); +//console.error('apiManager included!'); export class ApiManager { public cachedNetworkers: {[x: number]: MTPNetworker} = {}; diff --git a/src/lib/mtproto/mtproto.worker.js b/src/lib/mtproto/mtproto.worker.js index b6d42f10..abcd31a7 100644 --- a/src/lib/mtproto/mtproto.worker.js +++ b/src/lib/mtproto/mtproto.worker.js @@ -6,7 +6,7 @@ import networkerFactory from "./networkerFactory"; //const ctx: Worker = self as any; const ctx = self; -console.error('INCLUDE !!!', new Error().stack); +//console.error('INCLUDE !!!', new Error().stack); networkerFactory.setUpdatesProcessor((obj, bool) => { ctx.postMessage({update: {obj, bool}}); @@ -28,10 +28,10 @@ ctx.onmessage = function(e) { default: return apiManager[e.data.task].apply(apiManager, e.data.args).then(result => { - console.log(e.data.task + ' result:', result, taskID); + //console.log(e.data.task + ' result:', result, taskID); ctx.postMessage({taskID: taskID, result: result}); }).catch(err => { - console.error(e.data.task + ' err:', err, taskID); + //console.error(e.data.task + ' err:', err, taskID); ctx.postMessage({taskID: taskID, error: err}); }); //throw new Error('Unknown task: ' + e.data.task); diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts index a7c27610..0b24e0c2 100644 --- a/src/lib/mtproto/mtprotoworker.ts +++ b/src/lib/mtproto/mtprotoworker.ts @@ -19,7 +19,7 @@ class ApiManagerProxy extends CryptoWorkerMethods { } } = {} as any; private pending: Array = []; - private debug = true; + private debug = false; public updatesProcessor: (obj: any, bool: boolean) => void = null; diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index 549a31e9..e1cbd688 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -15,7 +15,7 @@ import HTTP from './transports/http'; import { logger } from '../polyfill'; import { Modes, App } from './mtproto_config'; -console.error('networker included!', new Error().stack); +//console.error('networker included!', new Error().stack); type Message = { msg_id: string, diff --git a/src/pages/pageSignUp.ts b/src/pages/pageSignUp.ts index 62f6e5a3..0bf671e4 100644 --- a/src/pages/pageSignUp.ts +++ b/src/pages/pageSignUp.ts @@ -5,6 +5,7 @@ import pageIm from './pageIm'; import apiManager from '../lib/mtproto/mtprotoworker'; import apiFileManager from '../lib/mtproto/apiFileManager'; import Page from './page'; +import { calcImageInBox } from '../lib/utils'; let authCode: { 'phone_number': string, @@ -14,9 +15,9 @@ let authCode: { let onFirstMount = () => { const pageElement = page.pageEl; const avatarInput = document.getElementById('avatar-input') as HTMLInputElement; - const avatarPopup = pageElement.getElementsByClassName('popup-avatar')[0]; + const avatarPopup = document.getElementsByClassName('popup-avatar')[0]; const avatarPreview = pageElement.querySelector('#canvas-avatar') as HTMLCanvasElement; - const cropContainer = avatarPopup.getElementsByClassName('crop')[0]; + const cropContainer = avatarPopup.getElementsByClassName('crop')[0] as HTMLDivElement; let avatarImage = new Image(); cropContainer.append(avatarImage); @@ -76,11 +77,14 @@ let onFirstMount = () => { avatarImage.src = contents; avatarImage.onload = () => { + /* let {w, h} = calcImageInBox(avatarImage.naturalWidth, avatarImage.naturalHeight, 460, 554); + cropContainer.style.width = w + 'px'; + cropContainer.style.height = h + 'px'; */ + avatarPopup.classList.add('active'); + cropper = resizeableImage(avatarImage, avatarPreview); avatarInput.value = ''; }; - - avatarPopup.classList.add('active'); }; reader.readAsDataURL(file); diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index 57601370..a4497a47 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -178,6 +178,12 @@ $chat-max-width: 696px; } } + &.is-channel:not(.is-chat) { + .bubble__container { + max-width: 100%; + } + } + &.is-scrolling .is-sticky { opacity: 1; } @@ -508,6 +514,29 @@ $chat-max-width: 696px; max-width: 100%; cursor: pointer; } + + .download { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 100%; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + + span { + width: 54px; + height: 54px; + line-height: 54px; + background-color: rgba(0, 0, 0, .7); + border-radius: 50%; + font-size: 23px; + color: #fff; + text-align: center; + } + } } &:not(.sticker) { @@ -592,7 +621,7 @@ $chat-max-width: 696px; .title { letter-spacing: -0.2px; line-height: 1.2; - font-weight: 500; + font-weight: 500 !important; } .name { @@ -627,7 +656,7 @@ $chat-max-width: 696px; } .name, .reply-title { - font-weight: 500; + font-weight: 500 !important; display: inline!important; } } @@ -789,7 +818,7 @@ $chat-max-width: 696px; /* padding: .2675rem .6rem 0 .6rem; */ /* padding: .32rem .6rem 0 .6rem; */ padding: 5px .6rem 0 .6rem; - font-weight: 500; + font-weight: 500 !important; /* padding-bottom: 4px; */ color: $darkblue; font-size: .9rem; @@ -1064,6 +1093,11 @@ $chat-max-width: 696px; right: -2.5rem; } + .document-ico:after { + border-top-color: #eeffde; + border-right-color: #eeffde; + } + .audio { &-waveform { rect { @@ -1134,8 +1168,6 @@ $chat-max-width: 696px; flex: 0 0 auto; font-size: 1.5rem; line-height: 1.5rem; - height: 54px; - width: 54px; color: #9e9e9e; background-color: #fff; align-self: flex-end; diff --git a/src/scss/partials/_chatlist.scss b/src/scss/partials/_chatlist.scss index 960a9914..70152c05 100644 --- a/src/scss/partials/_chatlist.scss +++ b/src/scss/partials/_chatlist.scss @@ -152,9 +152,7 @@ overflow: hidden; color: $color-gray; flex: 1 1 auto; - padding-right: 3.5px; - padding-left: 9px; - padding-top: 1px; + padding: 1px 3.5px 1px 9px; p:last-child { margin-top: -3px; @@ -164,6 +162,7 @@ .user-title { img.emoji { vertical-align: top; + margin-top: 4px; width: 18px; height: 18px; } @@ -222,7 +221,7 @@ line-height: 24px; color: #fff; border-radius: 12px; - margin-top: 1.5px; + margin-top: 4px; margin-right: -2px; } @@ -257,8 +256,45 @@ &__name { color: $color-gray; - padding: 0 1.85rem; + padding: 0 23px; padding-bottom: 1rem; + font-weight: 500; + } + + &:not(.search-group-messages) { + .user-avatar { + width: 48px; + height: 48px; + } + } + + &-contacts { + padding: 16px 0 7px; + + li > .rp { + padding: 9px 11.5px !important; + height: 66px; + } + + .search-group__name { + padding-bottom: 17px; + } + + .user-caption { + padding: 1px 3.5px 1px 13px; + } + + .user-title, b, .user-last-message b { + font-weight: normal; + } + + p { + height: 24px; + } + + span.user-last-message { + font-size: 14px; + } } } } diff --git a/src/scss/partials/_emojiDropdown.scss b/src/scss/partials/_emojiDropdown.scss index 7b8bf213..b37095f2 100644 --- a/src/scss/partials/_emojiDropdown.scss +++ b/src/scss/partials/_emojiDropdown.scss @@ -26,7 +26,7 @@ > .menu-horizontal { padding: 0px 58px 0px 58px; - font-weight: 500; + //font-weight: 500; margin-top: 2px; > li.active:after { diff --git a/src/scss/partials/_rightSIdebar.scss b/src/scss/partials/_rightSIdebar.scss index b284c007..8abdff62 100644 --- a/src/scss/partials/_rightSIdebar.scss +++ b/src/scss/partials/_rightSIdebar.scss @@ -45,6 +45,8 @@ display: flex; flex-direction: column; height: 100%; + position: relative; + width: 100%; [type="checkbox"] + span { padding-left: 54px; @@ -55,6 +57,7 @@ flex: 0 0 auto; display: flex; flex-direction: column; + margin-bottom: 36px; } .content-container { @@ -64,6 +67,13 @@ flex: 1 1 auto; position: relative; //height: 1%; // fix safari + + &.loaded { + .profile-tabs-content { + position: relative; + min-height: auto; + } + } } } @@ -96,7 +106,7 @@ flex-direction: column; padding-left: 80px; padding-right: 12px; - font-size: 15px; + //font-size: 15px; position: relative; margin-top: 31px; line-height: 1.4; @@ -126,6 +136,11 @@ color: $placeholder-color !important; font-size: 14px !important; } + + &-notifications { + margin-top: 29px; + line-height: 1.3; + } } &-avatar.user-avatar { @@ -140,11 +155,18 @@ } &-tabs { - margin-top: 36px; + //margin-top: 36px; + position: -webkit-sticky !important; + position: sticky !important; + top: 0; + z-index: 3; + background-color: #fff; &-content { - min-height: 100%; + //min-height: 100%; + min-height: calc(100% - 49px); position: absolute; // FIX THE SAFARI! + //position: relative; /* width: 500%; margin-left: -100%; */ @@ -167,7 +189,7 @@ .preloader { padding: 0; - position: absolute; + position: absolute !important; height: 100%; > svg { @@ -300,13 +322,97 @@ } #content-audio { - padding: 0 15px 15px 15px; + padding: 20px 15px 15px 20px; > div { - margin-top: 15px; - padding-bottom: 10px; min-height: 60px; } + + .audio { + padding-bottom: 26px; + padding-left: 61px; + /* min-height: 58px; */ + max-width: 368px; + justify-content: unset; + + &-details { + height: 66px; + } + + &.audio-show-progress .audio-subtitle { + overflow: visible; + } + + /* &-no-subtitle { + padding-bottom: 16px; + } */ + + &-ico { + width: 48px; + height: 48px; + + &.tgico-largeplay:before { + margin-right: -1px; + } + } + + &-download { + border-radius: 50%; + background-color: #50a2e9; + align-items: center; + } + + &-toggle, &-download { + font-size: 1.9rem; + } + + &-title { + font-size: 1rem; + color: #000; + line-height: 1.2; + padding-top: 5px; + margin-top: 0; + margin-left: -1px; + } + + &-subtitle { + font-size: 14px; + line-height: 1.25; + color: #707579; + margin-left: -1px; + margin-top: 3px; + } + + &-time { + margin-top: 1px; + } + + &-title, &-subtitle { + overflow: hidden; + text-overflow: ellipsis; + } + } + + .media-progress { + margin: 11px 0 8px; + + &__filled { + background-color: #0089ff; + transform-origin: left; + height: 2px; + } + + &__seek { + height: 2px; + //background-color: #e6ecf0; + background: rgba(193, 207, 220, 0.39); + + &::-webkit-slider-thumb { + height: 12px; + width: 12px; + } + } + } } } } diff --git a/src/scss/partials/_scrollable.scss b/src/scss/partials/_scrollable.scss new file mode 100644 index 00000000..e3c5f5b7 --- /dev/null +++ b/src/scss/partials/_scrollable.scss @@ -0,0 +1,91 @@ +div.scrollable::-webkit-scrollbar { + width: 0; + height: 0; +} + +div.scrollable::-webkit-scrollbar-thumb { + width: 0; + height: 0; +} + +::-webkit-scrollbar-button { + width: 0; + height: 0; + display: none; +} +::-webkit-scrollbar-corner { + background-color: transparent; +} + +.scrollable { + width: 100%; + height: 100%; + overflow-y: hidden; + overflow-x: hidden; + max-height: 100%; + //position: relative; + + //will-change: transform; + transform: translateZ(0); + -webkit-transform: translateZ(0); + + position: absolute; + top: 0px; + left: 0px; + bottom: 0px; + right: 0px; + display: flex; + flex-direction: column; + + &.scrollable-x { + overflow-x: auto; + scrollbar-width: none; + -ms-overflow-style: none; + } + + &.scrollable-y { + overflow-y: auto; + scrollbar-width: none; + -ms-overflow-style: none; + } + + &.scrollable-x ~ .scrollbar-thumb { + top: auto; + right: auto; + width: auto; + height: 4px; + bottom: 0px; + } + + .scroll-padding { + flex: 0 0 auto; + + &:first-child + * { + flex: 1 1 auto; + } + } +} + +.scrollbar-thumb { + position: absolute; + top: 0; + right: 2px; + width: 4px; + //margin-left: 2px; + background-color: #000; + //cursor: grab; + cursor: default; + opacity: 0; + transition-property: opacity; + transition-duration: .2s; + transition-timing-function: ease-in-out; + + //display: none; + + border-radius: $border-radius; + z-index: 2; +} + +:hover > .scrollbar-thumb { + opacity: .4; +} \ No newline at end of file diff --git a/src/scss/style.scss b/src/scss/style.scss index 64b91f27..346b3de6 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -41,6 +41,7 @@ $large-screen: 1680px; @import "partials/mediaViewer"; @import "partials/ckin"; @import "partials/emojiDropdown"; +@import "partials/scrollable"; html, body { height: 100%; @@ -78,13 +79,20 @@ button, input, optgroup, select, textarea, html { } h1, h2, h3, h4, h5, h6 { - letter-spacing: -.66px; font-weight: 500; } +body.is-mac * { + font-weight: normal !important; + /* h1, h2, h3, h4, h5, h6, .mac-thin { + font-weight: normal; + } */ +} + h4 { font-size: 2rem; - margin: 1.5rem 0 1rem 0; + //margin: 1.5rem 0 1rem 0; + margin: 22px 0 14px; line-height: 110%; } @@ -94,8 +102,8 @@ input { .subtitle { /* font-weight: 500; */ - color: $placeholder-color; - line-height: 1.25; + color: #707579; + line-height: 1.35; } .page-authCode { @@ -291,11 +299,11 @@ input { display: block; border-radius: 50%; border: 2px solid white; - background-color: #4DCD5E; - left: 70%; - top: 79%; - width: 12px; - height: 12px; + background-color: #0ac630; + left: 74%; + top: 73%; + width: 14px; + height: 14px; } &.tgico-avatar_deletedaccount { @@ -303,13 +311,10 @@ input { } } -/* .user-title, b { - color: #000; -} */ - .user-title, b, .user-last-message b { color: #000; - font-weight: normal; + font-weight: 500; + //font-weight: normal; } .rp { @@ -368,6 +373,27 @@ input { .document { padding-left: 4.5rem; height: 70px; + + &-ico { + background-color: $blue; + border-radius: 5px; + line-height: 10px; + + &:after { + content: ""; + display: block; + position: absolute; + top: 0; + right: 0; + width: 1.125rem; + height: 1.125rem; + border-bottom-left-radius: .25rem; + border-left: .5625rem solid rgba(0, 0, 0, .25); + border-bottom: .5625rem solid rgba(0, 0, 0, .25); + border-top: .5625rem solid #fff; + border-right: .5625rem solid #fff; + } + } &-ico, &-download { font-weight: 500; @@ -379,20 +405,43 @@ input { } &-download { - background-color: rgb(101, 161, 227); + background-color: $blue; border-radius: 8px; } + &.ext-zip { + .document-ico, .document-download { + background-color: #FB8C00; + } + } + + &.ext-pdf { + .document-ico, .document-download { + background-color: #DF3F40; + } + } + + &.ext-apk { + .document-ico, .document-download { + background-color: #43A047; + } + } + &:not(.photo) { .document-ico { padding-top: 1.5rem; - background-image: url('../assets/img/doc-in.svg'); + //background-image: url('../assets/img/doc-in.svg'); } } &.photo { .document-ico { + background: #000; border-radius: $border-radius; + + &:after { + display: none; + } } } @@ -512,6 +561,8 @@ input { .tabs-container { height: 100%; + transform: translateX(0); + width: 100%; &.animated { transition: .42s transform; @@ -564,19 +615,21 @@ input { cursor: pointer; position: relative; overflow: hidden; + margin-top: 10px; + margin-bottom: 14px; canvas { max-width: 100%; max-height: 100%; width: 100%; height: 100%; - background-color: $button-primary-background; + background-color: $blue; } svg { position: absolute; - width: 36px; - height: 36px; + width: 48px; + height: 48px; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%); @@ -904,7 +957,7 @@ input:focus, button:focus { } */ .btn-primary { - background: $button-primary-background; + background: $blue; color: #fff; border-radius: $border-radius-medium; width: 100%; @@ -918,14 +971,14 @@ input:focus, button:focus { padding: 0; // new &:hover { - background: darken($button-primary-background, 8%); + background: darken($blue, 8%); } svg, use { height: calc(100% - 20px); right: 12.5px; left: auto; - margin: auto 0; + margin: 4px 0 auto; } } @@ -943,39 +996,43 @@ $width: 100px; } } */ -.preloader-circular { - animation: rotate 2s linear infinite; - height: 100%; - transform-origin: center center; - /* width: 100%; */ - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; -} +.preloader { + &-circular { + animation: rotate 2s linear infinite; + height: 100%; + transform-origin: center center; + /* width: 100%; */ + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + } -.preloader-path { - stroke-dasharray: 1, 200; - stroke-dashoffset: 0; - animation: dash 1.5s ease-in-out infinite/* , color 6s ease-in-out infinite */; - stroke-linecap: round; - stroke: white; - stroke-width: 3; + &-path { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + animation: dash 1.5s ease-in-out infinite/* , color 6s ease-in-out infinite */; + stroke-linecap: round; + stroke: white; + stroke-width: 3; + } + + &-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: 50px; + height: 50px; + /* cursor: pointer; */ + } } .preloader-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; - width: 50px; - height: 50px; - /* cursor: pointer; */ - .you-spin-me-round { width: 100%; height: 100%; @@ -1165,8 +1222,8 @@ span.popup-close { .btn-circle { border-radius: 50%; - width: 44px; - height: 44px; + height: 54px; + width: 54px; path { fill: white; @@ -1185,49 +1242,49 @@ span.popup-close { .popup-avatar { .popup-container { - /* height: 400px; */ - /* width: 400px; */ max-width: 600px; - max-height: 600px; + //max-height: 600px; + border-radius: $border-radius-medium; + padding: 15px 16px 16px 24px; + overflow: hidden; + display: flex; + flex-direction: column; > button { position: absolute; - bottom: 15px; - right: 15px; + bottom: 20px; + right: 20px; } } .popup-close { font-size: 1.5rem; + margin-top: 4px; + } + + .popup-header { + margin-bottom: 1px; } h6 { - font-size: 1.1rem; + font-size: 1.25rem; text-align: left; margin: 0; - margin-left: 1.5rem; - font-weight: 400; + margin-left: 2rem; } .crop { - /* min-width: calc(100% - 8rem); - min-height: calc(100% - 8rem); */ - - max-width: 200%; - max-height: 200%; - - padding: 0 2.75rem 2.75rem; - - /* position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); */ - + max-width: 100%; + max-height: 100%; + padding: 24px 54px 46px 46px; border-radius: $border-radius; + + > img { + display: none; + } img { - /* max-width: 100%; */ - /* height: 100%; */ + //height: 100%; border-radius: $border-radius; } } @@ -1329,98 +1386,6 @@ span.popup-close { } } -div.scrollable::-webkit-scrollbar { - width: 0; - height: 0; -} - -div.scrollable::-webkit-scrollbar-thumb { - width: 0; - height: 0; -} - -::-webkit-scrollbar-button { - width: 0; - height: 0; - display: none; -} -::-webkit-scrollbar-corner { - background-color: transparent; -} - -.scrollable { - width: 100%; - height: 100%; - overflow-y: hidden; - overflow-x: hidden; - max-height: 100%; - //position: relative; - - //will-change: transform; - transform: translateZ(0); - -webkit-transform: translateZ(0); - - position: absolute; - top: 0px; - left: 0px; - bottom: 0px; - right: 0px; - display: flex; - flex-direction: column; - - &.scrollable-x { - overflow-x: auto; - scrollbar-width: none; - -ms-overflow-style: none; - } - - &.scrollable-y { - overflow-y: auto; - scrollbar-width: none; - -ms-overflow-style: none; - } - - &.scrollable-x ~ .scrollbar-thumb { - top: auto; - right: auto; - width: auto; - height: 4px; - bottom: 0px; - } - - .scroll-padding { - flex: 0 0 auto; - - &:first-child + * { - flex: 1 1 auto; - } - } -} - -.scrollbar-thumb { - position: absolute; - top: 0; - right: 2px; - width: 4px; - //margin-left: 2px; - background-color: #000; - //cursor: grab; - cursor: default; - opacity: 0; - transition-property: opacity; - transition-duration: .2s; - transition-timing-function: ease-in-out; - - //display: none; - - border-radius: $border-radius; - z-index: 2; -} - -:hover > .scrollbar-thumb { - opacity: .4; -} - [contenteditable] { -webkit-user-select: text; user-select: text; @@ -1450,6 +1415,7 @@ div.scrollable::-webkit-scrollbar-thumb { flex: 1; user-select: none; font-size: 1rem; + font-weight: 500; &.active { color: $blue; @@ -1644,7 +1610,7 @@ div.scrollable::-webkit-scrollbar-thumb { left: 50%; transform: translateY(-50%) translateX(-50%); - .preloader-path { + &-path { stroke: $button-primary-background; } }