Browse Source

Fix deleting messages as secondary admin

Fix uploading video
Fix canceling upload
Scale down uploading thumb
master
Eduard Kuzmenko 3 years ago
parent
commit
a1e1690af6
  1. 24
      src/components/popups/newMedia.ts
  2. 11
      src/components/wrappers.ts
  3. 49
      src/helpers/files.ts
  4. 65
      src/lib/appManagers/appMessagesManager.ts
  5. 2
      src/lib/mtproto/apiFileManager.ts
  6. 3
      src/scss/partials/_poll.scss

24
src/components/popups/newMedia.ts

@ -21,15 +21,20 @@ import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd"; import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd";
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import RichTextProcessor from "../../lib/richtextprocessor"; import RichTextProcessor from "../../lib/richtextprocessor";
import { MediaSize } from "../../helpers/mediaSizes";
type SendFileParams = Partial<{ type SendFileParams = Partial<{
file: File, file: File,
objectURL: string, objectURL: string,
thumbBlob: Blob, thumb: {
thumbURL: string, blob: Blob,
url: string,
size: MediaSize
},
width: number, width: number,
height: number, height: number,
duration: number duration: number,
noSound: boolean
}>; }>;
// TODO: .gif upload as video // TODO: .gif upload as video
@ -264,10 +269,17 @@ export default class PopupNewMedia extends PopupElement {
params.height = video.videoHeight; params.height = video.videoHeight;
params.duration = Math.floor(video.duration); params.duration = Math.floor(video.duration);
const audioDecodedByteCount = (video as any).webkitAudioDecodedByteCount;
if(audioDecodedByteCount !== undefined) {
params.noSound = !audioDecodedByteCount;
}
itemDiv.append(video); itemDiv.append(video);
createPosterFromVideo(video).then(blob => { createPosterFromVideo(video).then(thumb => {
params.thumbBlob = blob; params.thumb = {
params.thumbURL = URL.createObjectURL(blob); url: URL.createObjectURL(thumb.blob),
...thumb
};
resolve(itemDiv); resolve(itemDiv);
}); });
}); });

11
src/components/wrappers.ts

@ -309,8 +309,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
const cacheContext = appDownloadManager.getCacheContext(doc); const cacheContext = appDownloadManager.getCacheContext(doc);
const isUpload = !!message?.media?.preloader;
let preloader: ProgressivePreloader; let preloader: ProgressivePreloader;
if(message?.media?.preloader) { // means upload if(isUpload) { // means upload
preloader = message.media.preloader as ProgressivePreloader; preloader = message.media.preloader as ProgressivePreloader;
preloader.attach(container, false); preloader.attach(container, false);
noAutoDownload = undefined; noAutoDownload = undefined;
@ -333,7 +334,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
} }
let loadPromise: Promise<any> = Promise.resolve(); let loadPromise: Promise<any> = Promise.resolve();
if(preloader) { if(preloader && !isUpload) {
if(!cacheContext.downloaded && !doc.supportsStreaming) { if(!cacheContext.downloaded && !doc.supportsStreaming) {
const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId, noAutoDownload); const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId, noAutoDownload);
preloader.attach(container, false, promise); preloader.attach(container, false, promise);
@ -354,7 +355,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
console.error("Error " + video.error.code + "; details: " + video.error.message); console.error("Error " + video.error.code + "; details: " + video.error.message);
} }
if(preloader) { if(preloader && !isUpload) {
preloader.detach(); preloader.detach();
} }
@ -386,7 +387,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
animationIntersector.addAnimation(video, group); animationIntersector.addAnimation(video, group);
} }
if(preloader) { if(preloader && !isUpload) {
preloader.detach(); preloader.detach();
} }
@ -410,7 +411,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
return {download: loadPromise, render: deferred}; return {download: loadPromise, render: deferred};
}; };
if(preloader) { if(preloader && !isUpload) {
preloader.setDownloadFunction(load); preloader.setDownloadFunction(load);
} }

49
src/helpers/files.ts

@ -4,30 +4,55 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import { makeMediaSize, MediaSize } from "./mediaSizes";
import { pause } from "./schedulers/pause"; import { pause } from "./schedulers/pause";
import { isAppleMobile } from "./userAgent"; import { isAppleMobile } from "./userAgent";
export function scaleMediaElement(options: {
media: CanvasImageSource,
mediaSize: MediaSize,
boxSize: MediaSize,
quality?: number,
mimeType?: 'image/jpeg' | 'image/png'
}): Promise<{blob: Blob, size: MediaSize}> {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const size = options.mediaSize.aspectFitted(options.boxSize);
canvas.width = size.width * window.devicePixelRatio;
canvas.height = size.height * window.devicePixelRatio;
const ctx = canvas.getContext('2d');
ctx.drawImage(options.media, 0, 0, canvas.width, canvas.height);
canvas.toBlob(blob => {
resolve({blob, size});
}, options.mimeType ?? 'image/jpeg', options.quality ?? 1);
});
}
export function preloadVideo(url: string): Promise<HTMLVideoElement> { export function preloadVideo(url: string): Promise<HTMLVideoElement> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const video = document.createElement('video'); const video = document.createElement('video');
video.volume = 0; video.volume = 0;
video.onloadedmetadata = () => resolve(video); video.addEventListener('loadedmetadata', () => resolve(video), {once: true});
video.onerror = reject; video.addEventListener('error', reject, {once: true});
video.src = url; video.src = url;
}); });
} }
export function createPosterFromVideo(video: HTMLVideoElement): Promise<Blob> { export function createPosterFromVideo(video: HTMLVideoElement): ReturnType<typeof scaleMediaElement> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
video.onseeked = () => { video.onseeked = () => {
const canvas = document.createElement('canvas'); video.onseeked = () => {
canvas.width = Math.min(1280, video.videoWidth); scaleMediaElement({
canvas.height = Math.min(720, video.videoHeight); media: video,
const ctx = canvas.getContext('2d')!; mediaSize: makeMediaSize(video.videoWidth, video.videoHeight),
ctx.drawImage(video, 0, 0); boxSize: makeMediaSize(320, 240),
canvas.toBlob(blob => { quality: .9
resolve(blob); }).then(resolve);
}, 'image/jpeg', 1);
video.onseeked = undefined;
};
video.currentTime = 0;
}; };
video.onerror = reject; video.onerror = reject;
@ -35,7 +60,7 @@ export function createPosterFromVideo(video: HTMLVideoElement): Promise<Blob> {
}); });
} }
export async function createPosterForVideo(url: string): Promise<Blob | undefined> { export async function createPosterForVideo(url: string) {
const video = await preloadVideo(url); const video = await preloadVideo(url);
return Promise.race([ return Promise.race([

65
src/lib/appManagers/appMessagesManager.ts

@ -58,6 +58,7 @@ import telegramMeWebManager from "../mtproto/telegramMeWebManager";
import { getMiddleware } from "../../helpers/middleware"; import { getMiddleware } from "../../helpers/middleware";
import assumeType from "../../helpers/assumeType"; import assumeType from "../../helpers/assumeType";
import appMessagesIdsManager from "./appMessagesIdsManager"; import appMessagesIdsManager from "./appMessagesIdsManager";
import type { MediaSize } from "../../helpers/mediaSizes";
//console.trace('include'); //console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -599,13 +600,17 @@ export class AppMessagesManager {
width: number, width: number,
height: number, height: number,
objectURL: string, objectURL: string,
thumbBlob: Blob, thumb: {
thumbURL: string, blob: Blob,
url: string,
size: MediaSize
},
duration: number, duration: number,
background: true, background: true,
silent: true, silent: true,
clearDraft: true, clearDraft: true,
scheduleDate: number, scheduleDate: number,
noSound: boolean,
waveform: Uint8Array, waveform: Uint8Array,
}> = {}) { }> = {}) {
@ -696,10 +701,11 @@ export class AppMessagesManager {
apiFileName = 'video.mp4'; apiFileName = 'video.mp4';
actionName = 'sendMessageUploadVideoAction'; actionName = 'sendMessageUploadVideoAction';
let videoAttribute: DocumentAttribute.documentAttributeVideo = { const videoAttribute: DocumentAttribute.documentAttributeVideo = {
_: 'documentAttributeVideo', _: 'documentAttributeVideo',
pFlags: { pFlags: {
round_message: options.isRoundMessage round_message: options.isRoundMessage,
supports_streaming: true
}, },
duration: options.duration, duration: options.duration,
w: options.width, w: options.width,
@ -707,6 +713,15 @@ export class AppMessagesManager {
}; };
attributes.push(videoAttribute); attributes.push(videoAttribute);
// * must follow after video attribute
if(options.noSound &&
file.size > (10 * 1024) &&
file.size < (10 * 1024 * 1024)) {
attributes.push({
_: 'documentAttributeAnimated'
});
}
} else { } else {
attachType = 'document'; attachType = 'document';
apiFileName = 'document.' + fileType.split('/')[1]; apiFileName = 'document.' + fileType.split('/')[1];
@ -749,18 +764,18 @@ export class AppMessagesManager {
size: file.size size: file.size
}; };
} else if(attachType === 'video') { } else if(attachType === 'video') {
if(options.thumbURL) { if(options.thumb) {
thumb = { thumb = {
_: 'photoSize', _: 'photoSize',
w: options.width, w: options.thumb.size.width,
h: options.height, h: options.thumb.size.height,
type: 'full', type: 'local-thumb',
size: options.thumbBlob.size size: options.thumb.blob.size
}; };
const thumbCacheContext = appDownloadManager.getCacheContext(document, thumb.type); const thumbCacheContext = appDownloadManager.getCacheContext(document, thumb.type);
thumbCacheContext.downloaded = thumb.size; thumbCacheContext.downloaded = thumb.size;
thumbCacheContext.url = options.thumbURL; thumbCacheContext.url = options.thumb.url;
} }
} }
@ -800,7 +815,6 @@ export class AppMessagesManager {
if(err.name === 'AbortError' && !uploaded) { if(err.name === 'AbortError' && !uploaded) {
this.log('cancelling upload', media); this.log('cancelling upload', media);
sentDeferred.reject(err);
this.cancelPendingMessage(message.random_id); this.cancelPendingMessage(message.random_id);
this.setTyping(peerId, {_: 'sendMessageCancelAction'}); this.setTyping(peerId, {_: 'sendMessageCancelAction'});
@ -867,12 +881,12 @@ export class AppMessagesManager {
let thumbUploadPromise: typeof uploadPromise; let thumbUploadPromise: typeof uploadPromise;
if(attachType === 'video' && options.objectURL) { if(attachType === 'video' && options.objectURL) {
thumbUploadPromise = new Promise((resolve, reject) => { thumbUploadPromise = new Promise((resolve, reject) => {
const blobPromise = options.thumbBlob ? Promise.resolve(options.thumbBlob) : createPosterForVideo(options.objectURL); const thumbPromise = options.thumb && options.thumb.blob ? Promise.resolve(options.thumb) : createPosterForVideo(options.objectURL);
blobPromise.then(blob => { thumbPromise.then(thumb => {
if(!blob) { if(!thumb) {
resolve(null); resolve(null);
} else { } else {
appDownloadManager.upload(blob).then(resolve, reject); appDownloadManager.upload(thumb.blob).then(resolve, reject);
} }
}, reject); }, reject);
}); });
@ -3571,23 +3585,16 @@ export class AppMessagesManager {
if(peerId < 0 && appPeersManager.isChannel(peerId)) { if(peerId < 0 && appPeersManager.isChannel(peerId)) {
const channelId = -peerId; const channelId = -peerId;
const channel = appChatsManager.getChat(channelId); const channel: Chat.channel = appChatsManager.getChat(channelId);
if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) { if(!channel.pFlags.creator && !channel.admin_rights?.pFlags?.delete_messages) {
const goodMsgIds: number[] = []; mids = mids.filter((mid) => {
if(channel.pFlags.editor || channel.pFlags.megagroup) { const message = this.getMessageByPeer(peerId, mid);
mids.forEach((msgId, i) => { return !!message.pFlags.out;
const message = this.getMessageByPeer(peerId, mids[i]); });
if(message.pFlags.out) {
goodMsgIds.push(msgId);
}
});
}
if(!goodMsgIds.length) { if(!mids.length) {
return; return;
} }
mids = goodMsgIds;
} }
promise = apiManager.invokeApi('channels.deleteMessages', { promise = apiManager.invokeApi('channels.deleteMessages', {

2
src/lib/mtproto/apiFileManager.ts

@ -175,7 +175,7 @@ export class ApiFileManager {
} }
public cancelDownload(fileName: string) { public cancelDownload(fileName: string) {
const promises = (this.cachedDownloadPromises[fileName] ? [this.cachedDownloadPromises[fileName]] : []) || const promises = (this.cachedDownloadPromises[fileName] ? [this.cachedDownloadPromises[fileName]] : undefined) ||
(this.uploadPromises[fileName] ? Array.from(this.uploadPromises[fileName]) : []); (this.uploadPromises[fileName] ? Array.from(this.uploadPromises[fileName]) : []);
let canceled = false; let canceled = false;
for(let i = 0, length = promises.length; i < length; ++i) { for(let i = 0, length = promises.length; i < length; ++i) {

3
src/scss/partials/_poll.scss

@ -174,7 +174,8 @@ poll-element {
&-votes-count { &-votes-count {
color: var(--secondary-text-color); color: var(--secondary-text-color);
font-size: .875rem; font-size: .875rem;
padding-top: .1875rem; // padding-top: .1875rem; // THIS IS MOCKUP!
margin-top: -.5rem; // this is human-readable
} }
&-line { &-line {

Loading…
Cancel
Save