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

11
src/components/wrappers.ts

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

51
src/helpers/files.ts

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

65
src/lib/appManagers/appMessagesManager.ts

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

2
src/lib/mtproto/apiFileManager.ts

@ -175,7 +175,7 @@ export class ApiFileManager { @@ -175,7 +175,7 @@ export class ApiFileManager {
}
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]) : []);
let canceled = false;
for(let i = 0, length = promises.length; i < length; ++i) {

3
src/scss/partials/_poll.scss

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

Loading…
Cancel
Save