Browse Source

Search, pinned message, date follow in discussions

Fix time css again
master
Eduard Kuzmenko 4 years ago
parent
commit
a25170edf4
  1. 10
      src/components/appSearch.ts
  2. 25
      src/components/avatar.ts
  3. 6
      src/components/chat/bubbles.ts
  4. 4
      src/components/chat/chat.ts
  5. 4
      src/components/chat/messageRender.ts
  6. 13
      src/components/chat/pinnedMessage.ts
  7. 2
      src/components/chat/search.ts
  8. 95
      src/components/chat/topbar.ts
  9. 5
      src/components/lazyLoadQueue.ts
  10. 11
      src/components/sidebarRight/tabs/search.ts
  11. 8
      src/lib/appManagers/appMessagesManager.ts
  12. 8
      src/lib/rootScope.ts
  13. 13
      src/scss/partials/_chatBubble.scss

10
src/components/appSearch.ts

@ -77,6 +77,7 @@ export default class AppSearch { @@ -77,6 +77,7 @@ export default class AppSearch {
public listsContainer: HTMLDivElement = null;
private peerId = 0; // 0 - means global
private threadId = 0;
private scrollable: Scrollable;
@ -119,6 +120,7 @@ export default class AppSearch { @@ -119,6 +120,7 @@ export default class AppSearch {
this.searchInput.value = '';
this.query = '';
this.peerId = 0;
this.threadId = 0;
}
this.minMsgId = 0;
@ -134,10 +136,14 @@ export default class AppSearch { @@ -134,10 +136,14 @@ export default class AppSearch {
this.searchPromise = null;
}
public beginSearch(peerId?: number) {
public beginSearch(peerId?: number, threadId?: number) {
if(peerId) {
this.peerId = peerId;
}
if(threadId) {
this.threadId = threadId;
}
this.searchInput.input.focus();
}
@ -240,7 +246,7 @@ export default class AppSearch { @@ -240,7 +246,7 @@ export default class AppSearch {
});
}
return this.searchPromise = appMessagesManager.getSearch(this.peerId, query, {_: 'inputMessagesFilterEmpty'}, maxId, 20, this.offsetRate).then(res => {
return this.searchPromise = appMessagesManager.getSearch(this.peerId, query, {_: 'inputMessagesFilterEmpty'}, maxId, 20, this.offsetRate, undefined, this.threadId).then(res => {
this.searchPromise = null;
if(this.searchInput.value != query) {

25
src/components/avatar.ts

@ -4,6 +4,7 @@ import rootScope from "../lib/rootScope"; @@ -4,6 +4,7 @@ import rootScope from "../lib/rootScope";
import { attachClickEvent, cancelEvent } from "../helpers/dom";
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
import { Photo } from "../layer";
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
rootScope.on('avatar_update', (e) => {
let peerId = e.detail;
@ -19,6 +20,8 @@ export default class AvatarElement extends HTMLElement { @@ -19,6 +20,8 @@ export default class AvatarElement extends HTMLElement {
private peerId: number;
private isDialog = false;
public peerTitle: string;
public lazyLoadQueue: LazyLoadQueueIntersector;
private addedToQueue = false;
constructor() {
super();
@ -91,10 +94,13 @@ export default class AvatarElement extends HTMLElement { @@ -91,10 +94,13 @@ export default class AvatarElement extends HTMLElement {
}
}
//disconnectedCallback() {
disconnectedCallback() {
// браузер вызывает этот метод при удалении элемента из документа
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
//}
if(this.lazyLoadQueue) {
this.lazyLoadQueue.unobserve(this);
}
}
static get observedAttributes(): string[] {
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
@ -118,7 +124,20 @@ export default class AvatarElement extends HTMLElement { @@ -118,7 +124,20 @@ export default class AvatarElement extends HTMLElement {
}
public update() {
appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
if(this.lazyLoadQueue) {
if(this.addedToQueue) return;
this.lazyLoadQueue.push({
div: this,
load: () => {
return appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle).finally(() => {
this.addedToQueue = false;
});
}
});
this.addedToQueue = true;
} else {
appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
}
}
}

6
src/components/chat/bubbles.ts

@ -1701,7 +1701,7 @@ export default class ChatBubbles { @@ -1701,7 +1701,7 @@ export default class ChatBubbles {
bubble.classList.add(status);
}
const withReplyFooter = message.replies && message.replies.pFlags.comments;
const withReplyFooter = message.replies && message.replies.pFlags.comments && message.replies.channel_id !== 777;
const isOut = our && (!message.fwd_from || this.peerId != rootScope.myId);
let nameContainer = bubbleContainer;
@ -2087,6 +2087,7 @@ export default class ChatBubbles { @@ -2087,6 +2087,7 @@ export default class ChatBubbles {
</div>`;
const avatarElem = new AvatarElement();
//avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.setAttribute('peer', '' + message.media.user_id);
avatarElem.classList.add('contact-avatar', 'avatar-54');
@ -2218,6 +2219,7 @@ export default class ChatBubbles { @@ -2218,6 +2219,7 @@ export default class ChatBubbles {
if((!our && this.peerId < 0 && (!this.appPeersManager.isChannel(this.peerId) || this.appPeersManager.isMegagroup(this.peerId)))
|| (this.peerId == rootScope.myId && !message.reply_to_mid)) {
let avatarElem = new AvatarElement();
//avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.classList.add('user-avatar', 'avatar-40');
if(!message.fwdFromId && message.fwd_from && message.fwd_from.from_name) {
@ -2376,7 +2378,7 @@ export default class ChatBubbles { @@ -2376,7 +2378,7 @@ export default class ChatBubbles {
onDatePick = (timestamp: number) => {
const peerId = this.peerId;
this.appMessagesManager.requestHistory(peerId, 0, 2, -1, timestamp).then(history => {
this.appMessagesManager.requestHistory(peerId, 0, 2, -1, timestamp, this.chat.threadId).then(history => {
if(!history?.messages?.length) {
this.log.error('no history!');
return;

4
src/components/chat/chat.ts

@ -79,9 +79,13 @@ export default class Chat extends EventListenerBase<{ @@ -79,9 +79,13 @@ export default class Chat extends EventListenerBase<{
this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appChatsManager, this.appPeersManager, this.appPollsManager);
if(this.type === 'chat') {
this.topbar.constructUtils();
this.topbar.constructPeerHelpers();
} else if(this.type === 'pinned') {
this.topbar.constructPinnedHelpers();
} else if(this.type === 'discussion') {
this.topbar.constructUtils();
this.topbar.constructDiscussionHelpers();
}
this.topbar.construct();

4
src/components/chat/messageRender.ts

@ -21,7 +21,7 @@ export namespace MessageRender { @@ -21,7 +21,7 @@ export namespace MessageRender {
const postAuthor = message.post_author || message.fwd_from?.post_author;
bubble.classList.add('channel-post');
time = formatNumber(message.views, 1) + ' <i class="tgico-channelviews"></i> ' + (postAuthor ? RichTextProcessor.wrapEmojiText(postAuthor) + ', ' : '') + time;
time = formatNumber(message.views, 1) + ' <i class="tgico-channelviews time-icon"></i> ' + (postAuthor ? RichTextProcessor.wrapEmojiText(postAuthor) + ', ' : '') + time;
if(!message.savedFrom) {
const forward = document.createElement('div');
@ -45,7 +45,7 @@ export namespace MessageRender { @@ -45,7 +45,7 @@ export namespace MessageRender {
if(chat.type != 'pinned' && message.pFlags.pinned) {
bubble.classList.add('is-pinned');
time = '<i class="tgico tgico-pinnedchat"></i>' + time;
time = '<i class="tgico-pinnedchat time-icon"></i>' + time;
}
const title = getFullDate(date)

13
src/components/chat/pinnedMessage.ts

@ -240,6 +240,8 @@ export default class ChatPinnedMessage { @@ -240,6 +240,8 @@ export default class ChatPinnedMessage {
public btnOpen: HTMLButtonElement;
public setPinnedMessage: () => void;
private isStatic = false;
constructor(private topbar: ChatTopbar, private chat: Chat, private appMessagesManager: AppMessagesManager, private appPeersManager: AppPeersManager) {
this.listenerSetter = new ListenerSetter();
@ -309,6 +311,8 @@ export default class ChatPinnedMessage { @@ -309,6 +311,8 @@ export default class ChatPinnedMessage {
// * 200 - no lags
// * 100 - need test
this.setPinnedMessage = debounce(() => this._setPinnedMessage(), 100, true, true);
this.isStatic = this.chat.type === 'discussion';
}
public destroy() {
@ -319,6 +323,7 @@ export default class ChatPinnedMessage { @@ -319,6 +323,7 @@ export default class ChatPinnedMessage {
}
public setCorrectIndex(lastScrollDirection?: number) {
if(this.isStatic) return;
//return;
if(this.locked || this.hidden/* || this.chat.setPeerPromise || this.chat.bubbles.messagesQueuePromise */) {
@ -340,16 +345,18 @@ export default class ChatPinnedMessage { @@ -340,16 +345,18 @@ export default class ChatPinnedMessage {
const mid = el.dataset.mid;
if(el && mid !== undefined) {
this.chat.log('[PM]: setCorrectIndex will test mid:', mid);
//this.chat.log('[PM]: setCorrectIndex will test mid:', mid);
this.testMid(+mid, lastScrollDirection);
}
}
public testMid(mid: number, lastScrollDirection?: number) {
if(this.isStatic) return;
//if(lastScrollDirection !== undefined) return;
if(this.hidden) return;
this.chat.log('[PM]: testMid', mid);
//this.chat.log('[PM]: testMid', mid);
let currentIndex: number = this.mids.findIndex(_mid => _mid <= mid);
if(currentIndex !== -1 && !this.isNeededMore(currentIndex)) {
@ -371,7 +378,7 @@ export default class ChatPinnedMessage { @@ -371,7 +378,7 @@ export default class ChatPinnedMessage {
currentIndex = 0;
} */
this.chat.log('[PM]: testMid: pinned currentIndex', currentIndex, mid);
//this.chat.log('[PM]: testMid: pinned currentIndex', currentIndex, mid);
const changed = this.pinnedIndex != currentIndex;
if(changed) {

2
src/components/chat/search.ts

@ -73,7 +73,7 @@ export default class ChatSearch { @@ -73,7 +73,7 @@ export default class ChatSearch {
this.selectResult(this.searchGroup.list.children[0] as HTMLElement);
}
});
this.appSearch.beginSearch(this.chat.peerId);
this.appSearch.beginSearch(this.chat.peerId, this.chat.threadId);
//appImManager.topbar.parentElement.insertBefore(this.results, appImManager.bubblesContainer);
this.chat.bubbles.bubblesContainer.append(this.results);

95
src/components/chat/topbar.ts

@ -144,20 +144,7 @@ export default class ChatTopbar { @@ -144,20 +144,7 @@ export default class ChatTopbar {
}, {listenerSetter: this.listenerSetter});
}
public constructPeerHelpers() {
this.avatarElement = new AvatarElement();
this.avatarElement.setAttribute('dialog', '1');
this.avatarElement.setAttribute('clickable', '');
this.avatarElement.classList.add('avatar-40', 'person-avatar');
this.subtitle = document.createElement('div');
this.subtitle.classList.add('info');
this.pinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager);
this.btnJoin = Button('btn-primary chat-join hide');
this.btnJoin.append('SUBSCRIBE');
public constructUtils() {
this.menuButtons = [{
icon: 'search',
text: 'Search',
@ -176,14 +163,14 @@ export default class ChatTopbar { @@ -176,14 +163,14 @@ export default class ChatTopbar {
onClick: () => {
this.appMessagesManager.mutePeer(this.peerId);
},
verify: () => rootScope.myId != this.peerId && !this.appMessagesManager.isPeerMuted(this.peerId)
verify: () => this.chat.type === 'chat' && rootScope.myId != this.peerId && !this.appMessagesManager.isPeerMuted(this.peerId)
}, {
icon: 'unmute',
text: 'Unmute',
onClick: () => {
this.appMessagesManager.mutePeer(this.peerId);
},
verify: () => rootScope.myId != this.peerId && this.appMessagesManager.isPeerMuted(this.peerId)
verify: () => this.chat.type === 'chat' && rootScope.myId != this.peerId && this.appMessagesManager.isPeerMuted(this.peerId)
}, {
icon: 'select',
text: 'Select Messages',
@ -204,25 +191,40 @@ export default class ChatTopbar { @@ -204,25 +191,40 @@ export default class ChatTopbar {
onClick: () => {
new PopupDeleteDialog(this.peerId);
},
verify: () => !!this.appMessagesManager.getDialogByPeerId(this.peerId)[0]
verify: () => this.chat.type === 'chat' && !!this.appMessagesManager.getDialogByPeerId(this.peerId)[0]
}];
this.btnSearch = ButtonIcon('search');
this.listenerSetter.add(this.btnSearch, 'click', (e) => {
cancelEvent(e);
if(this.peerId) {
this.appSidebarRight.searchTab.open(this.peerId, this.chat.threadId);
}
});
}
public constructPeerHelpers() {
this.avatarElement = new AvatarElement();
this.avatarElement.setAttribute('dialog', '1');
this.avatarElement.setAttribute('clickable', '');
this.avatarElement.classList.add('avatar-40', 'person-avatar');
this.subtitle = document.createElement('div');
this.subtitle.classList.add('info');
this.pinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager);
this.btnJoin = Button('btn-primary chat-join hide');
this.btnJoin.append('SUBSCRIBE');
this.btnPinned = ButtonIcon('pinlist');
this.btnMute = ButtonIcon('mute');
this.btnSearch = ButtonIcon('search');
this.listenerSetter.add(this.btnPinned, 'click', (e) => {
cancelEvent(e);
this.openPinned(true);
});
this.listenerSetter.add(this.btnSearch, 'click', (e) => {
cancelEvent(e);
if(this.peerId) {
this.appSidebarRight.searchTab.open(this.peerId);
}
});
this.listenerSetter.add(this.btnMute, 'click', (e) => {
cancelEvent(e);
this.appMessagesManager.mutePeer(this.peerId);
@ -307,6 +309,10 @@ export default class ChatTopbar { @@ -307,6 +309,10 @@ export default class ChatTopbar {
}
});
}
public constructDiscussionHelpers() {
this.pinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager);
}
public openPinned(byCurrent: boolean) {
this.chat.appImManager.setInnerPeer(this.peerId, byCurrent ? +this.pinnedMessage.pinnedMessageContainer.divAndCaption.container.dataset.mid : 0, 'pinned');
@ -360,23 +366,30 @@ export default class ChatTopbar { @@ -360,23 +366,30 @@ export default class ChatTopbar {
const middleware = this.chat.bubbles.getMiddleware();
if(this.pinnedMessage) { // * replace with new one
if(this.wasPeerId) { // * change
const newPinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager);
this.pinnedMessage.pinnedMessageContainer.divAndCaption.container.replaceWith(newPinnedMessage.pinnedMessageContainer.divAndCaption.container);
this.pinnedMessage.destroy();
//this.pinnedMessage.pinnedMessageContainer.toggle(true);
this.pinnedMessage = newPinnedMessage;
}
appStateManager.getState().then((state) => {
if(!middleware()) return;
this.pinnedMessage.hidden = !!state.hiddenPinnedMessages[peerId];
if(!isTarget) {
this.pinnedMessage.setCorrectIndex(0);
if(this.chat.type === 'chat') {
if(this.wasPeerId) { // * change
const newPinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager);
this.pinnedMessage.pinnedMessageContainer.divAndCaption.container.replaceWith(newPinnedMessage.pinnedMessageContainer.divAndCaption.container);
this.pinnedMessage.destroy();
//this.pinnedMessage.pinnedMessageContainer.toggle(true);
this.pinnedMessage = newPinnedMessage;
}
});
appStateManager.getState().then((state) => {
if(!middleware()) return;
this.pinnedMessage.hidden = !!state.hiddenPinnedMessages[peerId];
if(!isTarget) {
this.pinnedMessage.setCorrectIndex(0);
}
});
} else if(this.chat.type === 'discussion') {
this.pinnedMessage.pinnedMid = this.chat.threadId;
this.pinnedMessage.count = 1;
this.pinnedMessage.pinnedIndex = 0;
this.pinnedMessage._setPinnedMessage();
}
}
window.requestAnimationFrame(() => {

5
src/components/lazyLoadQueue.ts

@ -199,6 +199,11 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase { @@ -199,6 +199,11 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
public unshift(el: LazyLoadElement) {
super.unshift(el);
}
public unobserve(el: HTMLElement) {
this.queue.findAndSplice(i => i.div === el);
this.intersector.unobserve(el);
}
}
export default class LazyLoadQueue extends LazyLoadQueueIntersector {

11
src/components/sidebarRight/tabs/search.ts

@ -11,13 +11,15 @@ export default class AppPrivateSearchTab implements SliderTab { @@ -11,13 +11,15 @@ export default class AppPrivateSearchTab implements SliderTab {
private appSearch: AppSearch;
private peerId = 0;
private threadId = 0;
onOpenAfterTimeout() {
this.appSearch.beginSearch(this.peerId);
this.appSearch.beginSearch(this.peerId, this.threadId);
}
onCloseAfterTimeout() {
this.peerId = 0;
this.threadId = 0;
this.appSearch.reset();
}
@ -31,18 +33,19 @@ export default class AppPrivateSearchTab implements SliderTab { @@ -31,18 +33,19 @@ export default class AppPrivateSearchTab implements SliderTab {
});
}
open(peerId: number) {
open(peerId: number, threadId?: number) {
if(this.init) {
this.init();
this.init = null;
}
if(this.peerId != 0) {
this.appSearch.beginSearch(this.peerId);
if(this.peerId !== 0) {
this.appSearch.beginSearch(this.peerId, this.threadId);
return;
}
this.peerId = peerId;
this.threadId = threadId;
appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.search);
appSidebarRight.toggleSidebar(true);

8
src/lib/appManagers/appMessagesManager.ts

@ -1493,9 +1493,11 @@ export class AppMessagesManager { @@ -1493,9 +1493,11 @@ export class AppMessagesManager {
if(threadId) {
const chatHistoryStorage = this.getHistoryStorage(peerId);
const readMaxId = Math.max(chatHistoryStorage.readMaxId, historyStorage.readMaxId);
return readMaxId < historyStorage.maxId;
const message = this.getMessageByPeer(peerId, historyStorage.maxId);
return !message.pFlags.out && readMaxId < historyStorage.maxId;
} else {
return (peerId > 0 ? Math.max(historyStorage.readMaxId, historyStorage.readOutboxMaxId) : historyStorage.readMaxId) < historyStorage.maxId;
const message = this.getMessageByPeer(peerId, historyStorage.maxId);
return !message.pFlags.out && (peerId > 0 ? Math.max(historyStorage.readMaxId, historyStorage.readOutboxMaxId) : historyStorage.readMaxId) < historyStorage.maxId;
}
}
@ -2956,7 +2958,7 @@ export class AppMessagesManager { @@ -2956,7 +2958,7 @@ export class AppMessagesManager {
max_id: 0,
min_id: 0,
hash: 0,
top_msg_id: threadId
top_msg_id: this.getLocalMessageId(threadId) || 0
}, {
//timeout: APITIMEOUT,
noErrorBox: true

8
src/lib/rootScope.ts

@ -5,7 +5,7 @@ import type { Poll, PollResults } from "./appManagers/appPollsManager"; @@ -5,7 +5,7 @@ import type { Poll, PollResults } from "./appManagers/appPollsManager";
import type { MyDialogFilter } from "./storages/filters";
import type { ConnectionStatusChange } from "../types";
import type { UserTyping } from "./appManagers/appChatsManager";
import { MOUNT_CLASS_TO, UserAuth } from "./mtproto/mtproto_config";
import { DEBUG, MOUNT_CLASS_TO, UserAuth } from "./mtproto/mtproto_config";
type BroadcastEvents = {
'user_update': number,
@ -96,8 +96,10 @@ class RootScope { @@ -96,8 +96,10 @@ class RootScope {
}
public broadcast = <T extends keyof BroadcastEvents>(name: T, detail?: BroadcastEvents[T]) => {
/* if(name != 'user_update') {
console.debug('Broadcasting ' + name + ' event, with args:', detail);
/* if(DEBUG) {
if(name != 'user_update') {
console.debug('Broadcasting ' + name + ' event, with args:', detail);
}
} */
const myCustomEvent = new CustomEvent(name, {detail});

13
src/scss/partials/_chatBubble.scss

@ -189,7 +189,8 @@ $bubble-margin: .25rem; @@ -189,7 +189,8 @@ $bubble-margin: .25rem;
} */
}
.chat.type-chat & .bubble__container {
.chat.type-chat & .bubble__container,
.chat.type-discussion & .bubble__container {
cursor: pointer;
pointer-events: all;
}
@ -1224,6 +1225,7 @@ $bubble-margin: .25rem; @@ -1224,6 +1225,7 @@ $bubble-margin: .25rem;
line-height: 18px;
pointer-events: all; // show title
white-space: nowrap;
height: 18px;
.inner {
display: none;
@ -1248,10 +1250,14 @@ $bubble-margin: .25rem; @@ -1248,10 +1250,14 @@ $bubble-margin: .25rem;
cursor: default;
/* display: inline-flex;
align-items: center; */
height: 12px;
i {
font-size: 1.15rem;
margin-right: .4rem;
}
&-icon {
margin-left: 2px;
}
@ -1270,6 +1276,7 @@ $bubble-margin: .25rem; @@ -1270,6 +1276,7 @@ $bubble-margin: .25rem;
line-height: 1;
padding: inherit;
white-space: nowrap;
height: 12px; // * as font-size
}
.tgico-pinnedchat:before {
@ -1492,7 +1499,7 @@ $bubble-margin: .25rem; @@ -1492,7 +1499,7 @@ $bubble-margin: .25rem;
}
&.is-unread {
.replies-text {
.replies-footer-text {
&:after {
content: " ";
background-color: $color-blue;
@ -1771,7 +1778,7 @@ $bubble-margin: .25rem; @@ -1771,7 +1778,7 @@ $bubble-margin: .25rem;
.inner {
color: $darkgreen;
bottom: 2px;
bottom: 4px;
}
&:after, .inner:after {

Loading…
Cancel
Save