diff --git a/package.json b/package.json index 0189565b..a8cc544e 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,13 @@ "main": "index.js", "scripts": { "start": "webpack-dev-server --config webpack.dev.js", - "start-production": "webpack-dev-server --config webpack.prod.js", + "start:production": "webpack-dev-server --config webpack.prod.js", "serve": "npm run build; node server.js", "build": "webpack --config webpack.prod.js", + "build:dev": "webpack --config webpack.dev.js", "test": "jest --config=jest.config.js", "profile": "webpack --profile --json > stats.json --config webpack.prod.js", - "profile-dev": "webpack --profile --json > stats.json --config webpack.dev.js" + "profile:dev": "webpack --profile --json > stats.json --config webpack.dev.js" }, "author": "", "license": "ISC", diff --git a/src/components/avatar.ts b/src/components/avatar.ts index 60f89ff5..16938fea 100644 --- a/src/components/avatar.ts +++ b/src/components/avatar.ts @@ -6,7 +6,7 @@ $rootScope.$on('avatar_update', (e: CustomEvent) => { appProfileManager.removeFromAvatarsCache(peerID); (Array.from(document.querySelectorAll('avatar-element[peer="' + peerID + '"]')) as AvatarElement[]).forEach(elem => { - console.log('updating avatar:', elem); + //console.log('updating avatar:', elem); elem.update(); }); }); diff --git a/src/components/emoticonsDropdown.ts b/src/components/emoticonsDropdown.ts index 5ce87a72..c1dc4d03 100644 --- a/src/components/emoticonsDropdown.ts +++ b/src/components/emoticonsDropdown.ts @@ -18,6 +18,7 @@ import animationIntersector from "./animationIntersector"; import appSidebarRight from "../lib/appManagers/appSidebarRight"; import appStateManager from "../lib/appManagers/appStateManager"; import { horizontalMenu } from "./horizontalMenu"; +import GifsMasonry from "./gifsMasonry"; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; @@ -329,7 +330,7 @@ class StickersTab implements EmoticonsTab { loop: false, */ lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue, group: EMOTICONSSTICKERGROUP, - onlyThumb: true + onlyThumb: doc.sticker == 2 }); return div; @@ -535,18 +536,13 @@ class GifsTab implements EmoticonsTab { init() { this.content = document.getElementById('content-gifs'); - const masonry = this.content.firstElementChild as HTMLDivElement; - - masonry.addEventListener('click', EmoticonsDropdown.onMediaClick); + const gifsContainer = this.content.firstElementChild as HTMLDivElement; + gifsContainer.addEventListener('click', EmoticonsDropdown.onMediaClick); + const masonry = new GifsMasonry(gifsContainer); const scroll = new Scrollable(this.content, 'y', 'GIFS', null); - const preloader = putPreloader(this.content, true); - const width = 400; - const maxSingleWidth = width - 100; - const height = 100; - apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((_res) => { let res = _res as { _: 'messages.savedGifs', @@ -557,155 +553,11 @@ class GifsTab implements EmoticonsTab { //let line: MTDocument[] = []; - let wastedWidth = 0; - - res.gifs.forEach((gif, idx) => { - res.gifs[idx] = appDocsManager.saveDoc(gif); - }); - preloader.remove(); - - for(let i = 0, length = res.gifs.length; i < length;) { - const doc = res.gifs[i]; - - let gifWidth = doc.w; - let gifHeight = doc.h; - if(gifHeight < height) { - gifWidth = height / gifHeight * gifWidth; - gifHeight = height; - } - - let willUseWidth = Math.min(maxSingleWidth, width - wastedWidth, gifWidth); - let {w, h} = calcImageInBox(gifWidth, gifHeight, willUseWidth, height); - - /* wastedWidth += w; - - if(wastedWidth == width || h < height) { - wastedWidth = 0; - console.log('completed line', i, line); - line = []; - continue; - } - - line.push(gif); */ - ++i; - - //console.log('gif:', gif, w, h); - - let div = document.createElement('div'); - div.classList.add('gif', 'fade-in-transition'); - div.style.width = w + 'px'; - div.style.opacity = '0'; - //div.style.height = h + 'px'; - div.dataset.docID = doc.id; - - masonry.append(div); - - //let preloader = new ProgressivePreloader(div); - - const posterURL = appDocsManager.getThumbURL(doc, false); - let img: HTMLImageElement; - if(posterURL) { - img = new Image(); - img.src = posterURL; - } - - let mouseOut = false; - const onMouseOver = (e: MouseEvent) => { - //console.log('onMouseOver', doc.id); - //cancelEvent(e); - mouseOut = false; - - wrapVideo({ - doc, - container: div, - //lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue, - //group: EMOTICONSSTICKERGROUP, - noInfo: true, - }); - - const video = div.querySelector('video'); - video.addEventListener('canplay', () => { - div.style.opacity = ''; - if(!mouseOut) { - img && img.classList.add('hide'); - } else { - img && img.classList.remove('hide'); - if(div.lastElementChild != img) { - div.lastElementChild.remove(); - } - } - }, {once: true}); - }; - - const afterRender = () => { - if(img) { - div.append(img); - div.style.opacity = ''; - } - - div.addEventListener('mouseover', onMouseOver, {once: true}); - div.addEventListener('mouseout', (e) => { - const toElement = (e as any).toElement as Element; - //console.log('onMouseOut', doc.id, e); - if(findUpClassName(toElement, 'gif') == div) { - return; - } - - //cancelEvent(e); - - mouseOut = true; - - const cb = () => { - if(div.lastElementChild != img) { - div.lastElementChild.remove(); - } - - div.addEventListener('mouseover', onMouseOver, {once: true}); - }; - - img && img.classList.remove('hide'); - /* window.requestAnimationFrame(() => { - window.requestAnimationFrame(); - }); */ - if(img) window.requestAnimationFrame(() => window.requestAnimationFrame(cb)); - else cb(); - }); - }; - - (posterURL ? renderImageFromUrl(img, posterURL, afterRender) : afterRender()); - - /* wrapVideo({ - doc, - container: div, - lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue, - group: EMOTICONSSTICKERGROUP, - noInfo: true, - }); */ - - /* EmoticonsDropdown.lazyLoadQueue.push({ - div, - load: () => { - const download = appDocsManager.downloadDocNew(doc); - - let thumbSize: string, posterURL: string; - if(doc.thumbs?.length) { - thumbSize = doc.thumbs[0].type; - posterURL = appDocsManager.getThumbURL(doc, thumbSize); - } - - preloader.attach(div, true, appDocsManager.getInputFileName(doc, thumbSize)); - - download.promise.then(blob => { - preloader.detach(); - - div.innerHTML = ``; - }); - - return download.promise; - } - }); */ - } + res.gifs.forEach((doc, idx) => { + res.gifs[idx] = appDocsManager.saveDoc(doc); + masonry.add(doc, EMOTICONSSTICKERGROUP, EmoticonsDropdown.lazyLoadQueue); + }); }); this.init = null; @@ -797,7 +649,7 @@ class EmoticonsDropdown { animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP); this.tabID = id; - this.searchButton.classList.toggle('hide', this.tabID != 1); + this.searchButton.classList.toggle('hide', this.tabID == 0); this.deleteBtn.classList.toggle('hide', this.tabID != 0); }, () => { const tab = this.tabs[this.tabID]; @@ -811,7 +663,11 @@ class EmoticonsDropdown { this.searchButton = this.element.querySelector('.emoji-tabs-search'); this.searchButton.addEventListener('click', () => { - appSidebarRight.stickersTab.init(); + if(this.tabID == 1) { + appSidebarRight.stickersTab.init(); + } else { + appSidebarRight.gifsTab.init(); + } }); this.deleteBtn = this.element.querySelector('.emoji-tabs-delete'); diff --git a/src/components/gifsMasonry.ts b/src/components/gifsMasonry.ts new file mode 100644 index 00000000..8a38fa42 --- /dev/null +++ b/src/components/gifsMasonry.ts @@ -0,0 +1,129 @@ +import { MTDocument } from "../types"; +import { calcImageInBox, findUpClassName } from "../lib/utils"; +import appDocsManager from "../lib/appManagers/appDocsManager"; +import { wrapVideo } from "./wrappers"; +import { renderImageFromUrl } from "./misc"; +import LazyLoadQueue from "./lazyLoadQueue"; + +const width = 400; +const maxSingleWidth = width - 100; +const height = 100; + +export default class GifsMasonry { + constructor(private element: HTMLElement) { + + } + + public add(doc: MTDocument, group: string, lazyLoadQueue?: LazyLoadQueue) { + let gifWidth = doc.w; + let gifHeight = doc.h; + if(gifHeight < height) { + gifWidth = height / gifHeight * gifWidth; + gifHeight = height; + } + + let willUseWidth = Math.min(maxSingleWidth, width, gifWidth); + let {w, h} = calcImageInBox(gifWidth, gifHeight, willUseWidth, height); + + /* wastedWidth += w; + + if(wastedWidth == width || h < height) { + wastedWidth = 0; + console.log('completed line', i, line); + line = []; + continue; + } + + line.push(gif); */ + + //console.log('gif:', gif, w, h); + + let div = document.createElement('div'); + div.classList.add('gif', 'fade-in-transition'); + div.style.width = w + 'px'; + div.style.opacity = '0'; + //div.style.height = h + 'px'; + div.dataset.docID = doc.id; + + this.element.append(div); + + //let preloader = new ProgressivePreloader(div); + + const posterURL = appDocsManager.getThumbURL(doc, false); + let img: HTMLImageElement; + if(posterURL) { + img = new Image(); + img.src = posterURL; + } + + let mouseOut = false; + const onMouseOver = (/* e: MouseEvent */) => { + //console.log('onMouseOver', doc.id); + //cancelEvent(e); + mouseOut = false; + + wrapVideo({ + doc, + container: div, + lazyLoadQueue, + //lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue, + group, + noInfo: true, + }); + + const video = div.querySelector('video'); + video.addEventListener('canplay', () => { + div.style.opacity = ''; + if(!mouseOut) { + img && img.classList.add('hide'); + } else { + img && img.classList.remove('hide'); + if(div.lastElementChild != img) { + div.lastElementChild.remove(); + } + } + }, {once: true}); + }; + + const afterRender = () => { + if(img) { + div.append(img); + div.style.opacity = ''; + } + + if(lazyLoadQueue) { + onMouseOver(); + } else { + div.addEventListener('mouseover', onMouseOver, {once: true}); + div.addEventListener('mouseout', (e) => { + const toElement = (e as any).toElement as Element; + //console.log('onMouseOut', doc.id, e); + if(findUpClassName(toElement, 'gif') == div) { + return; + } + + //cancelEvent(e); + + mouseOut = true; + + const cb = () => { + if(div.lastElementChild != img) { + div.lastElementChild.remove(); + } + + div.addEventListener('mouseover', onMouseOver, {once: true}); + }; + + img && img.classList.remove('hide'); + /* window.requestAnimationFrame(() => { + window.requestAnimationFrame(); + }); */ + if(img) window.requestAnimationFrame(() => window.requestAnimationFrame(cb)); + else cb(); + }); + } + }; + + (posterURL ? renderImageFromUrl(img, posterURL, afterRender) : afterRender()); + } +} \ No newline at end of file diff --git a/src/components/sidebarRight/gifs.ts b/src/components/sidebarRight/gifs.ts new file mode 100644 index 00000000..bc6dd41b --- /dev/null +++ b/src/components/sidebarRight/gifs.ts @@ -0,0 +1,105 @@ +import { SliderTab } from "../slider"; +import SearchInput from "../searchInput"; +import Scrollable from "../scrollable_new"; +import LazyLoadQueue from "../lazyLoadQueue"; +import animationIntersector from "../animationIntersector"; +import appSidebarRight, { AppSidebarRight } from "../../lib/appManagers/appSidebarRight"; +import appUsersManager, { User } from "../../lib/appManagers/appUsersManager"; +import appInlineBotsManager, { AppInlineBotsManager } from "../../lib/appManagers/AppInlineBotsManager"; +import GifsMasonry from "../gifsMasonry"; + +const ANIMATIONGROUP = 'GIFS-SEARCH'; + +export default class AppGifsTab implements SliderTab { + private container = document.getElementById('search-gifs-container') as HTMLDivElement; + private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement; + private backBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement; + //private input = this.container.querySelector('#stickers-search') as HTMLInputElement; + private searchInput: SearchInput; + private gifsDiv = this.contentDiv.firstElementChild as HTMLDivElement; + private scrollable: Scrollable; + private lazyLoadQueue: LazyLoadQueue; + + private nextOffset = ''; + + private gifBotPeerID: number; + private masonry: GifsMasonry; + + private searchPromise: ReturnType; + + constructor() { + this.scrollable = new Scrollable(this.contentDiv, 'y', ANIMATIONGROUP, undefined, undefined, 2); + this.scrollable.setVirtualContainer(this.gifsDiv); + + this.masonry = new GifsMasonry(this.gifsDiv); + + this.lazyLoadQueue = new LazyLoadQueue(); + + this.searchInput = new SearchInput('Search GIFs', (value) => { + this.reset(); + this.search(value); + }); + + this.scrollable.onScrolledBottom = () => { + this.search(this.searchInput.value, false); + }; + + this.backBtn.parentElement.append(this.searchInput.container); + } + + public onCloseAfterTimeout() { + this.reset(); + this.gifsDiv.innerHTML = ''; + this.searchInput.value = ''; + animationIntersector.checkAnimations(undefined, ANIMATIONGROUP); + } + + private reset() { + this.searchPromise = null; + this.nextOffset = ''; + this.lazyLoadQueue.clear(); + } + + public init() { + appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.gifs); + + appSidebarRight.toggleSidebar(true).then(() => { + //this.renderFeatured(); + this.search('', true); + this.reset(); + }); + } + + public async search(query: string, newSearch = true) { + if(this.searchPromise) return; + + if(!this.gifBotPeerID) { + this.gifBotPeerID = (await appUsersManager.resolveUsername('gif')).id; + } + + try { + this.searchPromise = appInlineBotsManager.getInlineResults(0, this.gifBotPeerID, query, this.nextOffset); + const { results, next_offset } = await this.searchPromise; + + if(this.searchInput.value != query) { + return; + } + + this.searchPromise = null; + this.nextOffset = next_offset; + if(newSearch) { + this.gifsDiv.innerHTML = ''; + } + + results.forEach((result) => { + if(result._ === 'botInlineMediaResult' && result.document) { + this.masonry.add(result.document, ANIMATIONGROUP, this.lazyLoadQueue); + } + }); + + this.scrollable.onScroll(); + } catch (err) { + throw new Error(JSON.stringify(err)); + } + } +} \ No newline at end of file diff --git a/src/components/sidebarRight/pollResults.ts b/src/components/sidebarRight/pollResults.ts new file mode 100644 index 00000000..7d9f3577 --- /dev/null +++ b/src/components/sidebarRight/pollResults.ts @@ -0,0 +1,130 @@ +import { SliderTab } from "../slider"; +import Scrollable from "../scrollable_new"; +import appSidebarRight, { AppSidebarRight } from "../../lib/appManagers/appSidebarRight"; +import appPollsManager from "../../lib/appManagers/appPollsManager"; +import { roundPercents } from "../poll"; +import { RichTextProcessor } from "../../lib/richtextprocessor"; +import appDialogsManager from "../../lib/appManagers/appDialogsManager"; +import { ripple } from "../ripple"; + +export default class AppPollResultsTab implements SliderTab { + private container = document.getElementById('poll-results-container') as HTMLDivElement; + private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement; + private resultsDiv = this.contentDiv.firstElementChild as HTMLDivElement; + private scrollable: Scrollable; + + private pollID: string; + private mid: number; + + constructor() { + this.scrollable = new Scrollable(this.contentDiv, 'y', 'POLL-RESULTS', undefined, undefined, 2); + } + + public cleanup() { + this.resultsDiv.innerHTML = ''; + this.pollID = ''; + this.mid = 0; + } + + public onCloseAfterTimeout() { + this.cleanup(); + } + + public init(pollID: string, mid: number) { + if(this.pollID == pollID && this.mid == mid) return; + + this.cleanup(); + + this.pollID = pollID; + this.mid = mid; + + appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.pollResults); + + const poll = appPollsManager.getPoll(pollID); + + const title = document.createElement('h3'); + title.innerHTML = poll.poll.rQuestion; + + const percents = poll.results.results.map(v => v.voters / poll.results.total_voters * 100); + roundPercents(percents); + + const fragment = document.createDocumentFragment(); + poll.results.results.forEach((result, idx) => { + if(!result.voters) return; + + const hr = document.createElement('hr'); + + const answer = poll.poll.answers[idx]; + + // Head + const answerEl = document.createElement('div'); + answerEl.classList.add('poll-results-answer'); + + const answerTitle = document.createElement('div'); + answerTitle.innerHTML = RichTextProcessor.wrapEmojiText(answer.text); + + const answerPercents = document.createElement('div'); + answerPercents.innerText = Math.round(percents[idx]) + '%'; + + answerEl.append(answerTitle, answerPercents); + + // Humans + const list = document.createElement('ul'); + list.classList.add('poll-results-voters'); + + appDialogsManager.setListClickListener(list); + + list.style.minHeight = Math.min(result.voters, 4) * 50 + 'px'; + + fragment.append(hr, answerEl, list); + + let offset: string, limit = 4, loading = false, left = result.voters - 4; + const load = () => { + if(loading) return; + loading = true; + + appPollsManager.getVotes(mid, answer.option, offset, limit).then(votesList => { + votesList.votes.forEach(vote => { + const {dom} = appDialogsManager.addDialog(vote.user_id, list, false, false, undefined, false); + dom.lastMessageSpan.parentElement.remove(); + }); + + if(offset) { + left -= votesList.votes.length; + (showMore.lastElementChild as HTMLElement).innerText = `Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}`; + } + + offset = votesList.next_offset; + limit = 20; + + if(!left || !votesList.votes.length) { + showMore.remove(); + } + }).finally(() => { + loading = false; + }); + }; + + load(); + + if(left <= 0) return; + + const showMore = document.createElement('div'); + showMore.classList.add('poll-results-more', 'show-more'); + showMore.addEventListener('click', load); + + showMore.innerHTML = `
Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}
`; + ripple(showMore); + + fragment.append(showMore); + }); + + this.resultsDiv.append(title, fragment); + + appSidebarRight.toggleSidebar(true).then(() => { + /* appPollsManager.getVotes(mid).then(votes => { + console.log('gOt VotEs', votes); + }); */ + }); + } +} \ No newline at end of file diff --git a/src/components/sidebarRight/stickers.ts b/src/components/sidebarRight/stickers.ts new file mode 100644 index 00000000..8b1c6a01 --- /dev/null +++ b/src/components/sidebarRight/stickers.ts @@ -0,0 +1,235 @@ +import { SliderTab } from "../slider"; +import SearchInput from "../searchInput"; +import Scrollable from "../scrollable_new"; +import LazyLoadQueue from "../lazyLoadQueue"; +import { findUpClassName } from "../../lib/utils"; +import appImManager from "../../lib/appManagers/appImManager"; +import appStickersManager, { MTStickerSet, MTStickerSetCovered, MTStickerSetMultiCovered } from "../../lib/appManagers/appStickersManager"; +import PopupStickers from "../popupStickers"; +import animationIntersector from "../animationIntersector"; +import { RichTextProcessor } from "../../lib/richtextprocessor"; +import { wrapSticker } from "../wrappers"; +import appSidebarRight, { AppSidebarRight } from "../../lib/appManagers/appSidebarRight"; + +export default class AppStickersTab implements SliderTab { + private container = document.getElementById('stickers-container') as HTMLDivElement; + private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement; + private backBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement; + //private input = this.container.querySelector('#stickers-search') as HTMLInputElement; + private searchInput: SearchInput; + private setsDiv = this.contentDiv.firstElementChild as HTMLDivElement; + private scrollable: Scrollable; + private lazyLoadQueue: LazyLoadQueue; + + constructor() { + this.scrollable = new Scrollable(this.contentDiv, 'y', 'STICKERS-SEARCH', undefined, undefined, 2); + this.scrollable.setVirtualContainer(this.setsDiv); + + this.lazyLoadQueue = new LazyLoadQueue(); + + this.searchInput = new SearchInput('Search Stickers', (value) => { + this.search(value); + }); + + this.backBtn.parentElement.append(this.searchInput.container); + + this.setsDiv.addEventListener('click', (e) => { + const sticker = findUpClassName(e.target, 'sticker-set-sticker'); + if(sticker) { + const docID = sticker.dataset.docID; + appImManager.chatInputC.sendMessageWithDocument(docID); + return; + } + + const target = findUpClassName(e.target, 'sticker-set'); + if(!target) return; + + + const id = target.dataset.stickerSet as string; + const access_hash = target.dataset.stickerSet as string; + + const button = findUpClassName(e.target, 'sticker-set-button') as HTMLElement; + if(button) { + e.preventDefault(); + e.cancelBubble = true; + + button.setAttribute('disabled', 'true'); + + appStickersManager.getStickerSet({id, access_hash}).then(full => { + appStickersManager.toggleStickerSet(full.set).then(changed => { + if(changed) { + button.innerText = full.set.installed_date ? 'Added' : 'Add'; + button.classList.toggle('gray', !!full.set.installed_date); + } + }).finally(() => { + //button.style.width = set.installed_date ? '68px' : '52px'; + button.removeAttribute('disabled'); + }); + }); + } else { + appStickersManager.getStickerSet({id, access_hash}).then(full => { + new PopupStickers(full.set).show(); + }); + } + }); + } + + public onCloseAfterTimeout() { + this.setsDiv.innerHTML = ''; + this.searchInput.value = ''; + animationIntersector.checkAnimations(undefined, 'STICKERS-SEARCH'); + } + + public renderSet(set: MTStickerSet) { + //console.log('renderSet:', set); + const div = document.createElement('div'); + div.classList.add('sticker-set'); + + const header = document.createElement('div'); + header.classList.add('sticker-set-header'); + + const details = document.createElement('div'); + details.classList.add('sticker-set-details'); + details.innerHTML = ` +
${RichTextProcessor.wrapEmojiText(set.title)}
+
${set.count} stickers
+ `; + + const button = document.createElement('button'); + button.classList.add('btn-primary', 'sticker-set-button'); + button.innerText = set.installed_date ? 'Added' : 'Add'; + // button.style.width = set.installed_date ? '68px' : '52px'; + + if(set.installed_date) { + button.classList.add('gray'); + } + + //ripple(button); + + header.append(details, button); + + const stickersDiv = document.createElement('div'); + stickersDiv.classList.add('sticker-set-stickers'); + + const count = Math.min(5, set.count); + for(let i = 0; i < count; ++i) { + const stickerDiv = document.createElement('div'); + stickerDiv.classList.add('sticker-set-sticker'); + + stickersDiv.append(stickerDiv); + } + + appStickersManager.getStickerSet(set).then(set => { + //console.log('renderSet got set:', set); + + for(let i = 0; i < count; ++i) { + const div = stickersDiv.children[i] as HTMLDivElement; + wrapSticker({ + doc: set.documents[i], + div, + lazyLoadQueue: this.lazyLoadQueue, + group: 'STICKERS-SEARCH', + /* play: false, + loop: false, */ + play: true, + loop: true, + width: 68, + height: 68 + }); + } + }); + + /* const onMouseOver = () => { + const animations: AnimationItem['animation'][] = []; + for(let i = 0; i < count; ++i) { + const stickerDiv = stickersDiv.children[i] as HTMLElement; + const animationItem = animationIntersector.getAnimation(stickerDiv); + if(!animationItem) continue; + + const animation = animationItem.animation; + + animations.push(animation); + animation.loop = true; + animation.play(); + } + + div.addEventListener('mouseout', () => { + animations.forEach(animation => { + animation.loop = false; + }); + + div.addEventListener('mouseover', onMouseOver, {once: true}); + }, {once: true}); + }; + + div.addEventListener('mouseover', onMouseOver, {once: true}); */ + + div.dataset.stickerSet = set.id; + div.dataset.access_hash = set.access_hash; + div.dataset.title = set.title; + + div.append(header, stickersDiv); + + this.scrollable.append(div); + } + + public init() { + appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.stickers); + + appSidebarRight.toggleSidebar(true).then(() => { + this.renderFeatured(); + }); + } + + public renderFeatured() { + return appStickersManager.getFeaturedStickers().then(coveredSets => { + if(this.searchInput.value) { + return; + } + + coveredSets = this.filterRendered('', coveredSets); + coveredSets.forEach(set => { + this.renderSet(set.set); + }); + }); + } + + private filterRendered(query: string, coveredSets: (MTStickerSetCovered | MTStickerSetMultiCovered)[]) { + coveredSets = coveredSets.slice(); + + const children = Array.from(this.setsDiv.children) as HTMLElement[]; + children.forEachReverse(el => { + const id = el.dataset.stickerSet; + const index = coveredSets.findIndex(covered => covered.set.id == id); + + if(index !== -1) { + coveredSets.splice(index, 1); + } else if(!query || !el.dataset.title.toLowerCase().includes(query.toLowerCase())) { + el.remove(); + } + }); + + animationIntersector.checkAnimations(undefined, 'STICKERS-SEARCH'); + + return coveredSets; + } + + public search(query: string) { + if(!query) { + return this.renderFeatured(); + } + + return appStickersManager.searchStickerSets(query, false).then(coveredSets => { + if(this.searchInput.value != query) { + return; + } + + //console.log('search result:', coveredSets); + + coveredSets = this.filterRendered(query, coveredSets); + coveredSets.forEach(set => { + this.renderSet(set.set); + }); + }); + } +} \ No newline at end of file diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 6b88a8c5..9203d75b 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -446,7 +446,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o } }; - if(thumb.bytes) { + if(thumb.bytes || thumb.url) { img = new Image(); if((!isSafari || doc.stickerThumbConverted)/* && false */) { diff --git a/src/index.hbs b/src/index.hbs index 3cbf4782..91fe23c7 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -672,6 +672,12 @@ + diff --git a/src/lib/appManagers/AppInlineBotsManager.ts b/src/lib/appManagers/AppInlineBotsManager.ts index 0b7e149b..f3b36a73 100644 --- a/src/lib/appManagers/AppInlineBotsManager.ts +++ b/src/lib/appManagers/AppInlineBotsManager.ts @@ -4,11 +4,99 @@ import appPeersManager from "../appManagers/appPeersManager"; import appMessagesIDsManager from "./appMessagesIDsManager"; import { RichTextProcessor } from "../richtextprocessor"; import { toast } from "../../components/toast"; +import appUsersManager, { User } from "./appUsersManager"; +import appPhotosManager, { MTPhoto } from "./appPhotosManager"; +import { MTDocument } from "../../types"; +import appDocsManager from "./appDocsManager"; + +type botInlineResult = { + _: 'botInlineResult', + flags: number, + id: string, + type: string, + title?: string, + description?: string, + url?: string, + thumb: any, + content: any, + send_message: any +}; +type botInlineMediaResult = { + _: 'botInlineMediaResult', + flags: number, + id: string, + type: string, + photo?: MTPhoto, + document?: MTDocument, + title?: string, + description?: string, + send_message: any +}; +type BotInlineResult = (botInlineResult | botInlineMediaResult) & Partial<{ + qID: string, + botID: number, + rTitle: string, + rDescription: string, + initials: string +}>; export class AppInlineBotsManager { - /* private inlineResults: any = {}; + private inlineResults: {[qID: string]: BotInlineResult} = {}; + + public getInlineResults(peerID: number, botID: number, query = '', offset = '', geo?: any) { + return apiManagerProxy.invokeApi('messages.getInlineBotResults', { + flags: 0 | (geo ? 1 : 0), + bot: appUsersManager.getUserInput(botID), + peer: appPeersManager.getInputPeerByID(peerID), + query: query, + geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']}, + offset + }, {timeout: 1, stopTime: -1, noErrorBox: true}).then((botResults: { + _: 'messages.botResults', + flags: number, + pFlags: Partial<{gallery: true}>, + query_id: string, + next_offset?: string, + switch_pm?: any, + results: BotInlineResult[], + cache_time: number, + users: User[] + }) => { + const queryID = botResults.query_id; + /* delete botResults._; + delete botResults.flags; + delete botResults.query_id; */ + + if(botResults.switch_pm) { + botResults.switch_pm.rText = RichTextProcessor.wrapRichText(botResults.switch_pm.text, {noLinebreaks: true, noLinks: true}); + } + + botResults.results.forEach((result: BotInlineResult) => { + const qID = queryID + '_' + result.id; + result.qID = qID; + result.botID = botID; + + result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true}); + result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true}); + result.initials = ((result as botInlineResult).url || result.title || result.type || '').substr(0, 1); + + if(result._ == 'botInlineMediaResult') { + if(result.document) { + result.document = appDocsManager.saveDoc(result.document); + } + if(result.photo) { + result.photo = appPhotosManager.savePhoto(result.photo); + } + } + + this.inlineResults[qID] = result; + }); + + return botResults; + }); + } - function getPopularBots () { + /* function getPopularBots () { return Storage.get('inline_bots_popular').then(function (bots) { var result = [] var i, len @@ -91,46 +179,6 @@ export class AppInlineBotsManager { }) } - function getInlineResults (peerID, botID, query, geo, offset) { - return MtpApiManager.invokeApi('messages.getInlineBotResults', { - flags: 0 | (geo ? 1 : 0), - bot: AppUsersManager.getUserInput(botID), - peer: AppPeersManager.getInputPeerByID(peerID), - query: query, - geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']}, - offset: offset - }, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function (botResults) { - var queryID = botResults.query_id - delete botResults._ - delete botResults.flags - delete botResults.query_id - - if (botResults.switch_pm) { - botResults.switch_pm.rText = RichTextProcessor.wrapRichText(botResults.switch_pm.text, {noLinebreaks: true, noLinks: true}) - } - - angular.forEach(botResults.results, function (result) { - var qID = queryID + '_' + result.id - result.qID = qID - result.botID = botID - - result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true}) - result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true}) - result.initials = (result.url || result.title || result.type || '').substr(0, 1) - - if (result.document) { - AppDocsManager.saveDoc(result.document) - } - if (result.photo) { - AppPhotosManager.savePhoto(result.photo) - } - - inlineResults[qID] = result - }) - return botResults - }) - } - function regroupWrappedResults (results, rowW, rowH) { if (!results || !results[0] || diff --git a/src/lib/appManagers/appSidebarRight.ts b/src/lib/appManagers/appSidebarRight.ts index 869f585b..b567f92e 100644 --- a/src/lib/appManagers/appSidebarRight.ts +++ b/src/lib/appManagers/appSidebarRight.ts @@ -18,15 +18,11 @@ import AvatarElement from "../../components/avatar"; import appForward from "../../components/appForward"; import { mediaSizes } from "../config"; import SidebarSlider, { SliderTab } from "../../components/slider"; -import appStickersManager, { MTStickerSet, MTStickerSetCovered, MTStickerSetMultiCovered } from "./appStickersManager"; -import animationIntersector from "../../components/animationIntersector"; -import PopupStickers from "../../components/popupStickers"; import SearchInput from "../../components/searchInput"; -import appPollsManager from "./appPollsManager"; -import { roundPercents } from "../../components/poll"; -import appDialogsManager from "./appDialogsManager"; -import { ripple } from "../../components/ripple"; import { horizontalMenu } from "../../components/horizontalMenu"; +import AppStickersTab from "../../components/sidebarRight/stickers"; +import AppPollResultsTab from "../../components/sidebarRight/pollResults"; +import AppGifsTab from "../../components/sidebarRight/gifs"; const testScroll = false; @@ -44,353 +40,9 @@ let setText = (text: string, el: HTMLDivElement) => { }); }; -class AppStickersTab implements SliderTab { - private container = document.getElementById('stickers-container') as HTMLDivElement; - private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement; - private backBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement; - //private input = this.container.querySelector('#stickers-search') as HTMLInputElement; - private searchInput: SearchInput; - private setsDiv = this.contentDiv.firstElementChild as HTMLDivElement; - private scrollable: Scrollable; - private lazyLoadQueue: LazyLoadQueue; - - constructor() { - this.scrollable = new Scrollable(this.contentDiv, 'y', 'STICKERS-SEARCH', undefined, undefined, 2); - this.scrollable.setVirtualContainer(this.setsDiv); - - this.lazyLoadQueue = new LazyLoadQueue(); - - this.searchInput = new SearchInput('Search Stickers', (value) => { - this.search(value); - }); - - this.backBtn.parentElement.append(this.searchInput.container); - - this.setsDiv.addEventListener('click', (e) => { - const sticker = findUpClassName(e.target, 'sticker-set-sticker'); - if(sticker) { - const docID = sticker.dataset.docID; - appImManager.chatInputC.sendMessageWithDocument(docID); - return; - } - - const target = findUpClassName(e.target, 'sticker-set'); - if(!target) return; - - - const id = target.dataset.stickerSet as string; - const access_hash = target.dataset.stickerSet as string; - - const button = findUpClassName(e.target, 'sticker-set-button') as HTMLElement; - if(button) { - e.preventDefault(); - e.cancelBubble = true; - - button.setAttribute('disabled', 'true'); - - appStickersManager.getStickerSet({id, access_hash}).then(full => { - appStickersManager.toggleStickerSet(full.set).then(changed => { - if(changed) { - button.innerText = full.set.installed_date ? 'Added' : 'Add'; - button.classList.toggle('gray', !!full.set.installed_date); - } - }).finally(() => { - //button.style.width = set.installed_date ? '68px' : '52px'; - button.removeAttribute('disabled'); - }); - }); - } else { - appStickersManager.getStickerSet({id, access_hash}).then(full => { - new PopupStickers(full.set).show(); - }); - } - }); - } - - public onCloseAfterTimeout() { - this.setsDiv.innerHTML = ''; - this.searchInput.value = ''; - animationIntersector.checkAnimations(undefined, 'STICKERS-SEARCH'); - } - - public renderSet(set: MTStickerSet) { - //console.log('renderSet:', set); - const div = document.createElement('div'); - div.classList.add('sticker-set'); - - const header = document.createElement('div'); - header.classList.add('sticker-set-header'); - - const details = document.createElement('div'); - details.classList.add('sticker-set-details'); - details.innerHTML = ` -
${RichTextProcessor.wrapEmojiText(set.title)}
-
${set.count} stickers
- `; - - const button = document.createElement('button'); - button.classList.add('btn-primary', 'sticker-set-button'); - button.innerText = set.installed_date ? 'Added' : 'Add'; - // button.style.width = set.installed_date ? '68px' : '52px'; - - if(set.installed_date) { - button.classList.add('gray'); - } - - //ripple(button); - - header.append(details, button); - - const stickersDiv = document.createElement('div'); - stickersDiv.classList.add('sticker-set-stickers'); - - const count = Math.min(5, set.count); - for(let i = 0; i < count; ++i) { - const stickerDiv = document.createElement('div'); - stickerDiv.classList.add('sticker-set-sticker'); - - stickersDiv.append(stickerDiv); - } - - appStickersManager.getStickerSet(set).then(set => { - //console.log('renderSet got set:', set); - - for(let i = 0; i < count; ++i) { - const div = stickersDiv.children[i] as HTMLDivElement; - wrapSticker({ - doc: set.documents[i], - div, - lazyLoadQueue: this.lazyLoadQueue, - group: 'STICKERS-SEARCH', - /* play: false, - loop: false, */ - play: true, - loop: true, - width: 68, - height: 68 - }); - } - }); - - /* const onMouseOver = () => { - const animations: AnimationItem['animation'][] = []; - for(let i = 0; i < count; ++i) { - const stickerDiv = stickersDiv.children[i] as HTMLElement; - const animationItem = animationIntersector.getAnimation(stickerDiv); - if(!animationItem) continue; - - const animation = animationItem.animation; - - animations.push(animation); - animation.loop = true; - animation.play(); - } - - div.addEventListener('mouseout', () => { - animations.forEach(animation => { - animation.loop = false; - }); - - div.addEventListener('mouseover', onMouseOver, {once: true}); - }, {once: true}); - }; - - div.addEventListener('mouseover', onMouseOver, {once: true}); */ - - div.dataset.stickerSet = set.id; - div.dataset.access_hash = set.access_hash; - div.dataset.title = set.title; - - div.append(header, stickersDiv); - - this.scrollable.append(div); - } - - public init() { - appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.stickers); - - appSidebarRight.toggleSidebar(true).then(() => { - this.renderFeatured(); - }); - } - - public renderFeatured() { - return appStickersManager.getFeaturedStickers().then(coveredSets => { - if(this.searchInput.value) { - return; - } - - coveredSets = this.filterRendered('', coveredSets); - coveredSets.forEach(set => { - this.renderSet(set.set); - }); - }); - } - - private filterRendered(query: string, coveredSets: (MTStickerSetCovered | MTStickerSetMultiCovered)[]) { - coveredSets = coveredSets.slice(); - - const children = Array.from(this.setsDiv.children) as HTMLElement[]; - children.forEachReverse(el => { - const id = el.dataset.stickerSet; - const index = coveredSets.findIndex(covered => covered.set.id == id); - - if(index !== -1) { - coveredSets.splice(index, 1); - } else if(!query || !el.dataset.title.toLowerCase().includes(query.toLowerCase())) { - el.remove(); - } - }); - - animationIntersector.checkAnimations(undefined, 'STICKERS-SEARCH'); - - return coveredSets; - } - - public search(query: string) { - if(!query) { - return this.renderFeatured(); - } - - return appStickersManager.searchStickerSets(query, false).then(coveredSets => { - if(this.searchInput.value != query) { - return; - } - - //console.log('search result:', coveredSets); - - coveredSets = this.filterRendered(query, coveredSets); - coveredSets.forEach(set => { - this.renderSet(set.set); - }); - }); - } -} - -class AppPollResultsTab implements SliderTab { - private container = document.getElementById('poll-results-container') as HTMLDivElement; - private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement; - private resultsDiv = this.contentDiv.firstElementChild as HTMLDivElement; - private scrollable: Scrollable; - - private pollID: string; - private mid: number; - - constructor() { - this.scrollable = new Scrollable(this.contentDiv, 'y', 'POLL-RESULTS', undefined, undefined, 2); - } - - public cleanup() { - this.resultsDiv.innerHTML = ''; - this.pollID = ''; - this.mid = 0; - } - - public onCloseAfterTimeout() { - this.cleanup(); - } - - public init(pollID: string, mid: number) { - if(this.pollID == pollID && this.mid == mid) return; - - this.cleanup(); - - this.pollID = pollID; - this.mid = mid; - - appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.pollResults); - - const poll = appPollsManager.getPoll(pollID); - - const title = document.createElement('h3'); - title.innerHTML = poll.poll.rQuestion; - - const percents = poll.results.results.map(v => v.voters / poll.results.total_voters * 100); - roundPercents(percents); - - const fragment = document.createDocumentFragment(); - poll.results.results.forEach((result, idx) => { - if(!result.voters) return; - - const hr = document.createElement('hr'); - - const answer = poll.poll.answers[idx]; - - // Head - const answerEl = document.createElement('div'); - answerEl.classList.add('poll-results-answer'); - - const answerTitle = document.createElement('div'); - answerTitle.innerHTML = RichTextProcessor.wrapEmojiText(answer.text); - - const answerPercents = document.createElement('div'); - answerPercents.innerText = Math.round(percents[idx]) + '%'; - - answerEl.append(answerTitle, answerPercents); - - // Humans - const list = document.createElement('ul'); - list.classList.add('poll-results-voters'); - - appDialogsManager.setListClickListener(list); - - list.style.minHeight = Math.min(result.voters, 4) * 50 + 'px'; - - fragment.append(hr, answerEl, list); - - let offset: string, limit = 4, loading = false, left = result.voters - 4; - const load = () => { - if(loading) return; - loading = true; - - appPollsManager.getVotes(mid, answer.option, offset, limit).then(votesList => { - votesList.votes.forEach(vote => { - const {dom} = appDialogsManager.addDialog(vote.user_id, list, false, false, undefined, false); - dom.lastMessageSpan.parentElement.remove(); - }); - - if(offset) { - left -= votesList.votes.length; - (showMore.lastElementChild as HTMLElement).innerText = `Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}`; - } - - offset = votesList.next_offset; - limit = 20; - - if(!left || !votesList.votes.length) { - showMore.remove(); - } - }).finally(() => { - loading = false; - }); - }; - - load(); - - if(left <= 0) return; - - const showMore = document.createElement('div'); - showMore.classList.add('poll-results-more', 'show-more'); - showMore.addEventListener('click', load); - - showMore.innerHTML = `
Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}
`; - ripple(showMore); - - fragment.append(showMore); - }); - - this.resultsDiv.append(title, fragment); - - appSidebarRight.toggleSidebar(true).then(() => { - /* appPollsManager.getVotes(mid).then(votes => { - console.log('gOt VotEs', votes); - }); */ - }); - } -} - const stickersTab = new AppStickersTab(); const pollResultsTab = new AppPollResultsTab(); +const gifsTab = new AppGifsTab(); export class AppSidebarRight extends SidebarSlider { public static SLIDERITEMSIDS = { @@ -398,6 +50,7 @@ export class AppSidebarRight extends SidebarSlider { forward: 2, stickers: 3, pollResults: 4, + gifs: 5, }; public profileContainer: HTMLDivElement; @@ -467,17 +120,20 @@ export class AppSidebarRight extends SidebarSlider { public stickersTab: AppStickersTab; public pollResultsTab: AppPollResultsTab; + public gifsTab: AppGifsTab; constructor() { super(document.getElementById('column-right') as HTMLElement, { [AppSidebarRight.SLIDERITEMSIDS.stickers]: stickersTab, [AppSidebarRight.SLIDERITEMSIDS.pollResults]: pollResultsTab, + [AppSidebarRight.SLIDERITEMSIDS.gifs]: gifsTab }); //this._selectTab(3); this.stickersTab = stickersTab; this.pollResultsTab = pollResultsTab; + this.gifsTab = gifsTab; this.profileContainer = this.sidebarEl.querySelector('.profile-container'); this.profileContentEl = this.sidebarEl.querySelector('.profile-content'); diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index 53d7a154..83b9acf3 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -133,6 +133,19 @@ export class AppUsersManager { }); } + public async resolveUsername(username: string) { + if(this.usernames[username]) { + return this.users[this.usernames[username]]; + } + + return await apiManager.invokeApi('contacts.resolveUsername', {username}).then(resolvedPeer => { + this.saveApiUser(resolvedPeer.users[0]); + appChatsManager.saveApiChats(resolvedPeer.chats); + + return this.users[this.usernames[username]]; + }); + } + public pushContact(userID: number) { this.contactsList.add(userID); searchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex); @@ -183,9 +196,9 @@ export class AppUsersManager { }); } - public resolveUsername(username: string) { + /* public resolveUsername(username: string) { return this.usernames[username] || 0; - } + } */ public saveApiUsers(apiUsers: any[]) { apiUsers.forEach((user) => this.saveApiUser(user)); diff --git a/src/lib/mtproto/mtproto.service.ts b/src/lib/mtproto/mtproto.service.ts index bfcf7a80..7cc7dd64 100644 --- a/src/lib/mtproto/mtproto.service.ts +++ b/src/lib/mtproto/mtproto.service.ts @@ -78,7 +78,7 @@ networkerFactory.setUpdatesProcessor((obj, bool) => { notify({update: {obj, bool}}); }); -ctx.addEventListener('message', async(e) => { +const onMessage = async(e: ExtendableMessageEvent) => { const taskID = e.data.taskID; log.debug('got message:', taskID, e, e.data); @@ -139,7 +139,9 @@ ctx.addEventListener('message', async(e) => { //throw new Error('Unknown task: ' + e.data.task); } } -}); +}; + +ctx.onmessage = onMessage; /** * Service Worker Installation @@ -195,14 +197,11 @@ function responseForSafariFirstRange(range: [number, number], mimeType: string, return null; } -ctx.addEventListener('error', (error) => { +ctx.onerror = (error) => { log.error('error:', error); -}); +}; -/** - * Fetch requests - */ -ctx.addEventListener('fetch', (event: FetchEvent): void => { +const onFetch = (event: FetchEvent): void => { const [, url, scope, params] = /http[:s]+\/\/.*?(\/(.*?)(?:$|\/(.*)$))/.exec(event.request.url) || []; log.debug('[fetch]:', event); @@ -440,11 +439,19 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => { if (url && url.endsWith('.tgs')) event.respondWith(fetchTGS(url)); else event.respondWith(fetch(event.request.url)); */ } -}); +}; + +/** + * Fetch requests + */ +//ctx.addEventListener('fetch', ); +ctx.onfetch = onFetch; const DOWNLOAD_CHUNK_LIMIT = 512 * 1024; -const STREAM_CHUNK_UPPER_LIMIT = 256 * 1024; -const SMALLEST_CHUNK_LIMIT = 256 * 4; +//const STREAM_CHUNK_UPPER_LIMIT = 256 * 1024; +//const SMALLEST_CHUNK_LIMIT = 256 * 4; +const STREAM_CHUNK_UPPER_LIMIT = 1024 * 1024; +const SMALLEST_CHUNK_LIMIT = 1024 * 4; function parseRange(header: string): [number, number] { if(!header) return [0, 0]; @@ -462,3 +469,9 @@ function alignOffset(offset: number, base = SMALLEST_CHUNK_LIMIT) { function alignLimit(limit: number) { return 2 ** Math.ceil(Math.log(limit) / Math.log(2)); } + +// @ts-ignore +if(process.env.NODE_ENV != 'production') { + (ctx as any).onMessage = onMessage; + (ctx as any).onFetch = onFetch; +} diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 3d66fe07..6e488ec1 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -878,7 +878,7 @@ $bubble-margin: .25rem; padding: 0; display: flex; align-items: center; - width: auto; + width: auto !important; .inner { margin-bottom: 0; @@ -910,7 +910,7 @@ $bubble-margin: .25rem; i { font-size: 1.15rem; margin-right: .4rem; - margin-left: .1rem; + /* margin-left: .1rem; */ } i.edited { @@ -960,7 +960,8 @@ $bubble-margin: .25rem; &.emoji-big, &.sticker { .time { - width: 81px !important; + /* width: 81px !important; */ + min-width: unset; } } } @@ -1167,7 +1168,8 @@ $bubble-margin: .25rem; .time { color: #a3adb6; - width: 36px; + /* width: 36px; */ + padding-left: 36px; .inner { padding: 0 7px 0 5px; diff --git a/src/scss/partials/_emojiDropdown.scss b/src/scss/partials/_emojiDropdown.scss index e6202264..f98580c9 100644 --- a/src/scss/partials/_emojiDropdown.scss +++ b/src/scss/partials/_emojiDropdown.scss @@ -322,33 +322,4 @@ } } } - - #content-gifs { - .gifs-masonry { - display: flex; - flex-wrap: wrap; - - > .gif { - flex: 1 0 auto; - max-width: 100%; - height: 100px; - margin: 2.5px; - cursor: pointer; - //background: #000; - position: relative; - - video, img { - object-fit: cover; - width: 100%; - height: 100%; - } - - img { - position: absolute; - left: 0; - top: 0; - } - } - } - } } diff --git a/src/scss/partials/_gifsMasonry.scss b/src/scss/partials/_gifsMasonry.scss new file mode 100644 index 00000000..7a0d43e2 --- /dev/null +++ b/src/scss/partials/_gifsMasonry.scss @@ -0,0 +1,26 @@ +.gifs-masonry { + display: flex; + flex-wrap: wrap; + + > .gif { + flex: 1 0 auto; + max-width: 100%; + height: 100px; + margin: 2.5px; + cursor: pointer; + //background: #000; + position: relative; + + video, img { + object-fit: cover; + width: 100%; + height: 100%; + } + + img { + position: absolute; + left: 0; + top: 0; + } + } +} \ No newline at end of file diff --git a/src/scss/style.scss b/src/scss/style.scss index bc0ec9bb..80089c68 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -48,6 +48,7 @@ $large-screen: 1680px; @import "partials/scrollable"; @import "partials/slider"; @import "partials/selector"; +@import "partials/gifsMasonry"; @import "partials/popups/popup"; @import "partials/popups/editAvatar"; diff --git a/src/types.d.ts b/src/types.d.ts index 06c6a64a..2f1b3421 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -9,6 +9,7 @@ export type MTDocument = { mime_type: string, size: number, thumbs: MTPhotoSize[], + video_thumbs?: MTVideoSize[], dc_id: number, attributes: any[], @@ -46,6 +47,8 @@ export type MTPhotoSize = { url?: string }; +export type MTVideoSize = Omit & {_: 'videoSize'}; + export type InvokeApiOptions = Partial<{ dcID: number, timeout: number,