Browse Source

Documents improve

Set uploaded source to new message media
master
Eduard Kuzmenko 4 years ago
parent
commit
730bd1168d
  1. 14
      src/components/chat/bubbles.ts
  2. 12
      src/components/popups/newMedia.ts
  3. 3
      src/components/preloader.ts
  4. 93
      src/components/wrappers.ts
  5. 4
      src/helpers/cancellablePromise.ts
  6. 2
      src/helpers/number.ts
  7. 24
      src/lib/appManagers/appDocsManager.ts
  8. 15
      src/lib/appManagers/appDownloadManager.ts
  9. 34
      src/lib/appManagers/appMessagesManager.ts
  10. 98
      src/scss/partials/_document.scss
  11. 8
      src/scss/partials/popups/_mediaAttacher.scss

14
src/components/chat/bubbles.ts

@ -215,12 +215,14 @@ export default class ChatBubbles {
} }
if(message.media?.document) { if(message.media?.document) {
if(['audio', 'voice'].includes(message.media.document.type)) { const element = bubble.querySelector(`audio-element[message-id="${tempId}"], .document[data-doc-id="${tempId}"]`) as HTMLElement;
const audio = bubble.querySelector(`audio-element[message-id="${tempId}"]`) as AudioElement; if(element instanceof AudioElement) {
audio.setAttribute('doc-id', message.media.document.id); element.setAttribute('doc-id', message.media.document.id);
audio.setAttribute('message-id', '' + mid); element.setAttribute('message-id', '' + mid);
audio.message = message; element.message = message;
audio.onLoad(true); element.onLoad(true);
} else {
element.dataset.docId = message.media.document.id;
} }
} }

12
src/components/popups/newMedia.ts

@ -8,7 +8,8 @@ import { toast } from "../toast";
import { prepareAlbum, wrapDocument } from "../wrappers"; import { prepareAlbum, wrapDocument } from "../wrappers";
import CheckboxField from "../checkbox"; import CheckboxField from "../checkbox";
import SendContextMenu from "../chat/sendContextMenu"; import SendContextMenu from "../chat/sendContextMenu";
import { createPosterForVideo, createPosterFromVideo } from "../../helpers/files"; import { createPosterForVideo, createPosterFromVideo, onVideoLoad } from "../../helpers/files";
import { MyDocument } from "../../lib/appManagers/appDocsManager";
type SendFileParams = Partial<{ type SendFileParams = Partial<{
file: File, file: File,
@ -241,7 +242,7 @@ export default class PopupNewMedia extends PopupElement {
video.muted = true; video.muted = true;
video.setAttribute('playsinline', 'true'); video.setAttribute('playsinline', 'true');
video.onloadeddata = () => { onVideoLoad(video).then(() => {
params.width = video.videoWidth; params.width = video.videoWidth;
params.height = video.videoHeight; params.height = video.videoHeight;
params.duration = Math.floor(video.duration); params.duration = Math.floor(video.duration);
@ -252,7 +253,7 @@ export default class PopupNewMedia extends PopupElement {
params.thumbURL = URL.createObjectURL(blob); params.thumbURL = URL.createObjectURL(blob);
resolve(itemDiv); resolve(itemDiv);
}); });
}; });
video.append(source); video.append(source);
} else { } else {
@ -293,8 +294,9 @@ export default class PopupNewMedia extends PopupElement {
file_name: file.name || '', file_name: file.name || '',
size: file.size, size: file.size,
type: isPhoto ? 'photo' : 'doc', type: isPhoto ? 'photo' : 'doc',
url: params.objectURL url: params.objectURL,
} downloaded: true
} as MyDocument
} }
} as any } as any
}); });

3
src/components/preloader.ts

@ -14,8 +14,7 @@ export default class ProgressivePreloader {
private tempId = 0; private tempId = 0;
private detached = true; private detached = true;
private promise: CancellablePromise<any> = null; public promise: CancellablePromise<any> = null;
public onCancel: () => any = null;
public isUpload = false; public isUpload = false;
private cancelable = true; private cancelable = true;

93
src/components/wrappers.ts

@ -385,8 +385,6 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
}): HTMLElement { }): HTMLElement {
if(!fontWeight) fontWeight = 500; if(!fontWeight) fontWeight = 500;
const uploading = message.pFlags.is_outgoing;
const doc = (message.media.document || message.media.webpage.document) as MyDocument; const doc = (message.media.document || message.media.webpage.document) as MyDocument;
if(doc.type === 'audio' || doc.type === 'voice') { if(doc.type === 'audio' || doc.type === 'voice') {
const audioElement = new AudioElement(); const audioElement = new AudioElement();
@ -409,23 +407,28 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
return audioElement; return audioElement;
} }
const uploading = message.pFlags.is_outgoing && message.media?.preloader;
let extSplitted = doc.file_name ? doc.file_name.split('.') : ''; let extSplitted = doc.file_name ? doc.file_name.split('.') : '';
let ext = ''; let ext = '';
ext = extSplitted.length > 1 && Array.isArray(extSplitted) ? extSplitted.pop().toLowerCase() : 'file'; ext = extSplitted.length > 1 && Array.isArray(extSplitted) ? extSplitted.pop().toLowerCase() : 'file';
let docDiv = document.createElement('div'); let docDiv = document.createElement('div');
docDiv.classList.add('document', `ext-${ext}`); docDiv.classList.add('document', `ext-${ext}`);
docDiv.dataset.docId = doc.id;
const icoDiv = document.createElement('div'); const icoDiv = document.createElement('div');
icoDiv.classList.add('document-ico'); icoDiv.classList.add('document-ico');
if(doc.thumbs?.length || (uploading && doc.url && doc.type === 'photo')) { if(doc.thumbs?.length || (message.pFlags.is_outgoing && doc.url && doc.type === 'photo')) {
docDiv.classList.add('document-with-thumb'); docDiv.classList.add('document-with-thumb');
if(uploading) { let imgs: HTMLImageElement[] = [];
if(message.pFlags.is_outgoing) {
icoDiv.innerHTML = `<img src="${doc.url}">`; icoDiv.innerHTML = `<img src="${doc.url}">`;
imgs.push(icoDiv.firstElementChild as HTMLImageElement);
} else { } else {
wrapPhoto({ const wrapped = wrapPhoto({
photo: doc, photo: doc,
message: null, message: null,
container: icoDiv, container: icoDiv,
@ -435,10 +438,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
withoutPreloader: true withoutPreloader: true
}); });
icoDiv.style.width = icoDiv.style.height = ''; icoDiv.style.width = icoDiv.style.height = '';
if(wrapped.images.thumb) imgs.push(wrapped.images.thumb);
if(wrapped.images.full) imgs.push(wrapped.images.full);
} }
const img = icoDiv.querySelector('img'); imgs.forEach(img => img.classList.add('document-thumb'));
if(img) img.classList.add('document-thumb');
} else { } else {
icoDiv.innerText = ext; icoDiv.innerText = ext;
} }
@ -461,49 +465,70 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
} }
docDiv.innerHTML = ` docDiv.innerHTML = `
${!uploading ? `<div class="document-download"></div>` : ''} ${doc.downloaded && !uploading ? '' : `<div class="document-download"></div>`}
<div class="document-name"><middle-ellipsis-element data-font-weight="${fontWeight}">${fileName}</middle-ellipsis-element>${titleAdditionHTML}</div> <div class="document-name"><middle-ellipsis-element data-font-weight="${fontWeight}">${fileName}</middle-ellipsis-element>${titleAdditionHTML}</div>
<div class="document-size">${size}</div> <div class="document-size">${size}</div>
`; `;
docDiv.prepend(icoDiv); docDiv.prepend(icoDiv);
if(!uploading) { if(!uploading && message.pFlags.is_outgoing) {
const downloadDiv = docDiv.querySelector('.document-download') as HTMLDivElement; return docDiv;
const preloader = new ProgressivePreloader(); }
const load = () => { let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null;
const download = appDocsManager.saveDocFile(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0); const onLoad = () => {
if(downloadDiv) {
downloadDiv.classList.add('downloaded');
const _downloadDiv = downloadDiv;
setTimeout(() => {
_downloadDiv.remove();
}, 200);
downloadDiv = null;
}
download.then(() => { if(preloader) {
downloadDiv.classList.add('downloaded'); preloader = null;
setTimeout(() => { }
downloadDiv.remove(); };
}, 200);
}); const load = () => {
const doc = appDocsManager.getDoc(docDiv.dataset.docId);
const download = appDocsManager.saveDocFile(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0);
if(downloadDiv) {
download.then(onLoad);
preloader.attach(downloadDiv, true, download); preloader.attach(downloadDiv, true, download);
}
return {download}; return {download};
}; };
preloader.construct(); if(!(doc.downloaded && !uploading)) {
preloader.setManual(); downloadDiv = docDiv.querySelector('.document-download');
preloader.attach(downloadDiv); preloader = message.media.preloader as ProgressivePreloader;
preloader.setDownloadFunction(load);
attachClickEvent(docDiv, (e) => { if(!preloader) {
preloader.onClick(e); preloader = new ProgressivePreloader();
});
/* if(doc.downloaded) { preloader.construct();
downloadDiv.classList.add('downloaded'); preloader.setManual();
} */ preloader.attach(downloadDiv);
} else if(message.media?.preloader) { preloader.setDownloadFunction(load);
const icoDiv = docDiv.querySelector('.document-ico'); } else {
message.media.preloader.attach(icoDiv, false); preloader.attach(downloadDiv);
message.media.promise.then(onLoad);
}
} }
attachClickEvent(docDiv, (e) => {
if(preloader) {
preloader.onClick(e);
} else {
load();
}
});
return docDiv; return docDiv;
} }

4
src/helpers/cancellablePromise.ts

@ -61,6 +61,10 @@ export function deferredPromise<T>() {
deferred.notify = null; deferred.notify = null;
deferred.listeners.length = 0; deferred.listeners.length = 0;
deferred.lastNotify = null; deferred.lastNotify = null;
if(deferred.cancel) {
deferred.cancel = () => {};
}
}); });
Object.assign(deferred, deferredHelper); Object.assign(deferred, deferredHelper);

2
src/helpers/number.ts

@ -5,7 +5,7 @@ export function numberThousandSplitter(x: number, joiner = ',') {
} }
export function formatBytes(bytes: number, decimals = 2) { export function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return '0 Bytes'; if(bytes === 0) return '0 Bytes';
const k = 1024; const k = 1024;
const dm = decimals < 0 ? 0 : decimals; const dm = decimals < 0 ? 0 : decimals;

24
src/lib/appManagers/appDocsManager.ts

@ -278,27 +278,20 @@ export class AppDocsManager {
return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name}); return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name});
} }
public downloadDoc(doc: MyDocument/* , thumb?: PhotoSize.photoSize */, queueId?: number): DownloadBlob { public downloadDoc(doc: MyDocument, queueId?: number): DownloadBlob {
const fileName = this.getInputFileName(doc/* , thumb?.type */); const fileName = this.getInputFileName(doc);
let download: DownloadBlob = appDownloadManager.getDownload(fileName); let download: DownloadBlob = appDownloadManager.getDownload(fileName);
if(download) { if(download) {
return download; return download;
} }
const downloadOptions = this.getFileDownloadOptions(doc, undefined/* thumb */, queueId); const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId);
download = appDownloadManager.download(downloadOptions); download = appDownloadManager.download(downloadOptions);
const originalPromise = download; const originalPromise = download;
originalPromise.then((blob) => { originalPromise.then((blob) => {
/* if(thumb) { doc.url = URL.createObjectURL(blob);
defineNotNumerableProperties(thumb, ['url']);
thumb.url = URL.createObjectURL(blob);
return;
} else */if(!doc.supportsStreaming) {
doc.url = URL.createObjectURL(blob);
}
doc.downloaded = true; doc.downloaded = true;
}); });
@ -390,8 +383,13 @@ export class AppDocsManager {
} }
public saveDocFile(doc: MyDocument, queueId?: number) { public saveDocFile(doc: MyDocument, queueId?: number) {
const options = this.getFileDownloadOptions(doc, undefined, queueId); /* const options = this.getFileDownloadOptions(doc, undefined, queueId);
return appDownloadManager.downloadToDisc(options, doc.file_name); return appDownloadManager.downloadToDisc(options, doc.file_name); */
const promise = this.downloadDoc(doc, queueId);
promise.then(() => {
appDownloadManager.createDownloadAnchor(doc.url, doc.file_name);
});
return promise;
} }
} }

15
src/lib/appManagers/appDownloadManager.ts

@ -77,6 +77,19 @@ export class AppDownloadManager {
delete this.downloads[fileName]; delete this.downloads[fileName];
} }
public fakeDownload(fileName: string, value: Blob | string) {
const deferred = this.getNewDeferred(fileName);
if(typeof(value) === 'string') {
fetch(value)
.then(response => response.blob())
.then(blob => deferred.resolve(blob));
} else {
deferred.resolve(value);
}
return deferred;
}
public download(options: DownloadOptions): DownloadBlob { public download(options: DownloadOptions): DownloadBlob {
const fileName = getFileNameByLocation(options.location, {fileName: options.fileName}); const fileName = getFileNameByLocation(options.location, {fileName: options.fileName});
if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName]; if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName];
@ -174,7 +187,7 @@ export class AppDownloadManager {
} }
} }
private createDownloadAnchor(url: string, fileName: string, onRemove?: () => void) { public createDownloadAnchor(url: string, fileName: string, onRemove?: () => void) {
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = fileName; a.download = fileName;

34
src/lib/appManagers/appMessagesManager.ts

@ -34,6 +34,7 @@ import appUsersManager from "./appUsersManager";
import appWebPagesManager from "./appWebPagesManager"; import appWebPagesManager from "./appWebPagesManager";
import appDraftsManager from "./appDraftsManager"; import appDraftsManager from "./appDraftsManager";
import pushHeavyTask from "../../helpers/heavyQueue"; import pushHeavyTask from "../../helpers/heavyQueue";
import { getFileNameByLocation } from "../../helpers/fileName";
//console.trace('include'); //console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -786,18 +787,21 @@ export class AppMessagesManager {
this.log('sendFile', attachType, apiFileName, file.type, options); this.log('sendFile', attachType, apiFileName, file.type, options);
const preloader = new ProgressivePreloader({ const preloader = isDocument ? undefined : new ProgressivePreloader({
attachMethod: 'prepend', attachMethod: 'prepend',
tryAgainOnFail: false, tryAgainOnFail: false,
isUpload: true isUpload: true
}); });
const media = { const sentDeferred = deferredPromise<InputMedia>();
const media = isDocument ? undefined : {
_: photo ? 'messageMediaPhoto' : 'messageMediaDocument', _: photo ? 'messageMediaPhoto' : 'messageMediaDocument',
pFlags: {}, pFlags: {},
preloader, preloader,
photo, photo,
document document,
promise: sentDeferred
}; };
const message: any = { const message: any = {
@ -833,7 +837,6 @@ export class AppMessagesManager {
let uploaded = false, let uploaded = false,
uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null; uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null;
const sentDeferred = deferredPromise<InputMedia>();
message.send = () => { message.send = () => {
if(isDocument) { if(isDocument) {
const {id, access_hash, file_reference} = file as MyDocument; const {id, access_hash, file_reference} = file as MyDocument;
@ -4394,18 +4397,31 @@ export class AppMessagesManager {
if(message.media.photo) { if(message.media.photo) {
const photo = appPhotosManager.getPhoto('' + tempId); const photo = appPhotosManager.getPhoto('' + tempId);
if(/* photo._ !== 'photoEmpty' */photo) { if(/* photo._ !== 'photoEmpty' */photo) {
const newPhoto = message.media.photo; const newPhoto = message.media.photo as MyPhoto;
// костыль // костыль
defineNotNumerableProperties(newPhoto, ['downloaded', 'url']); defineNotNumerableProperties(newPhoto, ['downloaded', 'url']);
newPhoto.downloaded = photo.downloaded; newPhoto.downloaded = photo.downloaded;
newPhoto.url = photo.url; newPhoto.url = photo.url;
const photoSize = newPhoto.sizes[newPhoto.sizes.length - 1] as PhotoSize.photoSize;
defineNotNumerableProperties(photoSize, ['url']);
photoSize.url = photo.url;
const downloadOptions = appPhotosManager.getPhotoDownloadOptions(newPhoto, photoSize);
const fileName = getFileNameByLocation(downloadOptions.location);
appDownloadManager.fakeDownload(fileName, photo.url);
} }
} else if(message.media.document) { } else if(message.media.document) {
const doc = appDocsManager.getDoc('' + tempId); const doc = appDocsManager.getDoc('' + tempId);
if(/* doc._ !== 'documentEmpty' && */doc?.type && doc.type !== 'sticker') { if(doc) {
const newDoc = message.media.document; if(/* doc._ !== 'documentEmpty' && */doc.type && doc.type !== 'sticker') {
newDoc.downloaded = doc.downloaded; const newDoc = message.media.document;
newDoc.url = doc.url; newDoc.downloaded = doc.downloaded;
newDoc.url = doc.url;
const fileName = appDocsManager.getInputFileName(newDoc);
appDownloadManager.fakeDownload(fileName, doc.url);
}
} }
} else if(message.media.poll) { } else if(message.media.poll) {
delete appPollsManager.polls[tempId]; delete appPollsManager.polls[tempId];

98
src/scss/partials/_document.scss

@ -1,15 +1,17 @@
.document { .document {
--background-color: #{$color-blue};
$border-radius: .375rem;
padding-left: 4.25rem; padding-left: 4.25rem;
height: 70px; height: 70px;
&-ico { &-ico {
background-color: $color-blue; background-color: var(--background-color);
border-radius: .5rem; border-radius: $border-radius;
line-height: 10px; line-height: 1;
text-align: center; text-align: center;
.document:not(.document-with-thumb) & { .document:not(.document-with-thumb) & {
padding: 1.5rem .25rem 0 .25rem; padding: 1.5625rem .25rem 0 .25rem;
@include respond-to(handhelds) { @include respond-to(handhelds) {
padding: 14px 0px 0px 0px; padding: 14px 0px 0px 0px;
@ -34,9 +36,7 @@
} }
&-ico, &-download { &-ico, &-download {
font-weight: 500; font-size: 1.125rem;
letter-spacing: 1px;
font-size: 1.1rem;
background-size: contain; background-size: contain;
} }
@ -47,39 +47,26 @@
} }
&-download { &-download {
background-color: $color-blue; background-color: var(--background-color);
border-radius: .5rem; border-radius: $border-radius;
} }
&.ext-zip { &.ext-zip {
.document-ico, .document-download { --background-color: #FB8C00;
background-color: #FB8C00;
}
} }
&.ext-pdf { &.ext-pdf {
.document-ico, .document-download { --background-color: #DF3F40;
background-color: #DF3F40;
}
} }
&.ext-apk { &.ext-apk {
.document-ico, .document-download { --background-color: #43A047;
background-color: #43A047;
}
} }
&.document-with-thumb { &.document-with-thumb {
.document-ico { --background-color: #fff;
background: #fff;
border-radius: $border-radius;
.document-thumb {
object-fit: cover;
width: 100%;
height: 100%;
}
.document-ico {
&:after { &:after {
display: none; display: none;
} }
@ -88,6 +75,22 @@
.document-download { .document-download {
background-color: rgba(0, 0, 0, .15); background-color: rgba(0, 0, 0, .15);
} }
.preloader-circular {
transition: background-color .2s;
}
.preloader-container:not(.manual) {
.preloader-circular {
background-color: rgba(0, 0, 0, .3) !important;
}
}
}
&-thumb {
object-fit: cover;
width: 100%;
height: 100%;
} }
&-name { &-name {
@ -106,14 +109,22 @@
} }
.preloader-container { .preloader-container {
width: 2.375rem; width: 2.5rem;
height: 2.375rem; height: 2.5rem;
@include respond-to(handhelds) { @include respond-to(handhelds) {
width: 26px; width: 1.625rem;
height: 26px; height: 1.625rem;
} }
} }
.preloader-circular {
background-color: transparent !important;
}
.preloader-path-new {
stroke-width: 2.5;
}
} }
.document, .audio { .document, .audio {
@ -127,20 +138,20 @@
&-ico, &-download { &-ico, &-download {
position: absolute; position: absolute;
left: 0; left: 0;
width: 54px; width: 3.375rem;
height: 54px; height: 3.375rem;
color: #fff; color: #fff;
@include respond-to(handhelds) { @include respond-to(handhelds) {
height: 36px; height: 2.25rem;
width: 36px; width: 2.25rem;
} }
} }
&-download { &-download {
z-index: 1; z-index: 1;
align-items: center; align-items: center;
font-size: 24px; font-size: 1.5rem;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -155,14 +166,8 @@
} }
.preloader-container:not(.preloader-streamable) { .preloader-container:not(.preloader-streamable) {
width: 100%;
height: 100%;
transform: scale(1) !important; transform: scale(1) !important;
} }
.preloader-circular {
background-color: transparent !important;
}
} }
.audio { .audio {
@ -174,4 +179,13 @@
margin-right: -1px; margin-right: -1px;
} }
} }
.preloader-circular {
background-color: transparent !important;
}
.preloader-container:not(.preloader-streamable) {
width: 100%;
height: 100%;
}
} }

8
src/scss/partials/popups/_mediaAttacher.scss

@ -167,16 +167,18 @@
.checkbox-field { .checkbox-field {
margin-bottom: 0; margin-bottom: 0;
padding-left: 0; margin-left: 0;
} }
.popup-album, .popup-container:not(.is-album) .popup-item-media { .popup-album,
.popup-container:not(.is-album) .popup-item-media {
position: relative; position: relative;
border-radius: inherit; border-radius: inherit;
overflow: hidden; overflow: hidden;
} }
.popup-album + .popup-album, .popup-container:not(.is-album) .popup-item-media + .popup-item-media { .popup-album + .popup-album,
.popup-container:not(.is-album) .popup-item-media + .popup-item-media {
margin-top: .5rem; margin-top: .5rem;
} }
} }
Loading…
Cancel
Save