Browse Source

'Send as...' menu

New profile design
Return back to main list by Escape
Fix filters menu ripple
master
Eduard Kuzmenko 3 years ago
parent
commit
cc6084dd72
  1. 15
      src/components/appNavigationController.ts
  2. 2
      src/components/buttonMenu.ts
  3. 19
      src/components/buttonMenuToggle.ts
  4. 12
      src/components/chat/chat.ts
  5. 4
      src/components/chat/inlineHelper.ts
  6. 257
      src/components/chat/input.ts
  7. 2
      src/components/chat/topbar.ts
  8. 150
      src/components/peerProfile.ts
  9. 5
      src/components/popups/createPoll.ts
  10. 13
      src/components/popups/newMedia.ts
  11. 23
      src/components/row.ts
  12. 21
      src/components/sidebarLeft/index.ts
  13. 6
      src/components/sidebarLeft/tabs/activeSessions.ts
  14. 4
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  15. 167
      src/components/sidebarLeft/tabs/settings.ts
  16. 6
      src/lang.ts
  17. 17
      src/lib/appManagers/appChatsManager.ts
  18. 24
      src/lib/appManagers/appDialogsManager.ts
  19. 8
      src/lib/appManagers/appImManager.ts
  20. 1
      src/lib/appManagers/appInlineBotsManager.ts
  21. 40
      src/lib/appManagers/appMessagesManager.ts
  22. 17
      src/lib/appManagers/appProfileManager.ts
  23. 2
      src/lib/rootScope.ts
  24. 133
      src/scripts/out/langPack.strings
  25. 3
      src/scss/partials/_avatar.scss
  26. 16
      src/scss/partials/_button.scss
  27. 134
      src/scss/partials/_chat.scss
  28. 27
      src/scss/partials/_leftSidebar.scss
  29. 19
      src/scss/partials/_profile.scss
  30. 4
      src/scss/partials/_row.scss
  31. 1
      src/scss/partials/_slider.scss

15
src/components/appNavigationController.ts

@ -16,7 +16,7 @@ import isSwipingBackSafari from "../helpers/dom/isSwipingBackSafari"; @@ -16,7 +16,7 @@ import isSwipingBackSafari from "../helpers/dom/isSwipingBackSafari";
export type NavigationItem = {
type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' |
'esg' | 'multiselect' | 'input-helper' | 'autocomplete-helper' | 'markup' |
'global-search' | 'voice' | 'mobile-search',
'global-search' | 'voice' | 'mobile-search' | 'filters',
onPop: (canAnimate: boolean) => boolean | void,
onEscape?: () => boolean,
noHistory?: boolean,
@ -169,8 +169,7 @@ export class AppNavigationController { @@ -169,8 +169,7 @@ export class AppNavigationController {
//}
}
public pushItem(item: NavigationItem) {
this.navigations.push(item);
private onItemAdded(item: NavigationItem) {
this.debug && this.log('pushstate', item, this.navigations);
if(!item.noHistory) {
@ -178,6 +177,16 @@ export class AppNavigationController { @@ -178,6 +177,16 @@ export class AppNavigationController {
}
}
public pushItem(item: NavigationItem) {
this.navigations.push(item);
this.onItemAdded(item);
}
public unshiftItem(item: NavigationItem) {
this.navigations.unshift(item);
this.onItemAdded(item);
}
private pushState() {
this.manual = false;
history.pushState(this.id, '');

2
src/components/buttonMenu.ts

@ -47,7 +47,7 @@ const ButtonMenuItem = (options: ButtonMenuItemOptions) => { @@ -47,7 +47,7 @@ const ButtonMenuItem = (options: ButtonMenuItemOptions) => {
const keepOpen = !!checkboxField || !!options.keepOpen;
// * cancel mobile keyboard close
attachClickEvent(el, /* CLICK_EVENT_NAME !== 'click' || keepOpen ? */ (e) => {
onClick && attachClickEvent(el, /* CLICK_EVENT_NAME !== 'click' || keepOpen ? */ (e) => {
cancelEvent(e);
const result = onClick(e);

19
src/components/buttonMenuToggle.ts

@ -11,13 +11,26 @@ import ButtonIcon from "./buttonIcon"; @@ -11,13 +11,26 @@ import ButtonIcon from "./buttonIcon";
import ButtonMenu, { ButtonMenuItemOptions } from "./buttonMenu";
import { closeBtnMenu, openBtnMenu } from "./misc";
const ButtonMenuToggle = (options: Partial<{noRipple: true, onlyMobile: true, listenerSetter: ListenerSetter, asDiv: boolean}> = {}, direction: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right', buttons: ButtonMenuItemOptions[], onOpen?: (e: Event) => void) => {
const ButtonMenuToggle = (
options: Partial<{
noRipple: true,
onlyMobile: true,
listenerSetter: ListenerSetter,
asDiv: boolean,
container: HTMLElement
}> = {},
direction: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right',
buttons: ButtonMenuItemOptions[],
onOpen?: (e: Event) => void,
onClose?: () => void
) => {
options.asDiv = true;
const button = ButtonIcon('more btn-menu-toggle', options);
const button = options.container ?? ButtonIcon('more', options);
button.classList.add('btn-menu-toggle');
const btnMenu = ButtonMenu(buttons, options.listenerSetter);
btnMenu.classList.add(direction);
ButtonMenuToggleHandler(button, onOpen, options);
ButtonMenuToggleHandler(button, onOpen, options, onClose);
button.append(btnMenu);
return button;
};

12
src/components/chat/chat.ts

@ -190,7 +190,7 @@ export default class Chat extends EventListenerBase<{ @@ -190,7 +190,7 @@ export default class Chat extends EventListenerBase<{
this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager, this.appNotificationsManager, this.appProfileManager, this.appUsersManager, this.appGroupCallsManager);
this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appPeersManager, this.appProfileManager, this.appDraftsManager, this.appMessagesIdsManager, this.appChatsManager, this.appReactionsManager);
this.input = new ChatInput(this, this.appMessagesManager, this.appMessagesIdsManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager, this.appNotificationsManager, this.appEmojiManager, this.appUsersManager, this.appInlineBotsManager);
this.input = new ChatInput(this, this.appMessagesManager, this.appMessagesIdsManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager, this.appNotificationsManager, this.appEmojiManager, this.appUsersManager, this.appInlineBotsManager, this.appProfileManager);
this.selection = new ChatSelection(this, this.bubbles, this.input, this.appMessagesManager);
this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appPeersManager, this.appPollsManager, this.appDocsManager, this.appMessagesIdsManager, this.appReactionsManager);
@ -425,4 +425,14 @@ export default class Chat extends EventListenerBase<{ @@ -425,4 +425,14 @@ export default class Chat extends EventListenerBase<{
!this.appMessagesManager.getDialogOnly(this.peerId) &&
!this.appMessagesManager.getHistoryStorage(this.peerId).history.length;
}
public getMessageSendingParams() {
return {
threadId: this.threadId,
replyToMsgId: this.input.replyToMsgId,
scheduleDate: this.input.scheduleDate,
sendSilent: this.input.sendSilent,
sendAsPeerId: this.input.sendAsPeerId
};
}
}

4
src/components/chat/inlineHelper.ts

@ -53,10 +53,8 @@ export default class InlineHelper extends AutocompleteHelper { @@ -53,10 +53,8 @@ export default class InlineHelper extends AutocompleteHelper {
return this.chat.input.getReadyToSend(() => {
const queryAndResultIds = this.appInlineBotsManager.generateQId(queryId, (target as HTMLElement).dataset.resultId);
this.appInlineBotsManager.sendInlineResult(peerId.toPeerId(), botId, queryAndResultIds, {
...this.chat.getMessageSendingParams(),
clearDraft: true,
scheduleDate: this.chat.input.scheduleDate,
silent: this.chat.input.sendSilent,
replyToMsgId: this.chat.input.replyToMsgId
});
this.chat.input.onMessageSent(true, true);

257
src/components/chat/input.ts

@ -32,7 +32,7 @@ import PopupNewMedia from '../popups/newMedia'; @@ -32,7 +32,7 @@ import PopupNewMedia from '../popups/newMedia';
import { toast } from "../toast";
import { wrapReply } from "../wrappers";
import InputField from '../inputField';
import { MessageEntity, DraftMessage, WebPage, Message } from '../../layer';
import { MessageEntity, DraftMessage, WebPage, Message, ChatFull } from '../../layer';
import StickersHelper from './stickersHelper';
import ButtonIcon from '../buttonIcon';
import ButtonMenuToggle from '../buttonMenuToggle';
@ -87,10 +87,16 @@ import DropdownHover from '../../helpers/dropdownHover'; @@ -87,10 +87,16 @@ import DropdownHover from '../../helpers/dropdownHover';
import RadioForm from '../radioForm';
import findUpTag from '../../helpers/dom/findUpTag';
import toggleDisability from '../../helpers/dom/toggleDisability';
import AvatarElement from '../avatar';
import type { AppProfileManager } from '../../lib/appManagers/appProfileManager';
import { indexOfAndSplice } from '../../helpers/array';
import callbackify from '../../helpers/callbackify';
const RECORD_MIN_TIME = 500;
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
const SEND_AS_ANIMATION_DURATION = 300;
type ChatInputHelperType = 'edit' | 'webpage' | 'forward' | 'reply';
export default class ChatInput {
@ -115,7 +121,7 @@ export default class ChatInput { @@ -115,7 +121,7 @@ export default class ChatInput {
private replyKeyboard: ReplyKeyboard;
private attachMenu: HTMLButtonElement;
private attachMenu: HTMLElement;
private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerId: PeerId, threadId: number) => boolean})[];
private sendMenu: SendMenu;
@ -208,9 +214,17 @@ export default class ChatInput { @@ -208,9 +214,17 @@ export default class ChatInput {
private fakeWrapperTo: HTMLElement;
private toggleBotStartBtnDisability: () => void;
private sendAsAvatar: AvatarElement;
private sendAsContainer: HTMLElement;
private sendAsCloseBtn: HTMLElement;
private sendAsBtnMenu: HTMLElement;
private sendAsPeerIds: PeerId[];
public sendAsPeerId: PeerId;
// private activeContainer: HTMLElement;
constructor(private chat: Chat,
constructor(
private chat: Chat,
private appMessagesManager: AppMessagesManager,
private appMessagesIdsManager: AppMessagesIdsManager,
private appDocsManager: AppDocsManager,
@ -223,7 +237,8 @@ export default class ChatInput { @@ -223,7 +237,8 @@ export default class ChatInput {
private appNotificationsManager: AppNotificationsManager,
private appEmojiManager: AppEmojiManager,
private appUsersManager: AppUsersManager,
private appInlineBotsManager: AppInlineBotsManager
private appInlineBotsManager: AppInlineBotsManager,
private appProfileManager: AppProfileManager
) {
this.listenerSetter = new ListenerSetter();
}
@ -461,6 +476,46 @@ export default class ChatInput { @@ -461,6 +476,46 @@ export default class ChatInput {
this.newMessageWrapper = document.createElement('div');
this.newMessageWrapper.classList.add('new-message-wrapper');
this.sendAsContainer = document.createElement('div');
this.sendAsContainer.classList.add('new-message-send-as-container');
this.sendAsCloseBtn = document.createElement('div');
this.sendAsCloseBtn.classList.add('new-message-send-as-close', 'new-message-send-as-avatar', 'tgico-close');
const sendAsButtons: ButtonMenuItemOptions[] = [{
text: 'SendMessageAsTitle',
onClick: undefined
}];
let previousAvatar: HTMLElement;
const onSendAsMenuToggle = (visible: boolean) => {
if(visible) {
previousAvatar = this.sendAsAvatar;
}
const isChanged = this.sendAsAvatar !== previousAvatar;
const useRafs = !visible && isChanged ? 2 : 0;
SetTransition(this.sendAsCloseBtn, 'is-visible', visible, SEND_AS_ANIMATION_DURATION, undefined, useRafs);
if(!isChanged) {
SetTransition(previousAvatar, 'is-visible', !visible, SEND_AS_ANIMATION_DURATION, undefined, useRafs);
}
};
ButtonMenuToggle({
noRipple: true,
listenerSetter: this.listenerSetter,
container: this.sendAsContainer
}, 'top-right', sendAsButtons, () => {
onSendAsMenuToggle(true);
}, () => {
onSendAsMenuToggle(false);
});
sendAsButtons[0].element.classList.add('btn-menu-item-header');
this.sendAsBtnMenu = this.sendAsContainer.firstElementChild as any;
this.sendAsContainer.append(this.sendAsCloseBtn);
this.btnToggleEmoticons = ButtonIcon('none toggle-emoticons', {noRipple: true});
this.inputMessageContainer = document.createElement('div');
@ -562,7 +617,7 @@ export default class ChatInput { @@ -562,7 +617,7 @@ export default class ChatInput {
this.fileInput.multiple = true;
this.fileInput.style.display = 'none';
this.newMessageWrapper.append(...[this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.btnToggleReplyMarkup, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean));
this.newMessageWrapper.append(...[this.sendAsContainer, this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.btnToggleReplyMarkup, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean));
this.rowsWrapper.append(this.replyElements.container);
this.autocompleteHelperController = new AutocompleteHelperController();
@ -664,6 +719,14 @@ export default class ChatInput { @@ -664,6 +719,14 @@ export default class ChatInput {
}
});
if(this.sendAsContainer) {
this.listenerSetter.add(rootScope)('peer_full_update', (peerId) => {
if(peerId.isChannel() && this.chat.peerId === peerId) {
this.updateSendAs();
}
});
}
if(this.chat.type === 'scheduled') {
this.listenerSetter.add(rootScope)('scheduled_delete', ({peerId, mids}) => {
if(this.chat.peerId === peerId && mids.includes(this.editMsgId)) {
@ -1129,7 +1192,7 @@ export default class ChatInput { @@ -1129,7 +1192,7 @@ export default class ChatInput {
public finishPeerChange(startParam?: string) {
const peerId = this.chat.peerId;
const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput} = this;
const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput, sendAsContainer} = this;
chatInput.style.display = '';
const isBroadcast = this.appPeersManager.isBroadcast(peerId);
@ -1160,6 +1223,19 @@ export default class ChatInput { @@ -1160,6 +1223,19 @@ export default class ChatInput {
});
}
if(sendAsContainer) {
if(this.sendAsAvatar) {
this.sendAsAvatar.remove();
this.sendAsAvatar = undefined;
}
sendAsContainer.remove();
SetTransition(this.newMessageWrapper, 'has-send-as', false, 0);
this.sendAsPeerId = undefined;
this.updateSendAs(true);
}
if(replyKeyboard) {
replyKeyboard.setPeer(peerId);
}
@ -1182,6 +1258,157 @@ export default class ChatInput { @@ -1182,6 +1258,157 @@ export default class ChatInput {
this.center(false);
}
private updateSendAsButtons(peerIds: PeerId[]) {
const buttons: ButtonMenuItemOptions[] = peerIds.map((sendAsPeerId, idx) => {
const textElement = document.createElement('div');
const subtitle = document.createElement('div');
subtitle.classList.add('btn-menu-item-subtitle');
if(sendAsPeerId.isUser()) {
subtitle.append(i18n('Chat.SendAs.PersonalAccount'));
} else {
subtitle.append(this.appProfileManager.getChatMembersString(sendAsPeerId.toChatId()));
}
textElement.append(
new PeerTitle({peerId: sendAsPeerId}).element,
subtitle
);
return {
onClick: idx ? () => {
const currentPeerId = this.chat.peerId;
if(currentPeerId.isChannel()) {
const channelFull = this.appProfileManager.getCachedFullChat(currentPeerId.toChatId()) as ChatFull.channelFull;
if(channelFull) {
channelFull.default_send_as = this.appPeersManager.getOutputPeer(sendAsPeerId);
this.sendAsPeerId = sendAsPeerId;
this.updateSendAsAvatar(sendAsPeerId);
const middleware = this.chat.bubbles.getMiddleware();
const executeButtonsUpdate = () => {
if(this.sendAsPeerId !== sendAsPeerId || !middleware()) return;
const peerIds = this.sendAsPeerIds.slice();
indexOfAndSplice(peerIds, sendAsPeerId);
peerIds.unshift(sendAsPeerId);
this.updateSendAsButtons(peerIds);
};
if(rootScope.settings.animationsEnabled) {
setTimeout(executeButtonsUpdate, 250);
} else {
executeButtonsUpdate();
}
}
}
// return;
apiManager.invokeApi('messages.saveDefaultSendAs', {
peer: this.appPeersManager.getInputPeerById(currentPeerId),
send_as: this.appPeersManager.getInputPeerById(sendAsPeerId)
});
} : undefined,
textElement
};
});
const btnMenu = ButtonMenu(buttons/* , this.listenerSetter */);
buttons.forEach((button, idx) => {
const peerId = peerIds[idx];
const avatar = new AvatarElement();
avatar.classList.add('avatar-32', 'btn-menu-item-icon');
avatar.setAttribute('peer', '' + peerId);
if(!idx) {
avatar.classList.add('active');
}
button.element.prepend(avatar);
});
Array.from(this.sendAsBtnMenu.children).slice(1).forEach(node => node.remove());
this.sendAsBtnMenu.append(...Array.from(btnMenu.children));
}
private updateSendAsAvatar(sendAsPeerId: PeerId, skipAnimation?: boolean) {
const previousAvatar = this.sendAsAvatar;
if(previousAvatar) {
if(+previousAvatar.getAttribute('peer') === sendAsPeerId) {
return;
}
}
if(!previousAvatar) {
skipAnimation = true;
}
let useRafs = skipAnimation ? 0 : 2;
const duration = skipAnimation ? 0 : SEND_AS_ANIMATION_DURATION;
const avatar = this.sendAsAvatar = new AvatarElement();
avatar.setAttribute('dialog', '0');
avatar.setAttribute('peer', '' + sendAsPeerId);
avatar.classList.add('new-message-send-as-avatar', 'avatar-30');
SetTransition(avatar, 'is-visible', true, duration, undefined, useRafs);
if(previousAvatar) {
SetTransition(previousAvatar, 'is-visible', false, duration, () => {
previousAvatar.remove();
}, useRafs);
}
this.sendAsContainer.append(avatar);
}
private getDefaultSendAs() {
// return rootScope.myId;
return callbackify(this.appProfileManager.getChannelFull(this.chat.peerId.toChatId()), (channelFull) => {
return channelFull.default_send_as ? this.appPeersManager.getPeerId(channelFull.default_send_as) : undefined;
});
}
private updateSendAs(skipAnimation?: boolean) {
const peerId = this.chat.peerId;
if(!peerId.isChannel()) {
return;
}
const middleware = this.chat.bubbles.getMiddleware();
const {sendAsContainer} = this;
const chatId = peerId.toChatId();
const result = this.getDefaultSendAs();
// const result = Promise.resolve(this.getDefaultSendAs());
if(result instanceof Promise) {
skipAnimation = undefined;
}
callbackify(result, (sendAsPeerId) => {
if(!middleware() || sendAsPeerId === undefined) return;
this.sendAsPeerId = sendAsPeerId;
this.updateSendAsAvatar(sendAsPeerId, skipAnimation);
this.appChatsManager.getSendAs(chatId).then(peers => {
if(!middleware()) return;
const peerIds = peers.map((peer) => this.appPeersManager.getPeerId(peer));
this.sendAsPeerIds = peerIds.slice();
indexOfAndSplice(peerIds, sendAsPeerId);
peerIds.unshift(sendAsPeerId);
this.updateSendAsButtons(peerIds);
});
let useRafs = 0;
if(!sendAsContainer.parentElement) {
this.newMessageWrapper.prepend(sendAsContainer);
useRafs = 2;
}
SetTransition(this.newMessageWrapper, 'has-send-as', true, skipAnimation ? 0 : SEND_AS_ANIMATION_DURATION, undefined, useRafs);
});
}
public updateMessageInput() {
const {chatInput, attachMenu, messageInput} = this;
const {peerId, threadId} = this.chat;
@ -2128,8 +2355,9 @@ export default class ChatInput { @@ -2128,8 +2355,9 @@ export default class ChatInput {
return;
}
const {threadId, peerId} = chat;
const {replyToMsgId, noWebPage, sendSilent, scheduleDate} = this;
const {peerId} = chat;
const {noWebPage} = this;
const sendingParams = this.chat.getMessageSendingParams();
const {value, entities} = getRichValue(this.messageInputField.input);
@ -2151,12 +2379,9 @@ export default class ChatInput { @@ -2151,12 +2379,9 @@ export default class ChatInput {
} else if(value.trim()) {
this.appMessagesManager.sendText(peerId, value, {
entities,
replyToMsgId: replyToMsgId,
threadId: threadId,
...sendingParams,
noWebPage: noWebPage,
webPage: this.getWebPagePromise ? undefined : this.willSendWebPage,
scheduleDate: scheduleDate,
silent: sendSilent,
clearDraft: true
});
@ -2170,8 +2395,7 @@ export default class ChatInput { @@ -2170,8 +2395,7 @@ export default class ChatInput {
setTimeout(() => {
for(const fromPeerId in forwarding) {
this.appMessagesManager.forwardMessages(peerId, fromPeerId.toPeerId(), forwarding[fromPeerId], {
silent: sendSilent,
scheduleDate: scheduleDate,
...sendingParams,
dropAuthor: this.forwardElements && this.forwardElements.hideSender.checkboxField.checked,
dropCaptions: this.isDroppingCaptions()
});
@ -2202,11 +2426,8 @@ export default class ChatInput { @@ -2202,11 +2426,8 @@ export default class ChatInput {
if(document) {
this.appMessagesManager.sendFile(this.chat.peerId, document, {
...this.chat.getMessageSendingParams(),
isMedia: true,
replyToMsgId: this.replyToMsgId,
threadId: this.chat.threadId,
silent: this.sendSilent,
scheduleDate: this.scheduleDate,
clearDraft: clearDraft || undefined
});
this.onMessageSent(clearDraft, true);

2
src/components/chat/topbar.ts

@ -67,7 +67,7 @@ export default class ChatTopbar { @@ -67,7 +67,7 @@ export default class ChatTopbar {
private btnGroupCall: HTMLButtonElement;
private btnMute: HTMLButtonElement;
private btnSearch: HTMLButtonElement;
private btnMore: HTMLButtonElement;
private btnMore: HTMLElement;
private chatAudio: ChatAudio;
public pinnedMessage: ChatPinnedMessage;

150
src/components/peerProfile.ts

@ -5,8 +5,10 @@ @@ -5,8 +5,10 @@
*/
import IS_PARALLAX_SUPPORTED from "../environment/parallaxSupport";
import callbackify from "../helpers/callbackify";
import { copyTextToClipboard } from "../helpers/clipboard";
import replaceContent from "../helpers/dom/replaceContent";
import ListenerSetter from "../helpers/listenerSetter";
import { fastRaf } from "../helpers/schedulers";
import { ChatFull, User } from "../layer";
import { Channel } from "../lib/appManagers/appChatsManager";
@ -38,7 +40,7 @@ let setText = (text: string, row: Row) => { @@ -38,7 +40,7 @@ let setText = (text: string, row: Row) => {
export default class PeerProfile {
public element: HTMLElement;
public avatars: PeerProfileAvatars;
private avatars: PeerProfileAvatars;
private avatar: AvatarElement;
private section: SettingSection;
private name: HTMLDivElement;
@ -48,6 +50,7 @@ export default class PeerProfile { @@ -48,6 +50,7 @@ export default class PeerProfile {
private phone: Row;
private notifications: Row;
private location: Row;
private link: Row;
private cleaned: boolean;
private setMoreDetailsTimeout: number;
@ -56,10 +59,18 @@ export default class PeerProfile { @@ -56,10 +59,18 @@ export default class PeerProfile {
private peerId: PeerId;
private threadId: number;
constructor(public scrollable: Scrollable) {
constructor(
public scrollable: Scrollable,
private listenerSetter?: ListenerSetter,
private isDialog = true
) {
if(!IS_PARALLAX_SUPPORTED) {
this.scrollable.container.classList.add('no-parallax');
}
if(!listenerSetter) {
this.listenerSetter = new ListenerSetter();
}
}
public init() {
@ -75,7 +86,7 @@ export default class PeerProfile { @@ -75,7 +86,7 @@ export default class PeerProfile {
this.avatar = new AvatarElement();
this.avatar.classList.add('profile-avatar', 'avatar-120');
this.avatar.setAttribute('dialog', '1');
this.avatar.setAttribute('dialog', '' + +this.isDialog);
this.avatar.setAttribute('clickable', '');
this.name = document.createElement('div');
@ -124,68 +135,85 @@ export default class PeerProfile { @@ -124,68 +135,85 @@ export default class PeerProfile {
}
});
this.link = new Row({
title: ' ',
subtitleLangKey: 'SetUrlPlaceholder',
icon: 'link',
clickable: () => {
Promise.resolve(appProfileManager.getChatFull(this.peerId.toChatId())).then(chatFull => {
copyTextToClipboard(chatFull.exported_invite.link);
toast(I18n.format('LinkCopied', true));
});
}
});
this.location = new Row({
title: ' ',
subtitleLangKey: 'ChatLocation',
icon: 'location'
});
this.notifications = new Row({
checkboxField: new CheckboxField({toggle: true}),
titleLangKey: 'Notifications',
icon: 'unmute'
});
this.section.content.append(
this.phone.container,
this.username.container,
this.location.container,
this.bio.container,
this.notifications.container
this.link.container
);
const {listenerSetter} = this;
if(this.isDialog) {
this.notifications = new Row({
checkboxField: new CheckboxField({toggle: true}),
titleLangKey: 'Notifications',
icon: 'unmute'
});
listenerSetter.add(this.notifications.checkboxField.input)('change', (e) => {
if(!e.isTrusted) {
return;
}
//let checked = this.notificationsCheckbox.checked;
appMessagesManager.togglePeerMute(this.peerId);
});
listenerSetter.add(rootScope)('dialog_notify_settings', (dialog) => {
if(this.peerId === dialog.peerId) {
const muted = appNotificationsManager.isPeerLocalMuted(this.peerId, false);
this.notifications.checkboxField.checked = !muted;
}
});
this.section.content.append(this.notifications.container);
}
this.element.append(this.section.container);
if(IS_PARALLAX_SUPPORTED) {
this.element.append(generateDelimiter());
}
this.notifications.checkboxField.input.addEventListener('change', (e) => {
if(!e.isTrusted) {
return;
}
//let checked = this.notificationsCheckbox.checked;
appMessagesManager.togglePeerMute(this.peerId);
});
rootScope.addEventListener('dialog_notify_settings', (dialog) => {
if(this.peerId === dialog.peerId) {
const muted = appNotificationsManager.isPeerLocalMuted(this.peerId, false);
this.notifications.checkboxField.checked = !muted;
}
});
rootScope.addEventListener('peer_typings', ({peerId}) => {
listenerSetter.add(rootScope)('peer_typings', ({peerId}) => {
if(this.peerId === peerId) {
this.setPeerStatus();
}
});
rootScope.addEventListener('peer_bio_edit', (peerId) => {
listenerSetter.add(rootScope)('peer_bio_edit', (peerId) => {
if(peerId === this.peerId) {
this.setMoreDetails(true);
}
});
rootScope.addEventListener('user_update', (userId) => {
if(this.peerId === userId) {
listenerSetter.add(rootScope)('user_update', (userId) => {
if(this.peerId === userId.toPeerId()) {
this.setPeerStatus();
}
});
rootScope.addEventListener('contacts_update', (userId) => {
if(this.peerId === userId) {
listenerSetter.add(rootScope)('contacts_update', (userId) => {
if(this.peerId === userId.toPeerId()) {
const user = appUsersManager.getUser(userId);
if(!user.pFlags.self) {
if(user.phone) {
@ -204,24 +232,37 @@ export default class PeerProfile { @@ -204,24 +232,37 @@ export default class PeerProfile {
if(!this.peerId) return;
const peerId = this.peerId;
appImManager.setPeerStatus(this.peerId, this.subtitle, needClear, true, () => peerId === this.peerId);
appImManager.setPeerStatus(this.peerId, this.subtitle, needClear, true, () => peerId === this.peerId, !this.isDialog);
};
public cleanupHTML() {
this.bio.container.style.display = 'none';
this.phone.container.style.display = 'none';
this.username.container.style.display = 'none';
this.location.container.style.display = 'none';
this.notifications.container.style.display = '';
this.notifications.checkboxField.checked = true;
[
this.bio,
this.phone,
this.username,
this.location,
this.link
].forEach(row => {
row.container.style.display = 'none';
});
if(this.notifications) {
this.notifications.container.style.display = '';
this.notifications.checkboxField.checked = true;
}
if(this.setMoreDetailsTimeout) {
window.clearTimeout(this.setMoreDetailsTimeout);
this.setMoreDetailsTimeout = 0;
}
}
private canBeDetailed() {
return this.peerId !== rootScope.myId || !this.isDialog;
}
public setAvatar() {
if(this.peerId !== rootScope.myId) {
if(this.canBeDetailed()) {
const photo = appPeersManager.getPeerPhoto(this.peerId);
if(photo) {
@ -268,15 +309,17 @@ export default class PeerProfile { @@ -268,15 +309,17 @@ export default class PeerProfile {
this.setAvatar();
// username
if(peerId !== rootScope.myId) {
if(this.canBeDetailed()) {
let username = appPeersManager.getPeerUsername(peerId);
if(username) {
setText(appPeersManager.getPeerUsername(peerId), this.username);
}
const muted = appNotificationsManager.isPeerLocalMuted(peerId, false);
this.notifications.checkboxField.checked = !muted;
} else {
if(this.notifications) {
const muted = appNotificationsManager.isPeerLocalMuted(peerId, false);
this.notifications.checkboxField.checked = !muted;
}
} else if(this.notifications) {
fastRaf(() => {
this.notifications.container.style.display = 'none';
});
@ -287,7 +330,7 @@ export default class PeerProfile { @@ -287,7 +330,7 @@ export default class PeerProfile {
//membersLi.style.display = 'none';
let user = appUsersManager.getUser(peerId);
if(user.phone && peerId !== rootScope.myId) {
if(user.phone && this.canBeDetailed()) {
setText(appUsersManager.formatUserPhone(user.phone), this.phone);
}
}/* else {
@ -298,7 +341,7 @@ export default class PeerProfile { @@ -298,7 +341,7 @@ export default class PeerProfile {
replaceContent(this.name, new PeerTitle({
peerId,
dialog: true,
dialog: this.isDialog,
}).element);
const peer = appPeersManager.getPeer(peerId);
@ -318,11 +361,11 @@ export default class PeerProfile { @@ -318,11 +361,11 @@ export default class PeerProfile {
const peerId = this.peerId;
const threadId = this.threadId;
if(!peerId || appPeersManager.isRestricted(peerId) || peerId === rootScope.myId) {
if(!peerId || appPeersManager.isRestricted(peerId) || !this.canBeDetailed()) {
return;
}
Promise.resolve(appProfileManager.getProfileByPeerId(peerId, override)).then((peerFull) => {
callbackify(appProfileManager.getProfileByPeerId(peerId, override), (peerFull) => {
if(this.peerId !== peerId || this.threadId !== threadId || appPeersManager.isRestricted(peerId)) {
//this.log.warn('peer changed');
return;
@ -334,9 +377,14 @@ export default class PeerProfile { @@ -334,9 +377,14 @@ export default class PeerProfile {
setText(RichTextProcessor.wrapRichText(peerFull.about), this.bio);
}
if((peerFull as ChatFull.channelFull)?.location?._ == 'channelLocation') {
// @ts-ignore
setText(chatFull.location.address, this.location);
const exportedInvite = (peerFull as ChatFull.channelFull).exported_invite;
if(exportedInvite) {
setText(exportedInvite.link, this.link);
}
const location = (peerFull as ChatFull.channelFull).location;
if(location?._ == 'channelLocation') {
setText(location.address, this.location);
}
this.setMoreDetailsTimeout = window.setTimeout(() => this.setMoreDetails(true), 60e3);

5
src/components/popups/createPoll.ts

@ -286,10 +286,7 @@ export default class PopupCreatePoll extends PopupElement { @@ -286,10 +286,7 @@ export default class PopupCreatePoll extends PopupElement {
//console.log('Will try to create poll:', inputMediaPoll);
this.chat.appMessagesManager.sendOther(this.chat.peerId, inputMediaPoll, {
threadId: this.chat.threadId,
replyToMsgId: this.chat.input.replyToMsgId,
scheduleDate: this.chat.input.scheduleDate,
silent: this.chat.input.sendSilent
...this.chat.getMessageSendingParams()
});
if(this.chat.input.helperType === 'reply') {

13
src/components/popups/newMedia.ts

@ -232,21 +232,17 @@ export default class PopupNewMedia extends PopupElement { @@ -232,21 +232,17 @@ export default class PopupNewMedia extends PopupElement {
//console.log('will send files with options:', willAttach);
const {peerId, input} = this.chat;
const {sendSilent, scheduleDate} = input;
sendFileDetails.forEach(d => {
d.itemDiv = undefined;
});
const {length} = sendFileDetails;
const replyToMsgId = input.replyToMsgId;
const sendingParams = this.chat.getMessageSendingParams();
this.iterate((sendFileDetails) => {
if(caption && sendFileDetails.length !== length) {
this.chat.appMessagesManager.sendText(peerId, caption, {
replyToMsgId,
threadId: this.chat.threadId,
silent: sendSilent,
scheduleDate,
...sendingParams,
clearDraft: true
});
@ -259,12 +255,9 @@ export default class PopupNewMedia extends PopupElement { @@ -259,12 +255,9 @@ export default class PopupNewMedia extends PopupElement {
};
this.chat.appMessagesManager.sendAlbum(peerId, w.sendFileDetails.map(d => d.file), Object.assign({
...sendingParams,
caption,
replyToMsgId,
threadId: this.chat.threadId,
isMedia: isMedia,
silent: sendSilent,
scheduleDate,
clearDraft: true as true
}, w));

23
src/components/row.ts

@ -16,6 +16,7 @@ import setInnerHTML from "../helpers/dom/setInnerHTML"; @@ -16,6 +16,7 @@ import setInnerHTML from "../helpers/dom/setInnerHTML";
export default class Row {
public container: HTMLElement;
public title: HTMLDivElement;
public titleRight: HTMLElement;
public subtitle: HTMLElement;
public media: HTMLElement;
@ -35,6 +36,7 @@ export default class Row { @@ -35,6 +36,7 @@ export default class Row {
title: string | HTMLElement,
titleLangKey: LangPackKey,
titleRight: string | HTMLElement,
titleRightSecondary: string | HTMLElement,
clickable: boolean | ((e: Event) => void),
navigationTab: SliderSuperTab,
havePadding: boolean,
@ -90,7 +92,8 @@ export default class Row { @@ -90,7 +92,8 @@ export default class Row {
if(options.title || options.titleLangKey) {
let c: HTMLElement;
if(options.titleRight) {
const titleRight = options.titleRight || options.titleRightSecondary;
if(titleRight) {
c = document.createElement('div');
c.classList.add('row-title-row');
this.container.append(c);
@ -112,17 +115,21 @@ export default class Row { @@ -112,17 +115,21 @@ export default class Row {
}
c.append(this.title);
if(options.titleRight) {
const titleRight = document.createElement('div');
titleRight.classList.add('row-title', 'row-title-right');
if(titleRight) {
const titleRightEl = this.titleRight = document.createElement('div');
titleRightEl.classList.add('row-title', 'row-title-right');
if(typeof(options.titleRight) === 'string') {
titleRight.innerHTML = options.titleRight;
if(options.titleRightSecondary) {
titleRightEl.classList.add('row-title-right-secondary');
}
if(typeof(titleRight) === 'string') {
titleRightEl.innerHTML = titleRight;
} else {
titleRight.append(options.titleRight);
titleRightEl.append(titleRight);
}
c.append(titleRight);
c.append(titleRightEl);
}
}

21
src/components/sidebarLeft/index.ts

@ -52,7 +52,7 @@ import { ripple } from "../ripple"; @@ -52,7 +52,7 @@ import { ripple } from "../ripple";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
export class AppSidebarLeft extends SidebarSlider {
private toolsBtn: HTMLButtonElement;
private toolsBtn: HTMLElement;
private backBtn: HTMLButtonElement;
//private searchInput = document.getElementById('global-search') as HTMLInputElement;
private inputSearch: InputSearch;
@ -671,7 +671,9 @@ export type SettingSectionOptions = { @@ -671,7 +671,9 @@ export type SettingSectionOptions = {
caption?: LangPackKey | true,
noDelimiter?: boolean,
fakeGradientDelimiter?: boolean,
noShadow?: boolean
noShadow?: boolean,
// fullWidth?: boolean,
// noPaddingTop?: boolean
};
const className = 'sidebar-left-section';
@ -682,6 +684,8 @@ export class SettingSection { @@ -682,6 +684,8 @@ export class SettingSection {
public title: HTMLElement;
public caption: HTMLElement;
private fullWidth: boolean;
constructor(options: SettingSectionOptions = {}) {
const container = this.container = document.createElement('div');
container.classList.add(className + '-container');
@ -703,6 +707,14 @@ export class SettingSection { @@ -703,6 +707,14 @@ export class SettingSection {
innerContainer.classList.add('no-delimiter');
}
// if(options.fullWidth) {
// this.fullWidth = true;
// }
// if(options.noPaddingTop) {
// innerContainer.classList.add('no-padding-top');
// }
const content = this.content = this.generateContentElement();
if(options.name) {
@ -728,6 +740,11 @@ export class SettingSection { @@ -728,6 +740,11 @@ export class SettingSection {
public generateContentElement() {
const content = document.createElement('div');
content.classList.add(className + '-content');
// if(this.fullWidth) {
// content.classList.add('full-width');
// }
this.innerContainer.append(content);
return content;
}

6
src/components/sidebarLeft/tabs/activeSessions.ts

@ -20,9 +20,9 @@ import PopupPeer from "../../popups/peer"; @@ -20,9 +20,9 @@ import PopupPeer from "../../popups/peer";
import findUpClassName from "../../../helpers/dom/findUpClassName";
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import toggleDisability from "../../../helpers/dom/toggleDisability";
import { SliderSuperTabEventable } from "../../sliderTab";
export default class AppActiveSessionsTab extends SliderSuperTab {
public privacyTab: AppPrivacyAndSecurityTab;
export default class AppActiveSessionsTab extends SliderSuperTabEventable {
public authorizations: Authorization.authorization[];
private menuElement: HTMLElement;
@ -76,7 +76,6 @@ export default class AppActiveSessionsTab extends SliderSuperTab { @@ -76,7 +76,6 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
//toggleDisability([btnTerminate], false);
btnTerminate.remove();
otherSection.container.remove();
this.privacyTab.updateActiveSessions();
}, onError).finally(() => {
toggle();
});
@ -127,7 +126,6 @@ export default class AppActiveSessionsTab extends SliderSuperTab { @@ -127,7 +126,6 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
.then(value => {
if(value) {
target.remove();
this.privacyTab.updateActiveSessions();
}
}, onError);
}

4
src/components/sidebarLeft/tabs/privacyAndSecurity.ts

@ -93,8 +93,10 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { @@ -93,8 +93,10 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
subtitleLangKey: SUBTITLE,
clickable: () => {
const tab = new AppActiveSessionsTab(this.slider);
tab.privacyTab = this;
tab.authorizations = this.authorizations;
tab.eventListener.addEventListener('destroy', () => {
this.updateActiveSessions();
}, {once: true});
tab.open();
}
});

167
src/components/sidebarLeft/tabs/settings.ts

@ -5,9 +5,7 @@ @@ -5,9 +5,7 @@
*/
import { SliderSuperTab } from "../../slider";
import AvatarElement from "../../avatar";
import apiManager from "../../../lib/mtproto/mtprotoworker";
import appUsersManager from "../../../lib/appManagers/appUsersManager";
import ButtonMenuToggle from "../../buttonMenuToggle";
import Button from "../../button";
import AppPrivacyAndSecurityTab from "./privacyAndSecurity";
@ -15,18 +13,24 @@ import AppGeneralSettingsTab from "./generalSettings"; @@ -15,18 +13,24 @@ import AppGeneralSettingsTab from "./generalSettings";
import AppEditProfileTab from "./editProfile";
import AppChatFoldersTab from "./chatFolders";
import AppNotificationsTab from "./notifications";
import PeerTitle from "../../peerTitle";
import AppLanguageTab from "./language";
import lottieLoader from "../../../lib/rlottie/lottieLoader";
import PopupPeer from "../../popups/peer";
import AppDataAndStorageTab from "./dataAndStorage";
import ButtonIcon from "../../buttonIcon";
import PeerProfile from "../../peerProfile";
import rootScope from "../../../lib/rootScope";
import { SettingSection } from "..";
import Row from "../../row";
import AppActiveSessionsTab from "./activeSessions";
import { i18n, LangPackKey } from "../../../lib/langPack";
import { AccountAuthorizations, Authorization } from "/Users/kuzmenko/Documents/projects/tweb/src/layer";
import { SliderSuperTabConstructable } from "../../sliderTab";
import PopupAvatar from "../../popups/avatar";
import appProfileManager from "../../../lib/appManagers/appProfileManager";
//import AppMediaViewer from "../../appMediaViewerNew";
export default class AppSettingsTab extends SliderSuperTab {
private avatarElem: AvatarElement;
private nameDiv: HTMLElement;
private phoneDiv: HTMLElement;
private buttons: {
edit: HTMLButtonElement,
folders: HTMLButtonElement,
@ -34,8 +38,14 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -34,8 +38,14 @@ export default class AppSettingsTab extends SliderSuperTab {
notifications: HTMLButtonElement,
storage: HTMLButtonElement,
privacy: HTMLButtonElement,
language: HTMLButtonElement
} = {} as any;
private profile: PeerProfile;
private languageRow: Row;
private devicesRow: Row;
private authorizations: Authorization.authorization[];
private getAuthorizationsPromise: Promise<AccountAuthorizations.accountAuthorizations>;
protected init() {
this.container.classList.add('settings-container');
@ -59,11 +69,25 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -59,11 +69,25 @@ export default class AppSettingsTab extends SliderSuperTab {
}
}]);
this.header.append(btnMenu);
this.buttons.edit = ButtonIcon('edit');
this.header.append(this.buttons.edit, btnMenu);
this.avatarElem = new AvatarElement();
this.avatarElem.setAttribute('clickable', '');
this.avatarElem.classList.add('profile-avatar', 'avatar-120');
this.profile = new PeerProfile(this.scrollable, this.listenerSetter, false);
this.profile.init();
this.profile.setPeer(rootScope.myId);
this.profile.fillProfileElements();
const changeAvatarBtn = Button('btn-circle btn-corner z-depth-1 profile-change-avatar', {icon: 'cameraadd'});
changeAvatarBtn.addEventListener('click', () => {
const canvas = document.createElement('canvas');
new PopupAvatar().open(canvas, (upload) => {
upload().then(inputFile => {
return appProfileManager.uploadProfilePhoto(inputFile);
});
});
});
this.profile.element.lastElementChild.firstElementChild.append(changeAvatarBtn);
/* const div = document.createElement('div');
//div.style.cssText = 'border-radius: 8px; overflow: hidden; width: 396px; height: 264px; flex: 0 0 auto; position: relative; margin: 10rem 0 10rem auto;';
@ -107,28 +131,66 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -107,28 +131,66 @@ export default class AppSettingsTab extends SliderSuperTab {
this.scrollable.append(div); */
this.nameDiv = document.createElement('div');
this.nameDiv.classList.add('profile-name');
this.phoneDiv = document.createElement('div');
this.phoneDiv.classList.add('profile-subtitle');
const buttonsDiv = document.createElement('div');
buttonsDiv.classList.add('profile-buttons');
const className = 'profile-button btn-primary btn-transparent';
buttonsDiv.append(
this.buttons.edit = Button(className, {icon: 'edit', text: 'EditAccount.Title'}),
this.buttons.folders = Button(className, {icon: 'folder', text: 'AccountSettings.Filters'}),
this.buttons.general = Button(className, {icon: 'settings', text: 'Telegram.GeneralSettingsViewController'}),
this.buttons.storage = Button(className, {icon: 'data', text: 'DataSettings'}),
this.buttons.notifications = Button(className, {icon: 'unmute', text: 'AccountSettings.Notifications'}),
this.buttons.privacy = Button(className, {icon: 'lock', text: 'AccountSettings.PrivacyAndSecurity'}),
this.buttons.language = Button(className, {icon: 'language', text: 'AccountSettings.Language'})
const b: [string, LangPackKey, SliderSuperTabConstructable][] = [
['unmute', 'AccountSettings.Notifications', AppNotificationsTab],
['data', 'DataSettings', AppDataAndStorageTab],
['lock', 'AccountSettings.PrivacyAndSecurity', AppPrivacyAndSecurityTab],
['settings', 'Telegram.GeneralSettingsViewController', AppGeneralSettingsTab],
['folder', 'AccountSettings.Filters', AppChatFoldersTab],
];
const rows = b.map(([icon, langPackKey, tabConstructor]) => {
return new Row({
titleLangKey: langPackKey,
icon,
clickable: () => {
new tabConstructor(this.slider, true).open();
}
});
});
rows.push(
this.devicesRow = new Row({
titleLangKey: 'Devices',
titleRightSecondary: ' ',
icon: 'activesessions',
clickable: async() => {
if(!this.authorizations) {
await this.updateActiveSessions();
}
const tab = new AppActiveSessionsTab(this.slider);
tab.authorizations = this.authorizations;
tab.eventListener.addEventListener('destroy', () => {
this.authorizations = undefined;
this.updateActiveSessions(true);
}, {once: true});
tab.open();
}
}),
this.languageRow = new Row({
titleLangKey: 'AccountSettings.Language',
titleRightSecondary: i18n('LanguageName'),
icon: 'language',
clickable: () => {
new AppLanguageTab(this.slider).open();
}
})
);
this.scrollable.append(this.avatarElem, this.nameDiv, this.phoneDiv, buttonsDiv);
this.scrollable.container.classList.add('profile-content-wrapper');
buttonsDiv.append(...rows.map(row => row.container));
// const profileSection = new SettingSection({fullWidth: true, noPaddingTop: true});
// profileSection.content.append(this.profile.element);
const buttonsSection = new SettingSection();
buttonsSection.content.append(buttonsDiv);
this.scrollable.append(this.profile.element/* profileSection.container */, buttonsSection.container);
/* rootScope.$on('user_auth', (e) => {
this.fillElements();
@ -139,41 +201,28 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -139,41 +201,28 @@ export default class AppSettingsTab extends SliderSuperTab {
tab.open();
});
this.buttons.folders.addEventListener('click', () => {
new AppChatFoldersTab(this.slider).open();
});
this.buttons.general.addEventListener('click', () => {
new AppGeneralSettingsTab(this.slider).open();
});
this.buttons.storage.addEventListener('click', () => {
new AppDataAndStorageTab(this.slider).open();
});
lottieLoader.loadLottieWorkers();
this.buttons.notifications.addEventListener('click', () => {
new AppNotificationsTab(this.slider).open();
});
this.updateActiveSessions();
}
this.buttons.privacy.addEventListener('click', () => {
new AppPrivacyAndSecurityTab(this.slider).open();
});
private getAuthorizations(overwrite?: boolean) {
if(this.getAuthorizationsPromise && !overwrite) return this.getAuthorizationsPromise;
this.buttons.language.addEventListener('click', () => {
new AppLanguageTab(this.slider).open();
const promise = this.getAuthorizationsPromise = apiManager.invokeApi('account.getAuthorizations')
.finally(() => {
if(this.getAuthorizationsPromise === promise) {
this.getAuthorizationsPromise = undefined;
}
});
lottieLoader.loadLottieWorkers();
this.fillElements();
return promise;
}
public fillElements() {
const user = appUsersManager.getSelf();
const peerId = user.id.toPeerId(false);
this.avatarElem.setAttribute('peer', '' + peerId);
this.nameDiv.append(new PeerTitle({peerId: peerId}).element);
this.phoneDiv.innerHTML = user.phone ? appUsersManager.formatUserPhone(user.phone) : '';
public updateActiveSessions(overwrite?: boolean) {
return this.getAuthorizations(overwrite).then(auths => {
this.authorizations = auths.authorizations;
this.devicesRow.titleRight.textContent = '' + this.authorizations.length;
});
}
}

6
src/lang.ts

@ -666,7 +666,7 @@ const lang = { @@ -666,7 +666,7 @@ const lang = {
"AutoDownloadPm": "PM",
"AutoDownloadGroups": "Groups",
"AutoDownloadChannels": "Channels",
"AutoDownloadAudioInfo": "Voice messages are tiny, so they\'re always downloaded automatically.",
"AutoDownloadAudioInfo": "Voice messages are tiny, so they're always downloaded automatically.",
"AutoplayMedia": "Auto-play media",
"AutoDownloadPhotosTitle": "Auto-download photos",
"AutoDownloadVideosTitle": "Auto-download videos and GIFs",
@ -677,6 +677,9 @@ const lang = { @@ -677,6 +677,9 @@ const lang = {
"ResetAutomaticMediaDownloadAlertTitle": "Reset settings",
"ResetAutomaticMediaDownloadAlert": "Are you sure you want to reset auto-download settings?",
"Reset": "Reset",
"SendMessageAsTitle": "Send message as...",
"Devices": "Devices",
"LanguageName": "English",
// * macos
"AccountSettings.Filters": "Chat Folders",
@ -811,6 +814,7 @@ const lang = { @@ -811,6 +814,7 @@ const lang = {
"Chat.Send.WithoutSound": "Send Without Sound",
"Chat.Send.SetReminder": "Set a Reminder",
"Chat.Send.ScheduledMessage": "Schedule Message",
"Chat.SendAs.PersonalAccount": "personal account",
"Chat.UnpinAllMessagesConfirmation": {
"one_value": "Do you want to unpin %d message in this chat?",
"other_value": "Do you want to unpin all %d messages in this chat?"

17
src/lib/appManagers/appChatsManager.ts

@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
import { MOUNT_CLASS_TO } from "../../config/debug";
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object";
import { isRestricted } from "../../helpers/restrictions";
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates, ChannelsCreateChannel } from "../../layer";
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates, ChannelsCreateChannel, Peer } from "../../layer";
import apiManagerProxy from "../mtproto/mtprotoworker";
import apiManager from '../mtproto/mtprotoworker';
import { RichTextProcessor } from "../richtextprocessor";
@ -787,6 +787,21 @@ export class AppChatsManager { @@ -787,6 +787,21 @@ export class AppChatsManager {
return !!(chat.pFlags.restricted && restrictionReasons && isRestricted(restrictionReasons));
}
public getSendAs(channelId: ChatId) {
return apiManager.invokeApiSingleProcess({
method: 'channels.getSendAs',
params: {
peer: this.getChannelInputPeer(channelId)
},
processResult: (sendAsPeers) => {
appUsersManager.saveApiUsers(sendAsPeers.users);
appChatsManager.saveApiChats(sendAsPeers.chats);
return sendAsPeers.peers;
}
});
}
}
const appChatsManager = new AppChatsManager();

24
src/lib/appManagers/appDialogsManager.ts

@ -15,7 +15,7 @@ import { ripple } from "../../components/ripple"; @@ -15,7 +15,7 @@ import { ripple } from "../../components/ripple";
//import Scrollable from "../../components/scrollable";
import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable";
import { formatDateAccordingToTodayNew } from "../../helpers/date";
import { IS_SAFARI } from "../../environment/userAgent";
import { IS_MOBILE_SAFARI, IS_SAFARI } from "../../environment/userAgent";
import { logger, LogTypes } from "../logger";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
@ -59,6 +59,7 @@ import groupCallActiveIcon from "../../components/groupCallActiveIcon"; @@ -59,6 +59,7 @@ import groupCallActiveIcon from "../../components/groupCallActiveIcon";
import { Chat } from "../../layer";
import IS_GROUP_CALL_SUPPORTED from "../../environment/groupCallSupport";
import mediaSizes from "../../helpers/mediaSizes";
import appNavigationController, { NavigationItem } from "../../components/appNavigationController";
export type DialogDom = {
avatarEl: AvatarElement,
@ -181,6 +182,8 @@ export class AppDialogsManager { @@ -181,6 +182,8 @@ export class AppDialogsManager {
private emptyDialogsPlaceholderSubtitle: I18n.IntlElement;
private updateContactsLengthPromise: Promise<number>;
private filtersNavigationItem: NavigationItem;
constructor() {
this.chatsPreloader = putPreloader(null, true);
@ -298,6 +301,25 @@ export class AppDialogsManager { @@ -298,6 +301,25 @@ export class AppDialogsManager {
id = +tabContent.dataset.filterId || 0;
if(!IS_MOBILE_SAFARI) {
if(id) {
if(!this.filtersNavigationItem) {
this.filtersNavigationItem = {
type: 'filters',
onPop: () => {
selectTab(0);
this.filtersNavigationItem = undefined;
}
};
appNavigationController.unshiftItem(this.filtersNavigationItem);
}
} else if(this.filtersNavigationItem) {
appNavigationController.removeItem(this.filtersNavigationItem);
this.filtersNavigationItem = undefined;
}
}
if(this.filterId === id) return;
this.sortedLists[id].clear();

8
src/lib/appManagers/appImManager.ts

@ -1793,7 +1793,7 @@ export class AppImManager { @@ -1793,7 +1793,7 @@ export class AppImManager {
}
}
public async getPeerStatus(peerId: PeerId) {
public async getPeerStatus(peerId: PeerId, ignoreSelf?: boolean) {
let subtitle: HTMLElement;
if(!peerId) return;
@ -1828,7 +1828,7 @@ export class AppImManager { @@ -1828,7 +1828,7 @@ export class AppImManager {
} else { // user
const user = appUsersManager.getUser(peerId);
if(rootScope.myId === peerId) {
if(rootScope.myId === peerId && !ignoreSelf) {
return;
} else if(user) {
subtitle = appUsersManager.getUserStatusString(user.id);
@ -1851,7 +1851,7 @@ export class AppImManager { @@ -1851,7 +1851,7 @@ export class AppImManager {
}
}
public setPeerStatus(peerId: PeerId, element: HTMLElement, needClear: boolean, useWhitespace: boolean, middleware: () => boolean) {
public setPeerStatus(peerId: PeerId, element: HTMLElement, needClear: boolean, useWhitespace: boolean, middleware: () => boolean, ignoreSelf?: boolean) {
if(needClear) {
element.innerHTML = useWhitespace ? '' : ''; // ! HERE U CAN FIND WHITESPACE
}
@ -1862,7 +1862,7 @@ export class AppImManager { @@ -1862,7 +1862,7 @@ export class AppImManager {
return;
}
this.getPeerStatus(peerId).then((subtitle) => {
this.getPeerStatus(peerId, ignoreSelf).then((subtitle) => {
if(!middleware()) {
return;
}

1
src/lib/appManagers/appInlineBotsManager.ts

@ -279,6 +279,7 @@ export class AppInlineBotsManager { @@ -279,6 +279,7 @@ export class AppInlineBotsManager {
clearDraft: true,
scheduleDate: number,
silent: true,
sendAsPeerId: PeerId,
geoPoint: GeoPoint
}> = {}) {
const inlineResult = this.inlineResults[queryAndResultIds];

40
src/lib/appManagers/appMessagesManager.ts

@ -449,7 +449,8 @@ export class AppMessagesManager { @@ -449,7 +449,8 @@ export class AppMessagesManager {
clearDraft: true,
webPage: WebPage,
scheduleDate: number,
silent: true
silent: true,
sendAsPeerId: PeerId,
}> = {}) {
if(!text.trim()) {
return Promise.resolve();
@ -520,6 +521,7 @@ export class AppMessagesManager { @@ -520,6 +521,7 @@ export class AppMessagesManager {
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
}
const sendAs = options.sendAsPeerId ? appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined
let apiPromise: any;
if(options.viaBotId) {
apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', {
@ -528,7 +530,8 @@ export class AppMessagesManager { @@ -528,7 +530,8 @@ export class AppMessagesManager {
reply_to_msg_id: replyToMsgId || undefined,
query_id: options.queryId,
id: options.resultId,
clear_draft: options.clearDraft
clear_draft: options.clearDraft,
send_as: sendAs
}, sentRequestOptions);
} else {
apiPromise = apiManager.invokeApiAfter('messages.sendMessage', {
@ -540,7 +543,8 @@ export class AppMessagesManager { @@ -540,7 +543,8 @@ export class AppMessagesManager {
entities: sendEntites,
clear_draft: options.clearDraft,
schedule_date: options.scheduleDate || undefined,
silent: options.silent
silent: options.silent,
send_as: sendAs
}, sentRequestOptions);
}
@ -633,6 +637,7 @@ export class AppMessagesManager { @@ -633,6 +637,7 @@ export class AppMessagesManager {
isMedia: true,
replyToMsgId: number,
sendAsPeerId: PeerId,
threadId: number,
groupId: string,
caption: string,
@ -1026,7 +1031,8 @@ export class AppMessagesManager { @@ -1026,7 +1031,8 @@ export class AppMessagesManager {
schedule_date: options.scheduleDate,
silent: options.silent,
entities,
clear_draft: options.clearDraft
clear_draft: options.clearDraft,
send_as: options.sendAsPeerId ? appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, (error) => {
@ -1055,6 +1061,7 @@ export class AppMessagesManager { @@ -1055,6 +1061,7 @@ export class AppMessagesManager {
isMedia: true,
entities: MessageEntity[],
replyToMsgId: number,
sendAsPeerId: PeerId,
threadId: number,
caption: string,
sendFileDetails: Partial<{
@ -1101,6 +1108,7 @@ export class AppMessagesManager { @@ -1101,6 +1108,7 @@ export class AppMessagesManager {
silent: options.silent,
replyToMsgId,
threadId: options.threadId,
sendAsPeerId: options.sendAsPeerId,
groupId,
...details
};
@ -1146,7 +1154,8 @@ export class AppMessagesManager { @@ -1146,7 +1154,8 @@ export class AppMessagesManager {
reply_to_msg_id: replyToMsgId,
schedule_date: options.scheduleDate,
silent: options.silent,
clear_draft: options.clearDraft
clear_draft: options.clearDraft,
send_as: options.sendAsPeerId ? appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
deferred.resolve();
@ -1222,7 +1231,8 @@ export class AppMessagesManager { @@ -1222,7 +1231,8 @@ export class AppMessagesManager {
resultId: string,
scheduleDate: number,
silent: true,
geoPoint: GeoPoint
geoPoint: GeoPoint,
sendAsPeerId: PeerId,
}> = {}) {
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
@ -1337,6 +1347,7 @@ export class AppMessagesManager { @@ -1337,6 +1347,7 @@ export class AppMessagesManager {
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
}
const sendAs = options.sendAsPeerId ? appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined;
let apiPromise: Promise<any>;
if(options.viaBotId) {
apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', {
@ -1347,7 +1358,8 @@ export class AppMessagesManager { @@ -1347,7 +1358,8 @@ export class AppMessagesManager {
id: options.resultId,
clear_draft: options.clearDraft,
schedule_date: options.scheduleDate,
silent: options.silent
silent: options.silent,
send_as: sendAs
}, sentRequestOptions);
} else {
apiPromise = apiManager.invokeApiAfter('messages.sendMedia', {
@ -1358,7 +1370,8 @@ export class AppMessagesManager { @@ -1358,7 +1370,8 @@ export class AppMessagesManager {
message: '',
clear_draft: options.clearDraft,
schedule_date: options.scheduleDate,
silent: options.silent
silent: options.silent,
send_as: sendAs
}, sentRequestOptions);
}
@ -1463,6 +1476,7 @@ export class AppMessagesManager { @@ -1463,6 +1476,7 @@ export class AppMessagesManager {
private generateOutgoingMessage(peerId: PeerId, options: Partial<{
scheduleDate: number,
replyToMsgId: number,
sendAsPeerId: PeerId,
threadId: number,
viaBotId: BotId,
groupId: string,
@ -1486,7 +1500,7 @@ export class AppMessagesManager { @@ -1486,7 +1500,7 @@ export class AppMessagesManager {
const message: Message.message = {
_: 'message',
id: this.generateTempMessageId(peerId),
from_id: this.generateFromId(peerId),
from_id: options.sendAsPeerId ? appPeersManager.getOutputPeer(options.sendAsPeerId) : this.generateFromId(peerId),
peer_id: appPeersManager.getOutputPeer(peerId),
post_author: postAuthor,
pFlags: this.generateFlags(peerId),
@ -1937,7 +1951,8 @@ export class AppMessagesManager { @@ -1937,7 +1951,8 @@ export class AppMessagesManager {
silent: true,
scheduleDate: number,
dropAuthor: boolean,
dropCaptions: boolean
dropCaptions: boolean,
sendAsPeerId: PeerId,
}> = {}) {
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
mids = mids.slice().sort((a, b) => a - b);
@ -2044,7 +2059,8 @@ export class AppMessagesManager { @@ -2044,7 +2059,8 @@ export class AppMessagesManager {
silent: options.silent,
schedule_date: options.scheduleDate,
drop_author: options.dropAuthor,
drop_media_captions: options.dropCaptions
drop_media_captions: options.dropCaptions,
send_as: options.sendAsPeerId ? appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined
}, sentRequestOptions).then((updates) => {
this.log('forwardMessages updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
@ -5047,6 +5063,8 @@ export class AppMessagesManager { @@ -5047,6 +5063,8 @@ export class AppMessagesManager {
this.dialogsStorage.dropDialogOnDeletion(peerId);
}
}
rootScope.dispatchEvent('channel_update', channelId);
};
private onUpdateChannelReload = (update: Update.updateChannelReload) => {

17
src/lib/appManagers/appProfileManager.ts

@ -121,6 +121,10 @@ export class AppProfileManager { @@ -121,6 +121,10 @@ export class AppProfileManager {
}
});
rootScope.addEventListener('channel_update', (chatId) => {
this.refreshFullPeer(chatId.toPeerId(true));
});
// * genius
rootScope.addEventListener('chat_full_update', (chatId) => {
rootScope.dispatchEvent('peer_full_update', chatId.toPeerId(true));
@ -477,10 +481,15 @@ export class AppProfileManager { @@ -477,10 +481,15 @@ export class AppProfileManager {
}
private refreshFullPeer(peerId: PeerId) {
if(peerId.isUser()) delete this.usersFull[peerId.toUserId()];
else delete this.chatsFull[peerId.toChatId()];
rootScope.dispatchEvent('peer_full_update', peerId);
if(peerId.isUser()) {
const userId = peerId.toUserId();
delete this.usersFull[userId];
rootScope.dispatchEvent('user_full_update', userId);
} else {
const chatId = peerId.toChatId();
delete this.chatsFull[chatId];
rootScope.dispatchEvent('chat_full_update', chatId);
}
// ! эта строчка будет создавать race condition:
// ! запрос вернёт chat с установленным флагом call_not_empty, хотя сам апдейт уже будет применён

2
src/lib/rootScope.ts

@ -28,6 +28,8 @@ import { MTAppConfig } from "./mtproto/appConfig"; @@ -28,6 +28,8 @@ import { MTAppConfig } from "./mtproto/appConfig";
export type BroadcastEvents = {
'chat_full_update': ChatId,
'chat_update': ChatId,
'channel_update': ChatId,
'user_update': UserId,
'user_auth': UserAuth,

133
src/scripts/out/langPack.strings

@ -12,8 +12,6 @@ @@ -12,8 +12,6 @@
"FilterAllNonContacts" = "All Non-Contacts";
"FilterAllChannels" = "All Channels";
"FilterAllBots" = "All Bots";
"WordDelimiter" = ", ";
"WordDelimiterLast" = " and ";
"EditContact.OriginalName" = "original name";
"EditProfile.FirstNameLabel" = "Name";
"EditProfile.BioLabel" = "Bio (optional)";
@ -55,7 +53,6 @@ @@ -55,7 +53,6 @@
"General.SendShortcut.CtrlEnter" = "Send by %s + Enter";
"General.SendShortcut.NewLine.ShiftEnter" = "New line by Shift + Enter";
"General.SendShortcut.NewLine.Enter" = "New line by Enter";
"General.AutoplayMedia" = "Auto-Play Media";
"General.TimeFormat" = "Time Format";
"General.TimeFormat.h12" = "12-hour";
"General.TimeFormat.h23" = "24-hour";
@ -564,6 +561,44 @@ @@ -564,6 +561,44 @@
"RequestToJoinSent" = "Join request sent";
"RequestToJoinGroupApproved" = "Your request to join the group was approved";
"RequestToJoinChannelApproved" = "Your request to join the channel was approved";
"Update" = "UPDATE";
"Reactions" = "Reactions";
"DoubleTapSetting" = "Quick Reaction";
"EnableReactions" = "Enable Reactions";
"EnableReactionsChannelInfo" = "Allow subscribers to react to channel posts.";
"EnableReactionsGroupInfo" = "Allow members to react to group messages.";
"AvailableReactions" = "Available reactions";
"NobodyViewed" = "Nobody viewed";
"MessageSeen_one" = "Seen";
"MessageSeen_other" = "%1$d Seen";
"DataSettings" = "Data and Storage";
"GroupsAndChannelsHelp" = "Change who can add you to groups and channels.";
"SessionsInfo" = "Control your sessions on other devices.";
"StickersBotInfo" = "Artists are welcome to add their own sticker sets using our @stickers bot.";
"AutomaticMediaDownload" = "Automatic media download";
"AutoDownloadPhotos" = "Photos";
"AutoDownloadVideos" = "Videos";
"AutoDownloadFiles" = "Files";
"AutoDownloadOnAllChats" = "On in all chats";
"AutoDownloadUpToOnAllChats" = "Up to %1$s in all chats";
"AutoDownloadOff" = "Off";
"AutoDownloadOnUpToFor" = "Up to %1$s for %2$s";
"AutoDownloadOnFor" = "On for %1$s";
"AutoDownloadContacts" = "Contacts";
"AutoDownloadPm" = "PM";
"AutoDownloadGroups" = "Groups";
"AutoDownloadChannels" = "Channels";
"AutoDownloadAudioInfo" = "Voice messages are tiny, so they're always downloaded automatically.";
"AutoplayMedia" = "Auto-play media";
"AutoDownloadPhotosTitle" = "Auto-download photos";
"AutoDownloadVideosTitle" = "Auto-download videos and GIFs";
"AutoDownloadFilesTitle" = "Auto-download files and music";
"AutoDownloadMaxFileSize" = "Maximum file size";
"AutodownloadSizeLimitUpTo" = "up to %1$s";
"ResetAutomaticMediaDownload" = "Reset Auto-Download Settings";
"ResetAutomaticMediaDownloadAlertTitle" = "Reset settings";
"ResetAutomaticMediaDownloadAlert" = "Are you sure you want to reset auto-download settings?";
"Reset" = "Reset";
"AccountSettings.Filters" = "Chat Folders";
"AccountSettings.Notifications" = "Notifications and Sounds";
"AccountSettings.PrivacyAndSecurity" = "Privacy and Security";
@ -571,6 +606,8 @@ @@ -571,6 +606,8 @@
"Alert.UserDoesntExists" = "Sorry, this user doesn't seem to exist.";
"Alert.Confirm.Discard" = "Discard";
"Appearance.Reset" = "Reset to Defaults";
"AutoDownloadSettings.Delimeter" = ", ";
"AutoDownloadSettings.LastDelimeter" = " and ";
"Bio.Description" = "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco";
"Call.Accept" = "Accept";
"Call.Decline" = "Decline";
@ -614,6 +651,8 @@ @@ -614,6 +651,8 @@
"Chat.Alert.Forward.Action.HideCaption_other" = "Hide Captions";
"Chat.CopySelectedText" = "Copy Selected Text";
"Chat.Confirm.Unpin" = "Would you like to unpin this message?";
"Chat.Context.Reacted" = "%1$@/%2$@ Reacted";
"Chat.Context.ReactedFast_other" = "%d Reacted";
"Chat.Date.ScheduledFor" = "Scheduled for %@";
"Chat.Date.ScheduledForToday" = "Scheduled for today";
"Chat.DropTitle" = "Drop files here to send them";
@ -708,6 +747,12 @@ @@ -708,6 +747,12 @@
"ChatList.Filter.Exclude.LimitReached" = "Sorry, you can only add up to 100 individual chats. Try using chat types.";
"ChatList.Filter.Confirm.Remove.Header" = "Remove Folder";
"ChatList.Filter.Confirm.Remove.Text" = "Are you sure you want to remove this folder? Your chats will not be deleted.";
"ChatList.Mute.1Hour" = "For 1 Hour";
"ChatList.Mute.4Hours" = "For 4 Hours";
"ChatList.Mute.8Hours" = "For 8 Hours";
"ChatList.Mute.1Day" = "For 1 Day";
"ChatList.Mute.3Days" = "For 3 Days";
"ChatList.Mute.Forever" = "Forever";
"Channel.DescriptionHolderDescrpiton" = "You can provide an optional description for your channel.";
"CreateGroup.NameHolder" = "Group Name";
"Date.Today" = "Today";
@ -724,20 +769,26 @@ @@ -724,20 +769,26 @@
"Emoji.TravelAndPlaces" = "Travel & Places";
"Emoji.Objects" = "Objects";
"Emoji.Flags" = "Flags";
"FileSize.B" = "%@ B";
"FileSize.KB" = "%@ KB";
"FileSize.MB" = "%@ MB";
"FileSize.GB" = "%@ GB";
"InstalledStickers.LoopAnimated" = "Loop Animated Stickers";
"LastSeen.HoursAgo_one" = "last seen %d hour ago";
"LastSeen.HoursAgo_other" = "last seen %d hours ago";
"Login.Register.LastName.Placeholder" = "Last Name";
"Message.Context.Select" = "Select";
"Message.Context.Pin" = "Pin";
"Message.Context.Unpin" = "Unpin";
"Message.Context.Goto" = "Show Message";
"MessageContext.CopyMessageLink1" = "Copy Message Link";
"Modal.Send" = "Send";
"Telegram.GeneralSettingsViewController" = "General Settings";
"Telegram.InstalledStickerPacksController" = "Stickers";
"Telegram.NotificationSettingsViewController" = "Notifications";
"Telegram.LanguageViewController" = "Language";
"Stickers.SearchAdd" = "Add";
"Stickers.SearchAdded" = "Added";
"Stickers.SuggestStickers" = "Suggest Stickers by Emoji";
"ShareModal.Search.Placeholder" = "Share to...";
"ShareModal.Search.ForwardPlaceholder" = "Forward to...";
"InstalledStickers.LoopAnimated" = "Loop Animated Stickers";
"NewPoll.Anonymous" = "Anonymous Voting";
"NewPoll.Explanation.Placeholder" = "Add a Comment (Optional)";
"NewPoll.OptionsAddOption" = "Add an Option";
"NewPoll.MultipleChoice" = "Multiple Answers";
"NewPoll.Quiz" = "Quiz Mode";
"Notification.Contact.Reacted" = "%1$@ to your \"%2$@\"";
"Peer.Activity.User.PlayingGame" = "playing a game";
"Peer.Activity.User.TypingText" = "typing";
"Peer.Activity.User.SendingPhoto" = "sending a photo";
@ -746,6 +797,7 @@ @@ -746,6 +797,7 @@
"Peer.Activity.User.RecordingAudio" = "recording voice";
"Peer.Activity.User.SendingFile" = "sending file";
"Peer.Activity.User.ChoosingSticker" = "choosing a sticker";
"Peer.Activity.User.EnjoyingAnimations" = "watching %@";
"Peer.Activity.Chat.PlayingGame" = "%@ is playing a game";
"Peer.Activity.Chat.TypingText" = "%@ is typing";
"Peer.Activity.Chat.SendingPhoto" = "%@ is sending a photo";
@ -754,6 +806,7 @@ @@ -754,6 +806,7 @@
"Peer.Activity.Chat.RecordingAudio" = "%@ is recording voice";
"Peer.Activity.Chat.SendingFile" = "%@ is sending a file";
"Peer.Activity.Chat.ChoosingSticker" = "%@ is choosing a sticker";
"Peer.Activity.Chat.EnjoyingAnimations" = "%@ is watching %@";
"Peer.Activity.Chat.Multi.PlayingGame1" = "%@ and %d others are playing a game";
"Peer.Activity.Chat.Multi.TypingText1" = "%@ and %d others are typing";
"Peer.Activity.Chat.Multi.SendingPhoto1" = "%@ and %d others are sending photos";
@ -824,16 +877,15 @@ @@ -824,16 +877,15 @@
"PrivacySettingsController.UserCount_other" = "%d users";
"RecentSessions.Error.FreshReset" = "For security reasons, you can't terminate older sessions from a device that you've just connected. Please use an earlier connection or wait for a few hours.";
"RequestJoin.Button" = "Request to Join";
"Message.Context.Select" = "Select";
"Message.Context.Pin" = "Pin";
"Message.Context.Unpin" = "Unpin";
"Message.Context.Goto" = "Show Message";
"MessageContext.CopyMessageLink1" = "Copy Message Link";
"NewPoll.Anonymous" = "Anonymous Voting";
"NewPoll.Explanation.Placeholder" = "Add a Comment (Optional)";
"NewPoll.OptionsAddOption" = "Add an Option";
"NewPoll.MultipleChoice" = "Multiple Answers";
"NewPoll.Quiz" = "Quiz Mode";
"Stickers.SearchAdd" = "Add";
"Stickers.SearchAdded" = "Added";
"Stickers.SuggestStickers" = "Suggest Stickers by Emoji";
"ShareModal.Search.Placeholder" = "Share to...";
"ShareModal.Search.ForwardPlaceholder" = "Forward to...";
"Telegram.GeneralSettingsViewController" = "General Settings";
"Telegram.InstalledStickerPacksController" = "Stickers";
"Telegram.NotificationSettingsViewController" = "Notifications";
"Telegram.LanguageViewController" = "Language";
"GeneralSettings.BigEmoji" = "Large Emoji";
"GeneralSettings.EmojiPrediction" = "Suggest Emoji";
"GroupPermission.Delete" = "Delete Exception";
@ -845,6 +897,24 @@ @@ -845,6 +897,24 @@
"Schedule.SendDate" = "Send on %@ at %@";
"Schedule.SendWhenOnline" = "Send When Online";
"Stickers.Recent" = "Recent";
"StickerSet.DontExist" = "Sorry, this sticker set doesn't seem to exist.";
"Text.Context.Copy.Username" = "Copy Username";
"Text.Context.Copy.Hashtag" = "Copy Hashtag";
"Time.TomorrowAt" = "tomorrow at %@";
"TwoStepAuth.SetPasswordHelp" = "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.";
"TwoStepAuth.GenericHelp" = "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.";
"TwoStepAuth.ChangePassword" = "Change Password";
"TwoStepAuth.RemovePassword" = "Turn Password Off";
"TwoStepAuth.SetupEmail" = "Set Recovery Email";
"TwoStepAuth.ChangeEmail" = "Change Recovery Email";
"TwoStepAuth.ConfirmEmailCodeDesc" = "Please enter the code we've just emailed to %@.";
"TwoStepAuth.RecoveryTitle" = "Email Code";
"TwoStepAuth.RecoveryCode" = "Code";
"TwoStepAuth.RecoveryCodeInvalid" = "Invalid code. Please try again.";
"TwoStepAuth.RecoveryCodeExpired" = "Code Expired";
"TwoStepAuth.SetupHintTitle" = "Password Hint";
"TwoStepAuth.SetupHintPlaceholder" = "Hint";
"UsernameSettings.ChangeDescription" = "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\n\nYou can use a-z, 0-9 and underscores. Minimum length is 5 characters.";
"VoiceChat.Chat.StartNew" = "Video chat ended. Start a new one?";
"VoiceChat.Chat.StartNew.OK" = "Start";
"VoiceChat.Chat.Ended" = "Video chat ended.";
@ -875,23 +945,6 @@ @@ -875,23 +945,6 @@
"VoiceChat.RemovePeer.Confirm.Channel" = "Do you want to remove %1$@ from the channel?";
"VoiceChat.RemovePeer.Confirm" = "Are you sure you want to remove %1$@ from the group?";
"VoiceChat.RemovePeer.Confirm.OK" = "Remove";
"Text.Context.Copy.Username" = "Copy Username";
"Text.Context.Copy.Hashtag" = "Copy Hashtag";
"Time.TomorrowAt" = "tomorrow at %@";
"TwoStepAuth.SetPasswordHelp" = "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.";
"TwoStepAuth.GenericHelp" = "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.";
"TwoStepAuth.ChangePassword" = "Change Password";
"TwoStepAuth.RemovePassword" = "Turn Password Off";
"TwoStepAuth.SetupEmail" = "Set Recovery Email";
"TwoStepAuth.ChangeEmail" = "Change Recovery Email";
"TwoStepAuth.ConfirmEmailCodeDesc" = "Please enter the code we've just emailed to %@.";
"TwoStepAuth.RecoveryTitle" = "Email Code";
"TwoStepAuth.RecoveryCode" = "Code";
"TwoStepAuth.RecoveryCodeInvalid" = "Invalid code. Please try again.";
"TwoStepAuth.RecoveryCodeExpired" = "Code Expired";
"TwoStepAuth.SetupHintTitle" = "Password Hint";
"TwoStepAuth.SetupHintPlaceholder" = "Hint";
"UsernameSettings.ChangeDescription" = "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\n\nYou can use a-z, 0-9 and underscores. Minimum length is 5 characters.";
"Login.Title" = "Sign in to Telegram";
"Login.CountrySelectorLabel" = "Country";
"Login.PhoneLabel" = "Phone Number";

3
src/scss/partials/_avatar.scss

@ -205,7 +205,8 @@ avatar-element { @@ -205,7 +205,8 @@ avatar-element {
&.avatar-30 {
--size: 30px;
--multiplier: 1.8;
// --multiplier: 1.8; // text won't be centered
--multiplier: 1.6875;
}
&.avatar-24 {

16
src/scss/partials/_button.scss

@ -200,6 +200,10 @@ @@ -200,6 +200,10 @@
&:before {
color: var(--secondary-text-color);
font-size: var(--icon-size);
}
&-icon,
&:before {
margin-right: var(--icon-margin);
position: relative;
}
@ -227,6 +231,18 @@ @@ -227,6 +231,18 @@
}
}
&-subtitle {
font-size: .875rem;
color: var(--secondary-text-color);
}
&-header {
color: var(--primary-color);
height: 2rem;
font-weight: 500;
pointer-events: none !important;
}
.stacked-avatars {
--margin-right: -.6875rem;
flex: 0 0 auto;

134
src/scss/partials/_chat.scss

@ -1018,19 +1018,6 @@ $background-transition-total-time: #{$input-transition-time - $background-transi @@ -1018,19 +1018,6 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
background-color: var(--light-primary-color) !important;
}
.btn-menu {
right: calc(var(--padding-horizontal) * -1 - .125rem);
bottom: calc(100% + 1.125rem);
@include respond-to(esg-bottom-new) {
bottom: calc(100% + .875rem);
}
&-item {
padding: 0 38px 0 16px;
}
}
&.btn-disabled {
opacity: var(--disabled-opacity);
}
@ -1175,10 +1162,123 @@ $background-transition-total-time: #{$input-transition-time - $background-transi @@ -1175,10 +1162,123 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
}
.new-message-wrapper {
--button-size: 2.125rem;
--button-horizontal-margin: .125rem;
--send-as-size: 1.875rem;
--send-as-margin-left: .25rem;
--send-as-margin-right: .375rem;
--send-as-total-size: calc(var(--send-as-size) + var(--send-as-margin-left) + var(--send-as-margin-right));
//padding: 4.5px 0;
//padding-bottom: 4.5px;
align-items: flex-end;
min-height: var(--chat-input-size);
.new-message-send-as {
&-container {
width: var(--send-as-size);
height: var(--send-as-size);
position: absolute;
flex: 0 0 auto;
// margin: 0 0.375rem .4375rem var(--send-as-margin-left);
margin: 0 0 .4375rem var(--send-as-margin-left);
cursor: pointer;
transform: scale(0);
background: none !important;
.btn-menu-item-icon {
margin-right: calc(var(--icon-margin) - .5rem);
&.active:before {
--offset: -.25rem;
content: " ";
position: absolute;
top: var(--offset);
right: var(--offset);
bottom: var(--offset);
left: var(--offset);
border: .125rem solid var(--primary-color);
border-radius: 50%;
}
}
}
&-avatar {
position: absolute;
transform: scale(0);
opacity: 0;
&.is-visible {
&:not(.backwards) {
transform: scale(1);
opacity: 1;
}
&.animating {
transition: transform var(--transition-standard-in), opacity var(--transition-standard-in);
}
}
}
&-close {
width: inherit;
height: inherit;
background-color: var(--primary-color);
font-size: 1.375rem;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
}
&.has-send-as {
.toggle-emoticons,
.input-message-container {
transform: translateX(0);
}
&:not(.backwards) {
.toggle-emoticons {
transform: translateX(var(--send-as-total-size));
}
.input-message-container {
--translateX: calc(var(--send-as-total-size));
padding-right: var(--translateX);
transform: translate(var(--translateX));
}
.new-message-send-as-container {
transform: scale(1);
}
}
&.animating {
.toggle-emoticons,
.input-message-container,
.new-message-send-as-container {
transition: transform var(--transition-standard-in);
}
}
}
.btn-menu {
bottom: calc(100% + 1.125rem);
@include respond-to(esg-bottom-new) {
bottom: calc(100% + .875rem);
}
&.top-left {
right: calc(var(--padding-horizontal) * -1 - .125rem);
}
&.top-right {
left: calc(var(--padding-horizontal) * -1 - .125rem);
}
}
}
.input-message-container {
@ -1199,14 +1299,14 @@ $background-transition-total-time: #{$input-transition-time - $background-transi @@ -1199,14 +1299,14 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
.btn-icon {
flex: 0 0 auto;
font-size: 24px;
font-size: 1.5rem;
color: var(--secondary-text-color);
// ! EXPERIMENTAL
margin: 0 .125rem 5px;
margin: 0 var(--button-horizontal-margin) 5px;
padding: 0;
width: 34px;
height: 34px;
width: var(--button-size);
height: var(--button-size);
&.active {
color: var(--primary-color);

27
src/scss/partials/_leftSidebar.scss

@ -584,21 +584,9 @@ @@ -584,21 +584,9 @@
.settings-container {
.profile {
&-button {
@include respond-to(handhelds) {
border-radius: 0;
}
}
&-buttons {
margin-top: 1.1875rem;
width: 100%;
padding: 0 .4375rem;
@include respond-to(handhelds) {
margin-top: .6875rem;
padding: 0;
}
&-avatars-container {
padding-bottom: 0;
height: 15rem;
}
}
}
@ -851,6 +839,10 @@ @@ -851,6 +839,10 @@
}
}
&.no-padding-top {
padding-top: 0 !important;
}
@include respond-to(handhelds) {
padding-bottom: .5rem;
}
@ -876,6 +868,10 @@ @@ -876,6 +868,10 @@
border-radius: 0;
}
}
&.full-width {
margin: 0 !important;
}
}
&-name {
@ -956,7 +952,6 @@ @@ -956,7 +952,6 @@
#chats-archived-container,
#contacts-container,
.add-members-container,
.settings-container,
#search-private-container,
#stickers-container,
#poll-results-container,

19
src/scss/partials/_profile.scss

@ -214,7 +214,7 @@ @@ -214,7 +214,7 @@
padding-left: 54px;
} */
&-wrapper {
/* &-wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: column;
@ -223,7 +223,7 @@ @@ -223,7 +223,7 @@
@include respond-to(not-handhelds) {
padding-top: 15px;
}
}
} */
.sidebar-left-section {
position: relative;
@ -258,6 +258,21 @@ @@ -258,6 +258,21 @@
}
}
&-button {
@include respond-to(handhelds) {
border-radius: 0;
}
}
&-change-avatar {
--size: 3.375rem;
position: absolute !important;
top: calc(var(--size) / -2);
right: 1.25rem;
transform: none;
transition: none !important;
}
&-container {
> .scrollable {
display: flex;

4
src/scss/partials/_row.scss

@ -50,6 +50,10 @@ @@ -50,6 +50,10 @@
&-right {
flex: 0 0 auto !important;
margin-left: 1rem;
&-secondary {
color: var(--secondary-text-color);
}
}
}

1
src/scss/partials/_slider.scss

@ -93,6 +93,7 @@ @@ -93,6 +93,7 @@
display: inline-flex;
align-items: center;
overflow: visible;
pointer-events: none;
}
}

Loading…
Cancel
Save