Eduard Kuzmenko b94dba3d9b Respect read comments position
Fix bluring keyboard on Android
Fix reading messages when page was blured
Fix instant reading messages if there only one page of them
Split dom functions
2021-05-08 21:46:50 +04:00

363 lines
12 KiB
TypeScript

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { generateSection } from "..";
import { averageColor } from "../../../helpers/averageColor";
import blur from "../../../helpers/blur";
import { deferredPromise } from "../../../helpers/cancellablePromise";
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import findUpClassName from "../../../helpers/dom/findUpClassName";
import { requestFile } from "../../../helpers/files";
import highlightningColor from "../../../helpers/highlightningColor";
import { copy } from "../../../helpers/object";
import sequentialDom from "../../../helpers/sequentialDom";
import { AccountWallPapers, PhotoSize, WallPaper } from "../../../layer";
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager";
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
import appImManager from "../../../lib/appManagers/appImManager";
import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
import appStateManager, { Theme, STATE_INIT } from "../../../lib/appManagers/appStateManager";
import apiManager from "../../../lib/mtproto/mtprotoworker";
import rootScope from "../../../lib/rootScope";
import Button from "../../button";
import CheckboxField from "../../checkboxField";
import ProgressivePreloader from "../../preloader";
import { SliderSuperTab } from "../../slider";
import { wrapPhoto } from "../../wrappers";
import AppBackgroundColorTab from "./backgroundColor";
let uploadTempId = 0;
export default class AppBackgroundTab extends SliderSuperTab {
private grid: HTMLElement;
private tempId = 0;
private theme: Theme;
private clicked: Set<string> = new Set();
private blurCheckboxField: CheckboxField;
init() {
this.container.classList.add('background-container', 'background-image-container');
this.setTitle('ChatBackground');
this.theme = rootScope.getTheme();
{
const container = generateSection(this.scrollable);
const uploadButton = Button('btn-primary btn-transparent', {icon: 'cameraadd', text: 'ChatBackground.UploadWallpaper'});
const colorButton = Button('btn-primary btn-transparent', {icon: 'colorize', text: 'SetColor'});
const resetButton = Button('btn-primary btn-transparent', {icon: 'favourites', text: 'Appearance.Reset'});
attachClickEvent(uploadButton, this.onUploadClick, {listenerSetter: this.listenerSetter});
attachClickEvent(colorButton, () => {
new AppBackgroundColorTab(this.slider).open();
}, {listenerSetter: this.listenerSetter});
attachClickEvent(resetButton, this.onResetClick, {listenerSetter: this.listenerSetter});
const blurCheckboxField = this.blurCheckboxField = new CheckboxField({
text: 'ChatBackground.Blur',
name: 'blur',
checked: this.theme.background.blur,
withRipple: true
});
this.listenerSetter.add(blurCheckboxField.input, 'change', () => {
this.theme.background.blur = blurCheckboxField.input.checked;
appStateManager.pushToState('settings', rootScope.settings);
const active = grid.querySelector('.active') as HTMLElement;
if(!active) return;
// * wait for animation end
setTimeout(() => {
this.setBackgroundDocument(active.dataset.slug, appDocsManager.getDoc(active.dataset.docId));
}, 100);
});
container.append(uploadButton, colorButton, resetButton, blurCheckboxField.label);
}
rootScope.on('background_change', this.setActive);
apiManager.invokeApiHashable('account.getWallPapers').then((accountWallpapers) => {
const wallpapers = (accountWallpapers as AccountWallPapers.accountWallPapers).wallpapers as WallPaper.wallPaper[];
wallpapers.forEach((wallpaper) => {
this.addWallPaper(wallpaper);
});
//console.log(accountWallpapers);
});
const grid = this.grid = document.createElement('div');
grid.classList.add('grid');
attachClickEvent(grid, this.onGridClick, {listenerSetter: this.listenerSetter});
this.scrollable.append(grid);
}
private onUploadClick = () => {
requestFile('image/x-png,image/png,image/jpeg').then(file => {
const id = 'wallpaper-upload-' + ++uploadTempId;
const thumb = {
_: 'photoSize',
h: 0,
w: 0,
location: {} as any,
size: file.size,
type: 'full',
} as PhotoSize.photoSize;
let document: MyDocument = {
_: 'document',
access_hash: '',
attributes: [],
dc_id: 0,
file_reference: [],
id,
mime_type: file.type,
size: file.size,
date: Date.now() / 1000,
pFlags: {},
thumbs: [thumb],
file_name: file.name
};
document = appDocsManager.saveDoc(document);
const cacheContext = appDownloadManager.getCacheContext(document);
cacheContext.downloaded = file.size;
cacheContext.url = URL.createObjectURL(file);
let wallpaper: WallPaper.wallPaper = {
_: 'wallPaper',
access_hash: '',
document: document,
id,
slug: id,
pFlags: {}
};
const upload = appDownloadManager.upload(file, file.name);
const deferred = deferredPromise<void>();
deferred.addNotifyListener = upload.addNotifyListener;
deferred.cancel = upload.cancel;
upload.then(inputFile => {
apiManager.invokeApi('account.uploadWallPaper', {
file: inputFile,
mime_type: file.type,
settings: {
_: 'wallPaperSettings'
}
}).then(_wallpaper => {
const newDoc = (_wallpaper as WallPaper.wallPaper).document as MyDocument;
const newCacheContext = appDownloadManager.getCacheContext(newDoc);
Object.assign(newCacheContext, cacheContext);
wallpaper = _wallpaper as WallPaper.wallPaper;
wallpaper.document = appDocsManager.saveDoc(wallpaper.document);
container.dataset.docId = wallpaper.document.id;
container.dataset.slug = wallpaper.slug;
this.setBackgroundDocument(wallpaper.slug, wallpaper.document).then(deferred.resolve, deferred.reject);
}, deferred.reject);
}, deferred.reject);
deferred.then(() => {
this.clicked.delete(wallpaper.document.id);
}, (err) => {
container.remove();
//console.error('i saw the body drop', err);
});
const preloader = new ProgressivePreloader({
isUpload: true,
cancelable: true,
tryAgainOnFail: false
});
const container = this.addWallPaper(wallpaper, false);
this.clicked.add(wallpaper.document.id);
preloader.attach(container, false, deferred);
});
};
private onResetClick = () => {
const defaultTheme = STATE_INIT.settings.themes.find(t => t.name === this.theme.name);
if(defaultTheme) {
++this.tempId;
this.theme.background = copy(defaultTheme.background);
appStateManager.pushToState('settings', rootScope.settings);
appImManager.applyCurrentTheme(undefined, undefined, true);
this.blurCheckboxField.setValueSilently(this.theme.background.blur);
}
};
private addWallPaper(wallpaper: WallPaper.wallPaper, append = true) {
if(wallpaper.pFlags.pattern || (wallpaper.document as MyDocument).mime_type.indexOf('application/') === 0) {
return;
}
wallpaper.document = appDocsManager.saveDoc(wallpaper.document);
const container = document.createElement('div');
container.classList.add('grid-item');
const media = document.createElement('div');
media.classList.add('grid-item-media');
const wrapped = wrapPhoto({
photo: wallpaper.document,
message: null,
container: media,
withoutPreloader: true,
size: appPhotosManager.choosePhotoSize(wallpaper.document, 200, 200)
});
container.dataset.docId = wallpaper.document.id;
container.dataset.slug = wallpaper.slug;
if(this.theme.background.type === 'image' && this.theme.background.slug === wallpaper.slug) {
container.classList.add('active');
}
(wrapped.loadPromises.thumb || wrapped.loadPromises.full).then(() => {
sequentialDom.mutate(() => {
container.append(media);
});
});
this.grid[append ? 'append' : 'prepend'](container);
return container;
}
private onGridClick = (e: MouseEvent | TouchEvent) => {
const target = findUpClassName(e.target, 'grid-item') as HTMLElement;
if(!target) return;
const {docId, slug} = target.dataset;
if(this.clicked.has(docId)) return;
this.clicked.add(docId);
const preloader = new ProgressivePreloader({
cancelable: true,
tryAgainOnFail: false
});
const doc = appDocsManager.getDoc(docId);
const load = () => {
const promise = this.setBackgroundDocument(slug, doc);
const cacheContext = appDownloadManager.getCacheContext(doc);
if(!cacheContext.url || this.theme.background.blur) {
preloader.attach(target, true, promise);
}
};
preloader.construct();
attachClickEvent(target, (e) => {
if(preloader.preloader.parentElement) {
preloader.onClick(e);
preloader.detach();
} else {
load();
}
}, {listenerSetter: this.listenerSetter});
load();
//console.log(doc);
};
private saveToCache = (slug: string, url: string) => {
fetch(url).then(response => {
appDownloadManager.cacheStorage.save('backgrounds/' + slug, response);
});
};
private setBackgroundDocument = (slug: string, doc: MyDocument) => {
let _tempId = ++this.tempId;
const middleware = () => _tempId === this.tempId;
const download = appDocsManager.downloadDoc(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0);
const deferred = deferredPromise<void>();
deferred.addNotifyListener = download.addNotifyListener;
deferred.cancel = download.cancel;
download.then(() => {
if(!middleware()) {
deferred.resolve();
return;
}
const background = this.theme.background;
const onReady = (url: string) => {
//const perf = performance.now();
averageColor(url).then(pixel => {
if(!middleware()) {
deferred.resolve();
return;
}
const hsla = highlightningColor(Array.from(pixel) as any);
//console.log(doc, hsla, performance.now() - perf);
background.slug = slug;
background.type = 'image';
background.highlightningColor = hsla;
appStateManager.pushToState('settings', rootScope.settings);
this.saveToCache(slug, url);
appImManager.applyCurrentTheme(slug, url).then(deferred.resolve);
});
};
const cacheContext = appDownloadManager.getCacheContext(doc);
if(background.blur) {
setTimeout(() => {
blur(cacheContext.url, 12, 4)
.then(url => {
if(!middleware()) {
deferred.resolve();
return;
}
onReady(url);
});
}, 200);
} else {
onReady(cacheContext.url);
}
});
return deferred;
};
private setActive = () => {
const active = this.grid.querySelector('.active');
const background = this.theme.background;
const target = background.type === 'image' ? this.grid.querySelector(`.grid-item[data-slug="${background.slug}"]`) : null;
if(active === target) {
return;
}
if(active) {
active.classList.remove('active');
}
if(target) {
target.classList.add('active');
}
};
}