Browse Source

Mentions

master
Eduard Kuzmenko 3 years ago
parent
commit
67d4ef33ba
  1. 2
      src/components/appMediaViewer.ts
  2. 14
      src/components/chat/bubbles.ts
  3. 21
      src/components/chat/input.ts
  4. 20
      src/components/singleTransition.ts
  5. 2
      src/config/app.ts
  6. 1
      src/helpers/slicedArray.ts
  7. 81
      src/lib/appManagers/appDialogsManager.ts
  8. 203
      src/lib/appManagers/appMessagesManager.ts
  9. 40
      src/scss/partials/_chat.scss
  10. 87
      src/scss/partials/_chatlist.scss
  11. 1
      src/scss/partials/_leftSidebar.scss
  12. 3
      src/scss/partials/_mediaViewer.scss
  13. 12
      src/scss/partials/_selector.scss

2
src/components/appMediaViewer.ts

@ -703,7 +703,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
//let borderRadius = '0px 0px 0px 0px'; //let borderRadius = '0px 0px 0px 0px';
if(closing && zoomValue > 1) { if(closing && zoomValue !== 1) {
// const width = this.moversContainer.scrollWidth * scaleX; // const width = this.moversContainer.scrollWidth * scaleX;
// const height = this.moversContainer.scrollHeight * scaleY; // const height = this.moversContainer.scrollHeight * scaleY;
const willBeLeft = windowSize.windowW / 2 - rect.width / 2; const willBeLeft = windowSize.windowW / 2 - rect.width / 2;

14
src/components/chat/bubbles.ts

@ -772,6 +772,16 @@ export default class ChatBubbles {
} }
}); });
const readContents: number[] = [];
for(const mid of this.unreadedSeen) {
const message: MyMessage = this.chat.getMessage(mid);
if(message.pFlags.media_unread) {
readContents.push(mid);
}
}
this.appMessagesManager.readMessages(this.peerId, readContents);
this.unreadedSeen.clear(); this.unreadedSeen.clear();
if(DEBUG) { if(DEBUG) {
@ -2156,7 +2166,9 @@ export default class ChatBubbles {
if(!our && !message.pFlags.out && this.unreadedObserver) { if(!our && !message.pFlags.out && this.unreadedObserver) {
//this.log('not our message', message, message.pFlags.unread); //this.log('not our message', message, message.pFlags.unread);
const isUnread = message.pFlags.unread || (this.historyStorage.readMaxId !== undefined && this.historyStorage.readMaxId < message.mid); const isUnread = message.pFlags.unread ||
(message.pFlags.media_unread && message.pFlags.mentioned) ||
(this.historyStorage.readMaxId !== undefined && this.historyStorage.readMaxId < message.mid);
if(isUnread) { if(isUnread) {
this.unreadedObserver.observe(bubble); this.unreadedObserver.observe(bubble);
this.unreaded.set(bubble, message.mid); this.unreaded.set(bubble, message.mid);

21
src/components/chat/input.ts

@ -160,6 +160,8 @@ export default class ChatInput {
private goDownBtn: HTMLButtonElement; private goDownBtn: HTMLButtonElement;
private goDownUnreadBadge: HTMLElement; private goDownUnreadBadge: HTMLElement;
private goMentionBtn: HTMLButtonElement;
private goMentionUnreadBadge: HTMLSpanElement;
private btnScheduled: HTMLButtonElement; private btnScheduled: HTMLButtonElement;
private btnPreloader: HTMLButtonElement; private btnPreloader: HTMLButtonElement;
@ -212,7 +214,7 @@ export default class ChatInput {
this.inputContainer.append(this.rowsWrapper, fakeRowsWrapper, fakeSelectionWrapper); this.inputContainer.append(this.rowsWrapper, fakeRowsWrapper, fakeSelectionWrapper);
this.chatInput.append(this.inputContainer); this.chatInput.append(this.inputContainer);
this.goDownBtn = ButtonCorner({icon: 'arrow_down', className: 'bubbles-go-down hide'}); this.goDownBtn = ButtonCorner({icon: 'arrow_down', className: 'bubbles-corner-button bubbles-go-down hide'});
this.inputContainer.append(this.goDownBtn); this.inputContainer.append(this.goDownBtn);
attachClickEvent(this.goDownBtn, (e) => { attachClickEvent(this.goDownBtn, (e) => {
@ -309,6 +311,17 @@ export default class ChatInput {
this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary'); this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary');
this.goDownBtn.append(this.goDownUnreadBadge); this.goDownBtn.append(this.goDownUnreadBadge);
this.goMentionBtn = ButtonCorner({icon: 'mention', className: 'bubbles-corner-button bubbles-go-mention'});
this.goMentionUnreadBadge = document.createElement('span');
this.goMentionUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary');
this.goMentionBtn.append(this.goMentionUnreadBadge);
this.inputContainer.append(this.goMentionBtn);
attachClickEvent(this.goMentionBtn, (e) => {
cancelEvent(e);
this.appMessagesManager.goToNextMention(this.chat.peerId);
}, {listenerSetter: this.listenerSetter});
this.btnScheduled = ButtonIcon('scheduled btn-scheduled float hide', {noRipple: true}); this.btnScheduled = ButtonIcon('scheduled btn-scheduled float hide', {noRipple: true});
attachClickEvent(this.btnScheduled, (e) => { attachClickEvent(this.btnScheduled, (e) => {
@ -676,6 +689,12 @@ export default class ChatInput {
const count = dialog?.unread_count; const count = dialog?.unread_count;
this.goDownUnreadBadge.innerText = '' + (count || ''); this.goDownUnreadBadge.innerText = '' + (count || '');
this.goDownUnreadBadge.classList.toggle('badge-gray', this.appNotificationsManager.isPeerLocalMuted(this.chat.peerId, true)); this.goDownUnreadBadge.classList.toggle('badge-gray', this.appNotificationsManager.isPeerLocalMuted(this.chat.peerId, true));
if(this.goMentionUnreadBadge && this.chat.type === 'chat') {
const hasMentions = !!dialog?.unread_mentions_count;
this.goMentionUnreadBadge.innerText = hasMentions ? '' + (dialog.unread_mentions_count) : '';
this.goMentionBtn.classList.toggle('is-visible', hasMentions);
}
} }
public saveDraft() { public saveDraft() {

20
src/components/singleTransition.ts

@ -6,12 +6,28 @@
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
const SetTransition = (element: HTMLElement, className: string, forwards: boolean, duration: number, onTransitionEnd?: () => void) => { const SetTransition = (element: HTMLElement, className: string, forwards: boolean, duration: number, onTransitionEnd?: () => void, useRafs?: number) => {
const timeout = element.dataset.timeout; const {timeout, raf} = element.dataset;
if(timeout !== undefined) { if(timeout !== undefined) {
clearTimeout(+timeout); clearTimeout(+timeout);
} }
if(raf !== undefined) {
window.cancelAnimationFrame(+raf);
if(!useRafs) {
delete element.dataset.raf;
}
}
if(useRafs) {
element.dataset.raf = '' + window.requestAnimationFrame(() => {
delete element.dataset.raf;
SetTransition(element, className, forwards, duration, onTransitionEnd, useRafs - 1);
});
return;
}
if(forwards && className) { if(forwards && className) {
element.classList.add(className); element.classList.add(className);
} }

2
src/config/app.ts

@ -16,7 +16,7 @@ export const MAIN_DOMAIN = 'web.telegram.org';
const App = { const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.7.2', version: '0.8.0',
langPackVersion: '0.3.3', langPackVersion: '0.3.3',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',

1
src/helpers/slicedArray.ts

@ -35,7 +35,6 @@ export interface SliceConstructor {
new(...items: ItemType[]): Slice; new(...items: ItemType[]): Slice;
} }
// TODO: Clear empty arrays after deleting items
export default class SlicedArray { export default class SlicedArray {
private slices: Slice[]/* = [[7,6,5],[4,3,2],[1,0,-1]] */; private slices: Slice[]/* = [[7,6,5],[4,3,2],[1,0,-1]] */;
private sliceConstructor: SliceConstructor; private sliceConstructor: SliceConstructor;

81
src/lib/appManagers/appDialogsManager.ts

@ -43,11 +43,11 @@ import ConnectionStatusComponent from "../../components/connectionStatus";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl"; import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import { fastRafPromise } from "../../helpers/schedulers"; import { fastRafPromise } from "../../helpers/schedulers";
import appPhotosManager from "./appPhotosManager";
import SortedUserList from "../../components/sortedUserList"; import SortedUserList from "../../components/sortedUserList";
import { isTouchSupported } from "../../helpers/touchSupport"; import { isTouchSupported } from "../../helpers/touchSupport";
import handleTabSwipe from "../../helpers/dom/handleTabSwipe"; import handleTabSwipe from "../../helpers/dom/handleTabSwipe";
import windowSize from "../../helpers/windowSize"; import windowSize from "../../helpers/windowSize";
import isInDOM from "../../helpers/dom/isInDOM";
export type DialogDom = { export type DialogDom = {
avatarEl: AvatarElement, avatarEl: AvatarElement,
@ -56,11 +56,12 @@ export type DialogDom = {
titleSpanContainer: HTMLSpanElement, titleSpanContainer: HTMLSpanElement,
statusSpan: HTMLSpanElement, statusSpan: HTMLSpanElement,
lastTimeSpan: HTMLSpanElement, lastTimeSpan: HTMLSpanElement,
unreadMessagesSpan: HTMLSpanElement, unreadBadge: HTMLElement,
mentionsBadge?: HTMLElement,
lastMessageSpan: HTMLSpanElement, lastMessageSpan: HTMLSpanElement,
containerEl: HTMLElement, containerEl: HTMLElement,
listEl: HTMLLIElement, listEl: HTMLLIElement,
muteAnimationTimeout?: number messageEl: HTMLElement
}; };
//const testScroll = false; //const testScroll = false;
@ -1331,7 +1332,7 @@ export class AppDialogsManager {
appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);
if(!lastMessage.deleted && lastMessage.pFlags.out && lastMessage.peerId !== rootScope.myId/* && if(!lastMessage.deleted && lastMessage.pFlags.out && lastMessage.peerId !== rootScope.myId/* &&
dialog.read_outbox_max_id */) { // maybe comment, 06.20.2020 dialog.read_outbox_max_id */) { // maybe comment, 06.20.2020
const isUnread = (lastMessage.pFlags && lastMessage.pFlags.unread) const isUnread = !!lastMessage.pFlags?.unread
/* && dialog.read_outbox_max_id !== 0 */; // maybe uncomment, 31.01.2020 /* && dialog.read_outbox_max_id !== 0 */; // maybe uncomment, 31.01.2020
if(isUnread) { if(isUnread) {
@ -1351,20 +1352,61 @@ export class AppDialogsManager {
isPinned = !!dialog.pFlags.pinned; isPinned = !!dialog.pFlags.pinned;
} }
const hasUnreadBadge = isPinned || !!dialog.unread_count || dialog.pFlags.unread_mark;
// dom.messageEl.classList.toggle('has-badge', hasBadge);
const isUnreadBadgeMounted = isInDOM(dom.unreadBadge);
if(hasUnreadBadge && !isUnreadBadgeMounted) {
dom.messageEl.append(dom.unreadBadge);
}
const hasMentionsBadge = dialog.unread_mentions_count > 1;
const isMentionBadgeMounted = dom.mentionsBadge && isInDOM(dom.mentionsBadge);
if(hasMentionsBadge) {
if(!dom.mentionsBadge) {
dom.mentionsBadge = document.createElement('div');
dom.mentionsBadge.className = 'dialog-subtitle-badge badge badge-24 mention mention-badge';
dom.mentionsBadge.innerText = '@';
dom.messageEl.insertBefore(dom.mentionsBadge, dom.lastMessageSpan.nextSibling);
}
}
SetTransition(dom.unreadBadge, 'is-visible', hasUnreadBadge, 200, hasUnreadBadge ? undefined : () => {
dom.unreadBadge.remove();
}, !isUnreadBadgeMounted ? 2 : 0);
if(dom.mentionsBadge) {
SetTransition(dom.mentionsBadge, 'is-visible', hasMentionsBadge, 200, hasMentionsBadge ? undefined : () => {
dom.mentionsBadge.remove();
delete dom.mentionsBadge;
}, !isMentionBadgeMounted ? 2 : 0);
}
if(!hasUnreadBadge) {
return;
}
if(isPinned) { if(isPinned) {
dom.unreadMessagesSpan.classList.add('tgico-chatspinned', 'tgico'); dom.unreadBadge.classList.add('tgico-chatspinned', 'tgico');
} else { } else {
dom.unreadMessagesSpan.classList.remove('tgico-chatspinned', 'tgico'); dom.unreadBadge.classList.remove('tgico-chatspinned', 'tgico');
} }
if(dialog.unread_count || dialog.pFlags.unread_mark) { let isUnread = true, isMention = false;
if(dialog.unread_mentions_count && dialog.unread_count === 1) {
dom.unreadBadge.innerText = '@';
isMention = true;
// dom.unreadBadge.classList.add('tgico-mention', 'tgico');
} else if(dialog.unread_count || dialog.pFlags.unread_mark) {
//dom.unreadMessagesSpan.innerText = '' + (dialog.unread_count ? formatNumber(dialog.unread_count, 1) : ' '); //dom.unreadMessagesSpan.innerText = '' + (dialog.unread_count ? formatNumber(dialog.unread_count, 1) : ' ');
dom.unreadMessagesSpan.innerText = '' + (dialog.unread_count || ' '); dom.unreadBadge.innerText = '' + (dialog.unread_count || ' ');
dom.unreadMessagesSpan.classList.add('unread');
} else { } else {
dom.unreadMessagesSpan.innerText = ''; dom.unreadBadge.innerText = '';
dom.unreadMessagesSpan.classList.remove('unread'); isUnread = false;
} }
dom.unreadBadge.classList.toggle('unread', isUnread);
dom.unreadBadge.classList.toggle('mention', isMention);
} }
private accumulateArchivedUnread() { private accumulateArchivedUnread() {
@ -1499,8 +1541,8 @@ export class AppDialogsManager {
const lastTimeSpan = document.createElement('span'); const lastTimeSpan = document.createElement('span');
lastTimeSpan.classList.add('message-time'); lastTimeSpan.classList.add('message-time');
const unreadMessagesSpan = document.createElement('div'); const unreadBadge = document.createElement('div');
unreadMessagesSpan.className = 'dialog-subtitle-badge badge badge-24'; unreadBadge.className = 'dialog-subtitle-badge badge badge-24';
const titleP = document.createElement('p'); const titleP = document.createElement('p');
titleP.classList.add('dialog-title'); titleP.classList.add('dialog-title');
@ -1510,11 +1552,11 @@ export class AppDialogsManager {
rightSpan.append(statusSpan, lastTimeSpan); rightSpan.append(statusSpan, lastTimeSpan);
titleP.append(titleSpanContainer, rightSpan); titleP.append(titleSpanContainer, rightSpan);
const messageP = document.createElement('p'); const messageEl = document.createElement('p');
messageP.classList.add('dialog-subtitle'); messageEl.classList.add('dialog-subtitle');
messageP.append(span, unreadMessagesSpan); messageEl.append(span);
captionDiv.append(titleP, messageP); captionDiv.append(titleP, messageEl);
const dom: DialogDom = { const dom: DialogDom = {
avatarEl, avatarEl,
@ -1523,10 +1565,11 @@ export class AppDialogsManager {
titleSpanContainer, titleSpanContainer,
statusSpan, statusSpan,
lastTimeSpan, lastTimeSpan,
unreadMessagesSpan, unreadBadge,
lastMessageSpan: span, lastMessageSpan: span,
containerEl: li, containerEl: li,
listEl: li listEl: li,
messageEl
}; };
/* let good = false; /* let good = false;

203
src/lib/appManagers/appMessagesManager.ts

@ -65,6 +65,7 @@ import type { MediaSize } from "../../helpers/mediaSizes";
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках // TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
const APITIMEOUT = 0; const APITIMEOUT = 0;
const DO_NOT_READ_HISTORY = false;
export type HistoryStorage = { export type HistoryStorage = {
count: number | null, count: number | null,
@ -205,6 +206,9 @@ export class AppMessagesManager {
private middleware: ReturnType<typeof getMiddleware>; private middleware: ReturnType<typeof getMiddleware>;
private unreadMentions: {[peerId: string]: SlicedArray} = {};
private goToNextMentionPromises: {[peerId: string]: Promise<any>} = {};
constructor() { constructor() {
this.clear(); this.clear();
@ -3632,7 +3636,10 @@ export class AppMessagesManager {
// TODO: cancel notification by peer when this function is being called // TODO: cancel notification by peer when this function is being called
public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) { public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) {
// return Promise.resolve(); if(DO_NOT_READ_HISTORY) {
return Promise.resolve();
}
// console.trace('start read') // console.trace('start read')
this.log('readHistory:', peerId, maxId, threadId); this.log('readHistory:', peerId, maxId, threadId);
if(!this.getReadMaxIdIfUnread(peerId, threadId) && !force) { if(!this.getReadMaxIdIfUnread(peerId, threadId) && !force) {
@ -3732,32 +3739,127 @@ export class AppMessagesManager {
} }
} }
public modifyCachedMentions(peerId: number, mid: number, add: boolean) {
const slicedArray = this.unreadMentions[peerId];
if(!slicedArray) return;
if(add) {
if(slicedArray.first.isEnd(SliceEnd.Top)) {
slicedArray.insertSlice([mid]);
}
} else {
slicedArray.delete(mid);
}
}
public goToNextMention(peerId: number) {
/* this.getUnreadMentions(peerId, 1, 2, 0).then(messages => {
console.log(messages);
}); */
const promise = this.goToNextMentionPromises[peerId];
if(promise) {
return promise;
}
const slicedArray = this.unreadMentions[peerId] ?? (this.unreadMentions[peerId] = new SlicedArray());
const length = slicedArray.length;
const isTopEnd = slicedArray.first.isEnd(SliceEnd.Top);
if(!length && isTopEnd) {
return Promise.resolve();
}
let loadNextPromise = Promise.resolve();
if(!isTopEnd && length < 25) {
loadNextPromise = this.loadNextMentions(peerId);
}
return this.goToNextMentionPromises[peerId] = loadNextPromise.then(() => {
const last = slicedArray.last;
const mid = last && last[last.length - 1];
if(mid) {
slicedArray.delete(mid);
rootScope.dispatchEvent('history_focus', {peerId, mid});
}
}).finally(() => {
delete this.goToNextMentionPromises[peerId];
});
}
public loadNextMentions(peerId: number) {
const slicedArray = this.unreadMentions[peerId];
const maxId = slicedArray.first[0] || 1;
const backLimit = 50;
const add_offset = -backLimit;
const limit = backLimit;
return this.getUnreadMentions(peerId, maxId, add_offset, limit).then(messages => {
this.mergeHistoryResult(slicedArray, messages, maxId === 1 ? 0 : maxId, limit, add_offset);
});
}
public getUnreadMentions(peerId: number, offsetId: number, add_offset: number, limit: number, maxId = 0, minId = 0) {
return apiManager.invokeApiSingle('messages.getUnreadMentions', {
peer: appPeersManager.getInputPeerById(peerId),
offset_id: appMessagesIdsManager.getServerMessageId(offsetId),
add_offset,
limit,
max_id: appMessagesIdsManager.getServerMessageId(maxId),
min_id: appMessagesIdsManager.getServerMessageId(minId)
}).then(messagesMessages => {
assumeType<Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>>(messagesMessages);
appUsersManager.saveApiUsers(messagesMessages.users);
appChatsManager.saveApiChats(messagesMessages.chats);
this.saveMessages(messagesMessages.messages);
return messagesMessages;
});
}
public readMessages(peerId: number, msgIds: number[]) { public readMessages(peerId: number, msgIds: number[]) {
if(DO_NOT_READ_HISTORY) {
return Promise.resolve();
}
if(!msgIds.length) {
return Promise.resolve();
}
msgIds = msgIds.map(mid => appMessagesIdsManager.getServerMessageId(mid)); msgIds = msgIds.map(mid => appMessagesIdsManager.getServerMessageId(mid));
let promise: Promise<any>, update: Update.updateChannelReadMessagesContents | Update.updateReadMessagesContents;
if(peerId < 0 && appPeersManager.isChannel(peerId)) { if(peerId < 0 && appPeersManager.isChannel(peerId)) {
const channelId = -peerId; const channelId = -peerId;
apiManager.invokeApi('channels.readMessageContents', {
channel: appChatsManager.getChannelInput(channelId), update = {
id: msgIds
}).then(() => {
apiUpdatesManager.processLocalUpdate({
_: 'updateChannelReadMessagesContents', _: 'updateChannelReadMessagesContents',
channel_id: channelId, channel_id: channelId,
messages: msgIds messages: msgIds
}); };
promise = apiManager.invokeApi('channels.readMessageContents', {
channel: appChatsManager.getChannelInput(channelId),
id: msgIds
}); });
} else { } else {
apiManager.invokeApi('messages.readMessageContents', { update = {
id: msgIds
}).then((affectedMessages) => {
apiUpdatesManager.processLocalUpdate({
_: 'updateReadMessagesContents', _: 'updateReadMessagesContents',
messages: msgIds, messages: msgIds,
pts: affectedMessages.pts, pts: undefined,
pts_count: affectedMessages.pts_count pts_count: undefined
}); };
promise = apiManager.invokeApi('messages.readMessageContents', {
id: msgIds
}).then((affectedMessages) => {
(update as Update.updateReadMessagesContents).pts = affectedMessages.pts;
(update as Update.updateReadMessagesContents).pts_count = affectedMessages.pts_count;
apiUpdatesManager.processLocalUpdate(update);
}); });
} }
apiUpdatesManager.processLocalUpdate(update);
return promise;
} }
public getHistoryStorage(peerId: number, threadId?: number) { public getHistoryStorage(peerId: number, threadId?: number) {
@ -3973,6 +4075,7 @@ export class AppMessagesManager {
if(message.pFlags.mentioned) { if(message.pFlags.mentioned) {
++dialog.unread_mentions_count; ++dialog.unread_mentions_count;
this.modifyCachedMentions(peerId, message.mid, true);
} }
} }
@ -4135,6 +4238,7 @@ export class AppMessagesManager {
if(message.pFlags.mentioned) { if(message.pFlags.mentioned) {
newUnreadMentionsCount = --foundDialog.unread_mentions_count; newUnreadMentionsCount = --foundDialog.unread_mentions_count;
this.modifyCachedMentions(peerId, message.mid, false);
} }
} }
@ -4157,6 +4261,10 @@ export class AppMessagesManager {
} else if(newUnreadCount && foundDialog.top_message > maxId) { } else if(newUnreadCount && foundDialog.top_message > maxId) {
foundDialog.unread_count = newUnreadCount; foundDialog.unread_count = newUnreadCount;
} }
if(newUnreadMentionsCount < 0) {
foundDialog.unread_mentions_count = 0;
}
} }
rootScope.dispatchEvent('dialog_unread', {peerId}); rootScope.dispatchEvent('dialog_unread', {peerId});
@ -4182,11 +4290,16 @@ export class AppMessagesManager {
const channelId = (update as Update.updateChannelReadMessagesContents).channel_id; const channelId = (update as Update.updateChannelReadMessagesContents).channel_id;
const mids = (update as Update.updateReadMessagesContents).messages.map(id => appMessagesIdsManager.generateMessageId(id)); const mids = (update as Update.updateReadMessagesContents).messages.map(id => appMessagesIdsManager.generateMessageId(id));
const peerId = channelId ? -channelId : this.getMessageById(mids[0]).peerId; const peerId = channelId ? -channelId : this.getMessageById(mids[0]).peerId;
for(const mid of mids) { for(let i = 0, length = mids.length; i < length; ++i) {
const mid = mids[i];
const message: MyMessage = this.getMessageByPeer(peerId, mid); const message: MyMessage = this.getMessageByPeer(peerId, mid);
if(!message.deleted) { if(!message.deleted && message.pFlags.media_unread) {
delete message.pFlags.media_unread; delete message.pFlags.media_unread;
this.setDialogToStateIfMessageIsTop(message); this.setDialogToStateIfMessageIsTop(message);
if(!message.pFlags.out && message.pFlags.mentioned) {
this.modifyCachedMentions(peerId, mid, false);
}
} }
} }
@ -4259,7 +4372,7 @@ export class AppMessagesManager {
const foundDialog = this.getDialogOnly(peerId); const foundDialog = this.getDialogOnly(peerId);
if(foundDialog) { if(foundDialog) {
if(historyUpdated.unreadMentions) { if(historyUpdated.unreadMentions) {
foundDialog.unread_mentions_count -= historyUpdated.unreadMentions; foundDialog.unread_mentions_count = Math.max(0, foundDialog.unread_mentions_count - historyUpdated.unreadMentions);
} }
if(historyUpdated.unread) { if(historyUpdated.unread) {
@ -4911,35 +5024,35 @@ export class AppMessagesManager {
}); });
} }
public fillHistoryStorage(peerId: number, offset_id: number, limit: number, add_offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> { public isHistoryResultEnd(historyResult: Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>, limit: number, add_offset: number) {
return this.requestHistory(peerId, offset_id, limit, add_offset, undefined, threadId).then((historyResult) => { const {offset_id_offset, messages} = historyResult as MessagesMessages.messagesMessagesSlice;
const {offset_id_offset, count, messages} = historyResult as MessagesMessages.messagesMessagesSlice;
historyStorage.count = count || messages.length; const count = (historyResult as MessagesMessages.messagesMessagesSlice).count || messages.length;
const offsetIdOffset = offset_id_offset || 0; const offsetIdOffset = offset_id_offset || 0;
const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit; const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;
const isTopEnd = offsetIdOffset >= (historyStorage.count - topWasMeantToLoad) || historyStorage.count < topWasMeantToLoad; const isTopEnd = offsetIdOffset >= (count - topWasMeantToLoad) || count < topWasMeantToLoad;
const isBottomEnd = !offsetIdOffset || (add_offset < 0 && (offsetIdOffset + add_offset) <= 0); const isBottomEnd = !offsetIdOffset || (add_offset < 0 && (offsetIdOffset + add_offset) <= 0);
/* if(!maxId && historyResult.messages.length) { return {count, offsetIdOffset, isTopEnd, isBottomEnd};
maxId = this.incrementMessageId((historyResult.messages[0] as MyMessage).mid, 1);
} }
const wasTotalCount = historyStorage.history.length; */ public mergeHistoryResult(slicedArray: SlicedArray,
historyResult: Parameters<AppMessagesManager['isHistoryResultEnd']>[0],
offset_id: number,
limit: number,
add_offset: number) {
const {messages} = historyResult as MessagesMessages.messagesMessagesSlice;
const isEnd = this.isHistoryResultEnd(historyResult, limit, add_offset);
const {count, offsetIdOffset, isTopEnd, isBottomEnd} = isEnd;
const mids = messages.map((message) => { const mids = messages.map((message) => {
if(this.mergeReplyKeyboard(historyStorage, message as MyMessage)) {
rootScope.dispatchEvent('history_reply_markup', {peerId});
}
return (message as MyMessage).mid; return (message as MyMessage).mid;
}); });
// * add bound manually. // * add bound manually.
// * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id') // * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id')
if(offset_id && !mids.includes(offset_id) && offsetIdOffset < historyStorage.count) { if(offset_id && !mids.includes(offset_id) && offsetIdOffset < count) {
let i = 0; let i = 0;
for(const length = mids.length; i < length; ++i) { for(const length = mids.length; i < length; ++i) {
if(offset_id > mids[i]) { if(offset_id > mids[i]) {
@ -4950,13 +5063,38 @@ export class AppMessagesManager {
mids.splice(i, 0, offset_id); mids.splice(i, 0, offset_id);
} }
const slice = historyStorage.history.insertSlice(mids) || historyStorage.history.slice; const slice = slicedArray.insertSlice(mids) || slicedArray.slice;
if(isTopEnd) { if(isTopEnd) {
slice.setEnd(SliceEnd.Top); slice.setEnd(SliceEnd.Top);
} }
if(isBottomEnd) { if(isBottomEnd) {
slice.setEnd(SliceEnd.Bottom); slice.setEnd(SliceEnd.Bottom);
}
return {slice, mids, messages, ...isEnd};
}
public fillHistoryStorage(peerId: number, offset_id: number, limit: number, add_offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> {
return this.requestHistory(peerId, offset_id, limit, add_offset, undefined, threadId).then((historyResult) => {
const {count, isBottomEnd, slice, messages} = this.mergeHistoryResult(historyStorage.history, historyResult, offset_id, limit, add_offset);
historyStorage.count = count;
/* if(!maxId && historyResult.messages.length) {
maxId = this.incrementMessageId((historyResult.messages[0] as MyMessage).mid, 1);
}
const wasTotalCount = historyStorage.history.length; */
for(let i = 0, length = messages.length; i < length; ++i) {
const message = messages[i] as MyMessage;
if(this.mergeReplyKeyboard(historyStorage, message)) {
rootScope.dispatchEvent('history_reply_markup', {peerId});
}
}
if(isBottomEnd) {
historyStorage.maxId = slice[0]; // ! WARNING historyStorage.maxId = slice[0]; // ! WARNING
} }
@ -5220,6 +5358,7 @@ export class AppMessagesManager {
if(message.pFlags.mentioned) { if(message.pFlags.mentioned) {
++history.unreadMentions; ++history.unreadMentions;
this.modifyCachedMentions(peerId, mid, false);
} }
} }

40
src/scss/partials/_chat.scss

@ -1091,7 +1091,8 @@ $chat-helper-size: 36px;
//mask-image: linear-gradient(0deg, transparent 0, #000 28px); //mask-image: linear-gradient(0deg, transparent 0, #000 28px);
//} //}
& + .chat-input .bubbles-go-down { & + .chat-input {
.bubbles-go-down {
cursor: pointer; cursor: pointer;
//--translateY: 0; //--translateY: 0;
opacity: 1; opacity: 1;
@ -1105,11 +1106,16 @@ $chat-helper-size: 36px;
--translateY: 79px !important; --translateY: 79px !important;
} */ } */
} }
.bubbles-go-mention {
--translateY: calc(var(--chat-input-size) * -1 + -.625rem);
}
}
//} //}
} }
.search-group.search-group-messages { .search-group.search-group-messages {
padding: 0.25rem 0 .5rem; padding: .25rem 0 .5rem;
} }
} }
@ -1198,6 +1204,30 @@ $chat-helper-size: 36px;
} }
.bubbles-go-down { .bubbles-go-down {
cursor: default;
opacity: 0;
visibility: hidden;
transform: none !important;
transition: opacity var(--layer-transition), visibility 0s .2s !important;
}
.bubbles-go-mention {
--translateY: 0;
cursor: default;
opacity: 0;
visibility: hidden;
&.is-visible {
cursor: pointer;
opacity: 1;
visibility: visible;
transition: transform var(--layer-transition), opacity var(--layer-transition), visibility 0s 0s !important;
}
transition: transform var(--layer-transition), opacity var(--layer-transition), visibility 0s .2s !important;
}
.bubbles-corner-button {
position: absolute; position: absolute;
background-color: var(--surface-color); background-color: var(--surface-color);
border-radius: 50%; border-radius: 50%;
@ -1209,17 +1239,11 @@ $chat-helper-size: 36px;
right: var(--chat-input-padding); right: var(--chat-input-padding);
//top: calc((var(--chat-input-size) * -1) - 6px); //top: calc((var(--chat-input-size) * -1) - 6px);
bottom: calc(var(--chat-input-size) + var(--bottom) + 10px); bottom: calc(var(--chat-input-size) + var(--bottom) + 10px);
cursor: default;
opacity: 0;
visibility: hidden;
z-index: 2; z-index: 2;
//transition: transform var(--layer-transition), opacity var(--layer-transition) !important; //transition: transform var(--layer-transition), opacity var(--layer-transition) !important;
overflow: visible; overflow: visible;
//--translateY: calc(var(--chat-input-size) + 10px); //--translateY: calc(var(--chat-input-size) + 10px);
//--translateY: calc(100% + 10px); //--translateY: calc(100% + 10px);
transition: opacity var(--layer-transition), visibility 0s .2s !important;
//transition: opacity var(--layer-transition);
transform: none !important;
@include animation-level(0) { @include animation-level(0) {
transition: none !important; transition: none !important;

87
src/scss/partials/_chatlist.scss

@ -160,7 +160,6 @@ ul.chatlist {
p { p {
margin: 0; margin: 0;
display: flex; display: flex;
justify-content: space-between;
flex-direction: row; flex-direction: row;
align-items: flex-start; align-items: flex-start;
height: 27px; height: 27px;
@ -222,7 +221,8 @@ ul.chatlist {
border-color: var(--primary-color); border-color: var(--primary-color);
} }
.badge.unread { .badge.unread,
.mention {
background-color: #fff !important; background-color: #fff !important;
color: var(--primary-color); color: var(--primary-color);
} }
@ -268,13 +268,32 @@ ul.chatlist {
margin-top: -3px; margin-top: -3px;
&-badge { &-badge {
&:not(:empty), &.tgico-pinnedchat { display: block !important;
margin-top: 4px;
margin-right: -3px;
margin-left: .5rem; margin-left: .5rem;
} flex: 0 0 auto;
&.unread { @include animation-level(2) {
transition: none; transition: none;
transform: scale(0);
&.is-visible:not(.backwards) {
transform: scale(1);
} }
&.animating {
transition: background-color .2s ease-in-out, transform .2s ease-in-out;
}
}
/* &:not(:empty), &.tgico-pinnedchat {
margin-left: .5rem;
} */
/* &.unread {
transition: none;
} */
} }
} }
} }
@ -292,7 +311,8 @@ ul.chatlist {
padding: .0625rem .4375rem .0625rem .5625rem; padding: .0625rem .4375rem .0625rem .5625rem;
} }
.dialog-avatar, .user-caption { .dialog-avatar,
.user-caption {
pointer-events: none; pointer-events: none;
position: relative; // for z-index position: relative; // for z-index
} }
@ -337,7 +357,10 @@ ul.chatlist {
} */ } */
} }
.user-title, .user-last-message { .user-title,
.user-last-message {
flex-grow: 1;
i { i {
font-style: normal; font-style: normal;
//color: var(--primary-color); //color: var(--primary-color);
@ -365,24 +388,18 @@ ul.chatlist {
vertical-align: middle; vertical-align: middle;
} }
.dialog-subtitle-badge {
margin-top: 4px;
margin-right: -3px;
margin-left: .25rem;
flex: 0 0 auto;
}
.tgico-chatspinned { .tgico-chatspinned {
background: transparent; background: transparent;
position: relative;
@include animation-level(2) {
&:before { &:before {
color: var(--chatlist-pinned-color);
transition: opacity .2s ease-in-out; transition: opacity .2s ease-in-out;
}
}
&:before {
color: var(--chatlist-pinned-color);
opacity: 1; opacity: 1;
position: absolute;
top: 0;
left: 0;
} }
&.unread { &.unread {
@ -392,11 +409,39 @@ ul.chatlist {
} }
} }
.unread, li.is-muted.backwards .unread { .tgico-chatspinned/* ,
.tgico-mention */ {
position: relative;
&:before {
position: absolute;
top: 0;
left: 0;
}
}
.mention {
padding: 0;
background-color: var(--chatlist-status-color) !important;
}
.mention-badge {
margin-right: -2px;
}
/* .tgico-mention {
&:before {
color: #fff !important;
opacity: 1 !important;
}
} */
.unread,
.is-muted.backwards .unread {
background-color: var(--chatlist-status-color); background-color: var(--chatlist-status-color);
} }
li.is-muted .unread { .is-muted .unread {
background-color: var(--secondary-color); background-color: var(--secondary-color);
} }

1
src/scss/partials/_leftSidebar.scss

@ -162,6 +162,7 @@
top: 6px; top: 6px;
z-index: 1; z-index: 1;
margin-left: 0; margin-left: 0;
line-height: 1.875rem;
} }
&.is-picked { &.is-picked {

3
src/scss/partials/_mediaViewer.scss

@ -122,7 +122,8 @@ $inactive-opacity: .4;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
z-index: 4; z-index: 4;
bottom: .75rem; // bottom: .75rem;
bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: .5rem .5rem 0; padding: .5rem .5rem 0;

12
src/scss/partials/_selector.scss

@ -54,14 +54,14 @@
&-user { &-user {
color: var(--primary-text-color); color: var(--primary-text-color);
background-color: var(--light-secondary-text-color); background-color: var(--light-secondary-text-color);
font-size: 16px; font-size: 1rem;
padding: 0 17px 0px 0px; padding: 0 17px 0px 0px;
line-height: 1.875rem; margin-left: -.25rem;
margin-left: -4px; margin-right: .75rem;
margin-right: 12px; height: 2rem;
height: 32px; line-height: 2rem;
margin-bottom: 7px; margin-bottom: 7px;
border-radius: 24px; border-radius: 1.5rem;
user-select: none; user-select: none;
flex: 0 0 auto; flex: 0 0 auto;
transition: .2s opacity, .2s transform, .2s background-color; transition: .2s opacity, .2s transform, .2s background-color;

Loading…
Cancel
Save