Restrict forwarding outgoing message
Verify attach menu buttons availability Fix peer typing flickering Colored replies
This commit is contained in:
parent
8d85570297
commit
02f0c9db0f
@ -38,6 +38,7 @@ import contextMenuController from "../../helpers/contextMenuController";
|
|||||||
import { attachContextMenuListener } from "../../helpers/dom/attachContextMenuListener";
|
import { attachContextMenuListener } from "../../helpers/dom/attachContextMenuListener";
|
||||||
import filterAsync from "../../helpers/array/filterAsync";
|
import filterAsync from "../../helpers/array/filterAsync";
|
||||||
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||||
|
import { SERVICE_PEER_ID } from "../../lib/mtproto/mtproto_config";
|
||||||
|
|
||||||
export default class ChatContextMenu {
|
export default class ChatContextMenu {
|
||||||
private buttons: (ButtonMenuItemOptions & {verify: () => boolean | Promise<boolean>, notDirect?: () => boolean, withSelection?: true, isSponsored?: true})[];
|
private buttons: (ButtonMenuItemOptions & {verify: () => boolean | Promise<boolean>, notDirect?: () => boolean, withSelection?: true, isSponsored?: true})[];
|
||||||
@ -171,68 +172,76 @@ export default class ChatContextMenu {
|
|||||||
this.mid = mid;
|
this.mid = mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSelected = this.chat.selection.isMidSelected(this.peerId, this.mid);
|
this.isSelected = this.chat.selection.isMidSelected(this.peerId, this.mid);
|
||||||
this.message = await this.chat.getMessage(this.mid);
|
this.message = await this.chat.getMessage(this.mid);
|
||||||
this.noForwards = !isSponsored && !(await this.managers.appMessagesManager.canForward(this.message));
|
this.noForwards = !isSponsored && !(await this.managers.appMessagesManager.canForward(this.message));
|
||||||
this.viewerPeerId = undefined;
|
|
||||||
this.canOpenReactedList = undefined;
|
|
||||||
|
|
||||||
const initResult = await this.init();
|
|
||||||
element = initResult.element;
|
|
||||||
const {cleanup, destroy, menuPadding, reactionsMenu, reactionsMenuPosition} = initResult;
|
|
||||||
let isReactionsMenuVisible = false;
|
|
||||||
if(reactionsMenu) {
|
|
||||||
const className = 'is-visible';
|
|
||||||
isReactionsMenuVisible = reactionsMenu.container.classList.contains(className);
|
|
||||||
if(isReactionsMenuVisible) reactionsMenu.container.classList.remove(className);
|
|
||||||
|
|
||||||
if(reactionsMenuPosition === 'horizontal') {
|
|
||||||
const offsetSize = element[/* reactionsMenuPosition === 'vertical' ? 'offsetHeight' : */'offsetWidth'];
|
|
||||||
// if(reactionsMenu.scrollable.container.scrollWidth > offsetWidth) {
|
|
||||||
const INNER_CONTAINER_PADDING = 8;
|
|
||||||
const visibleLength = (offsetSize - INNER_CONTAINER_PADDING) / REACTION_CONTAINER_SIZE;
|
|
||||||
const nextVisiblePart = visibleLength % 1;
|
|
||||||
const MIN_NEXT_VISIBLE_PART = 0.65;
|
|
||||||
if(nextVisiblePart < MIN_NEXT_VISIBLE_PART) {
|
|
||||||
const minSize = (offsetSize + (MIN_NEXT_VISIBLE_PART - nextVisiblePart) * REACTION_CONTAINER_SIZE) | 0;
|
|
||||||
element.style[/* reactionsMenuPosition === 'vertical' ? 'minHeight' : */'minWidth'] = minSize + 'px';
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right';
|
|
||||||
//bubble.parentElement.append(element);
|
|
||||||
//appImManager.log('contextmenu', e, bubble, side);
|
|
||||||
positionMenu((e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent, element, side, menuPadding);
|
|
||||||
|
|
||||||
if(reactionsMenu) {
|
|
||||||
reactionsMenu.widthContainer.style.top = element.style.top;
|
|
||||||
reactionsMenu.widthContainer.style.left = element.style.left;
|
|
||||||
reactionsMenu.widthContainer.style.setProperty('--menu-width', element[reactionsMenuPosition === 'vertical' ? 'offsetHeight' : 'offsetWidth'] + 'px');
|
|
||||||
element.parentElement.append(reactionsMenu.widthContainer);
|
|
||||||
if(isReactionsMenuVisible) void reactionsMenu.container.offsetLeft; // reflow
|
|
||||||
}
|
|
||||||
|
|
||||||
openBtnMenu(element, () => {
|
|
||||||
if(reactionsMenu) {
|
|
||||||
reactionsMenu.container.classList.remove('is-visible');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mid = 0;
|
|
||||||
this.peerId = undefined;
|
|
||||||
this.target = null;
|
|
||||||
this.viewerPeerId = undefined;
|
this.viewerPeerId = undefined;
|
||||||
this.canOpenReactedList = undefined;
|
this.canOpenReactedList = undefined;
|
||||||
|
|
||||||
setTimeout(() => {
|
const initResult = await this.init();
|
||||||
destroy();
|
if(!initResult) {
|
||||||
}, 300);
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
element = initResult.element;
|
||||||
|
const {cleanup, destroy, menuPadding, reactionsMenu, reactionsMenuPosition} = initResult;
|
||||||
|
let isReactionsMenuVisible = false;
|
||||||
|
if(reactionsMenu) {
|
||||||
|
const className = 'is-visible';
|
||||||
|
isReactionsMenuVisible = reactionsMenu.container.classList.contains(className);
|
||||||
|
if(isReactionsMenuVisible) reactionsMenu.container.classList.remove(className);
|
||||||
|
|
||||||
if(isReactionsMenuVisible) {
|
if(reactionsMenuPosition === 'horizontal') {
|
||||||
reactionsMenu.container.classList.add('is-visible');
|
const offsetSize = element[/* reactionsMenuPosition === 'vertical' ? 'offsetHeight' : */'offsetWidth'];
|
||||||
}
|
// if(reactionsMenu.scrollable.container.scrollWidth > offsetWidth) {
|
||||||
|
const INNER_CONTAINER_PADDING = 8;
|
||||||
|
const visibleLength = (offsetSize - INNER_CONTAINER_PADDING) / REACTION_CONTAINER_SIZE;
|
||||||
|
const nextVisiblePart = visibleLength % 1;
|
||||||
|
const MIN_NEXT_VISIBLE_PART = 0.65;
|
||||||
|
if(nextVisiblePart < MIN_NEXT_VISIBLE_PART) {
|
||||||
|
const minSize = (offsetSize + (MIN_NEXT_VISIBLE_PART - nextVisiblePart) * REACTION_CONTAINER_SIZE) | 0;
|
||||||
|
element.style[/* reactionsMenuPosition === 'vertical' ? 'minHeight' : */'minWidth'] = minSize + 'px';
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right';
|
||||||
|
//bubble.parentElement.append(element);
|
||||||
|
//appImManager.log('contextmenu', e, bubble, side);
|
||||||
|
positionMenu((e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent, element, side, menuPadding);
|
||||||
|
|
||||||
|
if(reactionsMenu) {
|
||||||
|
reactionsMenu.widthContainer.style.top = element.style.top;
|
||||||
|
reactionsMenu.widthContainer.style.left = element.style.left;
|
||||||
|
reactionsMenu.widthContainer.style.setProperty('--menu-width', element[reactionsMenuPosition === 'vertical' ? 'offsetHeight' : 'offsetWidth'] + 'px');
|
||||||
|
element.parentElement.append(reactionsMenu.widthContainer);
|
||||||
|
if(isReactionsMenuVisible) void reactionsMenu.container.offsetLeft; // reflow
|
||||||
|
}
|
||||||
|
|
||||||
|
contextMenuController.openBtnMenu(element, () => {
|
||||||
|
if(reactionsMenu) {
|
||||||
|
reactionsMenu.container.classList.remove('is-visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mid = 0;
|
||||||
|
this.peerId = undefined;
|
||||||
|
this.target = null;
|
||||||
|
this.viewerPeerId = undefined;
|
||||||
|
this.canOpenReactedList = undefined;
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
destroy();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(isReactionsMenuVisible) {
|
||||||
|
reactionsMenu.container.classList.add('is-visible');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
r();
|
||||||
};
|
};
|
||||||
|
|
||||||
public cleanup() {
|
public cleanup() {
|
||||||
@ -425,7 +434,7 @@ export default class ChatContextMenu {
|
|||||||
icon: 'forward',
|
icon: 'forward',
|
||||||
text: 'Forward',
|
text: 'Forward',
|
||||||
onClick: this.onForwardClick, // let forward the message if it's outgoing but not ours (like a changelog)
|
onClick: this.onForwardClick, // let forward the message if it's outgoing but not ours (like a changelog)
|
||||||
verify: () => !this.noForwards && this.chat.type !== 'scheduled' && (!this.message.pFlags.is_outgoing || !this.message.pFlags.out) && this.message._ !== 'messageService'
|
verify: () => !this.noForwards && this.chat.type !== 'scheduled' && (!this.message.pFlags.is_outgoing || this.message.fromId === SERVICE_PEER_ID) && this.message._ !== 'messageService'
|
||||||
}, {
|
}, {
|
||||||
icon: 'forward',
|
icon: 'forward',
|
||||||
text: 'Message.Context.Selection.Forward',
|
text: 'Message.Context.Selection.Forward',
|
||||||
@ -500,6 +509,10 @@ export default class ChatContextMenu {
|
|||||||
this.setButtons();
|
this.setButtons();
|
||||||
|
|
||||||
const filteredButtons = await this.filterButtons(this.buttons);
|
const filteredButtons = await this.filterButtons(this.buttons);
|
||||||
|
if(!filteredButtons.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const element = this.element = ButtonMenu(filteredButtons, this.listenerSetter);
|
const element = this.element = ButtonMenu(filteredButtons, this.listenerSetter);
|
||||||
element.id = 'bubble-contextmenu';
|
element.id = 'bubble-contextmenu';
|
||||||
element.classList.add('contextmenu');
|
element.classList.add('contextmenu');
|
||||||
@ -540,8 +553,9 @@ export default class ChatContextMenu {
|
|||||||
fakeText.classList.add('btn-menu-item-text-fake');
|
fakeText.classList.add('btn-menu-item-text-fake');
|
||||||
viewsButton.element.append(fakeText);
|
viewsButton.element.append(fakeText);
|
||||||
|
|
||||||
|
const AVATAR_SIZE = 22;
|
||||||
const MAX_AVATARS = 3;
|
const MAX_AVATARS = 3;
|
||||||
const PADDING_PER_AVATAR = .875;
|
const PADDING_PER_AVATAR = 1.125;
|
||||||
i18nElem.element.style.visibility = 'hidden';
|
i18nElem.element.style.visibility = 'hidden';
|
||||||
i18nElem.element.style.paddingRight = isViewingReactions ? PADDING_PER_AVATAR * Math.min(MAX_AVATARS, recentReactions.length) + 'rem' : '1rem';
|
i18nElem.element.style.paddingRight = isViewingReactions ? PADDING_PER_AVATAR * Math.min(MAX_AVATARS, recentReactions.length) + 'rem' : '1rem';
|
||||||
const middleware = this.middleware.get();
|
const middleware = this.middleware.get();
|
||||||
@ -595,7 +609,7 @@ export default class ChatContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(reactions.length) {
|
if(reactions.length) {
|
||||||
const avatars = new StackedAvatars({avatarSize: 24});
|
const avatars = new StackedAvatars({avatarSize: AVATAR_SIZE});
|
||||||
avatars.render(recentReactions ? recentReactions.map((r) => getPeerId(r.peer_id)) : reactions.map((reaction) => reaction.peerId));
|
avatars.render(recentReactions ? recentReactions.map((r) => getPeerId(r.peer_id)) : reactions.map((reaction) => reaction.peerId));
|
||||||
viewsButton.element.append(avatars.container);
|
viewsButton.element.append(avatars.container);
|
||||||
|
|
||||||
@ -645,7 +659,7 @@ export default class ChatContextMenu {
|
|||||||
},
|
},
|
||||||
destroy: () => {
|
destroy: () => {
|
||||||
element.remove();
|
element.remove();
|
||||||
reactionsMenu.widthContainer.remove();
|
reactionsMenu && reactionsMenu.widthContainer.remove();
|
||||||
},
|
},
|
||||||
menuPadding,
|
menuPadding,
|
||||||
reactionsMenu,
|
reactionsMenu,
|
||||||
|
@ -92,6 +92,7 @@ import contextMenuController from "../../helpers/contextMenuController";
|
|||||||
import { emojiFromCodePoints } from "../../vendor/emoji";
|
import { emojiFromCodePoints } from "../../vendor/emoji";
|
||||||
import { modifyAckedPromise } from "../../helpers/modifyAckedResult";
|
import { modifyAckedPromise } from "../../helpers/modifyAckedResult";
|
||||||
import ChatSendAs from "./sendAs";
|
import ChatSendAs from "./sendAs";
|
||||||
|
import filterAsync from "../../helpers/array/filterAsync";
|
||||||
|
|
||||||
const RECORD_MIN_TIME = 500;
|
const RECORD_MIN_TIME = 500;
|
||||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||||
@ -1211,7 +1212,17 @@ export default class ChatInput {
|
|||||||
const previousSendAs = this.sendAs;
|
const previousSendAs = this.sendAs;
|
||||||
const sendAs = this.createSendAs();
|
const sendAs = this.createSendAs();
|
||||||
|
|
||||||
const [isBroadcast, canPinMessage, isBot, canSend, neededFakeContainer, ackedPeerFull, ackedScheduledMids, setSendAsCallback] = await Promise.all([
|
const [
|
||||||
|
isBroadcast,
|
||||||
|
canPinMessage,
|
||||||
|
isBot,
|
||||||
|
canSend,
|
||||||
|
neededFakeContainer,
|
||||||
|
ackedPeerFull,
|
||||||
|
ackedScheduledMids,
|
||||||
|
setSendAsCallback,
|
||||||
|
filteredAttachMenuButtons
|
||||||
|
] = await Promise.all([
|
||||||
this.managers.appPeersManager.isBroadcast(peerId),
|
this.managers.appPeersManager.isBroadcast(peerId),
|
||||||
this.managers.appPeersManager.canPinMessage(peerId),
|
this.managers.appPeersManager.canPinMessage(peerId),
|
||||||
this.managers.appPeersManager.isBot(peerId),
|
this.managers.appPeersManager.isBot(peerId),
|
||||||
@ -1219,7 +1230,8 @@ export default class ChatInput {
|
|||||||
this.getNeededFakeContainer(),
|
this.getNeededFakeContainer(),
|
||||||
modifyAckedPromise(this.managers.acknowledged.appProfileManager.getProfileByPeerId(peerId)),
|
modifyAckedPromise(this.managers.acknowledged.appProfileManager.getProfileByPeerId(peerId)),
|
||||||
btnScheduled ? modifyAckedPromise(this.managers.acknowledged.appMessagesManager.getScheduledMessages(peerId)) : undefined,
|
btnScheduled ? modifyAckedPromise(this.managers.acknowledged.appMessagesManager.getScheduledMessages(peerId)) : undefined,
|
||||||
sendAs ? (sendAs.setPeerId(this.chat.peerId), sendAs.updateManual(true)) : undefined
|
sendAs ? (sendAs.setPeerId(this.chat.peerId), sendAs.updateManual(true)) : undefined,
|
||||||
|
this.filterAttachMenuButtons()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const placeholderKey = this.messageInput ? await this.getPlaceholderKey() : undefined;
|
const placeholderKey = this.messageInput ? await this.getPlaceholderKey() : undefined;
|
||||||
@ -1291,7 +1303,7 @@ export default class ChatInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(this.messageInput) {
|
if(this.messageInput) {
|
||||||
this.updateMessageInput(canSend, placeholderKey);
|
this.updateMessageInput(canSend, placeholderKey, filteredAttachMenuButtons);
|
||||||
} else if(this.pinnedControlBtn) {
|
} else if(this.pinnedControlBtn) {
|
||||||
this.pinnedControlBtn.append(i18n(canPinMessage ? 'Chat.Input.UnpinAll' : 'Chat.Pinned.DontShow'));
|
this.pinnedControlBtn.append(i18n(canPinMessage ? 'Chat.Input.UnpinAll' : 'Chat.Pinned.DontShow'));
|
||||||
}
|
}
|
||||||
@ -1373,7 +1385,14 @@ export default class ChatInput {
|
|||||||
i.compareAndUpdate({key});
|
i.compareAndUpdate({key});
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateMessageInput(canSend: boolean, placeholderKey: LangPackKey) {
|
private filterAttachMenuButtons() {
|
||||||
|
const {peerId, threadId} = this.chat;
|
||||||
|
return filterAsync(this.attachMenuButtons, (button) => {
|
||||||
|
return button.verify(peerId, threadId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateMessageInput(canSend: boolean, placeholderKey: LangPackKey, visible: ChatInput['attachMenuButtons']) {
|
||||||
const {chatInput, attachMenu, messageInput} = this;
|
const {chatInput, attachMenu, messageInput} = this;
|
||||||
const {peerId, threadId} = this.chat;
|
const {peerId, threadId} = this.chat;
|
||||||
const isHidden = chatInput.classList.contains('is-hidden');
|
const isHidden = chatInput.classList.contains('is-hidden');
|
||||||
@ -1387,10 +1406,8 @@ export default class ChatInput {
|
|||||||
|
|
||||||
this.updateMessageInputPlaceholder(placeholderKey);
|
this.updateMessageInputPlaceholder(placeholderKey);
|
||||||
|
|
||||||
const visible = this.attachMenuButtons.filter((button) => {
|
this.attachMenuButtons.forEach((button) => {
|
||||||
const good = button.verify(peerId, threadId);
|
button.element.classList.toggle('hide', !visible.includes(button));
|
||||||
button.element.classList.toggle('hide', !good);
|
|
||||||
return good;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!canSend) {
|
if(!canSend) {
|
||||||
|
@ -333,6 +333,7 @@ class AppSelection extends EventListenerBase<{
|
|||||||
this.appendCheckbox(element, checkboxField);
|
this.appendCheckbox(element, checkboxField);
|
||||||
} else if(hasCheckbox) {
|
} else if(hasCheckbox) {
|
||||||
this.getCheckboxInputFromElement(element).parentElement.remove();
|
this.getCheckboxInputFromElement(element).parentElement.remove();
|
||||||
|
SetTransition(element, 'is-selected', false, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -976,6 +977,7 @@ export default class ChatSelection extends AppSelection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected onCancelSelection = async() => {
|
protected onCancelSelection = async() => {
|
||||||
|
return;
|
||||||
const promises: Promise<HTMLElement>[] = [];
|
const promises: Promise<HTMLElement>[] = [];
|
||||||
for(const [peerId, mids] of this.selectedMids) {
|
for(const [peerId, mids] of this.selectedMids) {
|
||||||
for(const mid of mids) {
|
for(const mid of mids) {
|
||||||
|
@ -141,7 +141,7 @@ export default class ChatSendAs {
|
|||||||
buttons.forEach((button, idx) => {
|
buttons.forEach((button, idx) => {
|
||||||
const peerId = peerIds[idx];
|
const peerId = peerIds[idx];
|
||||||
const avatar = new AvatarElement();
|
const avatar = new AvatarElement();
|
||||||
avatar.classList.add('avatar-32', 'btn-menu-item-icon');
|
avatar.classList.add('avatar-26', 'btn-menu-item-icon');
|
||||||
avatar.updateWithOptions({peerId});
|
avatar.updateWithOptions({peerId});
|
||||||
|
|
||||||
if(!idx) {
|
if(!idx) {
|
||||||
|
@ -294,7 +294,7 @@ export default class PollElement extends HTMLElement {
|
|||||||
setInnerHTML(this.firstElementChild, wrapEmojiText(poll.question));
|
setInnerHTML(this.firstElementChild, wrapEmojiText(poll.question));
|
||||||
|
|
||||||
Array.from(this.querySelectorAll('.poll-answer-text')).forEach((el, idx) => {
|
Array.from(this.querySelectorAll('.poll-answer-text')).forEach((el, idx) => {
|
||||||
setInnerHTML(el, RichTextProcessor.wrapEmojiText(poll.answers[idx].text));
|
setInnerHTML(el, wrapEmojiText(poll.answers[idx].text));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.descDiv = this.firstElementChild.nextElementSibling as HTMLElement;
|
this.descDiv = this.firstElementChild.nextElementSibling as HTMLElement;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { hexToRgb } from "../../helpers/color";
|
||||||
import { Message } from "../../layer";
|
import { Message } from "../../layer";
|
||||||
import getPeerColorById from "../../lib/appManagers/utils/peers/getPeerColorById";
|
import getPeerColorById from "../../lib/appManagers/utils/peers/getPeerColorById";
|
||||||
import ReplyContainer from "../chat/replyContainer";
|
import ReplyContainer from "../chat/replyContainer";
|
||||||
@ -19,8 +20,11 @@ export default function wrapReply(
|
|||||||
|
|
||||||
if(setColorPeerId) {
|
if(setColorPeerId) {
|
||||||
const hex = getPeerColorById(setColorPeerId, false);
|
const hex = getPeerColorById(setColorPeerId, false);
|
||||||
replyContainer.border.style.backgroundColor = hex;
|
const [r, g, b] = hexToRgb(hex);
|
||||||
replyContainer.title.style.color = hex;
|
replyContainer.container.style.setProperty('--override-color', `${r}, ${g}, ${b}`);
|
||||||
|
replyContainer.container.classList.add('is-overriding-color');
|
||||||
|
// replyContainer.border.style.backgroundColor = hex;
|
||||||
|
// replyContainer.title.style.color = hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {container: replyContainer.container, fillPromise};
|
return {container: replyContainer.container, fillPromise};
|
||||||
|
@ -22,7 +22,6 @@ import I18n from './lib/langPack';
|
|||||||
import './helpers/peerIdPolyfill';
|
import './helpers/peerIdPolyfill';
|
||||||
import './lib/polyfill';
|
import './lib/polyfill';
|
||||||
import apiManagerProxy from './lib/mtproto/mtprotoworker';
|
import apiManagerProxy from './lib/mtproto/mtprotoworker';
|
||||||
import loadState from './lib/appManagers/utils/state/loadState';
|
|
||||||
import getProxiedManagers from './lib/appManagers/getProxiedManagers';
|
import getProxiedManagers from './lib/appManagers/getProxiedManagers';
|
||||||
import themeController from './helpers/themeController';
|
import themeController from './helpers/themeController';
|
||||||
import overlayCounter from './helpers/overlayCounter';
|
import overlayCounter from './helpers/overlayCounter';
|
||||||
@ -32,7 +31,6 @@ document.addEventListener('DOMContentLoaded', async() => {
|
|||||||
toggleAttributePolyfill();
|
toggleAttributePolyfill();
|
||||||
|
|
||||||
rootScope.managers = getProxiedManagers();
|
rootScope.managers = getProxiedManagers();
|
||||||
apiManagerProxy;
|
|
||||||
|
|
||||||
singleInstance.start();
|
singleInstance.start();
|
||||||
|
|
||||||
@ -193,7 +191,8 @@ document.addEventListener('DOMContentLoaded', async() => {
|
|||||||
const langPromise = I18n.getCacheLangPack();
|
const langPromise = I18n.getCacheLangPack();
|
||||||
|
|
||||||
const [stateResult, langPack] = await Promise.all([
|
const [stateResult, langPack] = await Promise.all([
|
||||||
loadState(),
|
// loadState(),
|
||||||
|
apiManagerProxy.sendState().then(([stateResult]) => stateResult),
|
||||||
langPromise
|
langPromise
|
||||||
]);
|
]);
|
||||||
I18n.setTimeFormat(stateResult.state.settings.timeFormat);
|
I18n.setTimeFormat(stateResult.state.settings.timeFormat);
|
||||||
|
@ -1842,6 +1842,19 @@ export class AppImManager extends EventListenerBase<{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let peerTitlePromise: Promise<any>;
|
||||||
|
let args: any[];
|
||||||
|
if(peerId.isAnyChat()) {
|
||||||
|
const peerTitle = new PeerTitle();
|
||||||
|
peerTitlePromise = peerTitle.update({peerId: typing.userId.toPeerId(false), onlyFirstName: true});
|
||||||
|
args = [
|
||||||
|
peerTitle.element,
|
||||||
|
typings.length - 1
|
||||||
|
];
|
||||||
|
|
||||||
|
await peerTitlePromise;
|
||||||
|
}
|
||||||
|
|
||||||
if(!container) {
|
if(!container) {
|
||||||
container = document.createElement('span');
|
container = document.createElement('span');
|
||||||
container.classList.add('online', 'peer-typing-container');
|
container.classList.add('online', 'peer-typing-container');
|
||||||
@ -1859,17 +1872,6 @@ export class AppImManager extends EventListenerBase<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let peerTitlePromise: Promise<any>;
|
|
||||||
let args: any[];
|
|
||||||
if(peerId.isAnyChat()) {
|
|
||||||
const peerTitle = new PeerTitle();
|
|
||||||
peerTitlePromise = peerTitle.update({peerId: typing.userId.toPeerId(false), onlyFirstName: true});
|
|
||||||
args = [
|
|
||||||
peerTitle.element,
|
|
||||||
typings.length - 1
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(action._ === 'sendMessageEmojiInteractionSeen') {
|
if(action._ === 'sendMessageEmojiInteractionSeen') {
|
||||||
if(args) {
|
if(args) {
|
||||||
args.pop();
|
args.pop();
|
||||||
@ -1886,10 +1888,6 @@ export class AppImManager extends EventListenerBase<{
|
|||||||
|
|
||||||
if(container.childElementCount > 1) container.lastElementChild.replaceWith(descriptionElement);
|
if(container.childElementCount > 1) container.lastElementChild.replaceWith(descriptionElement);
|
||||||
else container.append(descriptionElement);
|
else container.append(descriptionElement);
|
||||||
|
|
||||||
if(peerTitlePromise) {
|
|
||||||
await peerTitlePromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log('returning typing');
|
// log('returning typing');
|
||||||
return container;
|
return container;
|
||||||
|
@ -54,6 +54,7 @@ export class AppManagersManager {
|
|||||||
const appStoragesManager = new AppStoragesManager();
|
const appStoragesManager = new AppStoragesManager();
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
// new Promise(() => {}),
|
||||||
appStoragesManager.loadStorages(),
|
appStoragesManager.loadStorages(),
|
||||||
this.cryptoPortPromise
|
this.cryptoPortPromise
|
||||||
]);
|
]);
|
||||||
|
@ -369,6 +369,10 @@ export default class IDBStorage<T extends Database<any>, StoreName extends strin
|
|||||||
entryName = [].concat(entryName);
|
entryName = [].concat(entryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!entryName.length) {
|
||||||
|
return Promise.resolve([]) as any;
|
||||||
|
}
|
||||||
|
|
||||||
return this.getObjectStore<T>('readonly', (objectStore) => {
|
return this.getObjectStore<T>('readonly', (objectStore) => {
|
||||||
return (entryName as string[]).map((entryName) => objectStore.get(entryName));
|
return (entryName as string[]).map((entryName) => objectStore.get(entryName));
|
||||||
}, DEBUG ? 'get: ' + entryName.join(', ') : '', storeName);
|
}, DEBUG ? 'get: ' + entryName.join(', ') : '', storeName);
|
||||||
@ -421,7 +425,7 @@ export default class IDBStorage<T extends Database<any>, StoreName extends strin
|
|||||||
// transaction.oncomplete = () => onComplete('transaction');
|
// transaction.oncomplete = () => onComplete('transaction');
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.log.error('transaction not finished', transaction);
|
this.log.error('transaction not finished', transaction, log);
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
/* transaction.addEventListener('abort', (e) => {
|
/* transaction.addEventListener('abort', (e) => {
|
||||||
|
@ -31,11 +31,6 @@ port.addMultipleEventsListeners({
|
|||||||
transportController.waitForWebSocket();
|
transportController.waitForWebSocket();
|
||||||
},
|
},
|
||||||
|
|
||||||
// windowSize: ({width, height}) => {
|
|
||||||
// windowSize.width = width;
|
|
||||||
// windowSize.height = height;
|
|
||||||
// },
|
|
||||||
|
|
||||||
crypto: ({method, args}) => {
|
crypto: ({method, args}) => {
|
||||||
return cryptoWorker.invokeCrypto(method as any, ...args as any);
|
return cryptoWorker.invokeCrypto(method as any, ...args as any);
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,6 @@ type MTProtoBroadcastEvent = {
|
|||||||
|
|
||||||
export default class MTProtoMessagePort<Master extends boolean = true> extends SuperMessagePort<{
|
export default class MTProtoMessagePort<Master extends boolean = true> extends SuperMessagePort<{
|
||||||
environment: (environment: ReturnType<typeof getEnvironment>) => void,
|
environment: (environment: ReturnType<typeof getEnvironment>) => void,
|
||||||
// windowSize: (payload: {width: number, height: number}) => void,
|
|
||||||
crypto: (payload: {method: string, args: any[]}) => Promise<any>,
|
crypto: (payload: {method: string, args: any[]}) => Promise<any>,
|
||||||
state: (payload: {userId: UserId} & Awaited<ReturnType<typeof loadState>> & {storagesResults?: StoragesResults}) => void,
|
state: (payload: {userId: UserId} & Awaited<ReturnType<typeof loadState>> & {storagesResults?: StoragesResults}) => void,
|
||||||
manager: (payload: MTProtoManagerTaskPayload) => any,
|
manager: (payload: MTProtoManagerTaskPayload) => any,
|
||||||
|
@ -172,10 +172,7 @@ class ApiManagerProxy extends MTProtoMessagePort {
|
|||||||
|
|
||||||
this.log('Passing environment:', ENVIRONMENT);
|
this.log('Passing environment:', ENVIRONMENT);
|
||||||
this.invoke('environment', ENVIRONMENT);
|
this.invoke('environment', ENVIRONMENT);
|
||||||
this.sendState();
|
// this.sendState();
|
||||||
// setTimeout(() => {
|
|
||||||
// this.getConfig();
|
|
||||||
// }, 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerServiceWorker() {
|
private registerServiceWorker() {
|
||||||
@ -318,61 +315,38 @@ class ApiManagerProxy extends MTProtoMessagePort {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// protected s() {
|
|
||||||
// const originalPostMessage = this.postMessage;
|
|
||||||
// const postQueue: any[] = [];
|
|
||||||
// this.postMessage = (source, task) => {
|
|
||||||
// if(task.type === 'invoke' && task.payload.type === 'state') {
|
|
||||||
// this.postMessage = originalPostMessage;
|
|
||||||
// postQueue.unshift(task);
|
|
||||||
// postQueue.forEach((task) => this.postMessage(undefined, task));
|
|
||||||
// postQueue.length = 0;
|
|
||||||
// } else {
|
|
||||||
// postQueue.push(task);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
private onWorkerFirstMessage(worker: any) {
|
private onWorkerFirstMessage(worker: any) {
|
||||||
this.log('set webWorker');
|
this.log('set webWorker');
|
||||||
// return;
|
|
||||||
|
|
||||||
this.worker = worker;
|
this.worker = worker;
|
||||||
/// #if MTPROTO_SW
|
/// #if MTPROTO_SW
|
||||||
this.attachSendPort(worker);
|
this.attachSendPort(worker);
|
||||||
/// #else
|
/// #else
|
||||||
this.attachWorkerToPort(worker, this, 'mtproto');
|
this.attachWorkerToPort(worker, this, 'mtproto');
|
||||||
// this.s();
|
|
||||||
// port.addEventListener('message', () => {
|
|
||||||
// this.registerServiceWorker();
|
|
||||||
// }, {once: true});
|
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
// this.startSendingWindowSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public addServiceWorkerTaskListener(name: keyof ApiManagerProxy['taskListenersSW'], callback: ApiManagerProxy['taskListenersSW'][typeof name]) {
|
public addServiceWorkerTaskListener(name: keyof ApiManagerProxy['taskListenersSW'], callback: ApiManagerProxy['taskListenersSW'][typeof name]) {
|
||||||
this.taskListenersSW[name] = callback;
|
this.taskListenersSW[name] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private startSendingWindowSize() {
|
private loadState() {
|
||||||
// const sendWindowSize = () => {
|
|
||||||
// this.invoke('windowSize', {width: windowSize.width, height: windowSize.height});
|
|
||||||
// };
|
|
||||||
|
|
||||||
// mediaSizes.addEventListener('resize', sendWindowSize);
|
|
||||||
// sendWindowSize();
|
|
||||||
// }
|
|
||||||
|
|
||||||
private sendState() {
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
loadState(),
|
loadState().then((stateResult) => {
|
||||||
|
this.newVersion = stateResult.newVersion;
|
||||||
|
this.oldVersion = stateResult.oldVersion;
|
||||||
|
this.mirrors['state'] = stateResult.state;
|
||||||
|
return stateResult;
|
||||||
|
}),
|
||||||
// loadStorages(createStorages()),
|
// loadStorages(createStorages()),
|
||||||
]).then(([stateResult/* , storagesResults */]) => {
|
]);
|
||||||
this.newVersion = stateResult.newVersion;
|
}
|
||||||
this.oldVersion = stateResult.oldVersion;
|
|
||||||
this.mirrors['state'] = stateResult.state;
|
public sendState() {
|
||||||
|
return this.loadState().then((result) => {
|
||||||
|
const [stateResult] = result;
|
||||||
this.invoke('state', {...stateResult, userId: rootScope.myId.toUserId()});
|
this.invoke('state', {...stateResult, userId: rootScope.myId.toUserId()});
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,8 +304,8 @@ export default function wrapRichText(text: string, options: Partial<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
const href = (currentContext || typeof electronHelpers === 'undefined')
|
const href = (currentContext || typeof electronHelpers === 'undefined')
|
||||||
? encodeEntities(url)
|
? url
|
||||||
: `javascript:electronHelpers.openExternal('${encodeEntities(url)}');`;
|
: `javascript:electronHelpers.openExternal('${url}');`;
|
||||||
|
|
||||||
element = document.createElement('a');
|
element = document.createElement('a');
|
||||||
element.className = 'anchor-url';
|
element.className = 'anchor-url';
|
||||||
|
@ -219,6 +219,11 @@ avatar-element {
|
|||||||
--multiplier: 2.25;
|
--multiplier: 2.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.avatar-22 {
|
||||||
|
--size: 22px;
|
||||||
|
--multiplier: 2.454545;
|
||||||
|
}
|
||||||
|
|
||||||
&.avatar-18 {
|
&.avatar-18 {
|
||||||
--size: 18px;
|
--size: 18px;
|
||||||
--multiplier: 3;
|
--multiplier: 3;
|
||||||
|
@ -296,7 +296,7 @@
|
|||||||
.stacked-avatars {
|
.stacked-avatars {
|
||||||
--margin-right: -.6875rem;
|
--margin-right: -.6875rem;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
right: 1rem;
|
right: .5rem;
|
||||||
// margin-right: -1.5rem;
|
// margin-right: -1.5rem;
|
||||||
// margin-left: 1rem;
|
// margin-left: 1rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -2359,6 +2359,20 @@ $bubble-beside-button-width: 38px;
|
|||||||
code {
|
code {
|
||||||
color: var(--monospace-text-color);
|
color: var(--monospace-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reply.is-overriding-color {
|
||||||
|
.reply-border {
|
||||||
|
background-color: rgb(var(--override-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-title {
|
||||||
|
color: rgb(var(--override-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
@include hover() {
|
||||||
|
background-color: rgba(var(--override-color), #{$hover-alpha});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bubble.is-out {
|
.bubble.is-out {
|
||||||
|
@ -94,9 +94,12 @@ html:not(.is-safari):not(.is-ios) {
|
|||||||
overflow-y: overlay;
|
overflow-y: overlay;
|
||||||
scrollbar-width: thin; // Firefox only
|
scrollbar-width: thin; // Firefox only
|
||||||
scrollbar-color: rgba(0, 0, 0, 0) rgba(0, 0, 0, 0);
|
scrollbar-color: rgba(0, 0, 0, 0) rgba(0, 0, 0, 0);
|
||||||
transition: scrollbar-color .3s ease;
|
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
|
||||||
|
// html.is-firefox & {
|
||||||
|
// transition: scrollbar-color .3s ease;
|
||||||
|
// }
|
||||||
|
|
||||||
/* html.is-safari & {
|
/* html.is-safari & {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user