Browse Source

Close search, contacts on list click

master
Eduard Kuzmenko 4 years ago
parent
commit
275bd4f77d
  1. 4
      src/components/appSearch.ts
  2. 3
      src/components/appSearchSuper..ts
  3. 84
      src/components/chat/bubbles.ts
  4. 5
      src/components/chat/chat.ts
  5. 7
      src/components/chat/pinnedMessage.ts
  6. 12
      src/components/sidebarLeft/index.ts
  7. 7
      src/components/sidebarLeft/tabs/contacts.ts
  8. 8
      src/helpers/dom.ts
  9. 34
      src/lib/appManagers/appImManager.ts
  10. 1
      src/lib/appManagers/appStateManager.ts
  11. 12
      src/lib/sessionStorage.ts
  12. 4
      src/scss/partials/_chatBubble.scss

4
src/components/appSearch.ts

@ -8,7 +8,7 @@ export class SearchGroup { @@ -8,7 +8,7 @@ export class SearchGroup {
nameEl: HTMLDivElement;
list: HTMLUListElement;
constructor(public name: string, public type: string, private clearable = true, className?: string, clickable = true, public autonomous = true) {
constructor(public name: string, public type: string, private clearable = true, className?: string, clickable = true, public autonomous = true, public onFound?: () => void) {
this.list = document.createElement('ul');
this.container = document.createElement('div');
if(className) this.container.className = className;
@ -25,7 +25,7 @@ export class SearchGroup { @@ -25,7 +25,7 @@ export class SearchGroup {
this.container.style.display = 'none';
if(clickable) {
appDialogsManager.setListClickListener(this.list, undefined, undefined, autonomous);
appDialogsManager.setListClickListener(this.list, onFound, undefined, autonomous);
}
}

3
src/components/appSearchSuper..ts

@ -22,7 +22,6 @@ import { ripple } from "./ripple"; @@ -22,7 +22,6 @@ import { ripple } from "./ripple";
import Scrollable, { ScrollableX } from "./scrollable";
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
import { p } from "../mock/srp";
const testScroll = false;
@ -706,7 +705,7 @@ export default class AppSearchSuper { @@ -706,7 +705,7 @@ export default class AppSearchSuper {
drawStatus: false,
meAsSaved: true,
avatarSize: 48,
autonomous: false
autonomous: true
});
dom.lastMessageSpan.innerText = peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appChatsManager.getChatMembersString(peerId);

84
src/components/chat/bubbles.ts

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import type { AppMessagesManager, Dialog, HistoryResult, MyMessage } from "../../lib/appManagers/appMessagesManager";
import type { AppSidebarRight } from "../sidebarRight";
import { AppImManager, CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import type { AppMessagesManager, HistoryResult, MyMessage } from "../../lib/appManagers/appMessagesManager";
import type { AppStickersManager } from "../../lib/appManagers/appStickersManager";
import type { AppUsersManager } from "../../lib/appManagers/appUsersManager";
import type { AppInlineBotsManager } from "../../lib/appManagers/appInlineBotsManager";
import type { AppPhotosManager } from "../../lib/appManagers/appPhotosManager";
import type { AppDocsManager } from "../../lib/appManagers/appDocsManager";
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type sessionStorage from '../../lib/sessionStorage';
import { findUpClassName, cancelEvent, findUpTag, whichChild, getElementByPoint, attachClickEvent, positionElementByIndex } from "../../helpers/dom";
import { getObjectKeysAndSort } from "../../helpers/object";
import { isTouchSupported } from "../../helpers/touchSupport";
@ -24,7 +24,7 @@ import animationIntersector from "../animationIntersector"; @@ -24,7 +24,7 @@ import animationIntersector from "../animationIntersector";
import { months } from "../../helpers/date";
import RichTextProcessor from "../../lib/richtextprocessor";
import mediaSizes from "../../helpers/mediaSizes";
import { isAndroid, isApple, isSafari, isAppleMobile } from "../../helpers/userAgent";
import { isAndroid, isApple, isSafari } from "../../helpers/userAgent";
import { langPack } from "../../lib/langPack";
import AvatarElement from "../avatar";
import { formatPhoneNumber } from "../misc";
@ -37,13 +37,12 @@ import Chat from "./chat"; @@ -37,13 +37,12 @@ import Chat from "./chat";
import ListenerSetter from "../../helpers/listenerSetter";
import PollElement from "../poll";
import AudioElement from "../audio";
import { Message, MessageEntity, MessageReplies, MessageReplyHeader } from "../../layer";
import { DEBUG, MOUNT_CLASS_TO, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
import { Message, MessageEntity, MessageReplyHeader } from "../../layer";
import { DEBUG, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
import { FocusDirection } from "../../helpers/fastSmoothScroll";
import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent } from "../../hooks/useHeavyAnimationCheck";
import { fastRaf } from "../../helpers/schedulers";
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
import EventListenerBase from "../../helpers/eventListenerBase";
import { deferredPromise } from "../../helpers/cancellablePromise";
const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
@ -117,7 +116,7 @@ export default class ChatBubbles { @@ -117,7 +116,7 @@ export default class ChatBubbles {
public isFirstLoad = true;
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager) {
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) {
//this.chat.log.error('Bubbles construction');
this.listenerSetter = new ListenerSetter();
@ -889,6 +888,28 @@ export default class ChatBubbles { @@ -889,6 +888,28 @@ export default class ChatBubbles {
}
}
public getBubbleByPoint(verticalSide: 'top' | 'bottom') {
let element = getElementByPoint(this.scrollable.container, verticalSide, 'center');
/* if(element) {
if(element.classList.contains('bubbles-date-group')) {
const children = Array.from(element.children) as HTMLElement[];
if(verticalSide === 'top') {
element = children[this.stickyIntersector ? 2 : 1];
} else {
element = children[children.length - 1];
}
} else {
element = findUpClassName(element, 'bubble');
if(element && element.classList.contains('is-date')) {
element = element.nextElementSibling as HTMLElement;
}
}
} */
if(element) element = findUpClassName(element, 'bubble');
return element;
}
public getGroupedBubble(groupId: string) {
const group = this.appMessagesManager.groupedMessagesStorage[groupId];
for(const mid in group) {
@ -1325,14 +1346,22 @@ export default class ChatBubbles { @@ -1325,14 +1346,22 @@ export default class ChatBubbles {
topMessage = 0;
}
let readMaxId = 0;
if(!isTarget && topMessage) {
readMaxId = this.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId);
if(/* dialog.unread_count */readMaxId && !samePeer) {
lastMsgId = readMaxId;
} else {
lastMsgId = topMessage;
//lastMsgID = topMessage;
let readMaxId = 0;//, savedPosition: ReturnType<AppImManager['getChatSavedPosition']>;
if(!isTarget) {
/* if(!samePeer) {
savedPosition = this.chat.appImManager.getChatSavedPosition(this.chat);
}
if(savedPosition) {
lastMsgId = savedPosition.mid;
} else */if(topMessage) {
readMaxId = this.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId);
if(/* dialog.unread_count */readMaxId && !samePeer) {
lastMsgId = readMaxId;
} else {
lastMsgId = topMessage;
//lastMsgID = topMessage;
}
}
}
@ -1378,13 +1407,10 @@ export default class ChatBubbles { @@ -1378,13 +1407,10 @@ export default class ChatBubbles {
let maxBubbleId = 0;
if(samePeer) {
let el = getElementByPoint(this.chat.bubbles.scrollable.container, 'bottom'); // ! this may not work if being called when chat is hidden
let el = this.getBubbleByPoint('bottom'); // ! this may not work if being called when chat is hidden
//this.chat.log('[PM]: setCorrectIndex: get last element perf:', performance.now() - perf, el);
if(el) {
el = findUpClassName(el, 'bubble');
if(el) { // TODO: а что делать, если id будет -1, -2, -3?
maxBubbleId = +el.dataset.mid;
}
maxBubbleId = +el.dataset.mid;
}
if(maxBubbleId <= 0) {
@ -1438,7 +1464,19 @@ export default class ChatBubbles { @@ -1438,7 +1464,19 @@ export default class ChatBubbles {
this.lazyLoadQueue.unlock();
//if(dialog && lastMsgID && lastMsgID != topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
if((topMessage && isJump) || isTarget) {
/* if(savedPosition) {
const mountedByLastMsgId = this.getMountedBubble(lastMsgId);
let bubble: HTMLElement = mountedByLastMsgId?.bubble;
if(!bubble?.parentElement) {
bubble = this.findNextMountedBubbleByMsgId(lastMsgId);
}
if(bubble) {
const top = bubble.getBoundingClientRect().top;
const distance = savedPosition.top - top;
this.scrollable.scrollTop += distance;
}
} else */if((topMessage && isJump) || isTarget) {
const fromUp = maxBubbleId > 0 && (maxBubbleId < lastMsgId || lastMsgId < 0);
const followingUnread = readMaxId === lastMsgId && !isTarget;
if(!fromUp && samePeer) {

5
src/components/chat/chat.ts

@ -13,6 +13,7 @@ import type { AppWebPagesManager } from "../../lib/appManagers/appWebPagesManage @@ -13,6 +13,7 @@ import type { AppWebPagesManager } from "../../lib/appManagers/appWebPagesManage
import type { ApiManagerProxy } from "../../lib/mtproto/mtprotoworker";
import type { AppDraftsManager } from "../../lib/appManagers/appDraftsManager";
import type { ServerTimeManager } from "../../lib/mtproto/serverTimeManager";
import type sessionStorage from '../../lib/sessionStorage';
import EventListenerBase from "../../helpers/eventListenerBase";
import { logger, LogLevels } from "../../lib/logger";
import rootScope from "../../lib/rootScope";
@ -49,7 +50,7 @@ export default class Chat extends EventListenerBase<{ @@ -49,7 +50,7 @@ export default class Chat extends EventListenerBase<{
public type: ChatType = 'chat';
constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, public appDocsManager: AppDocsManager, public appInlineBotsManager: AppInlineBotsManager, public appMessagesManager: AppMessagesManager, public appPeersManager: AppPeersManager, public appPhotosManager: AppPhotosManager, public appProfileManager: AppProfileManager, public appStickersManager: AppStickersManager, public appUsersManager: AppUsersManager, public appWebPagesManager: AppWebPagesManager, public appPollsManager: AppPollsManager, public apiManager: ApiManagerProxy, public appDraftsManager: AppDraftsManager, public serverTimeManager: ServerTimeManager) {
constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, public appDocsManager: AppDocsManager, public appInlineBotsManager: AppInlineBotsManager, public appMessagesManager: AppMessagesManager, public appPeersManager: AppPeersManager, public appPhotosManager: AppPhotosManager, public appProfileManager: AppProfileManager, public appStickersManager: AppStickersManager, public appUsersManager: AppUsersManager, public appWebPagesManager: AppWebPagesManager, public appPollsManager: AppPollsManager, public apiManager: ApiManagerProxy, public appDraftsManager: AppDraftsManager, public serverTimeManager: ServerTimeManager, public storage: typeof sessionStorage) {
super();
this.container = document.createElement('div');
@ -80,7 +81,7 @@ export default class Chat extends EventListenerBase<{ @@ -80,7 +81,7 @@ export default class Chat extends EventListenerBase<{
this.initPeerId = peerId;
this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager);
this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager);
this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager, this.storage);
this.input = new ChatInput(this, this.appMessagesManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager);
this.selection = new ChatSelection(this, this.bubbles, this.input, this.appMessagesManager);
this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appChatsManager, this.appPeersManager, this.appPollsManager);

7
src/components/chat/pinnedMessage.ts

@ -1,13 +1,12 @@ @@ -1,13 +1,12 @@
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager";
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type ChatTopbar from "./topbar";
import { ScreenSize } from "../../helpers/mediaSizes";
import PopupPinMessage from "../popups/unpinMessage";
import PinnedContainer from "./pinnedContainer";
import PinnedMessageBorder from "./pinnedMessageBorder";
import ReplyContainer, { wrapReplyDivAndCaption } from "./replyContainer";
import rootScope from "../../lib/rootScope";
import { attachClickEvent, cancelEvent, findUpClassName, getElementByPoint, handleScrollSideEvent } from "../../helpers/dom";
import { attachClickEvent, cancelEvent, handleScrollSideEvent } from "../../helpers/dom";
import Chat from "./chat";
import ListenerSetter from "../../helpers/listenerSetter";
import ButtonIcon from "../buttonIcon";
@ -336,11 +335,9 @@ export default class ChatPinnedMessage { @@ -336,11 +335,9 @@ export default class ChatPinnedMessage {
}
//const perf = performance.now();
let el = getElementByPoint(this.chat.bubbles.scrollable.container, 'bottom');
let el = this.chat.bubbles.getBubbleByPoint('bottom');
//this.chat.log('[PM]: setCorrectIndex: get last element perf:', performance.now() - perf, el);
if(!el) return;
el = findUpClassName(el, 'bubble');
if(!el) return;
//return;

12
src/components/sidebarLeft/index.ts

@ -172,12 +172,16 @@ export class AppSidebarLeft extends SidebarSlider { @@ -172,12 +172,16 @@ export class AppSidebarLeft extends SidebarSlider {
const scrollable = new Scrollable(searchContainer);
const close = () => {
this.backBtn.click();
};
this.searchGroups = {
contacts: new SearchGroup('Chats', 'contacts'),
globalContacts: new SearchGroup('Global Search', 'contacts'),
contacts: new SearchGroup('Chats', 'contacts', undefined, undefined, undefined, undefined, close),
globalContacts: new SearchGroup('Global Search', 'contacts', undefined, undefined, undefined, undefined, close),
messages: new SearchGroup('Messages', 'messages'),
people: new SearchGroup('', 'contacts', true, 'search-group-people', true, false),
recent: new SearchGroup('Recent', 'contacts', true, 'search-group-recent', true, false)
people: new SearchGroup('', 'contacts', true, 'search-group-people', true, false, close),
recent: new SearchGroup('Recent', 'contacts', true, 'search-group-recent', true, true, close)
};
const searchSuper = this.searchSuper = new AppSearchSuper([{

7
src/components/sidebarLeft/tabs/contacts.ts

@ -21,7 +21,10 @@ export default class AppContactsTab implements SliderTab { @@ -21,7 +21,10 @@ export default class AppContactsTab implements SliderTab {
this.container = document.getElementById('contacts-container');
this.list = this.container.querySelector('#contacts');
appDialogsManager.setListClickListener(this.list);
appDialogsManager.setListClickListener(this.list, () => {
(this.container.querySelector('.sidebar-close-button') as HTMLElement).click();
}, undefined, true);
this.scrollable = new Scrollable(this.list.parentElement);
this.inputSearch = new InputSearch('Search', (value) => {
@ -95,7 +98,7 @@ export default class AppContactsTab implements SliderTab { @@ -95,7 +98,7 @@ export default class AppContactsTab implements SliderTab {
container: this.list,
drawStatus: false,
avatarSize: 48,
autonomous: false
autonomous: true
});
let status = appUsersManager.getUserStatusString(user.id);

8
src/helpers/dom.ts

@ -643,13 +643,15 @@ export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', @@ -643,13 +643,15 @@ export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom',
}
};
export const getElementByPoint = (container: HTMLElement, verticalSide: 'top' | 'bottom'): HTMLElement => {
export const getElementByPoint = (container: HTMLElement, verticalSide: 'top' | 'bottom', horizontalSide: 'center' | 'left'): HTMLElement => {
const rect = container.getBoundingClientRect();
const x = Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1);
const y = verticalSide == 'bottom' ? Math.floor(rect.top + rect.height - 1) : Math.ceil(rect.top + 1);
const x = horizontalSide === 'center' ? Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1) : Math.ceil(rect.left + 1);
const y = verticalSide === 'bottom' ? Math.floor(rect.top + rect.height - 1) : Math.ceil(rect.top + 1);
return document.elementFromPoint(x, y) as any;
};
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.getElementByPoint = getElementByPoint);
export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes = false): Promise<any[]> {
const files: any[] = [];

34
src/lib/appManagers/appImManager.ts

@ -32,6 +32,7 @@ import lottieLoader from '../lottieLoader'; @@ -32,6 +32,7 @@ import lottieLoader from '../lottieLoader';
import useHeavyAnimationCheck, { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import appDraftsManager from './appDraftsManager';
import serverTimeManager from '../mtproto/serverTimeManager';
import sessionStorage from '../sessionStorage';
//console.log('appImManager included33!');
@ -152,8 +153,39 @@ export class AppImManager { @@ -152,8 +153,39 @@ export class AppImManager {
animationIntersector.setOnlyOnePlayableGroup('');
animationIntersector.checkAnimations(false);
});
/* rootScope.on('peer_changing', (chat) => {
this.saveChatPosition(chat);
});
sessionStorage.get('chatPositions').then((c) => {
sessionStorage.setToCache('chatPositions', c || {});
}); */
}
/* public saveChatPosition(chat: Chat) {
const bubble = chat.bubbles.getBubbleByPoint('top');
if(bubble) {
const top = bubble.getBoundingClientRect().top;
this.log('saving position by bubble:', bubble, top);
const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : '');
const chatPositions = sessionStorage.getFromCache('chatPositions');
chatPositions[key] = {
mid: +bubble.dataset.mid,
top
};
sessionStorage.set({chatPositions});
}
}
public getChatSavedPosition(chat: Chat) {
const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : '');
return sessionStorage.getFromCache('chatPositions')[key];
} */
private setSettings() {
document.documentElement.style.setProperty('--messages-text-size', rootScope.settings.messagesTextSize + 'px');
@ -469,7 +501,7 @@ export class AppImManager { @@ -469,7 +501,7 @@ export class AppImManager {
}
private createNewChat() {
const chat = new Chat(this, appChatsManager, appDocsManager, appInlineBotsManager, appMessagesManager, appPeersManager, appPhotosManager, appProfileManager, appStickersManager, appUsersManager, appWebPagesManager, appPollsManager, apiManager, appDraftsManager, serverTimeManager);
const chat = new Chat(this, appChatsManager, appDocsManager, appInlineBotsManager, appMessagesManager, appPeersManager, appPhotosManager, appProfileManager, appStickersManager, appUsersManager, appWebPagesManager, appPollsManager, apiManager, appDraftsManager, serverTimeManager, sessionStorage);
this.chats.push(chat);
}

1
src/lib/appManagers/appStateManager.ts

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
import type { Dialog } from './appMessagesManager';
import type { AppStickersManager } from './appStickersManager';
import { App, MOUNT_CLASS_TO, UserAuth } from '../mtproto/mtproto_config';
import EventListenerBase from '../../helpers/eventListenerBase';
import rootScope from '../rootScope';

12
src/lib/sessionStorage.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import type { AppImManager } from './appManagers/appImManager';
import type { State } from './appManagers/appStateManager';
import { MOUNT_CLASS_TO } from './mtproto/mtproto_config';
import AppStorage from './storage';
import { State } from './appManagers/appStateManager';
const sessionStorage = new AppStorage<{
dc: number,
@ -11,7 +12,14 @@ const sessionStorage = new AppStorage<{ @@ -11,7 +12,14 @@ const sessionStorage = new AppStorage<{
dc4_auth_key: any,
dc5_auth_key: any,
max_seen_msg: number,
server_time_offset: number
server_time_offset: number,
chatPositions: {
[peerId_threadId: string]: {
mid: number,
top: number
}
},
} & State>({
storeName: 'session'
});

4
src/scss/partials/_chatBubble.scss

@ -281,6 +281,10 @@ $bubble-margin: .25rem; @@ -281,6 +281,10 @@ $bubble-margin: .25rem;
opacity: .99999; // for safari
pointer-events: none;
&:before, &:after {
display: none;
}
body.animation-level-0 & {
transition: none;
}

Loading…
Cancel
Save