Search messages pagination
This commit is contained in:
parent
971135ff8b
commit
70324b96ad
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ node_modules
|
|||||||
coverage
|
coverage
|
||||||
__pycache__
|
__pycache__
|
||||||
dist
|
dist
|
||||||
|
.DS_Store
|
||||||
|
@ -5,6 +5,8 @@ import CryptoWorker from '../lib/crypto/cryptoworker';
|
|||||||
import LottieLoader from '../lib/lottieLoader';
|
import LottieLoader from '../lib/lottieLoader';
|
||||||
import appStickersManager from "../lib/appManagers/appStickersManager";
|
import appStickersManager from "../lib/appManagers/appStickersManager";
|
||||||
import appDocsManager from "../lib/appManagers/appDocsManager";
|
import appDocsManager from "../lib/appManagers/appDocsManager";
|
||||||
|
import {AppImManager} from "../lib/appManagers/appImManager";
|
||||||
|
import {AppMediaViewer} from '../lib/appManagers/appMediaViewer';
|
||||||
|
|
||||||
export type MTDocument = {
|
export type MTDocument = {
|
||||||
_: 'document',
|
_: 'document',
|
||||||
@ -213,9 +215,20 @@ export class LazyLoadQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapVideo(doc: MTDocument, container: HTMLDivElement, middleware: () => boolean, messageID: number, justLoader = true, preloader?: ProgressivePreloader) {
|
export function wrapVideo(this: any, doc: MTDocument, container: HTMLDivElement, message: any, justLoader = true, preloader?: ProgressivePreloader) {
|
||||||
if(!container.firstElementChild || container.firstElementChild.tagName != 'IMG') {
|
//if(!container.firstElementChild || container.firstElementChild.tagName != 'IMG') {
|
||||||
let size = appPhotosManager.setAttachmentSize(doc, container);
|
let size = appPhotosManager.setAttachmentSize(doc, container);
|
||||||
|
//}
|
||||||
|
|
||||||
|
let peerID = this.peerID ? this.peerID : this.currentMessageID;
|
||||||
|
|
||||||
|
//container.classList.add('video');
|
||||||
|
|
||||||
|
let img = container.firstElementChild as HTMLImageElement || new Image();
|
||||||
|
img.setAttribute('message-id', '' + message.id);
|
||||||
|
|
||||||
|
if(!container.contains(img)) {
|
||||||
|
container.append(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
//return Promise.resolve();
|
//return Promise.resolve();
|
||||||
@ -227,17 +240,18 @@ export function wrapVideo(doc: MTDocument, container: HTMLDivElement, middleware
|
|||||||
let loadVideo = () => {
|
let loadVideo = () => {
|
||||||
let promise = appDocsManager.downloadDoc(doc);
|
let promise = appDocsManager.downloadDoc(doc);
|
||||||
|
|
||||||
promise.notify = (details: {done: number, total: number}) => {
|
/* promise.notify = (details: {done: number, total: number}) => {
|
||||||
console.log('doc download', promise, details);
|
console.log('doc download', promise, details);
|
||||||
preloader.setProgress(details.done);
|
preloader.setProgress(details.done);
|
||||||
};
|
}; */
|
||||||
|
|
||||||
return promise.then(blob => {
|
return promise.then(blob => {
|
||||||
if(!middleware()) {
|
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
|
||||||
|
this.log.warn('peer changed');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('loaded doc:', doc, blob, container.firstElementChild);
|
console.log('loaded doc:', doc, blob, container);
|
||||||
|
|
||||||
let video = document.createElement('video');
|
let video = document.createElement('video');
|
||||||
video.loop = true;
|
video.loop = true;
|
||||||
@ -245,52 +259,50 @@ export function wrapVideo(doc: MTDocument, container: HTMLDivElement, middleware
|
|||||||
|
|
||||||
if(!justLoader) {
|
if(!justLoader) {
|
||||||
video.controls = true;
|
video.controls = true;
|
||||||
|
} else {
|
||||||
|
video.volume = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
video.setAttribute('message-id', '' + messageID);
|
video.setAttribute('message-id', '' + message.id);
|
||||||
|
|
||||||
let source = document.createElement('source');
|
let source = document.createElement('source');
|
||||||
//source.src = doc.url;
|
//source.src = doc.url;
|
||||||
source.src = URL.createObjectURL(blob);
|
source.src = URL.createObjectURL(blob);
|
||||||
source.type = doc.mime_type;
|
source.type = doc.mime_type;
|
||||||
|
|
||||||
|
if(img && container.contains(img)) {
|
||||||
|
container.removeChild(img);
|
||||||
|
}
|
||||||
|
|
||||||
video.append(source);
|
video.append(source);
|
||||||
container.append(video);
|
container.append(video);
|
||||||
|
|
||||||
if(container.firstElementChild) {
|
|
||||||
container.firstElementChild.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
preloader.detach();
|
preloader.detach();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if(doc.type == 'gif') {
|
if(doc.type == 'gif') {
|
||||||
return loadVideo();
|
return this.peerID ? this.loadMediaQueuePush(loadVideo) : loadVideo();
|
||||||
} else { // if video
|
} else { // if video
|
||||||
return appPhotosManager.preloadPhoto(doc).then((blob) => {
|
let load = () => appPhotosManager.preloadPhoto(doc).then((blob) => {
|
||||||
if(!middleware()) {
|
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
|
||||||
|
this.log.warn('peer changed');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(container.firstElementChild) {
|
img.src = URL.createObjectURL(blob);
|
||||||
container.firstElementChild.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = new Image();
|
|
||||||
image.src = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
/* image.style.height = doc.h + 'px';
|
/* image.style.height = doc.h + 'px';
|
||||||
image.style.width = doc.w + 'px'; */
|
image.style.width = doc.w + 'px'; */
|
||||||
|
|
||||||
image.setAttribute('message-id', '' + messageID);
|
|
||||||
|
|
||||||
container.append(image);
|
|
||||||
|
|
||||||
if(!justLoader) {
|
if(!justLoader) {
|
||||||
return loadVideo();
|
return loadVideo();
|
||||||
|
} else {
|
||||||
|
preloader.detach();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this.peerID ? this.loadMediaQueuePush(load) : load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,6 +362,37 @@ export function scrollable(el: HTMLDivElement, x = false, y = true) {
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function wrapPhoto(this: AppImManager, photo: any, message: any, container: HTMLDivElement) {
|
||||||
|
//container.classList.add('photo');
|
||||||
|
|
||||||
|
let peerID = this.peerID;
|
||||||
|
|
||||||
|
let size = appPhotosManager.setAttachmentSize(photo.id, container);
|
||||||
|
let image = container.firstElementChild as HTMLImageElement || new Image();
|
||||||
|
image.setAttribute('message-id', message.mid);
|
||||||
|
|
||||||
|
if(!container.contains(image)) {
|
||||||
|
container.append(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
let preloader = new ProgressivePreloader(container, false);
|
||||||
|
|
||||||
|
let load = () => appPhotosManager.preloadPhoto(photo.id, size).then((blob) => {
|
||||||
|
if(this.peerID != peerID) {
|
||||||
|
this.log.warn('peer changed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
image.src = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
preloader.detach();
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('wrapPhoto', load, container, image);
|
||||||
|
|
||||||
|
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
|
||||||
|
}
|
||||||
|
|
||||||
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean) {
|
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean) {
|
||||||
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
|
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
|
||||||
|
|
||||||
|
@ -2,13 +2,23 @@ import { MTProto } from "../mtproto/mtproto";
|
|||||||
import { $rootScope, findUpTag } from "../utils";
|
import { $rootScope, findUpTag } from "../utils";
|
||||||
import appImManager from "./appImManager";
|
import appImManager from "./appImManager";
|
||||||
import appPeersManager from './appPeersManager';
|
import appPeersManager from './appPeersManager';
|
||||||
import { DialogDom } from "../services";
|
|
||||||
import appMessagesManager from "./appMessagesManager";
|
import appMessagesManager from "./appMessagesManager";
|
||||||
import appUsersManager from "./appUsersManager";
|
import appUsersManager from "./appUsersManager";
|
||||||
import appSidebarRight from "./appSidebarRight";
|
import appSidebarRight from "./appSidebarRight";
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import { ripple } from "../../components/misc";
|
import { ripple } from "../../components/misc";
|
||||||
|
|
||||||
|
type DialogDom = {
|
||||||
|
avatarDiv: HTMLDivElement,
|
||||||
|
captionDiv: HTMLDivElement,
|
||||||
|
titleSpan: HTMLSpanElement,
|
||||||
|
statusSpan: HTMLSpanElement,
|
||||||
|
lastTimeSpan: HTMLSpanElement,
|
||||||
|
unreadMessagesSpan: HTMLSpanElement,
|
||||||
|
lastMessageSpan: HTMLSpanElement,
|
||||||
|
listEl: HTMLLIElement
|
||||||
|
};
|
||||||
|
|
||||||
export class AppDialogsManager {
|
export class AppDialogsManager {
|
||||||
public pinnedChatList = document.getElementById('dialogs-pinned') as HTMLUListElement;
|
public pinnedChatList = document.getElementById('dialogs-pinned') as HTMLUListElement;
|
||||||
public chatList = document.getElementById('dialogs') as HTMLUListElement;
|
public chatList = document.getElementById('dialogs') as HTMLUListElement;
|
||||||
@ -190,13 +200,13 @@ export class AppDialogsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(document.type == 'video') {
|
if(document.type == 'video') {
|
||||||
lastMessageText += '<i>Video</i>';
|
lastMessageText = '<i>Video' + (lastMessage.message ? ', ' : '') + '</i>';
|
||||||
found = true;
|
found = true;
|
||||||
} else if(document.type == 'voice') {
|
} else if(document.type == 'voice') {
|
||||||
lastMessageText += '<i>Voice message</i>';
|
lastMessageText = '<i>Voice message</i>';
|
||||||
found = true;
|
found = true;
|
||||||
} else if(document.type == 'gif') {
|
} else if(document.type == 'gif') {
|
||||||
lastMessageText += '<i>GIF</i>';
|
lastMessageText = '<i>GIF' + (lastMessage.message ? ', ' : '') + '</i>';
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import appUsersManager from "./appUsersManager";
|
|||||||
import appMessagesManager from "./appMessagesManager";
|
import appMessagesManager from "./appMessagesManager";
|
||||||
import appPeersManager from "./appPeersManager";
|
import appPeersManager from "./appPeersManager";
|
||||||
import appProfileManager from "./appProfileManager";
|
import appProfileManager from "./appProfileManager";
|
||||||
import { ProgressivePreloader, wrapDocument, wrapSticker, wrapVideo } from "../../components/misc";
|
import { ProgressivePreloader, wrapDocument, wrapSticker, wrapVideo, wrapPhoto } from "../../components/misc";
|
||||||
import appDialogsManager from "./appDialogsManager";
|
import appDialogsManager from "./appDialogsManager";
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import appPhotosManager from "./appPhotosManager";
|
import appPhotosManager from "./appPhotosManager";
|
||||||
@ -121,7 +121,7 @@ export class AppImManager {
|
|||||||
public scroll: HTMLDivElement = null;
|
public scroll: HTMLDivElement = null;
|
||||||
public scrollPosition: ScrollPosition = null;
|
public scrollPosition: ScrollPosition = null;
|
||||||
|
|
||||||
private log: ReturnType<typeof logger>;
|
public log: ReturnType<typeof logger>;
|
||||||
|
|
||||||
private preloader: ProgressivePreloader = null;
|
private preloader: ProgressivePreloader = null;
|
||||||
|
|
||||||
@ -763,28 +763,9 @@ export class AppImManager {
|
|||||||
let photo = message.media.photo;
|
let photo = message.media.photo;
|
||||||
this.log('messageMediaPhoto', photo);
|
this.log('messageMediaPhoto', photo);
|
||||||
|
|
||||||
bubble.classList.add('photo');
|
bubble.classList.add('hide-name', 'photo');
|
||||||
|
|
||||||
let size = appPhotosManager.setAttachmentSize(photo.id, attachmentDiv);
|
wrapPhoto.call(this, photo, message, attachmentDiv);
|
||||||
let load = () => appPhotosManager.preloadPhoto(photo.id, size).then((blob) => {
|
|
||||||
if(this.peerID != peerID) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(attachmentDiv.firstElementChild) {
|
|
||||||
attachmentDiv.firstElementChild.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = new Image();
|
|
||||||
image.src = URL.createObjectURL(blob);
|
|
||||||
image.setAttribute('message-id', message.mid);
|
|
||||||
attachmentDiv.append(image);
|
|
||||||
});
|
|
||||||
|
|
||||||
bubble.classList.add('hide-name');
|
|
||||||
|
|
||||||
this.loadMediaQueuePush(load);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,58 +808,18 @@ export class AppImManager {
|
|||||||
doc = webpage.document;
|
doc = webpage.document;
|
||||||
|
|
||||||
if(doc.type == 'gif' || doc.type == 'video') {
|
if(doc.type == 'gif' || doc.type == 'video') {
|
||||||
appPhotosManager.setAttachmentSize(doc, preview);
|
|
||||||
|
|
||||||
bubble.classList.add('video');
|
bubble.classList.add('video');
|
||||||
|
wrapVideo.call(this, doc, preview, message);
|
||||||
let load = () => wrapVideo(doc, preview, () => {
|
|
||||||
if(this.peerID != peerID) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedVideo = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, message.mid);
|
|
||||||
|
|
||||||
this.loadMediaQueuePush(load);
|
|
||||||
} else {
|
} else {
|
||||||
doc = null;
|
doc = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(webpage.photo && !doc) {
|
if(webpage.photo && !doc) {
|
||||||
|
bubble.classList.add('photo');
|
||||||
appPhotosManager.savePhoto(webpage.photo); // hot-fix because no webpage manager
|
appPhotosManager.savePhoto(webpage.photo); // hot-fix because no webpage manager
|
||||||
|
|
||||||
bubble.classList.add('photo');
|
wrapPhoto.call(this, webpage.photo, message, preview);
|
||||||
|
|
||||||
let size = appPhotosManager.setAttachmentSize(webpage.photo.id, preview);
|
|
||||||
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, size).then((blob) => {
|
|
||||||
if(this.peerID != peerID) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(loadedVideo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let img = preview.firstElementChild as HTMLImageElement || new Image();
|
|
||||||
this.log('night running 1', bubble, bubble.scrollHeight, img.src);
|
|
||||||
|
|
||||||
//setTimeout(() => {
|
|
||||||
img.src = URL.createObjectURL(blob);
|
|
||||||
///}, 5e3);
|
|
||||||
|
|
||||||
if(!preview.contains(img)) {
|
|
||||||
preview.append(img);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log('night running 2', bubble, bubble.scrollHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadMediaQueuePush(load);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(preview) {
|
if(preview) {
|
||||||
@ -943,18 +884,7 @@ export class AppImManager {
|
|||||||
this.log('never get free 2', doc);
|
this.log('never get free 2', doc);
|
||||||
|
|
||||||
bubble.classList.add('video');
|
bubble.classList.add('video');
|
||||||
|
wrapVideo.call(this, doc, attachmentDiv, message);
|
||||||
appPhotosManager.setAttachmentSize(doc, attachmentDiv);
|
|
||||||
let load = () => wrapVideo(doc, attachmentDiv, () => {
|
|
||||||
if(this.peerID != peerID) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, message.mid);
|
|
||||||
|
|
||||||
this.loadMediaQueuePush(load);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -1055,6 +985,7 @@ export class AppImManager {
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!our && this.peerID < 0) {
|
||||||
let avatarDiv = document.createElement('div');
|
let avatarDiv = document.createElement('div');
|
||||||
avatarDiv.classList.add('user-avatar');
|
avatarDiv.classList.add('user-avatar');
|
||||||
|
|
||||||
@ -1069,6 +1000,7 @@ export class AppImManager {
|
|||||||
|
|
||||||
bubble.append(avatarDiv);
|
bubble.append(avatarDiv);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let type = our ? 'out' : 'in';
|
let type = our ? 'out' : 'in';
|
||||||
|
|
||||||
|
@ -7,8 +7,9 @@ import { $rootScope } from "../utils";
|
|||||||
import appMessagesManager from "./appMessagesManager";
|
import appMessagesManager from "./appMessagesManager";
|
||||||
import { CancellablePromise } from "../mtproto/apiFileManager";
|
import { CancellablePromise } from "../mtproto/apiFileManager";
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
|
import { logger } from "../polyfill";
|
||||||
|
|
||||||
class AppMediaViewer {
|
export class AppMediaViewer {
|
||||||
private overlaysDiv = document.querySelector('.overlays') as HTMLDivElement;
|
private overlaysDiv = document.querySelector('.overlays') as HTMLDivElement;
|
||||||
private author = {
|
private author = {
|
||||||
avatarEl: this.overlaysDiv.querySelector('.user-avatar') as HTMLDivElement,
|
avatarEl: this.overlaysDiv.querySelector('.user-avatar') as HTMLDivElement,
|
||||||
@ -29,17 +30,21 @@ class AppMediaViewer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private reverse = false;
|
private reverse = false;
|
||||||
private currentMessageID = 0;
|
public currentMessageID = 0;
|
||||||
private higherMsgID: number | undefined = 0;
|
private higherMsgID: number | undefined = 0;
|
||||||
private lowerMsgID: number | undefined = 0;
|
private lowerMsgID: number | undefined = 0;
|
||||||
private preloader: ProgressivePreloader = null;
|
private preloader: ProgressivePreloader = null;
|
||||||
|
|
||||||
|
public log: ReturnType<typeof logger>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.log = logger('AMV');
|
||||||
this.preloader = new ProgressivePreloader();
|
this.preloader = new ProgressivePreloader();
|
||||||
|
|
||||||
this.buttons.close.addEventListener('click', () => {
|
this.buttons.close.addEventListener('click', () => {
|
||||||
this.overlaysDiv.classList.remove('active');
|
this.overlaysDiv.classList.remove('active');
|
||||||
this.content.container.innerHTML = '';
|
this.content.container.innerHTML = '';
|
||||||
|
this.currentMessageID = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.buttons.prev.addEventListener('click', () => {
|
this.buttons.prev.addEventListener('click', () => {
|
||||||
@ -127,7 +132,7 @@ class AppMediaViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public openMedia(message: any, reverse = false) {
|
public openMedia(message: any, reverse = false) {
|
||||||
console.log('openMedia doc:', message);
|
this.log('openMedia doc:', message);
|
||||||
let media = message.media.photo || message.media.document || message.media.webpage.document;
|
let media = message.media.photo || message.media.document || message.media.webpage.document;
|
||||||
|
|
||||||
let isVideo = media.mime_type == 'video/mp4';
|
let isVideo = media.mime_type == 'video/mp4';
|
||||||
@ -138,7 +143,7 @@ class AppMediaViewer {
|
|||||||
let container = this.content.container;
|
let container = this.content.container;
|
||||||
|
|
||||||
if(container.firstElementChild) {
|
if(container.firstElementChild) {
|
||||||
container.firstElementChild.remove();
|
container.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
let date = new Date(media.date * 1000);
|
let date = new Date(media.date * 1000);
|
||||||
@ -163,21 +168,12 @@ class AppMediaViewer {
|
|||||||
this.overlaysDiv.classList.add('active');
|
this.overlaysDiv.classList.add('active');
|
||||||
|
|
||||||
if(isVideo) {
|
if(isVideo) {
|
||||||
appPhotosManager.setAttachmentSize(media, container);
|
//this.preloader.attach(container);
|
||||||
|
|
||||||
this.preloader.attach(container);
|
|
||||||
//this.preloader.setProgress(75);
|
//this.preloader.setProgress(75);
|
||||||
|
|
||||||
console.log('will wrap video');
|
this.log('will wrap video');
|
||||||
|
|
||||||
wrapVideo(media, container, () => {
|
wrapVideo.call(this, media, container, message, false, this.preloader);
|
||||||
if(this.currentMessageID != message.mid) {
|
|
||||||
console.warn('media viewer changed photo');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, message.mid, false, this.preloader);
|
|
||||||
} else {
|
} else {
|
||||||
let size = appPhotosManager.setAttachmentSize(media.id, container, appPhotosManager.windowW, appPhotosManager.windowH);
|
let size = appPhotosManager.setAttachmentSize(media.id, container, appPhotosManager.windowW, appPhotosManager.windowH);
|
||||||
|
|
||||||
@ -187,11 +183,11 @@ class AppMediaViewer {
|
|||||||
let cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
|
let cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
|
||||||
cancellablePromise.then((blob) => {
|
cancellablePromise.then((blob) => {
|
||||||
if(this.currentMessageID != message.mid) {
|
if(this.currentMessageID != message.mid) {
|
||||||
console.warn('media viewer changed photo');
|
this.log.warn('media viewer changed photo');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('indochina', blob);
|
this.log('indochina', blob);
|
||||||
if(container.firstElementChild) {
|
if(container.firstElementChild) {
|
||||||
container.firstElementChild.remove();
|
container.firstElementChild.remove();
|
||||||
}
|
}
|
||||||
|
@ -1639,7 +1639,11 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
public getSearch(peerID = 0, query: string = '', inputFilter: {
|
public getSearch(peerID = 0, query: string = '', inputFilter: {
|
||||||
_?: string
|
_?: string
|
||||||
} = {_: 'inputMessagesFilterEmpty'}, maxID: number, limit: number) {
|
} = {_: 'inputMessagesFilterEmpty'}, maxID: number, limit: number, offsetRate = 0): Promise<{
|
||||||
|
count: number,
|
||||||
|
next_rate: number,
|
||||||
|
history: number[]
|
||||||
|
}> {
|
||||||
//peerID = peerID ? parseInt(peerID) : 0;
|
//peerID = peerID ? parseInt(peerID) : 0;
|
||||||
var foundMsgs: number[] = [];
|
var foundMsgs: number[] = [];
|
||||||
var useSearchCache = !query;
|
var useSearchCache = !query;
|
||||||
@ -1713,6 +1717,7 @@ export class AppMessagesManager {
|
|||||||
default:
|
default:
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
count: 0,
|
count: 0,
|
||||||
|
next_rate: 0,
|
||||||
history: [] as number[]
|
history: [] as number[]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1756,6 +1761,7 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
count: 0,
|
count: 0,
|
||||||
|
next_rate: 0,
|
||||||
history: foundMsgs
|
history: foundMsgs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1793,7 +1799,7 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
apiPromise = MTProto.apiManager.invokeApi('messages.searchGlobal', {
|
apiPromise = MTProto.apiManager.invokeApi('messages.searchGlobal', {
|
||||||
q: query,
|
q: query,
|
||||||
offset_date: offsetDate,
|
offset_rate: offsetRate,
|
||||||
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
|
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
|
||||||
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
|
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
|
||||||
limit: limit || 20
|
limit: limit || 20
|
||||||
@ -1808,6 +1814,8 @@ export class AppMessagesManager {
|
|||||||
appChatsManager.saveApiChats(searchResult.chats);
|
appChatsManager.saveApiChats(searchResult.chats);
|
||||||
this.saveMessages(searchResult.messages);
|
this.saveMessages(searchResult.messages);
|
||||||
|
|
||||||
|
console.log('messages.search result:', searchResult);
|
||||||
|
|
||||||
var foundCount: number = searchResult.count || searchResult.messages.length;
|
var foundCount: number = searchResult.count || searchResult.messages.length;
|
||||||
|
|
||||||
foundMsgs = [];
|
foundMsgs = [];
|
||||||
@ -1830,12 +1838,14 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
count: foundCount,
|
count: foundCount,
|
||||||
|
next_rate: searchResult.next_rate,
|
||||||
history: foundMsgs
|
history: foundMsgs
|
||||||
}
|
};
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
if(error.code == 400) {
|
if(error.code == 400) {
|
||||||
error.handled = true;
|
error.handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -151,13 +151,13 @@ export class AppPhotosManager {
|
|||||||
let image = new Image();
|
let image = new Image();
|
||||||
image.src = URL.createObjectURL(blob);
|
image.src = URL.createObjectURL(blob);
|
||||||
|
|
||||||
image.style.width = '100%';
|
// image.style.width = '100%';
|
||||||
image.style.height = '100%';
|
// image.style.height = '100%';
|
||||||
div.append(image);
|
div.append(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAttachmentSize(photoID: any, div: HTMLDivElement, w = 380, h = 0) {
|
public setAttachmentSize(photoID: any, div: HTMLDivElement, w = 380, h = 0/* 380 */) {
|
||||||
let photo: /* MTDocument | MTPhoto */any = null;
|
let photo: /* MTDocument | MTPhoto */any = null;
|
||||||
|
|
||||||
if(typeof(photoID) === 'string') {
|
if(typeof(photoID) === 'string') {
|
||||||
@ -170,11 +170,9 @@ export class AppPhotosManager {
|
|||||||
let photoSize = this.choosePhotoSize(photo, w, h);
|
let photoSize = this.choosePhotoSize(photo, w, h);
|
||||||
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
||||||
|
|
||||||
let thumb: any;
|
|
||||||
let sizes = photo.sizes || photo.thumbs;
|
let sizes = photo.sizes || photo.thumbs;
|
||||||
if(sizes && (sizes[0]._ == 'photoStrippedSize' || sizes[0]._ == 'photoCachedSize' && sizes[0].bytes)) {
|
if(sizes && sizes[0].bytes) {
|
||||||
thumb = sizes[0];
|
this.setAttachmentPreview(sizes[0].bytes, div);
|
||||||
this.setAttachmentPreview(thumb.bytes, div);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(photo._ == 'document' /* && photo.type != 'video' */ && photo.type != 'gif') {
|
if(photo._ == 'document' /* && photo.type != 'video' */ && photo.type != 'gif') {
|
||||||
|
@ -2,6 +2,8 @@ import { logger } from "../polyfill";
|
|||||||
import { scrollable } from "../../components/misc";
|
import { scrollable } from "../../components/misc";
|
||||||
import appMessagesManager from "./appMessagesManager";
|
import appMessagesManager from "./appMessagesManager";
|
||||||
import appDialogsManager from "./appDialogsManager";
|
import appDialogsManager from "./appDialogsManager";
|
||||||
|
import { isElementInViewport } from "../utils";
|
||||||
|
import appMessagesIDsManager from "./appMessagesIDsManager";
|
||||||
|
|
||||||
class AppSidebarLeft {
|
class AppSidebarLeft {
|
||||||
private sidebarEl = document.querySelector('.page-chats .chats-container') as HTMLDivElement;
|
private sidebarEl = document.querySelector('.page-chats .chats-container') as HTMLDivElement;
|
||||||
@ -15,13 +17,22 @@ class AppSidebarLeft {
|
|||||||
private log = logger('SL');
|
private log = logger('SL');
|
||||||
|
|
||||||
private peerID = 0;
|
private peerID = 0;
|
||||||
|
private minMsgID = 0;
|
||||||
|
private loadedCount = 0;
|
||||||
|
private foundCount = 0;
|
||||||
|
private offsetRate = 0;
|
||||||
|
|
||||||
private searchPromise: Promise<void> = null;
|
private searchPromise: Promise<void> = null;
|
||||||
|
private searchTimeout: number = 0;
|
||||||
|
|
||||||
|
private query = '';
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.listsContainer = scrollable(this.searchContainer);
|
this.listsContainer = scrollable(this.searchContainer);
|
||||||
this.searchMessagesList = document.createElement('ul');
|
this.searchMessagesList = document.createElement('ul');
|
||||||
|
|
||||||
|
this.listsContainer.onscroll = this.onSidebarScroll.bind(this);
|
||||||
|
|
||||||
this.searchContainer.append(this.listsContainer);
|
this.searchContainer.append(this.listsContainer);
|
||||||
|
|
||||||
appDialogsManager.setListClickListener(this.searchMessagesList);
|
appDialogsManager.setListClickListener(this.searchMessagesList);
|
||||||
@ -42,7 +53,9 @@ class AppSidebarLeft {
|
|||||||
this.searchContainer.classList.remove('active');
|
this.searchContainer.classList.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.peerID = 0;
|
/* this.peerID = 0;
|
||||||
|
this.loadedCount = 0;
|
||||||
|
this.minMsgID = 0; */
|
||||||
}, {once: true});
|
}, {once: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,16 +72,83 @@ class AppSidebarLeft {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appMessagesManager.getSearch(this.peerID, value, null, 0, 20).then(res => {
|
this.query = value;
|
||||||
if(this.searchInput.value != value) {
|
this.minMsgID = 0;
|
||||||
|
this.loadedCount = 0;
|
||||||
|
this.foundCount = 0;
|
||||||
|
this.offsetRate = 0;
|
||||||
|
this.searchMessagesList.innerHTML = '';
|
||||||
|
this.searchPromise = null;
|
||||||
|
this.searchMore().then(() => {
|
||||||
|
this.listsContainer.append(this.searchMessagesList);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.toolsBtn.addEventListener('click', () => {
|
||||||
|
if(this.toolsBtn.classList.contains('tgico-back')) {
|
||||||
|
this.searchInput.value = '';
|
||||||
|
this.toolsBtn.classList.add('tgico-menu');
|
||||||
|
this.toolsBtn.classList.remove('tgico-back');
|
||||||
|
this.searchContainer.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
setTimeout(() => this.onSidebarScroll(), 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSidebarScroll() {
|
||||||
|
let elements = Array.from(this.searchMessagesList.childNodes).slice(-5);
|
||||||
|
for(let li of elements) {
|
||||||
|
if(isElementInViewport(li)) {
|
||||||
|
this.log('Will load more search');
|
||||||
|
|
||||||
|
if(!this.searchTimeout) {
|
||||||
|
this.searchTimeout = setTimeout(() => {
|
||||||
|
this.searchMore();
|
||||||
|
this.searchTimeout = 0;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public beginSearch(peerID?: number) {
|
||||||
|
if(peerID) {
|
||||||
|
this.peerID = peerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private searchMore() {
|
||||||
|
if(this.searchPromise) return this.searchPromise;
|
||||||
|
|
||||||
|
let query = this.query;
|
||||||
|
|
||||||
|
if(this.loadedCount != 0 && this.loadedCount >= this.foundCount) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxID = 0;//appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0] - 1;
|
||||||
|
|
||||||
|
return this.searchPromise = appMessagesManager.getSearch(this.peerID, query, null, maxID, 20, this.offsetRate).then(res => {
|
||||||
|
this.searchPromise = null;
|
||||||
|
|
||||||
|
if(this.searchInput.value != query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log('input search result:', res);
|
this.log('input search result:', this.peerID, query, null, maxID, 20, res);
|
||||||
|
|
||||||
let {count, history} = res;
|
let {count, history, next_rate} = res;
|
||||||
|
|
||||||
this.searchMessagesList.innerHTML = '';
|
if(history[0] == this.minMsgID) {
|
||||||
|
history.shift();
|
||||||
|
}
|
||||||
|
|
||||||
history.forEach((msgID: number) => {
|
history.forEach((msgID: number) => {
|
||||||
let message = appMessagesManager.getMessage(msgID);
|
let message = appMessagesManager.getMessage(msgID);
|
||||||
@ -83,27 +163,18 @@ class AppSidebarLeft {
|
|||||||
appDialogsManager.setLastMessage(dialog, message, dom);
|
appDialogsManager.setLastMessage(dialog, message, dom);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.listsContainer.append(this.searchMessagesList);
|
this.minMsgID = history[history.length - 1];
|
||||||
});
|
this.offsetRate = next_rate;
|
||||||
});
|
this.loadedCount += history.length;
|
||||||
|
|
||||||
this.toolsBtn.addEventListener('click', () => {
|
if(!this.foundCount) {
|
||||||
if(this.toolsBtn.classList.contains('tgico-back')) {
|
this.foundCount = count;
|
||||||
this.searchInput.value = '';
|
|
||||||
this.toolsBtn.classList.add('tgico-menu');
|
|
||||||
this.toolsBtn.classList.remove('tgico-back');
|
|
||||||
this.searchContainer.classList.remove('active');
|
|
||||||
}
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
this.log.error('search error', err);
|
||||||
|
this.searchPromise = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public beginSearch(peerID?: number) {
|
|
||||||
if(peerID) {
|
|
||||||
this.peerID = peerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.searchInput.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new AppSidebarLeft();
|
export default new AppSidebarLeft();
|
||||||
|
@ -9,7 +9,6 @@ import { RichTextProcessor } from "../richtextprocessor";
|
|||||||
import { logger } from "../polyfill";
|
import { logger } from "../polyfill";
|
||||||
import appImManager from "./appImManager";
|
import appImManager from "./appImManager";
|
||||||
import appMediaViewer from "./appMediaViewer";
|
import appMediaViewer from "./appMediaViewer";
|
||||||
import appDocsManager from "./appDocsManager";
|
|
||||||
|
|
||||||
class AppSidebarRight {
|
class AppSidebarRight {
|
||||||
public sidebarEl = document.querySelector('.profile-container') as HTMLDivElement;
|
public sidebarEl = document.querySelector('.profile-container') as HTMLDivElement;
|
||||||
|
@ -30,18 +30,6 @@ export const appStickersManager = AppStickersManager;
|
|||||||
export const appSidebarRight = AppSidebarRight;
|
export const appSidebarRight = AppSidebarRight;
|
||||||
export const appSidebarLeft = AppSidebarLeft;
|
export const appSidebarLeft = AppSidebarLeft;
|
||||||
|
|
||||||
|
|
||||||
export type DialogDom = {
|
|
||||||
avatarDiv: HTMLDivElement,
|
|
||||||
captionDiv: HTMLDivElement,
|
|
||||||
titleSpan: HTMLSpanElement,
|
|
||||||
statusSpan: HTMLSpanElement,
|
|
||||||
lastTimeSpan: HTMLSpanElement,
|
|
||||||
unreadMessagesSpan: HTMLSpanElement,
|
|
||||||
lastMessageSpan: HTMLSpanElement,
|
|
||||||
listEl: HTMLLIElement
|
|
||||||
};
|
|
||||||
|
|
||||||
(window as any).Services = {
|
(window as any).Services = {
|
||||||
appUsersManager,
|
appUsersManager,
|
||||||
appChatsManager,
|
appChatsManager,
|
||||||
|
@ -237,7 +237,8 @@
|
|||||||
|
|
||||||
.box.web {
|
.box.web {
|
||||||
width: min-content;
|
width: min-content;
|
||||||
max-width: min-content;
|
/* max-width: min-content; */
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +249,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
img, video {
|
img, video {
|
||||||
object-fit: contain;
|
/* object-fit: contain; */
|
||||||
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji {
|
.emoji {
|
||||||
@ -361,14 +363,17 @@
|
|||||||
&:not(.sticker) {
|
&:not(.sticker) {
|
||||||
.attachment {
|
.attachment {
|
||||||
max-width: 380px;
|
max-width: 380px;
|
||||||
|
max-height: 380px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.video {
|
&.video {
|
||||||
.attachment {
|
.attachment {
|
||||||
|
//max-height: fit-content;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
/* height: 100%; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ $time-size: 12px;
|
|||||||
@import "partials/chat";
|
@import "partials/chat";
|
||||||
@import "partials/sidebar";
|
@import "partials/sidebar";
|
||||||
@import "partials/leftSidebar";
|
@import "partials/leftSidebar";
|
||||||
|
@import "partials/mediaViewer";
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -1077,136 +1078,7 @@ $width: 100px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-viewer {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0, 0, 0, .9);
|
|
||||||
/* color: $darkgrey; */
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.media-viewer-author {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 60px;
|
|
||||||
padding: 8px 8px 8px 80px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
color: #8b8b8b;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-name {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-date {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-buttons {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin: 0 .25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.media-viewer-stub {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-container {
|
|
||||||
align-self: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-media {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
> img, > video {
|
|
||||||
max-height: calc(100vh - 100px);
|
|
||||||
max-width: calc(100vw - 16px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-switcher-left, .media-viewer-switcher-right {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 10rem;
|
|
||||||
height: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> span {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-switcher-right {
|
|
||||||
left: auto;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-prev-button, .media-viewer-next-button {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 3rem;
|
|
||||||
left: 2rem;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%) rotate(90deg);
|
|
||||||
opacity: 0;
|
|
||||||
transition: .2s opacity;
|
|
||||||
z-index: 3;
|
|
||||||
/* box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.07); */
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-next-button {
|
|
||||||
left: auto;
|
|
||||||
right: 2rem;
|
|
||||||
transform: translateY(-50%) rotate(-90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-viewer-caption {
|
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: $darkgrey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.scrollable-y::-webkit-scrollbar {
|
div.scrollable-y::-webkit-scrollbar {
|
||||||
/* width: 4px; */
|
/* width: 4px; */
|
||||||
|
9665
stats.json
9665
stats.json
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user