Browse Source

Fix loading lists in media viewer

master
morethanwords 3 years ago
parent
commit
9f168465b5
  1. 179
      src/components/appMediaViewer.ts
  2. 18
      src/lib/appManagers/appPhotosManager.ts
  3. 11
      src/lib/appManagers/appProfileManager.ts

179
src/components/appMediaViewer.ts

@ -76,6 +76,7 @@ type MediaQueueLoaderOptions<Item extends {}> = {
}; };
class MediaQueueLoader<Item extends {}> { class MediaQueueLoader<Item extends {}> {
public target: Item = false as any;
public prevTargets: Item[] = []; public prevTargets: Item[] = [];
public nextTargets: Item[] = []; public nextTargets: Item[] = [];
@ -129,6 +130,10 @@ class MediaQueueLoader<Item extends {}> {
} }
const promise = this.getLoadPromise(older, anchor, loadCount).then(items => { const promise = this.getLoadPromise(older, anchor, loadCount).then(items => {
if((older && this.loadMediaPromiseDown !== promise) || (!older && this.loadMediaPromiseUp !== promise)) {
return;
}
if(items.length < loadCount) { if(items.length < loadCount) {
/* if(this.reverse) { /* if(this.reverse) {
if(older) this.loadedAllMediaUp = true; if(older) this.loadedAllMediaUp = true;
@ -169,7 +174,6 @@ class MediaQueueLoader<Item extends {}> {
} }
class MediaSearchQueueLoader<Item extends {mid: number, peerId: number}> extends MediaQueueLoader<Item> { class MediaSearchQueueLoader<Item extends {mid: number, peerId: number}> extends MediaQueueLoader<Item> {
public currentMessageId = 0;
public searchContext: SearchSuperContext; public searchContext: SearchSuperContext;
constructor(options: Omit<MediaQueueLoaderOptions<Item>, 'getLoadPromise'> = {}) { constructor(options: Omit<MediaQueueLoaderOptions<Item>, 'getLoadPromise'> = {}) {
@ -177,13 +181,14 @@ class MediaSearchQueueLoader<Item extends {mid: number, peerId: number}> extends
...options, ...options,
getLoadPromise: (older, anchor, loadCount) => { getLoadPromise: (older, anchor, loadCount) => {
const backLimit = older ? 0 : loadCount; const backLimit = older ? 0 : loadCount;
let maxId = this.currentMessageId; let maxId = this.target?.mid;
if(anchor) maxId = anchor.mid; if(anchor) maxId = anchor.mid;
if(!older) maxId = appMessagesIdsManager.incrementMessageId(maxId, 1); if(!older) maxId = appMessagesIdsManager.incrementMessageId(maxId, 1);
return appMessagesManager.getSearch({ return appMessagesManager.getSearch({
...this.searchContext, ...this.searchContext,
peerId: anchor?.peerId,
maxId, maxId,
limit: backLimit ? 0 : loadCount, limit: backLimit ? 0 : loadCount,
backLimit backLimit
@ -204,6 +209,40 @@ class MediaSearchQueueLoader<Item extends {mid: number, peerId: number}> extends
public setSearchContext(context: SearchSuperContext) { public setSearchContext(context: SearchSuperContext) {
this.searchContext = context; this.searchContext = context;
if(this.searchContext.folderId !== undefined) {
this.loadedAllMediaUp = true;
if(this.searchContext.nextRate === undefined) {
this.loadedAllMediaDown = true;
}
}
}
}
class MediaAvatarQueueLoader<Item extends {photoId: string}> extends MediaQueueLoader<Item> {
private peerId: number;
constructor(options: Omit<MediaQueueLoaderOptions<Item>, 'getLoadPromise'> & {peerId: number}) {
super({
...options,
getLoadPromise: (older, anchor, loadCount) => {
if(this.peerId < 0) return Promise.resolve([]); // ! это значит, что открыло аватар чата, но следующих фотографий нет.
return appPhotosManager.getUserPhotos(this.peerId, anchor?.photoId, loadCount).then(value => {
const idx = value.photos.indexOf(this.target.photoId);
if(idx !== -1) {
value.photos.splice(idx, 1);
}
return value.photos.map(photoId => {
return {element: null as HTMLElement, photoId} as any;
});
});
}
});
this.peerId = options.peerId;
} }
} }
@ -220,7 +259,6 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected preloader: ProgressivePreloader = null; protected preloader: ProgressivePreloader = null;
protected preloaderStreamable: ProgressivePreloader = null; protected preloaderStreamable: ProgressivePreloader = null;
protected target: TargetType = null;
protected prevTargets: TargetType[] = []; protected prevTargets: TargetType[] = [];
protected nextTargets: TargetType[] = []; protected nextTargets: TargetType[] = [];
//protected targetContainer: HTMLElement = null; //protected targetContainer: HTMLElement = null;
@ -229,10 +267,6 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected log: ReturnType<typeof logger>; protected log: ReturnType<typeof logger>;
protected isFirstOpen = true; protected isFirstOpen = true;
protected loadMediaPromiseUp: Promise<void> = null;
protected loadMediaPromiseDown: Promise<void> = null;
protected loadedAllMediaUp = false;
protected loadedAllMediaDown = false;
protected reverse = false; // reverse means next = higher msgid protected reverse = false; // reverse means next = higher msgid
protected needLoadMore = true; protected needLoadMore = true;
@ -249,7 +283,6 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected onDownloadClick: (e: MouseEvent) => void; protected onDownloadClick: (e: MouseEvent) => void;
protected onPrevClick: (target: TargetType) => void; protected onPrevClick: (target: TargetType) => void;
protected onNextClick: (target: TargetType) => void; protected onNextClick: (target: TargetType) => void;
protected loadMoreMedia: (older: boolean) => Promise<void>;
protected videoPlayer: VideoPlayer; protected videoPlayer: VideoPlayer;
@ -268,7 +301,16 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected ctrlKeyDown: boolean; protected ctrlKeyDown: boolean;
constructor(topButtons: Array<keyof AppMediaViewerBase<ContentAdditionType, ButtonsAdditionType, TargetType>['buttons']>) { get target() {
return this.queueLoader.target;
}
set target(value) {
this.queueLoader.target = value;
}
constructor(protected queueLoader: MediaQueueLoader<TargetType>,
topButtons: Array<keyof AppMediaViewerBase<ContentAdditionType, ButtonsAdditionType, TargetType>['buttons']>) {
this.log = logger('AMV'); this.log = logger('AMV');
this.preloader = new ProgressivePreloader(); this.preloader = new ProgressivePreloader();
this.preloaderStreamable = new ProgressivePreloader({ this.preloaderStreamable = new ProgressivePreloader({
@ -564,13 +606,12 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
const promise = this.setMoverToTarget(this.target?.element, true).then(({onAnimationEnd}) => onAnimationEnd); const promise = this.setMoverToTarget(this.target?.element, true).then(({onAnimationEnd}) => onAnimationEnd);
this.target = null; this.target = false as any;
this.prevTargets = []; this.prevTargets.length = 0;
this.nextTargets = []; this.nextTargets.length = 0;
this.loadedAllMediaUp = this.loadedAllMediaDown = false;
this.loadMediaPromiseUp = this.loadMediaPromiseDown = null;
this.setMoverPromise = null; this.setMoverPromise = null;
this.tempId = -1; this.tempId = -1;
(window as any).appMediaViewer = undefined;
if(this.zoomSwipeHandler) { if(this.zoomSwipeHandler) {
this.zoomSwipeHandler.removeListeners(); this.zoomSwipeHandler.removeListeners();
@ -1331,6 +1372,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
this.reverse = reverse; this.reverse = reverse;
this.needLoadMore = needLoadMore; this.needLoadMore = needLoadMore;
this.isFirstOpen = false; this.isFirstOpen = false;
this.queueLoader.setTargets(this.prevTargets, this.nextTargets, this.reverse);
(window as any).appMediaViewer = this;
//this.loadMore = loadMore; //this.loadMore = loadMore;
/* if(appSidebarRight.historyTabIDs.slice(-1)[0] === AppSidebarRight.SLIDERITEMSIDS.forward) { /* if(appSidebarRight.historyTabIDs.slice(-1)[0] === AppSidebarRight.SLIDERITEMSIDS.forward) {
@ -1358,11 +1401,11 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
if(this.needLoadMore) { if(this.needLoadMore) {
if(this.nextTargets.length < 20) { if(this.nextTargets.length < 20) {
this.loadMoreMedia(!this.reverse); this.queueLoader.loadMoreMedia(!this.reverse);
} }
if(this.prevTargets.length < 20) { if(this.prevTargets.length < 20) {
this.loadMoreMedia(this.reverse); this.queueLoader.loadMoreMedia(this.reverse);
} }
} }
@ -1702,23 +1745,16 @@ type AppMediaViewerTargetType = {
peerId: number peerId: number
}; };
export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> { export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delete' | 'forward', AppMediaViewerTargetType> {
protected currentMessageId = 0;
protected currentPeerId = 0;
protected searchContext: SearchSuperContext;
protected btnMenuDelete: HTMLElement; protected btnMenuDelete: HTMLElement;
protected queueLoader: MediaSearchQueueLoader<AppMediaViewerTargetType>; protected queueLoader: MediaSearchQueueLoader<AppMediaViewerTargetType>;
constructor() { get searchContext() {
super(['delete', 'forward']); return this.queueLoader.searchContext;
}
/* const stub = document.createElement('div');
stub.classList.add(MEDIA_VIEWER_CLASSNAME + '-stub');
this.content.main.prepend(stub); */
this.queueLoader = new MediaSearchQueueLoader({ constructor() {
prevTargets: this.prevTargets, super(new MediaSearchQueueLoader({
nextTargets: this.nextTargets,
generateItem: (item) => { generateItem: (item) => {
const isForDocument = this.searchContext.inputFilter._ === 'inputMessagesFilterDocument'; const isForDocument = this.searchContext.inputFilter._ === 'inputMessagesFilterDocument';
const {mid, peerId} = item; const {mid, peerId} = item;
@ -1732,7 +1768,11 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
return {element: null as HTMLElement, mid, peerId}; return {element: null as HTMLElement, mid, peerId};
} }
}); }), ['delete', 'forward']);
/* const stub = document.createElement('div');
stub.classList.add(MEDIA_VIEWER_CLASSNAME + '-stub');
this.content.main.prepend(stub); */
this.content.caption = document.createElement('div'); this.content.caption = document.createElement('div');
this.content.caption.classList.add(MEDIA_VIEWER_CLASSNAME + '-caption'/* , 'media-viewer-stub' */); this.content.caption.classList.add(MEDIA_VIEWER_CLASSNAME + '-caption'/* , 'media-viewer-stub' */);
@ -1837,17 +1877,19 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
}; };
onDeleteClick = () => { onDeleteClick = () => {
new PopupDeleteMessages(this.currentPeerId, [this.currentMessageId], 'chat', () => { const target = this.target;
new PopupDeleteMessages(target.peerId, [target.mid], 'chat', () => {
this.target = {element: this.content.media} as any; this.target = {element: this.content.media} as any;
this.close(); this.close();
}); });
}; };
onForwardClick = () => { onForwardClick = () => {
if(this.currentMessageId) { const target = this.target;
//appSidebarRight.forwardTab.open([this.currentMessageId]); if(target.mid) {
//appSidebarRight.forwardTab.open([target.mid]);
new PopupForward({ new PopupForward({
[this.currentPeerId]: [this.currentMessageId] [target.peerId]: [target.mid]
}, () => { }, () => {
return this.close(); return this.close();
}); });
@ -1855,9 +1897,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
}; };
onAuthorClick = (e: MouseEvent) => { onAuthorClick = (e: MouseEvent) => {
if(this.currentMessageId && this.currentMessageId !== Number.MAX_SAFE_INTEGER) { const {mid, peerId} = this.target;
const mid = this.currentMessageId; if(mid && mid !== Number.MAX_SAFE_INTEGER) {
const peerId = this.currentPeerId;
const threadId = this.searchContext.threadId; const threadId = this.searchContext.threadId;
this.close(e) this.close(e)
//.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve()) //.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve())
@ -1876,7 +1917,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
}; };
onDownloadClick = () => { onDownloadClick = () => {
const message = appMessagesManager.getMessageByPeer(this.currentPeerId, this.currentMessageId); const {peerId, mid} = this.target;
const message = appMessagesManager.getMessageByPeer(peerId, mid);
if(message.media.photo) { if(message.media.photo) {
appPhotosManager.savePhotoFile(message.media.photo, appImManager.chat.bubbles.lazyLoadQueue.queueId); appPhotosManager.savePhotoFile(message.media.photo, appImManager.chat.bubbles.lazyLoadQueue.queueId);
} else { } else {
@ -1892,11 +1934,6 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
} }
}; };
// нет смысла делать проверку для reverse и loadMediaPromise
protected loadMoreMedia = (older = true) => {
return this.queueLoader.loadMoreMedia(older);
};
private setCaption(message: Message.message) { private setCaption(message: Message.message) {
const caption = message.message; const caption = message.message;
let html = ''; let html = '';
@ -1913,15 +1950,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
} }
public setSearchContext(context: SearchSuperContext) { public setSearchContext(context: SearchSuperContext) {
this.searchContext = context; this.queueLoader.setSearchContext(context);
if(this.searchContext.folderId !== undefined) {
this.loadedAllMediaUp = true;
if(this.searchContext.nextRate === undefined) {
this.loadedAllMediaDown = true;
}
}
return this; return this;
} }
@ -1941,8 +1970,6 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
button.classList.toggle('hide', !canDeleteMessage); button.classList.toggle('hide', !canDeleteMessage);
}); });
this.currentMessageId = mid;
this.currentPeerId = message.peerId;
this.setCaption(message); this.setCaption(message);
const promise = super._openMedia(media, message.date, fromId, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore); const promise = super._openMedia(media, message.date, fromId, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore);
this.target.mid = mid; this.target.mid = mid;
@ -1958,11 +1985,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string}; type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string};
export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> { export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMediaViewerAvatarTargetType> {
public currentPhotoId: string;
public peerId: number; public peerId: number;
constructor(peerId: number) { constructor(peerId: number) {
super([/* 'delete' */]); super(new MediaAvatarQueueLoader({peerId}), [/* 'delete' */]);
this.peerId = peerId; this.peerId = peerId;
@ -1990,54 +2016,13 @@ export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMe
}; };
onDownloadClick = () => { onDownloadClick = () => {
appPhotosManager.savePhotoFile(appPhotosManager.getPhoto(this.currentPhotoId), appImManager.chat.bubbles.lazyLoadQueue.queueId); appPhotosManager.savePhotoFile(appPhotosManager.getPhoto(this.target.photoId), appImManager.chat.bubbles.lazyLoadQueue.queueId);
};
protected loadMoreMedia = (older = true) => {
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;
}
// if(DEBUG) {
// 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: 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, prevTargets?: AppMediaViewerAvatarTargetType[], nextTargets?: AppMediaViewerAvatarTargetType[]) { public async openMedia(photoId: string, target?: HTMLElement, fromRight = 0, prevTargets?: AppMediaViewerAvatarTargetType[], nextTargets?: AppMediaViewerAvatarTargetType[]) {
if(this.setMoverPromise) return this.setMoverPromise; if(this.setMoverPromise) return this.setMoverPromise;
const photo = appPhotosManager.getPhoto(photoId); const photo = appPhotosManager.getPhoto(photoId);
this.currentPhotoId = photo.id;
const ret = super._openMedia(photo, photo.date, this.peerId, fromRight, target, false, prevTargets, nextTargets); const ret = super._openMedia(photo, photo.date, this.peerId, fromRight, target, false, prevTargets, nextTargets);
this.target.photoId = photo.id; this.target.photoId = photo.id;

18
src/lib/appManagers/appPhotosManager.ts

@ -15,7 +15,7 @@ import { CancellablePromise } from "../../helpers/cancellablePromise";
import { getFileNameByLocation } from "../../helpers/fileName"; import { getFileNameByLocation } from "../../helpers/fileName";
import { safeReplaceArrayInObject, isObject } from "../../helpers/object"; import { safeReplaceArrayInObject, isObject } from "../../helpers/object";
import { isSafari } from "../../helpers/userAgent"; import { isSafari } from "../../helpers/userAgent";
import { InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from "../../layer"; import { InputFileLocation, InputMedia, InputPhoto, Photo, PhotoSize, PhotosPhotos } from "../../layer";
import apiManager from "../mtproto/mtprotoworker"; import apiManager from "../mtproto/mtprotoworker";
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase"; import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
import { MyDocument } from "./appDocsManager"; import { MyDocument } from "./appDocsManager";
@ -383,15 +383,19 @@ export class AppPhotosManager {
return isObject(photoId) ? photoId as MyPhoto : this.photos[photoId as any as string]; return isObject(photoId) ? photoId as MyPhoto : this.photos[photoId as any as string];
} }
public getInput(photo: MyPhoto): InputPhoto.inputPhoto {
return {
_: 'inputPhoto',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference
};
}
public getMediaInput(photo: MyPhoto): InputMedia.inputMediaPhoto { public getMediaInput(photo: MyPhoto): InputMedia.inputMediaPhoto {
return { return {
_: 'inputMediaPhoto', _: 'inputMediaPhoto',
id: { id: this.getInput(photo),
_: 'inputPhoto',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference
},
ttl_seconds: 0 ttl_seconds: 0
}; };
} }

11
src/lib/appManagers/appProfileManager.ts

@ -488,6 +488,17 @@ export class AppProfileManager {
}); });
} }
public deletePhotos(photoIds: string[]) {
return apiManager.invokeApiSingle('photos.deletePhotos', {
id: photoIds.map(photoId => {
const photo = appPhotosManager.getPhoto(photoId);
return appPhotosManager.getInput(photo);
})
}).then((deletedList) => {
});
}
public getChatMembersString(id: number) { public getChatMembersString(id: number) {
const chat: Chat = appChatsManager.getChat(id); const chat: Chat = appChatsManager.getChat(id);
if(chat._ === 'chatForbidden') { if(chat._ === 'chatForbidden') {

Loading…
Cancel
Save