Media viewer:
Avatars are now viewable, with pagination Fix border-radius transition
This commit is contained in:
parent
cab8bc52cb
commit
d91b97bc02
@ -7,7 +7,6 @@ import appImManager from "../lib/appManagers/appImManager";
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import appPeersManager from "../lib/appManagers/appPeersManager";
|
||||
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
||||
import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import { logger } from "../lib/logger";
|
||||
import VideoPlayer from "../lib/mediaPlayer";
|
||||
import { RichTextProcessor } from "../lib/richtextprocessor";
|
||||
@ -49,7 +48,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
public log: ReturnType<typeof logger>;
|
||||
|
||||
protected peerID = 0;
|
||||
protected isFirstOpen = true;
|
||||
protected loadMediaPromiseUp: Promise<void> = null;
|
||||
protected loadMediaPromiseDown: Promise<void> = null;
|
||||
protected loadedAllMediaUp = false;
|
||||
@ -221,7 +220,6 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
if(this.setMoverAnimationPromise) return;
|
||||
|
||||
this.peerID = 0;
|
||||
this.lazyLoadQueue.clear();
|
||||
|
||||
const promise = this.setMoverToTarget(this.lastTarget, true).then(({onAnimationEnd}) => onAnimationEnd);
|
||||
@ -786,15 +784,14 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
this.setAuthorInfo(fromID, media.date);
|
||||
|
||||
const isVideo = (media as MyDocument).type == 'video' || (media as MyDocument).type == 'gif';
|
||||
const isFirstOpen = !this.peerID;
|
||||
|
||||
if(isFirstOpen) {
|
||||
this.peerID = $rootScope.selectedPeerID;
|
||||
if(this.isFirstOpen) {
|
||||
//this.targetContainer = targetContainer;
|
||||
this.prevTargets = prevTargets;
|
||||
this.nextTargets = nextTargets;
|
||||
this.reverse = reverse;
|
||||
this.needLoadMore = needLoadMore;
|
||||
this.isFirstOpen = false;
|
||||
//this.loadMore = loadMore;
|
||||
|
||||
if(appSidebarRight.historyTabIDs.slice(-1)[0] == AppSidebarRight.SLIDERITEMSIDS.forward) {
|
||||
@ -1073,8 +1070,9 @@ type AppMediaViewerTargetType = {
|
||||
};
|
||||
export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> {
|
||||
public currentMessageID = 0;
|
||||
public peerID: number;
|
||||
|
||||
constructor() {
|
||||
constructor(private inputFilter: 'inputMessagesFilterPhotoVideo' | 'inputMessagesFilterChatPhotos' = 'inputMessagesFilterPhotoVideo') {
|
||||
super(['delete', 'forward']);
|
||||
|
||||
const stub = document.createElement('div');
|
||||
@ -1110,16 +1108,17 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
this.author.container.addEventListener('click', this.onAuthorClick);
|
||||
}
|
||||
|
||||
public close(e?: MouseEvent) {
|
||||
/* public close(e?: MouseEvent) {
|
||||
const good = !this.setMoverAnimationPromise;
|
||||
const promise = super.close(e);
|
||||
|
||||
if(good) { // clear
|
||||
this.currentMessageID = 0;
|
||||
this.peerID = 0;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
} */
|
||||
|
||||
onPrevClick = (target: AppMediaViewerTargetType) => {
|
||||
this.nextTargets.unshift({element: this.lastTarget, mid: this.currentMessageID});
|
||||
@ -1138,7 +1137,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
};
|
||||
|
||||
onAuthorClick = (e: MouseEvent) => {
|
||||
if(this.currentMessageID) {
|
||||
if(this.currentMessageID && this.currentMessageID != Number.MAX_SAFE_INTEGER) {
|
||||
const mid = this.currentMessageID;
|
||||
this.close(e).then(() => {
|
||||
const message = appMessagesManager.getMessage(mid);
|
||||
@ -1168,8 +1167,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
protected loadMoreMedia = (older = true) => {
|
||||
//if(!older && this.reverse) return;
|
||||
|
||||
if(older && this.loadedAllMediaDown) return;
|
||||
else if(!older && this.loadedAllMediaUp) return;
|
||||
if(older && this.loadedAllMediaDown) return Promise.resolve();
|
||||
else if(!older && this.loadedAllMediaUp) return Promise.resolve();
|
||||
|
||||
if(older && this.loadMediaPromiseDown) return this.loadMediaPromiseDown;
|
||||
else if(!older && this.loadMediaPromiseUp) return this.loadMediaPromiseUp;
|
||||
@ -1191,7 +1190,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
const peerID = this.peerID;
|
||||
|
||||
const promise = appMessagesManager.getSearch(peerID, '',
|
||||
{_: 'inputMessagesFilterPhotoVideo'}, maxID, loadCount/* older ? loadCount : 0 */, 0, backLimit).then(value => {
|
||||
{_: this.inputFilter}, maxID, loadCount/* older ? loadCount : 0 */, 0, backLimit).then(value => {
|
||||
if(this.peerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
return;
|
||||
@ -1212,10 +1211,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
const method = older ? value.history.forEach : value.history.forEachReverse;
|
||||
method.call(value.history, mid => {
|
||||
const message = appMessagesManager.getMessage(mid);
|
||||
const media = message.media;
|
||||
const media = this.getMediaFromMessage(message);
|
||||
|
||||
if(!media || !(media.photo || media.document || (media.webpage && media.webpage.document))) return;
|
||||
if(media._ == 'document' && media.type != 'video') return;
|
||||
if(!media) return;
|
||||
//if(media._ == 'document' && media.type != 'video') return;
|
||||
|
||||
const t = {element: null as HTMLElement, mid: mid};
|
||||
if(older) {
|
||||
@ -1227,8 +1226,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
}
|
||||
});
|
||||
|
||||
this.buttons.prev.style.display = this.prevTargets.length ? '' : 'none';
|
||||
this.buttons.next.style.display = this.nextTargets.length ? '' : 'none';
|
||||
this.buttons.prev.classList.toggle('hide', !this.prevTargets.length);
|
||||
this.buttons.next.classList.toggle('hide', !this.nextTargets.length);
|
||||
}, () => {}).then(() => {
|
||||
if(older) this.loadMediaPromiseDown = null;
|
||||
else this.loadMediaPromiseUp = null;
|
||||
@ -1240,6 +1239,12 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
return promise;
|
||||
};
|
||||
|
||||
private getMediaFromMessage(message: any) {
|
||||
return message.action ? message.action.photo : message.media.photo
|
||||
|| message.media.document
|
||||
|| (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo));
|
||||
}
|
||||
|
||||
private setCaption(message: any) {
|
||||
const caption = message.message;
|
||||
if(caption) {
|
||||
@ -1255,20 +1260,21 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
prevTargets: AppMediaViewer['prevTargets'] = [], nextTargets: AppMediaViewer['prevTargets'] = [], needLoadMore = true) {
|
||||
if(this.setMoverPromise) return this.setMoverPromise;
|
||||
|
||||
const mid = message.mid;
|
||||
const fromID = message.fromID;
|
||||
const media = message.media.photo || message.media.document || message.media.webpage.document || message.media.webpage.photo;
|
||||
const media = this.getMediaFromMessage(message);
|
||||
|
||||
const isFirstOpen = !this.peerID;
|
||||
let fromRight = 0;
|
||||
if(!isFirstOpen) {
|
||||
if(!this.isFirstOpen) {
|
||||
//if(this.lastTarget === prevTarget) {
|
||||
if(this.reverse) fromRight = this.currentMessageID < message.mid ? 1 : -1;
|
||||
else fromRight = this.currentMessageID > message.mid ? 1 : -1;
|
||||
if(this.reverse) fromRight = this.currentMessageID < mid ? 1 : -1;
|
||||
else fromRight = this.currentMessageID > mid ? 1 : -1;
|
||||
} else {
|
||||
this.reverse = reverse;
|
||||
this.peerID = $rootScope.selectedPeerID;
|
||||
}
|
||||
|
||||
this.currentMessageID = message.mid;
|
||||
this.currentMessageID = mid;
|
||||
const promise = super._openMedia(media, fromID, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore);
|
||||
this.setCaption(message);
|
||||
|
||||
@ -1276,10 +1282,16 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
}
|
||||
}
|
||||
|
||||
export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', {}> {
|
||||
constructor() {
|
||||
type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoID: string};
|
||||
export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> {
|
||||
public currentPhotoID: string;
|
||||
public peerID: number;
|
||||
|
||||
constructor(peerID: number) {
|
||||
super(['delete']);
|
||||
|
||||
this.peerID = peerID;
|
||||
|
||||
this.setBtnMenuToggle([{
|
||||
icon: 'download',
|
||||
text: 'Download',
|
||||
@ -1295,33 +1307,63 @@ export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', {}> {
|
||||
this.setListeners();
|
||||
}
|
||||
|
||||
onPrevClick = (target: AppMediaViewerAvatarTargetType) => {
|
||||
this.nextTargets.unshift({element: this.lastTarget, photoID: this.currentPhotoID});
|
||||
this.openMedia(target.photoID, target.element, -1);
|
||||
};
|
||||
|
||||
onNextClick = (target: AppMediaViewerAvatarTargetType) => {
|
||||
this.prevTargets.push({element: this.lastTarget, photoID: this.currentPhotoID});
|
||||
this.openMedia(target.photoID, target.element, 1);
|
||||
};
|
||||
|
||||
onDownloadClick = (e: MouseEvent) => {
|
||||
|
||||
};
|
||||
|
||||
protected loadMoreMedia = (older = true) => {
|
||||
return Promise.resolve();
|
||||
};
|
||||
if(this.peerID < 0) return Promise.resolve(); // ! это значит, что открыло аватар чата, но следующих фотографий нет.
|
||||
if(this.loadedAllMediaDown) return Promise.resolve();
|
||||
if(this.loadMediaPromiseDown) return this.loadMediaPromiseDown;
|
||||
|
||||
public async openMedia(peerID: number, target?: HTMLElement, reverse = false,
|
||||
prevTargets: AppMediaViewerAvatar['prevTargets'] = [], nextTargets: AppMediaViewerAvatar['prevTargets'] = [], needLoadMore = true) {
|
||||
if(this.setMoverPromise) return this.setMoverPromise;
|
||||
const peerID = this.peerID;
|
||||
const loadCount = 50;
|
||||
|
||||
return appProfileManager.getFullPhoto(peerID).then(photo => {
|
||||
const fromID = peerID;
|
||||
const maxID = this.nextTargets.length ? this.nextTargets[this.nextTargets.length - 1].photoID : this.currentPhotoID;
|
||||
|
||||
const isFirstOpen = !this.peerID;
|
||||
let fromRight = 0;
|
||||
if(!isFirstOpen) {
|
||||
//if(this.lastTarget === prevTarget) {
|
||||
/* if(this.reverse) fromRight = this.currentMessageID < message.mid ? 1 : -1;
|
||||
else fromRight = this.currentMessageID > message.mid ? 1 : -1; */
|
||||
fromRight = 1;
|
||||
} else {
|
||||
this.reverse = reverse;
|
||||
const promise = appPhotosManager.getUserPhotos(peerID, maxID, loadCount).then(value => {
|
||||
if(this.peerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = super._openMedia(photo, fromID, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore);
|
||||
this.log('loaded more media by maxID:', /* maxID, */value, older, this.reverse);
|
||||
|
||||
if(value.photos.length < loadCount) {
|
||||
this.loadedAllMediaDown = true;
|
||||
}
|
||||
|
||||
value.photos.forEach(photoID => {
|
||||
if(this.currentPhotoID == photoID) return;
|
||||
this.nextTargets.push({element: null as HTMLElement, photoID});
|
||||
});
|
||||
|
||||
this.buttons.prev.classList.toggle('hide', !this.prevTargets.length);
|
||||
this.buttons.next.classList.toggle('hide', !this.nextTargets.length);
|
||||
}, () => {}).then(() => {
|
||||
this.loadMediaPromiseDown = null;
|
||||
});
|
||||
|
||||
return this.loadMediaPromiseDown = promise;
|
||||
};
|
||||
|
||||
public async openMedia(photoID: string, target?: HTMLElement, fromRight = 0) {
|
||||
if(this.setMoverPromise) return this.setMoverPromise;
|
||||
|
||||
const photo = appPhotosManager.getPhoto(photoID);
|
||||
|
||||
this.currentPhotoID = photo.id;
|
||||
|
||||
return super._openMedia(photo, this.peerID, fromRight, target, false);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import $rootScope from "../lib/rootScope";
|
||||
import { cancelEvent } from "../lib/utils";
|
||||
import { AppMediaViewerAvatar } from "./appMediaViewer";
|
||||
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
|
||||
|
||||
$rootScope.$on('avatar_update', (e) => {
|
||||
let peerID = e.detail;
|
||||
@ -30,17 +31,60 @@ export default class AvatarElement extends HTMLElement {
|
||||
this.isDialog = !!this.getAttribute('dialog');
|
||||
if(this.getAttribute('clickable') === '') {
|
||||
this.setAttribute('clickable', 'set');
|
||||
this.addEventListener('click', (e) => {
|
||||
let loading = false;
|
||||
this.addEventListener('click', async(e) => {
|
||||
cancelEvent(e);
|
||||
if(loading) return;
|
||||
//console.log('avatar clicked');
|
||||
const peerID = this.peerID;
|
||||
appProfileManager.getFullPhoto(this.peerID).then(photo => {
|
||||
if(this.peerID != peerID) return;
|
||||
if(photo) {
|
||||
loading = true;
|
||||
|
||||
const photo = await appProfileManager.getFullPhoto(this.peerID);
|
||||
if(this.peerID != peerID || !photo) {
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(peerID < 0) {
|
||||
const maxID = Number.MAX_SAFE_INTEGER;
|
||||
const inputFilter = 'inputMessagesFilterChatPhotos';
|
||||
const mid = await appMessagesManager.getSearch(peerID, '', {_: inputFilter}, maxID, 2, 0, 1).then(value => {
|
||||
//console.log(lol);
|
||||
// ! by descend
|
||||
return value.history[0];
|
||||
});
|
||||
|
||||
if(mid) {
|
||||
// ! гений в деле, костылируем (но это гениально)
|
||||
let message = appMessagesManager.getMessage(mid);
|
||||
const messagePhoto = message.action.photo;
|
||||
if(messagePhoto.id != photo.id) {
|
||||
message = {
|
||||
_: 'message',
|
||||
mid: maxID,
|
||||
media: {
|
||||
_: 'messageMediaPhoto',
|
||||
photo: photo
|
||||
},
|
||||
fromID: peerID
|
||||
};
|
||||
|
||||
appMessagesManager.messagesStorage[maxID] = message;
|
||||
}
|
||||
|
||||
const good = Array.from(this.querySelectorAll('img')).find(img => !img.classList.contains('emoji'));
|
||||
new AppMediaViewerAvatar().openMedia(peerID, good ? this : null);
|
||||
new AppMediaViewer(inputFilter).openMedia(message, good ? this : null);
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(photo) {
|
||||
const good = Array.from(this.querySelectorAll('img')).find(img => !img.classList.contains('emoji'));
|
||||
new AppMediaViewerAvatar(peerID).openMedia(photo.id, good ? this : null);
|
||||
}
|
||||
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -448,7 +448,8 @@ type MyInputMessagesFilter = 'inputMessagesFilterEmpty'
|
||||
| 'inputMessagesFilterRoundVideo'
|
||||
| 'inputMessagesFilterMusic'
|
||||
| 'inputMessagesFilterUrl'
|
||||
| 'inputMessagesFilterMyMentions';
|
||||
| 'inputMessagesFilterMyMentions'
|
||||
| 'inputMessagesFilterChatPhotos';
|
||||
|
||||
export class AppMessagesManager {
|
||||
public messagesStorage: {[mid: string]: any} = {};
|
||||
@ -2314,13 +2315,14 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
if(apiMessage.action) {
|
||||
let migrateFrom;
|
||||
let migrateTo;
|
||||
let migrateFrom: number;
|
||||
let migrateTo: number;
|
||||
switch(apiMessage.action._) {
|
||||
//case 'messageActionChannelEditPhoto':
|
||||
case 'messageActionChatEditPhoto':
|
||||
apiMessage.action.photo = appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
|
||||
//appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
|
||||
if(isBroadcast) {
|
||||
if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода.
|
||||
apiMessage.action._ = 'messageActionChannelEditPhoto';
|
||||
}
|
||||
break;
|
||||
@ -2947,6 +2949,10 @@ export class AppMessagesManager {
|
||||
neededContents['url'] = true;
|
||||
break;
|
||||
|
||||
case 'inputMessagesFilterChatPhotos':
|
||||
neededContents['avatar'] = true;
|
||||
break;
|
||||
|
||||
/* case 'inputMessagesFilterMyMentions':
|
||||
neededContents['mentioned'] = true;
|
||||
break; */
|
||||
@ -2975,6 +2981,8 @@ export class AppMessagesManager {
|
||||
found = true;
|
||||
} else if(neededContents['url'] && message.message && RichTextProcessor.matchUrl(message.message)) {
|
||||
found = true;
|
||||
} else if(neededContents['avatar'] && message.action && ['messageActionChannelEditPhoto', 'messageActionChatEditPhoto'].includes(message.action._)) {
|
||||
found = true;
|
||||
}
|
||||
|
||||
if(found) {
|
||||
@ -4402,6 +4410,8 @@ export class AppMessagesManager {
|
||||
} else if(this.needSingleMessages.indexOf(msgID) == -1) {
|
||||
this.needSingleMessages.push(msgID);
|
||||
return this.fetchSingleMessages();
|
||||
} else if(this.fetchSingleMessagesPromise) {
|
||||
return this.fetchSingleMessagesPromise;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { CancellablePromise } from "../../helpers/cancellablePromise";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
import { FileLocation, InputFileLocation, Photo, PhotoSize } from "../../layer";
|
||||
import { FileLocation, InputFileLocation, Photo, PhotoSize, PhotosPhotos } from "../../layer";
|
||||
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
|
||||
import { DownloadOptions } from "../mtproto/apiFileManager";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
|
||||
import { calcImageInBox, isObject, safeReplaceArrayInObject } from "../utils";
|
||||
import { MyDocument } from "./appDocsManager";
|
||||
import appDownloadManager from "./appDownloadManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
|
||||
export type MyPhoto = Photo.photo;
|
||||
|
||||
@ -108,29 +110,27 @@ export class AppPhotosManager {
|
||||
return bestPhotoSize;
|
||||
}
|
||||
|
||||
/* public getUserPhotos(userID: number, maxID: number, limit: number) {
|
||||
var inputUser = appUsersManager.getUserInput(userID);
|
||||
public getUserPhotos(userID: number, maxID: string = '0', limit: number = 20) {
|
||||
const inputUser = appUsersManager.getUserInput(userID);
|
||||
return apiManager.invokeApi('photos.getUserPhotos', {
|
||||
user_id: inputUser,
|
||||
offset: 0,
|
||||
limit: limit || 20,
|
||||
max_id: maxID || 0
|
||||
}).then((photosResult: any) => {
|
||||
limit: limit,
|
||||
max_id: maxID
|
||||
}).then((photosResult) => {
|
||||
appUsersManager.saveApiUsers(photosResult.users);
|
||||
var photoIDs = [];
|
||||
var context = {user_id: userID};
|
||||
for(var i = 0; i < photosResult.photos.length; i++) {
|
||||
//this.savePhoto(photosResult.photos[i], context);
|
||||
photosResult.photos[i] = this.savePhoto(photosResult.photos[i], context);
|
||||
photoIDs.push(photosResult.photos[i].id);
|
||||
}
|
||||
const photoIDs: string[] = [];
|
||||
photosResult.photos.forEach((photo, idx) => {
|
||||
photosResult.photos[idx] = this.savePhoto(photo, {type: 'profilePhoto', peerID: userID});
|
||||
photoIDs.push(photo.id);
|
||||
});
|
||||
|
||||
return {
|
||||
count: photosResult.count || photosResult.photos.length,
|
||||
count: (photosResult as PhotosPhotos.photosPhotosSlice).count || photosResult.photos.length,
|
||||
photos: photoIDs
|
||||
};
|
||||
});
|
||||
} */
|
||||
}
|
||||
|
||||
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
|
||||
let arr: Uint8Array;
|
||||
|
Loading…
Reference in New Issue
Block a user