GIFs search
GIFs autoplay in panel Fixed (not) bubble time (edited, etc)
This commit is contained in:
parent
0102df4247
commit
489ecb974b
@ -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",
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
res.gifs.forEach((doc, idx) => {
|
||||
res.gifs[idx] = appDocsManager.saveDoc(doc);
|
||||
masonry.add(doc, EMOTICONSSTICKERGROUP, EmoticonsDropdown.lazyLoadQueue);
|
||||
});
|
||||
|
||||
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 = `<video autoplay="true" muted="true" loop="true" src="${doc.url}" poster="${posterURL}" type="${doc.mime_type}"></video>`;
|
||||
});
|
||||
|
||||
return download.promise;
|
||||
}
|
||||
}); */
|
||||
}
|
||||
});
|
||||
|
||||
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', () => {
|
||||
if(this.tabID == 1) {
|
||||
appSidebarRight.stickersTab.init();
|
||||
} else {
|
||||
appSidebarRight.gifsTab.init();
|
||||
}
|
||||
});
|
||||
|
||||
this.deleteBtn = this.element.querySelector('.emoji-tabs-delete');
|
||||
|
129
src/components/gifsMasonry.ts
Normal file
129
src/components/gifsMasonry.ts
Normal file
@ -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());
|
||||
}
|
||||
}
|
105
src/components/sidebarRight/gifs.ts
Normal file
105
src/components/sidebarRight/gifs.ts
Normal file
@ -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<AppInlineBotsManager['getInlineResults']>;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
130
src/components/sidebarRight/pollResults.ts
Normal file
130
src/components/sidebarRight/pollResults.ts
Normal file
@ -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 = `<div class="tgico-down"></div><div>Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}</div>`;
|
||||
ripple(showMore);
|
||||
|
||||
fragment.append(showMore);
|
||||
});
|
||||
|
||||
this.resultsDiv.append(title, fragment);
|
||||
|
||||
appSidebarRight.toggleSidebar(true).then(() => {
|
||||
/* appPollsManager.getVotes(mid).then(votes => {
|
||||
console.log('gOt VotEs', votes);
|
||||
}); */
|
||||
});
|
||||
}
|
||||
}
|
235
src/components/sidebarRight/stickers.ts
Normal file
235
src/components/sidebarRight/stickers.ts
Normal file
@ -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 = `
|
||||
<div class="sticker-set-name">${RichTextProcessor.wrapEmojiText(set.title)}</div>
|
||||
<div class="sticker-set-count">${set.count} stickers</div>
|
||||
`;
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -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 */) {
|
||||
|
@ -672,6 +672,12 @@
|
||||
</div>
|
||||
<div class="sidebar-content"><div class="poll-results"></div></div>
|
||||
</div>
|
||||
<div class="sidebar-slider-item sidebar-search chats-container" id="search-gifs-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
|
||||
</div>
|
||||
<div class="sidebar-content"><div class="gifs-masonry"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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} = {};
|
||||
|
||||
function getPopularBots () {
|
||||
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 () {
|
||||
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] ||
|
||||
|
@ -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 = `
|
||||
<div class="sticker-set-name">${RichTextProcessor.wrapEmojiText(set.title)}</div>
|
||||
<div class="sticker-set-count">${set.count} stickers</div>
|
||||
`;
|
||||
|
||||
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 = `<div class="tgico-down"></div><div>Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}</div>`;
|
||||
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');
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
src/scss/partials/_gifsMasonry.scss
Normal file
26
src/scss/partials/_gifsMasonry.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
|
3
src/types.d.ts
vendored
3
src/types.d.ts
vendored
@ -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<MTPhotoSize, '_'> & {_: 'videoSize'};
|
||||
|
||||
export type InvokeApiOptions = Partial<{
|
||||
dcID: number,
|
||||
timeout: number,
|
||||
|
Loading…
x
Reference in New Issue
Block a user