Browse Source

Media viewer:

Avatars are now viewable, with pagination
Fix border-radius transition
master
morethanwords 4 years ago
parent
commit
d91b97bc02
  1. 128
      src/components/appMediaViewer.ts
  2. 58
      src/components/avatar.ts
  3. 18
      src/lib/appManagers/appMessagesManager.ts
  4. 30
      src/lib/appManagers/appPhotosManager.ts

128
src/components/appMediaViewer.ts

@ -7,7 +7,6 @@ import appImManager from "../lib/appManagers/appImManager"; @@ -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 @@ -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 @@ -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 @@ -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 = { @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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', {}> { @@ -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;
const peerID = this.peerID;
const loadCount = 50;
const maxID = this.nextTargets.length ? this.nextTargets[this.nextTargets.length - 1].photoID : this.currentPhotoID;
const promise = appPhotosManager.getUserPhotos(peerID, maxID, loadCount).then(value => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
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(peerID: number, target?: HTMLElement, reverse = false,
prevTargets: AppMediaViewerAvatar['prevTargets'] = [], nextTargets: AppMediaViewerAvatar['prevTargets'] = [], needLoadMore = true) {
public async openMedia(photoID: string, target?: HTMLElement, fromRight = 0) {
if(this.setMoverPromise) return this.setMoverPromise;
return appProfileManager.getFullPhoto(peerID).then(photo => {
const fromID = peerID;
const photo = appPhotosManager.getPhoto(photoID);
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;
}
this.currentPhotoID = photo.id;
const promise = super._openMedia(photo, fromID, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore);
});
return super._openMedia(photo, this.peerID, fromRight, target, false);
}
}

58
src/components/avatar.ts

@ -1,7 +1,8 @@ @@ -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 { @@ -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;
});
}
}

18
src/lib/appManagers/appMessagesManager.ts

@ -448,7 +448,8 @@ type MyInputMessagesFilter = 'inputMessagesFilterEmpty' @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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;
}
}

30
src/lib/appManagers/appPhotosManager.ts

@ -1,12 +1,14 @@ @@ -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 { @@ -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…
Cancel
Save