Browse Source

Upload wallpaper

master
Eduard Kuzmenko 4 years ago
parent
commit
dede33e9e4
  1. 425
      src/components/sidebarLeft/tabs/background.ts
  2. 30
      src/helpers/files.ts
  3. 4
      src/lib/appManagers/appStateManager.ts
  4. 10
      src/scss/partials/_leftSidebar.scss

425
src/components/sidebarLeft/tabs/background.ts

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

30
src/helpers/files.ts

@ -120,3 +120,33 @@ export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes
return files; return files;
} }
export function requestFile(accept?: string) {
const input = document.createElement('input');
input.type = 'file';
input.style.display = 'none';
if(accept) {
input.accept = accept;
}
document.body.append(input);
const promise = new Promise<File>((resolve, reject) => {
input.addEventListener('change', (e: any) => {
const file: File = e.target.files[0];
if(!file) {
reject('NO_FILE_SELECTED');
return;
}
resolve(file);
}, {once: true});
}).finally(() => {
input.remove();
});
input.click();
return promise;
}

4
src/lib/appManagers/appStateManager.ts

@ -24,7 +24,7 @@ import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug';
const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day
const STATE_VERSION = App.version; const STATE_VERSION = App.version;
type Background = { export type Background = {
type: 'color' | 'image' | 'default', type: 'color' | 'image' | 'default',
blur: boolean, blur: boolean,
highlightningColor?: string, highlightningColor?: string,
@ -32,7 +32,7 @@ type Background = {
slug?: string, slug?: string,
}; };
type Theme = { export type Theme = {
name: 'day' | 'night', name: 'day' | 'night',
background: Background background: Background
}; };

10
src/scss/partials/_leftSidebar.scss

@ -1139,6 +1139,16 @@
transform: scale(1); transform: scale(1);
} }
} }
.media-photo {
width: 100%;
height: 100%;
object-fit: cover;
}
.preloader-container {
z-index: 1;
}
} }
} }

Loading…
Cancel
Save