Browse Source

Search by date & peer (beta)

master
Eduard Kuzmenko 4 years ago
parent
commit
294099e8b7
  1. 2
      src/components/appMediaPlaybackController.ts
  2. 2
      src/components/appMediaViewer.ts
  3. 2
      src/components/appSearch.ts
  4. 79
      src/components/appSearchSuper..ts
  5. 7
      src/components/audio.ts
  6. 2
      src/components/avatar.ts
  7. 2
      src/components/chat/bubbles.ts
  8. 2
      src/components/chat/pinnedMessage.ts
  9. 2
      src/components/inputSearch.ts
  10. 156
      src/components/sidebarLeft/index.ts
  11. 289
      src/helpers/date.ts
  12. 2
      src/index.hbs
  13. 32
      src/lib/appManagers/appMessagesManager.ts
  14. 9
      src/scss/partials/_avatar.scss
  15. 2
      src/scss/partials/_input.scss
  16. 46
      src/scss/partials/_leftSidebar.scss
  17. 12
      src/scss/partials/_rightSidebar.scss

2
src/components/appMediaPlaybackController.ts

@ -199,7 +199,7 @@ class AppMediaPlaybackController {
const media = this.playingMedia; const media = this.playingMedia;
this.prevMid = this.nextMid = 0; this.prevMid = this.nextMid = 0;
return appMessagesManager.getSearchNew({ return appMessagesManager.getSearch({
peerId, peerId,
query: '', query: '',
inputFilter: { inputFilter: {

2
src/components/appMediaViewer.ts

@ -1244,7 +1244,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
if(anchor) maxId = anchor.mid; if(anchor) maxId = anchor.mid;
if(!older) maxId = appMessagesManager.incrementMessageId(maxId, 1); if(!older) maxId = appMessagesManager.incrementMessageId(maxId, 1);
const promise = appMessagesManager.getSearchNew({ const promise = appMessagesManager.getSearch({
peerId: this.searchContext.peerId, peerId: this.searchContext.peerId,
query: this.searchContext.query, query: this.searchContext.query,
inputFilter: { inputFilter: {

2
src/components/appSearch.ts

@ -150,7 +150,7 @@ export default class AppSearch {
const maxId = this.minMsgId || 0; const maxId = this.minMsgId || 0;
return this.searchPromise = appMessagesManager.getSearchNew({ return this.searchPromise = appMessagesManager.getSearch({
peerId: this.peerId, peerId: this.peerId,
query, query,
inputFilter: {_: 'inputMessagesFilterEmpty'}, inputFilter: {_: 'inputMessagesFilterEmpty'},

79
src/components/appSearchSuper..ts

@ -19,7 +19,7 @@ import { horizontalMenu } from "./horizontalMenu";
import LazyLoadQueue from "./lazyLoadQueue"; import LazyLoadQueue from "./lazyLoadQueue";
import { renderImageFromUrl, putPreloader, formatPhoneNumber } from "./misc"; import { renderImageFromUrl, putPreloader, formatPhoneNumber } from "./misc";
import { ripple } from "./ripple"; import { ripple } from "./ripple";
import Scrollable from "./scrollable"; import Scrollable, { ScrollableX } from "./scrollable";
import { wrapDocument } from "./wrappers"; import { wrapDocument } from "./wrappers";
const testScroll = false; const testScroll = false;
@ -33,7 +33,9 @@ export type SearchSuperContext = {
folderId?: number, folderId?: number,
threadId?: number, threadId?: number,
date?: number, date?: number,
nextRate?: number nextRate?: number,
minDate?: number,
maxDate?: number
}; };
export default class AppSearchSuper { export default class AppSearchSuper {
@ -43,6 +45,7 @@ export default class AppSearchSuper {
public tabSelected: HTMLElement; public tabSelected: HTMLElement;
public container: HTMLElement; public container: HTMLElement;
public nav: HTMLElement;
private tabsContainer: HTMLElement; private tabsContainer: HTMLElement;
private tabsMenu: HTMLUListElement; private tabsMenu: HTMLUListElement;
private prevTabId = -1; private prevTabId = -1;
@ -74,15 +77,24 @@ export default class AppSearchSuper {
} }
}> = {}; }> = {};
private searchGroupMedia: SearchGroup;
constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false) { constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false) {
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add('search-super'); this.container.classList.add('search-super');
const nav = document.createElement('nav'); const navScrollableContainer = document.createElement('div');
navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable');
const navScrollable = new ScrollableX(navScrollableContainer);
const nav = this.nav = document.createElement('nav');
nav.classList.add('search-super-tabs', 'menu-horizontal'); nav.classList.add('search-super-tabs', 'menu-horizontal');
this.tabsMenu = document.createElement('ul'); this.tabsMenu = document.createElement('ul');
nav.append(this.tabsMenu); nav.append(this.tabsMenu);
navScrollable.container.append(nav);
for(const type of types) { for(const type of types) {
const li = document.createElement('li'); const li = document.createElement('li');
const span = document.createElement('span'); const span = document.createElement('span');
@ -115,10 +127,12 @@ export default class AppSearchSuper {
this.tabs[type.inputFilter] = content; this.tabs[type.inputFilter] = content;
} }
this.container.append(nav, this.tabsContainer); this.container.append(navScrollableContainer, this.tabsContainer);
// * construct end // * construct end
this.searchGroupMedia = new SearchGroup('', 'messages', true);
this.scrollable.onScrolledBottom = () => { this.scrollable.onScrolledBottom = () => {
if(this.tabSelected && this.tabSelected.childElementCount/* && false */) { if(this.tabSelected && this.tabSelected.childElementCount/* && false */) {
//this.log('onScrolledBottom will load media'); //this.log('onScrolledBottom will load media');
@ -296,10 +310,19 @@ export default class AppSearchSuper {
public async performSearchResult(messages: any[], type: SearchSuperType, append = true) { public async performSearchResult(messages: any[], type: SearchSuperType, append = true) {
const elemsToAppend: {element: HTMLElement, message: any}[] = []; const elemsToAppend: {element: HTMLElement, message: any}[] = [];
const sharedMediaDiv: HTMLElement = this.tabs[type];
const promises: Promise<any>[] = []; const promises: Promise<any>[] = [];
const sharedMediaDiv: HTMLElement = type === 'inputMessagesFilterEmpty' ? this.searchGroups.messages.list : this.tabs[type];
const middleware = this.getMiddleware(); const middleware = this.getMiddleware();
let searchGroup: SearchGroup;
if(type === 'inputMessagesFilterPhotoVideo' && !!this.searchContext.query.trim()) {
type = 'inputMessagesFilterEmpty';
searchGroup = this.searchGroupMedia;
sharedMediaDiv.append(searchGroup.container);
} else if(type === 'inputMessagesFilterEmpty') {
searchGroup = this.searchGroups.messages;
}
// https://core.telegram.org/type/MessagesFilter // https://core.telegram.org/type/MessagesFilter
switch(type) { switch(type) {
@ -307,14 +330,14 @@ export default class AppSearchSuper {
for(const message of messages) { for(const message of messages) {
const {dialog, dom} = appDialogsManager.addDialogNew({ const {dialog, dom} = appDialogsManager.addDialogNew({
dialog: message.peerId, dialog: message.peerId,
container: sharedMediaDiv as HTMLUListElement/* searchGroup.list */, container: searchGroup.list,
drawStatus: false, drawStatus: false,
avatarSize: 54 avatarSize: 54
}); });
appDialogsManager.setLastMessage(dialog, message, dom, this.searchContext.query); appDialogsManager.setLastMessage(dialog, message, dom, this.searchContext.query);
} }
this.searchGroups.messages.setActive(); searchGroup.setActive();
break; break;
} }
@ -689,7 +712,24 @@ export default class AppSearchSuper {
.then((contacts) => { .then((contacts) => {
if(contacts) { if(contacts) {
setResults(contacts.my_results, this.searchGroups.contacts, true); setResults(contacts.my_results, this.searchGroups.contacts, true);
setResults(contacts.results, this.searchGroups.globalContacts); setResults(contacts.results/* .concat(contacts.results, contacts.results, contacts.results) */, this.searchGroups.globalContacts);
if(this.searchGroups.globalContacts.nameEl.lastElementChild) {
this.searchGroups.globalContacts.nameEl.lastElementChild.remove();
}
this.searchGroups.globalContacts.container.classList.add('is-short');
if(this.searchGroups.globalContacts.list.childElementCount > 3) {
const showMore = document.createElement('div');
showMore.classList.add('search-group__show-more');
showMore.innerText = 'Show more';
this.searchGroups.globalContacts.nameEl.append(showMore);
showMore.addEventListener('click', () => {
const isShort = this.searchGroups.globalContacts.container.classList.toggle('is-short');
showMore.innerText = isShort ? 'Show more' : 'Show less';
});
}
} }
}), }),
@ -835,7 +875,7 @@ export default class AppSearchSuper {
//this.log(logStr + 'search house of glass pre', type, maxId); //this.log(logStr + 'search house of glass pre', type, maxId);
//let loadCount = history.length ? 50 : 15; //let loadCount = history.length ? 50 : 15;
return this.loadPromises[type] = appMessagesManager.getSearchNew({ return this.loadPromises[type] = appMessagesManager.getSearch({
peerId, peerId,
query: this.searchContext.query, query: this.searchContext.query,
inputFilter: {_: type}, inputFilter: {_: type},
@ -843,7 +883,9 @@ export default class AppSearchSuper {
limit: loadCount, limit: loadCount,
nextRate: this.nextRates[type] ?? (this.nextRates[type] = 0), nextRate: this.nextRates[type] ?? (this.nextRates[type] = 0),
threadId: this.searchContext.threadId, threadId: this.searchContext.threadId,
folderId: this.searchContext.folderId folderId: this.searchContext.folderId,
minDate: this.searchContext.minDate,
maxDate: this.searchContext.maxDate
}).then(value => { }).then(value => {
history.push(...value.history.map(m => ({mid: m.mid, peerId: m.peerId}))); history.push(...value.history.map(m => ({mid: m.mid, peerId: m.peerId})));
@ -870,13 +912,15 @@ export default class AppSearchSuper {
this.usedFromHistory[type] = history.length; this.usedFromHistory[type] = history.length;
if(!this.loaded[type]) { if(!this.loaded[type]) {
this.loadPromises[type].then(() => { (this.loadPromises[type] || Promise.resolve()).then(() => {
setTimeout(() => { setTimeout(() => {
if(!middleware()) return;
//this.log('will preload more'); //this.log('will preload more');
if(this.type === type) { if(this.type === type) {
const promise = this.load(true, true); const promise = this.load(true, true);
if(promise) { if(promise) {
promise.then(() => { promise.then(() => {
if(!middleware()) return;
//this.log('preloaded more'); //this.log('preloaded more');
setTimeout(() => { setTimeout(() => {
this.scrollable.checkForTriggers(); this.scrollable.checkForTriggers();
@ -988,6 +1032,7 @@ export default class AppSearchSuper {
}); });
this.monthContainers = {}; this.monthContainers = {};
this.searchGroupMedia.clear();
if(testScroll) { if(testScroll) {
for(let i = 0; i < 1500; ++i) { for(let i = 0; i < 1500; ++i) {
@ -1016,19 +1061,23 @@ export default class AppSearchSuper {
return context; return context;
} }
public setQuery({peerId, query, threadId, historyStorage, folderId}: { public setQuery({peerId, query, threadId, historyStorage, folderId, minDate, maxDate}: {
peerId: number, peerId: number,
query?: string, query?: string,
threadId?: number, threadId?: number,
historyStorage?: AppSearchSuper['historyStorage'], historyStorage?: AppSearchSuper['historyStorage'],
folderId?: number folderId?: number,
minDate?: number,
maxDate?: number
}) { }) {
this.searchContext = { this.searchContext = {
peerId: peerId || 0, peerId: peerId || 0,
query: query || '', query: query || '',
inputFilter: this.type, inputFilter: this.type,
threadId, threadId,
folderId folderId,
minDate,
maxDate
}; };
this.historyStorage = historyStorage ?? {}; this.historyStorage = historyStorage ?? {};

7
src/components/audio.ts

@ -290,7 +290,7 @@ function wrapAudio(audioEl: AudioElement) {
const html = ` const html = `
<div class="audio-details"> <div class="audio-details">
<div class="audio-title"><middle-ellipsis-element data-font-weight="${audioEl.dataset.fontWeight}">${title}</middle-ellipsis-element>${titleAdditionHTML}</div> <div class="audio-title"><middle-ellipsis-element data-font-weight="${audioEl.dataset.fontWeight}">${title}</middle-ellipsis-element>${titleAdditionHTML}</div>
<div class="audio-subtitle"><div class="audio-time"></div>${subtitle}</div> <div class="audio-subtitle"><div class="audio-time"></div>${subtitle || '<div></div>'}</div>
</div>`; </div>`;
audioEl.insertAdjacentHTML('beforeend', html); audioEl.insertAdjacentHTML('beforeend', html);
@ -356,7 +356,8 @@ export default class AudioElement extends HTMLElement {
this.classList.add('audio'); this.classList.add('audio');
const doc = this.message.media.document || this.message.media.webpage.document; const doc = this.message.media.document || this.message.media.webpage.document;
const isVoice = !this.voiceAsMusic && doc.type == 'voice'; const isRealVoice = doc.type == 'voice';
const isVoice = !this.voiceAsMusic && isRealVoice;
const uploading = this.message.pFlags.is_outgoing; const uploading = this.message.pFlags.is_outgoing;
const durationStr = String(doc.duration | 0).toHHMMSS(); const durationStr = String(doc.duration | 0).toHHMMSS();
@ -429,7 +430,7 @@ export default class AudioElement extends HTMLElement {
if(!uploading) { if(!uploading) {
let preloader: ProgressivePreloader = this.preloader; let preloader: ProgressivePreloader = this.preloader;
if(isVoice) { if(isRealVoice) {
let download: Download; let download: Download;
const onClick = (e: Event) => { const onClick = (e: Event) => {

2
src/components/avatar.ts

@ -52,7 +52,7 @@ export default class AvatarElement extends HTMLElement {
if(peerId < 0) { if(peerId < 0) {
const maxId = Number.MAX_SAFE_INTEGER; const maxId = Number.MAX_SAFE_INTEGER;
const inputFilter = 'inputMessagesFilterChatPhotos'; const inputFilter = 'inputMessagesFilterChatPhotos';
let message: any = await appMessagesManager.getSearchNew({ let message: any = await appMessagesManager.getSearch({
peerId, peerId,
inputFilter: {_: inputFilter}, inputFilter: {_: inputFilter},
maxId, maxId,

2
src/components/chat/bubbles.ts

@ -2350,7 +2350,7 @@ export default class ChatBubbles {
if(this.chat.type === 'chat' || this.chat.type === 'discussion') { if(this.chat.type === 'chat' || this.chat.type === 'discussion') {
return this.appMessagesManager.getHistory(this.peerId, maxId, loadCount, backLimit, this.chat.threadId); return this.appMessagesManager.getHistory(this.peerId, maxId, loadCount, backLimit, this.chat.threadId);
} else if(this.chat.type === 'pinned') { } else if(this.chat.type === 'pinned') {
const promise = this.appMessagesManager.getSearchNew({ const promise = this.appMessagesManager.getSearch({
peerId: this.peerId, peerId: this.peerId,
inputFilter: {_: 'inputMessagesFilterPinned'}, inputFilter: {_: 'inputMessagesFilterPinned'},
maxId, maxId,

2
src/components/chat/pinnedMessage.ts

@ -410,7 +410,7 @@ export default class ChatPinnedMessage {
try { try {
let gotRest = false; let gotRest = false;
const promises = [ const promises = [
this.appMessagesManager.getSearchNew({ this.appMessagesManager.getSearch({
peerId: this.topbar.peerId, peerId: this.topbar.peerId,
inputFilter: {_: 'inputMessagesFilterPinned'}, inputFilter: {_: 'inputMessagesFilterPinned'},
maxId: mid, maxId: mid,

2
src/components/inputSearch.ts

@ -10,6 +10,7 @@ export default class InputSearch {
public prevValue = ''; public prevValue = '';
public timeout = 0; public timeout = 0;
public onChange: (value: string) => void; public onChange: (value: string) => void;
public onClear: () => void;
constructor(placeholder: string, onChange?: (value: string) => void) { constructor(placeholder: string, onChange?: (value: string) => void) {
this.inputField = new InputField({ this.inputField = new InputField({
@ -57,6 +58,7 @@ export default class InputSearch {
onClearClick = () => { onClearClick = () => {
this.value = ''; this.value = '';
this.onChange && this.onChange(''); this.onChange && this.onChange('');
this.onClear && this.onClear();
}; };
get value() { get value() {

156
src/components/sidebarLeft/index.ts

@ -1,7 +1,5 @@
//import { logger } from "../polyfill"; //import { logger } from "../polyfill";
import { formatNumber } from "../../helpers/number"; import { formatNumber } from "../../helpers/number";
import appChatsManager from "../../lib/appManagers/appChatsManager";
import appDialogsManager from "../../lib/appManagers/appDialogsManager";
import appImManager from "../../lib/appManagers/appImManager"; import appImManager from "../../lib/appManagers/appImManager";
import appPeersManager from "../../lib/appManagers/appPeersManager"; import appPeersManager from "../../lib/appManagers/appPeersManager";
import appStateManager from "../../lib/appManagers/appStateManager"; import appStateManager from "../../lib/appManagers/appStateManager";
@ -29,6 +27,7 @@ import AppSettingsTab from "./tabs/settings";
import appMessagesManager from "../../lib/appManagers/appMessagesManager"; import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import apiManagerProxy from "../../lib/mtproto/mtprotoworker"; import apiManagerProxy from "../../lib/mtproto/mtprotoworker";
import AppSearchSuper from "../appSearchSuper."; import AppSearchSuper from "../appSearchSuper.";
import { DateData, fillTipDates } from "../../helpers/date";
const newChannelTab = new AppNewChannelTab(); const newChannelTab = new AppNewChannelTab();
const addMembersTab = new AppAddMembersTab(); const addMembersTab = new AppAddMembersTab();
@ -213,21 +212,157 @@ export class AppSidebarLeft extends SidebarSlider {
scrollable.container.append(searchSuper.container); scrollable.container.append(searchSuper.container);
searchSuper.setQuery({ const resetSearch = () => {
peerId: 0, searchSuper.setQuery({
folderId: 0 peerId: 0,
folderId: 0
});
searchSuper.selectTab(0);
searchSuper.load(true);
};
resetSearch();
let pickedElements: HTMLElement[] = [];
let selectedPeerId = 0;
let selectedMinDate = 0;
let selectedMaxDate = 0;
const updatePicked = () => {
(this.inputSearch.input as HTMLInputElement).placeholder = pickedElements.length ? 'Search' : 'Telegram Search';
this.inputSearch.container.classList.toggle('is-picked-twice', pickedElements.length === 2);
this.inputSearch.container.classList.toggle('is-picked', !!pickedElements.length);
if(pickedElements.length) {
this.inputSearch.input.style.setProperty('--paddingLeft', (pickedElements[pickedElements.length - 1].getBoundingClientRect().right - this.inputSearch.input.getBoundingClientRect().left) + 'px');
} else {
this.inputSearch.input.style.removeProperty('--paddingLeft');
}
};
const helper = document.createElement('div');
helper.classList.add('search-helper');
helper.addEventListener('click', (e) => {
const target = findUpClassName(e.target, 'selector-user');
if(!target) {
return;
}
const key = target.dataset.key;
if(key.indexOf('date_') === 0) {
const [_, minDate, maxDate] = key.split('_');
selectedMinDate = +minDate;
selectedMaxDate = +maxDate;
} else {
selectedPeerId = +key;
}
target.addEventListener('click', () => {
unselectEntity(target);
});
this.inputSearch.container.append(target);
this.inputSearch.onChange(this.inputSearch.value = '');
pickedElements.push(target);
updatePicked();
}); });
searchSuper.selectTab(0);
searchSuper.load(true); searchSuper.nav.parentElement.append(helper);
const renderEntity = (peerId: any, title?: string) => {
const div = document.createElement('div');
div.classList.add('selector-user'/* , 'scale-in' */);
const avatarEl = document.createElement('avatar-element');
avatarEl.classList.add('selector-user-avatar', 'tgico');
avatarEl.setAttribute('dialog', '1');
avatarEl.classList.add('avatar-30');
div.dataset.key = '' + peerId;
if(typeof(peerId) === 'number') {
if(title === undefined) {
title = peerId == rootScope.myId ? 'Saved' : appPeersManager.getPeerTitle(peerId, false, true);
}
avatarEl.setAttribute('peer', '' + peerId);
} else {
avatarEl.classList.add('tgico-calendarfilter');
}
if(title) {
div.innerHTML = title;
}
div.insertAdjacentElement('afterbegin', avatarEl);
return div;
};
const unselectEntity = (target: HTMLElement) => {
const key = target.dataset.key;
if(key.indexOf('date_') === 0) {
selectedMinDate = selectedMaxDate = 0;
} else {
selectedPeerId = 0;
}
target.remove();
pickedElements.findAndSplice(t => t === target);
setTimeout(() => {
updatePicked();
this.inputSearch.onChange(this.inputSearch.value);
}, 0);
};
this.inputSearch.onClear = () => {
pickedElements.forEach(el => {
unselectEntity(el);
});
};
this.inputSearch.onChange = (value) => { this.inputSearch.onChange = (value) => {
searchSuper.cleanupHTML(); searchSuper.cleanupHTML();
searchSuper.setQuery({ searchSuper.setQuery({
peerId: 0, peerId: selectedPeerId,
folderId: 0, folderId: selectedPeerId ? undefined : 0,
query: value query: value,
minDate: selectedMinDate,
maxDate: selectedMaxDate
}); });
searchSuper.load(true); searchSuper.load(true);
helper.innerHTML = '';
searchSuper.nav.classList.remove('hide');
if(!value) {
}
if(!selectedPeerId && value.trim()) {
const middleware = searchSuper.getMiddleware();
Promise.all([
appMessagesManager.getConversationsAll(value).then(dialogs => dialogs.map(d => d.peerId)),
appUsersManager.getContacts(value, true)
]).then(results => {
if(!middleware()) return;
const peerIds = new Set(results[0].concat(results[1]));
peerIds.forEach(peerId => {
helper.append(renderEntity(peerId));
});
searchSuper.nav.classList.toggle('hide', !!helper.innerHTML);
//console.log('got peerIds by value:', value, [...peerIds]);
});
}
if(!selectedMinDate && value.trim()) {
const dates: DateData[] = [];
fillTipDates(value, dates);
dates.forEach(dateData => {
helper.append(renderEntity('date_' + dateData.minDate + '_' + dateData.maxDate, dateData.title));
});
searchSuper.nav.classList.toggle('hide', !!helper.innerHTML);
}
}; };
searchSuper.tabs.inputMessagesFilterEmpty.addEventListener('click', (e) => { searchSuper.tabs.inputMessagesFilterEmpty.addEventListener('click', (e) => {
@ -272,6 +407,7 @@ export class AppSidebarLeft extends SidebarSlider {
if(id === 0) { if(id === 0) {
this.inputSearch.onClearClick(); this.inputSearch.onClearClick();
resetSearch();
hideNewBtnMenuTimeout = window.setTimeout(() => { hideNewBtnMenuTimeout = window.setTimeout(() => {
hideNewBtnMenuTimeout = 0; hideNewBtnMenuTimeout = 0;
this.newBtnMenu.classList.remove('is-hidden'); this.newBtnMenu.classList.remove('is-hidden');

289
src/helpers/date.ts

@ -1,3 +1,5 @@
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
export const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; export const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
export const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; export const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
@ -49,4 +51,289 @@ export const getFullDate = (date: Date, options: Partial<{
export function tsNow(seconds?: true) { export function tsNow(seconds?: true) {
const t = Date.now(); const t = Date.now();
return seconds ? Math.floor(t / 1000) : t; return seconds ? Math.floor(t / 1000) : t;
} }
// https://github.com/DrKLO/Telegram/blob/d52b2c921abd3c1e8d6368858313ad0cb0468c07/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java
const minYear = 2013;
const yearPattern = new RegExp("20[0-9]{1,2}");
const monthYearOrDayPattern = new RegExp("(\\w{3,}) ([0-9]{0,4})", 'i');
const yearOrDayAndMonthPattern = new RegExp("([0-9]{0,4}) (\\w{2,})", 'i');
const shortDate = new RegExp("^([0-9]{1,4})(\\.| |/|\\-)([0-9]{1,4})$", 'i');
const longDate = new RegExp("^([0-9]{1,2})(\\.| |/|\\-)([0-9]{1,2})(\\.| |/|\\-)([0-9]{1,4})$", 'i');
const numberOfDaysEachMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
export type DateData = {
title: string,
minDate: number,
maxDate: number,
};
export function fillTipDates(query: string, dates: DateData[]) {
const q = query.trim();
if(q.length < 3) {
return;
}
let matches: any[];
if((matches = shortDate.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[3];
const k = parseInt(g1);
const k1 = parseInt(g2);
if(k > 0 && k <= 31) {
if(k1 >= minYear && k <= 12) {
const selectedYear = k1;
const month = k - 1;
createForMonthYear(dates, month, selectedYear);
return;
} else if (k1 <= 12) {
const day = k - 1;
const month = k1 - 1;
createForDayMonth(dates, day, month);
}
} else if (k >= minYear && k1 <= 12) {
const selectedYear = k;
const month = k1 - 1;
createForMonthYear(dates, month, selectedYear);
}
return;
}
if((matches = longDate.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[3];
const g3 = matches[5];
if(!matches[2] == matches[4]) {
return;
}
const day = parseInt(g1);
const month = parseInt(g2) - 1;
let year = parseInt(g3);
if(year >= 10 && year <= 99) {
year += 2000;
}
const currentYear = new Date().getFullYear();
if(validDateForMonth(day - 1, month) && year >= minYear && year <= currentYear) {
const date = new Date();
date.setFullYear(year, month, day);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(year, month, day + 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: formatterYearMax(minDate),
minDate,
maxDate
});
return;
}
return;
}
if((matches = yearPattern.exec(q)) !== null) {
let selectedYear = +q;
const currentYear = new Date().getFullYear();
if(selectedYear < minYear) {
selectedYear = minYear;
for(let i = currentYear; i >= selectedYear; i--) {
const date = new Date();
date.setFullYear(i, 0, 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(i + 1, 0, 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: '' + i,
minDate,
maxDate
});
}
} else if(selectedYear <= currentYear) {
const date = new Date();
date.setFullYear(selectedYear, 0, 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(selectedYear + 1, 0, 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: '' + selectedYear,
minDate,
maxDate
});
}
return;
}
if((matches = monthYearOrDayPattern.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[2];
const month = getMonth(g1);
if(month >= 0) {
const k = +g2;
if(k > 0 && k <= 31) {
const day = k - 1;
createForDayMonth(dates, day, month);
return;
} else if(k >= minYear) {
const selectedYear = k;
createForMonthYear(dates, month, selectedYear);
return;
}
}
}
if((matches = yearOrDayAndMonthPattern.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[2];
const month = getMonth(g2);
if(month >= 0) {
const k = +g1;
if(k > 0 && k <= 31) {
const day = k - 1;
createForDayMonth(dates, day, month);
return;
} else if (k >= minYear) {
const selectedYear = k;
createForMonthYear(dates, month, selectedYear);
}
}
}
}
function createForMonthYear(dates: DateData[], month: number, selectedYear: number) {
const currentYear = new Date().getFullYear();
const today = Date.now();
if(selectedYear >= minYear && selectedYear <= currentYear) {
const date = new Date();
date.setFullYear(selectedYear, month, 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
if(minDate > today) {
return;
}
date.setMonth(date.getMonth() + 1);
const maxDate = date.getTime() - 1;
dates.push({
title: formatterMonthYear(minDate),
minDate,
maxDate
});
}
}
function createForDayMonth(dates: DateData[], day: number, month: number) {
if(validDateForMonth(day, month)) {
const currentYear = new Date().getFullYear();
const today = Date.now();
for(let i = currentYear; i >= minYear; i--) {
if(month == 1 && day == 28 && !isLeapYear(i)) {
continue;
}
const date = new Date();
date.setFullYear(i, month, day + 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
if(minDate > today) {
continue;
}
date.setFullYear(i, month, day + 2);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
if(i == currentYear) {
dates.push({
title: formatterDayMonth(minDate),
minDate,
maxDate
});
} else {
dates.push({
title: formatterYearMax(minDate),
minDate,
maxDate
});
}
}
}
}
function formatterMonthYear(timestamp: number) {
const date = new Date(timestamp);
return months[date.getMonth()].slice(0, 3) + ' ' + date.getFullYear();
}
function formatterDayMonth(timestamp: number) {
const date = new Date(timestamp);
return months[date.getMonth()].slice(0, 3) + ' ' + date.getDate();
}
function formatterYearMax(timestamp: number) {
const date = new Date(timestamp);
return ('0' + date.getDate()).slice(-2) + '.' + ('0' + (date.getMonth() + 1)).slice(-2) + '.' + date.getFullYear();
}
function validDateForMonth(day: number, month: number) {
if(month >= 0 && month < 12) {
if(day >= 0 && day < numberOfDaysEachMonth[month]) {
return true;
}
}
return false;
}
function isLeapYear(year: number) {
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
function getMonth(q: string) {
/* String[] months = new String[]{
LocaleController.getString("January", R.string.January).toLowerCase(),
LocaleController.getString("February", R.string.February).toLowerCase(),
LocaleController.getString("March", R.string.March).toLowerCase(),
LocaleController.getString("April", R.string.April).toLowerCase(),
LocaleController.getString("May", R.string.May).toLowerCase(),
LocaleController.getString("June", R.string.June).toLowerCase(),
LocaleController.getString("July", R.string.July).toLowerCase(),
LocaleController.getString("August", R.string.August).toLowerCase(),
LocaleController.getString("September", R.string.September).toLowerCase(),
LocaleController.getString("October", R.string.October).toLowerCase(),
LocaleController.getString("November", R.string.November).toLowerCase(),
LocaleController.getString("December", R.string.December).toLowerCase()
}; */
/* String[] monthsEng = new String[12];
Calendar c = Calendar.getInstance();
for (int i = 1; i <= 12; i++) {
c.set(0, 0, 0, 0, 0, 0);
c.set(Calendar.MONTH, i);
monthsEng[i - 1] = c.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.ENGLISH).toLowerCase();
} */
q = q.toLowerCase();
for(let i = 0; i < 12; i++) {
const month = months[i].toLowerCase();
if(month.indexOf(q) === 0) {
return i;
}
}
return -1;
}
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.fillTipDates = fillTipDates);

2
src/index.hbs

@ -143,7 +143,7 @@
</div> </div>
<div class="sidebar-content transition zoom-fade"> <div class="sidebar-content transition zoom-fade">
<div class="transition-item active" id="chatlist-container"> <div class="transition-item active" id="chatlist-container">
<div class="folders-tabs-scrollable hide"> <div class="folders-tabs-scrollable menu-horizontal-scrollable hide">
<nav class="menu-horizontal" id="folders-tabs"> <nav class="menu-horizontal" id="folders-tabs">
<ul> <ul>
<li class="rp"><span>All<span class="badge badge-20 badge-blue"></span><i></i></span></li> <li class="rp"><span>All<span class="badge badge-20 badge-blue"></span><i></i></span></li>

32
src/lib/appManagers/appMessagesManager.ts

@ -1887,7 +1887,7 @@ export class AppMessagesManager {
if(p.promise) return p.promise; if(p.promise) return p.promise;
else if(p.maxId) return Promise.resolve(p); else if(p.maxId) return Promise.resolve(p);
return p.promise = this.getSearchNew({ return p.promise = this.getSearch({
peerId, peerId,
inputFilter: {_: 'inputMessagesFilterPinned'}, inputFilter: {_: 'inputMessagesFilterPinned'},
maxId: 0, maxId: 0,
@ -2887,7 +2887,7 @@ export class AppMessagesManager {
}); });
} }
public getSearchNew({peerId, query, inputFilter, maxId, limit, nextRate, backLimit, threadId, folderId}: { public getSearch({peerId, query, inputFilter, maxId, limit, nextRate, backLimit, threadId, folderId, minDate, maxDate}: {
peerId?: number, peerId?: number,
maxId?: number, maxId?: number,
limit?: number, limit?: number,
@ -2899,18 +2899,24 @@ export class AppMessagesManager {
inputFilter?: { inputFilter?: {
_: MyInputMessagesFilter _: MyInputMessagesFilter
}, },
}) { minDate?: number,
return this.getSearch(peerId, query, inputFilter, maxId, limit, nextRate, backLimit, threadId, folderId); maxDate?: number
} }): Promise<{
public getSearch(peerId = 0, query: string = '', inputFilter: {
_: MyInputMessagesFilter
} = {_: 'inputMessagesFilterEmpty'}, maxId: number, limit = 20, nextRate = 0, backLimit = 0, threadId?: number, folderId?: number): Promise<{
count: number, count: number,
next_rate: number, next_rate: number,
offset_id_offset: number, offset_id_offset: number,
history: MyMessage[] history: MyMessage[]
}> { }> {
if(!peerId) peerId = 0;
if(!query) query = '';
if(!inputFilter) inputFilter = {_: 'inputMessagesFilterEmpty'};
if(!limit) limit = 20;
if(!nextRate) nextRate = 0;
if(!backLimit) backLimit = 0;
minDate = minDate ? minDate / 1000 | 0 : 0;
maxDate = maxDate ? maxDate / 1000 | 0 : 0;
const foundMsgs: Message.message[] = []; const foundMsgs: Message.message[] = [];
//this.log('search', maxId); //this.log('search', maxId);
@ -3077,8 +3083,8 @@ export class AppMessagesManager {
peer: appPeersManager.getInputPeerById(peerId), peer: appPeersManager.getInputPeerById(peerId),
q: query || '', q: query || '',
filter: inputFilter as any as MessagesFilter, filter: inputFilter as any as MessagesFilter,
min_date: 0, min_date: minDate,
max_date: 0, max_date: maxDate,
limit, limit,
offset_id: this.getLocalMessageId(maxId) || 0, offset_id: this.getLocalMessageId(maxId) || 0,
add_offset: backLimit ? -backLimit : 0, add_offset: backLimit ? -backLimit : 0,
@ -3105,8 +3111,8 @@ export class AppMessagesManager {
apiPromise = apiManager.invokeApi('messages.searchGlobal', { apiPromise = apiManager.invokeApi('messages.searchGlobal', {
q: query, q: query,
filter: inputFilter as any as MessagesFilter, filter: inputFilter as any as MessagesFilter,
min_date: 0, min_date: minDate,
max_date: 0, max_date: maxDate,
offset_rate: nextRate, offset_rate: nextRate,
offset_peer: appPeersManager.getInputPeerById(offsetPeerId), offset_peer: appPeersManager.getInputPeerById(offsetPeerId),
offset_id: offsetId, offset_id: offsetId,

9
src/scss/partials/_avatar.scss

@ -21,6 +21,10 @@ avatar-element {
&.tgico-avatar_deletedaccount:before { &.tgico-avatar_deletedaccount:before {
font-size: calc(56px / var(--multiplier)); font-size: calc(56px / var(--multiplier));
} }
&.tgico-calendarfilter:before {
font-size: calc(32px / var(--multiplier));
}
/* kostil */ /* kostil */
display: flex; display: flex;
@ -113,6 +117,11 @@ avatar-element {
--multiplier: 1.6875; --multiplier: 1.6875;
} }
&.avatar-30 {
--size: 30px;
--multiplier: 1.8;
}
&.avatar-18 { &.avatar-18 {
--size: 18px; --size: 18px;
--multiplier: 3; --multiplier: 3;

2
src/scss/partials/_input.scss

@ -232,7 +232,7 @@ input:focus, button:focus {
} }
} }
.tgico { > .tgico {
position: absolute; position: absolute;
left: 12px; left: 12px;
top: 50%; top: 50%;

46
src/scss/partials/_leftSidebar.scss

@ -47,7 +47,7 @@
} }
} }
.folders-tabs-scrollable { .menu-horizontal-scrollable {
z-index: 1; z-index: 1;
background-color: #fff; background-color: #fff;
border-bottom: 1px solid #dadce0; border-bottom: 1px solid #dadce0;
@ -60,6 +60,7 @@
.menu-horizontal { .menu-horizontal {
border-bottom: none; border-bottom: none;
position: relative !important;
ul { ul {
justify-content: flex-start; justify-content: flex-start;
@ -101,6 +102,49 @@
} }
} }
.item-main {
.input-search {
/* &-input {
--paddingLeft: 0px;
padding-left: calc(42px - var(--border-width) + var(--paddingLeft));
} */
.selector-user {
height: 30px;
position: absolute!important;
left: 5px;
top: 5px;
background: #f1f3f4;
z-index: 1;
margin-left: 0;
}
&.is-picked {
.input-search-input {
padding-left: calc(var(--paddingLeft) + 12px - var(--border-width));
}
.tgico-close {
display: block !important;
}
}
&.is-picked-twice {
.selector-user:first-of-type {
width: 30px;
}
.selector-user:last-of-type {
left: 38px;
}
}
}
.search-helper {
display: flex;
}
}
#chatlist-container { #chatlist-container {
max-height: 100%; max-height: 100%;
overflow: hidden; overflow: hidden;

12
src/scss/partials/_rightSidebar.scss

@ -267,6 +267,12 @@
} }
} }
&-tabs-scrollable {
.scrollable {
position: relative;
}
}
&-tabs-container { &-tabs-container {
//min-height: 100%; //min-height: 100%;
min-height: calc(100% - 49px); min-height: calc(100% - 49px);
@ -524,6 +530,12 @@
flex: 0 0 auto; flex: 0 0 auto;
margin-left: .5rem; margin-left: .5rem;
} }
.search-group.is-short {
li:nth-child(n + 4) {
display: none;
}
}
} }
#stickers-container { #stickers-container {

Loading…
Cancel
Save