Browse Source

More translations

master
Eduard Kuzmenko 4 years ago
parent
commit
49a3fe551e
  1. 11
      src/components/appSelectPeers.ts
  2. 38
      src/components/chat/contextMenu.ts
  3. 12
      src/components/chat/selection.ts
  4. 12
      src/components/dialogsContextMenu.ts
  5. 10
      src/components/inputField.ts
  6. 3
      src/components/inputSearch.ts
  7. 4
      src/components/misc.ts
  8. 2
      src/components/popups/forward.ts
  9. 5
      src/components/popups/pickUser.ts
  10. 2
      src/components/privacySection.ts
  11. 33
      src/components/sidebarLeft/index.ts
  12. 11
      src/components/sidebarLeft/tabs/addMembers.ts
  13. 2
      src/components/sidebarLeft/tabs/blockedUsers.ts
  14. 9
      src/components/sidebarLeft/tabs/editFolder.ts
  15. 4
      src/components/sidebarLeft/tabs/includedChats.ts
  16. 13
      src/components/sidebarLeft/tabs/newChannel.ts
  17. 8
      src/components/sidebarLeft/tabs/newGroup.ts
  18. 2
      src/components/sidebarRight/tabs/groupPermissions.ts
  19. 2
      src/config/app.ts
  20. 8
      src/index.hbs
  21. 72
      src/lang.ts
  22. 6
      src/lib/appManagers/appDialogsManager.ts
  23. 26
      src/lib/appManagers/appMessagesManager.ts
  24. 6
      src/scss/partials/_leftSidebar.scss

11
src/components/appSelectPeers.ts

@ -11,6 +11,7 @@ import { FocusDirection } from "../helpers/fastSmoothScroll";
import CheckboxField from "./checkboxField"; import CheckboxField from "./checkboxField";
import appProfileManager from "../lib/appManagers/appProfileManager"; import appProfileManager from "../lib/appManagers/appProfileManager";
import { safeAssign } from "../helpers/object"; import { safeAssign } from "../helpers/object";
import { LangPackKey, _i18n } from "../lib/langPack";
type PeerType = 'contacts' | 'dialogs' | 'channelParticipants'; type PeerType = 'contacts' | 'dialogs' | 'channelParticipants';
@ -58,6 +59,8 @@ export default class AppSelectPeers {
private tempIds: {[k in keyof AppSelectPeers['loadedWhat']]: number} = {}; private tempIds: {[k in keyof AppSelectPeers['loadedWhat']]: number} = {};
private peerId = 0; private peerId = 0;
private placeholder: LangPackKey;
constructor(options: { constructor(options: {
appendTo: AppSelectPeers['appendTo'], appendTo: AppSelectPeers['appendTo'],
onChange?: AppSelectPeers['onChange'], onChange?: AppSelectPeers['onChange'],
@ -69,6 +72,7 @@ export default class AppSelectPeers {
multiSelect?: AppSelectPeers['multiSelect'], multiSelect?: AppSelectPeers['multiSelect'],
rippleEnabled?: boolean, rippleEnabled?: boolean,
avatarSize?: AppSelectPeers['avatarSize'], avatarSize?: AppSelectPeers['avatarSize'],
placeholder?: LangPackKey
}) { }) {
safeAssign(this, options); safeAssign(this, options);
@ -94,7 +98,12 @@ export default class AppSelectPeers {
this.input = document.createElement('input'); this.input = document.createElement('input');
this.input.classList.add('selector-search-input'); this.input.classList.add('selector-search-input');
this.input.placeholder = !this.peerType.includes('dialogs') ? 'Add People...' : 'Select chat'; if(this.placeholder) {
_i18n(this.input, this.placeholder, undefined, 'placeholder');
} else {
_i18n(this.input, !this.peerType.includes('dialogs') ? 'SendMessageTo' : 'SelectChat', undefined, 'placeholder');
}
this.input.type = 'text'; this.input.type = 'text';
if(this.multiSelect) { if(this.multiSelect) {

38
src/components/chat/contextMenu.ts

@ -13,6 +13,7 @@ import PopupPinMessage from "../popups/unpinMessage";
import { copyTextToClipboard } from "../../helpers/clipboard"; import { copyTextToClipboard } from "../../helpers/clipboard";
import PopupSendNow from "../popups/sendNow"; import PopupSendNow from "../popups/sendNow";
import { toast } from "../toast"; import { toast } from "../toast";
import I18n, { LangPackKey } from "../../lib/langPack";
export default class ChatContextMenu { export default class ChatContextMenu {
private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[]; private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[];
@ -150,19 +151,19 @@ export default class ChatContextMenu {
private init() { private init() {
this.buttons = [{ this.buttons = [{
icon: 'send2', icon: 'send2',
text: 'Send Now', text: 'Chat.Context.Scheduled.SendNow',
onClick: this.onSendScheduledClick, onClick: this.onSendScheduledClick,
verify: () => this.chat.type === 'scheduled' && !this.message.pFlags.is_outgoing verify: () => this.chat.type === 'scheduled' && !this.message.pFlags.is_outgoing
}, { }, {
icon: 'send2', icon: 'send2',
text: 'Send Now selected', text: 'Message.Context.Selection.SendNow',
onClick: this.onSendScheduledClick, onClick: this.onSendScheduledClick,
verify: () => this.chat.type === 'scheduled' && this.chat.selection.selectedMids.has(this.mid) && !this.chat.selection.selectionSendNowBtn.hasAttribute('disabled'), verify: () => this.chat.type === 'scheduled' && this.chat.selection.selectedMids.has(this.mid) && !this.chat.selection.selectionSendNowBtn.hasAttribute('disabled'),
notDirect: () => true, notDirect: () => true,
withSelection: true withSelection: true
}, { }, {
icon: 'schedule', icon: 'schedule',
text: 'Reschedule', text: 'Chat.Context.Scheduled.Reschedule',
onClick: () => { onClick: () => {
this.chat.input.scheduleSending(() => { this.chat.input.scheduleSending(() => {
this.appMessagesManager.editMessage(this.message, this.message.message, { this.appMessagesManager.editMessage(this.message, this.message.message, {
@ -195,30 +196,30 @@ export default class ChatContextMenu {
verify: () => !!this.message.message && !this.isTextSelected verify: () => !!this.message.message && !this.isTextSelected
}, { }, {
icon: 'copy', icon: 'copy',
text: 'Copy Selected Text', text: 'Chat.CopySelectedText',
onClick: this.onCopyClick, onClick: this.onCopyClick,
verify: () => !!this.message.message && this.isTextSelected verify: () => !!this.message.message && this.isTextSelected
}, { }, {
icon: 'copy', icon: 'copy',
text: 'Copy selected', text: 'Message.Context.Selection.Copy',
onClick: this.onCopyClick, onClick: this.onCopyClick,
verify: () => this.chat.selection.selectedMids.has(this.mid) && !![...this.chat.selection.selectedMids].find(mid => !!this.chat.getMessage(mid).message), verify: () => this.chat.selection.selectedMids.has(this.mid) && !![...this.chat.selection.selectedMids].find(mid => !!this.chat.getMessage(mid).message),
notDirect: () => true, notDirect: () => true,
withSelection: true withSelection: true
}, { }, {
icon: 'copy', icon: 'copy',
text: 'Copy Link', text: 'CopyLink',
onClick: this.onCopyAnchorLinkClick, onClick: this.onCopyAnchorLinkClick,
verify: () => this.isAnchorTarget, verify: () => this.isAnchorTarget,
withSelection: true withSelection: true
}, { }, {
icon: 'link', icon: 'link',
text: 'Copy Link', text: 'CopyLink',
onClick: this.onCopyLinkClick, onClick: this.onCopyLinkClick,
verify: () => this.appPeersManager.isChannel(this.peerId) && !this.message.pFlags.is_outgoing verify: () => this.appPeersManager.isChannel(this.peerId) && !this.message.pFlags.is_outgoing
}, { }, {
icon: 'pin', icon: 'pin',
text: 'Pin', text: 'Message.Context.Pin',
onClick: this.onPinClick, onClick: this.onPinClick,
verify: () => !this.message.pFlags.is_outgoing && verify: () => !this.message.pFlags.is_outgoing &&
this.message._ !== 'messageService' && this.message._ !== 'messageService' &&
@ -227,12 +228,12 @@ export default class ChatContextMenu {
this.chat.type !== 'scheduled', this.chat.type !== 'scheduled',
}, { }, {
icon: 'unpin', icon: 'unpin',
text: 'Unpin', text: 'Message.Context.Unpin',
onClick: this.onUnpinClick, onClick: this.onUnpinClick,
verify: () => this.message.pFlags.pinned && this.appPeersManager.canPinMessage(this.peerId), verify: () => this.message.pFlags.pinned && this.appPeersManager.canPinMessage(this.peerId),
}, { }, {
icon: 'revote', icon: 'revote',
text: 'Revote', text: 'Chat.Poll.Unvote',
onClick: this.onRetractVote, onClick: this.onRetractVote,
verify: () => { verify: () => {
const poll = this.message.media?.poll as Poll; const poll = this.message.media?.poll as Poll;
@ -241,7 +242,7 @@ export default class ChatContextMenu {
cancelEvent: true */ cancelEvent: true */
}, { }, {
icon: 'stop', icon: 'stop',
text: 'Stop poll', text: 'Chat.Poll.Stop',
onClick: this.onStopPoll, onClick: this.onStopPoll,
verify: () => { verify: () => {
const poll = this.message.media?.poll; const poll = this.message.media?.poll;
@ -255,7 +256,7 @@ export default class ChatContextMenu {
verify: () => this.chat.type !== 'scheduled' && !this.message.pFlags.is_outgoing && this.message._ !== 'messageService' verify: () => this.chat.type !== 'scheduled' && !this.message.pFlags.is_outgoing && this.message._ !== 'messageService'
}, { }, {
icon: 'forward', icon: 'forward',
text: 'Forward selected', text: 'Message.Context.Selection.Forward',
onClick: this.onForwardClick, onClick: this.onForwardClick,
verify: () => this.chat.selection.selectionForwardBtn && verify: () => this.chat.selection.selectionForwardBtn &&
this.chat.selection.selectedMids.has(this.mid) && this.chat.selection.selectedMids.has(this.mid) &&
@ -264,14 +265,14 @@ export default class ChatContextMenu {
withSelection: true withSelection: true
}, { }, {
icon: 'select', icon: 'select',
text: 'Select', text: 'Message.Context.Select',
onClick: this.onSelectClick, onClick: this.onSelectClick,
verify: () => !this.message.action && !this.chat.selection.selectedMids.has(this.mid) && this.isSelectable, verify: () => !this.message.action && !this.chat.selection.selectedMids.has(this.mid) && this.isSelectable,
notDirect: () => true, notDirect: () => true,
withSelection: true withSelection: true
}, { }, {
icon: 'select', icon: 'select',
text: 'Clear selection', text: 'Message.Context.Selection.Clear',
onClick: this.onClearSelectionClick, onClick: this.onClearSelectionClick,
verify: () => this.chat.selection.selectedMids.has(this.mid), verify: () => this.chat.selection.selectedMids.has(this.mid),
notDirect: () => true, notDirect: () => true,
@ -283,7 +284,7 @@ export default class ChatContextMenu {
verify: () => this.appMessagesManager.canDeleteMessage(this.message) verify: () => this.appMessagesManager.canDeleteMessage(this.message)
}, { }, {
icon: 'delete danger', icon: 'delete danger',
text: 'Delete selected', text: 'Message.Context.Selection.Delete',
onClick: this.onDeleteClick, onClick: this.onDeleteClick,
verify: () => this.chat.selection.selectedMids.has(this.mid) && !this.chat.selection.selectionDeleteBtn.hasAttribute('disabled'), verify: () => this.chat.selection.selectedMids.has(this.mid) && !this.chat.selection.selectionDeleteBtn.hasAttribute('disabled'),
notDirect: () => true, notDirect: () => true,
@ -334,14 +335,17 @@ export default class ChatContextMenu {
const username = this.appPeersManager.getPeerUsername(this.peerId); const username = this.appPeersManager.getPeerUsername(this.peerId);
const msgId = this.appMessagesManager.getServerMessageId(this.mid); const msgId = this.appMessagesManager.getServerMessageId(this.mid);
let url = 'https://t.me/'; let url = 'https://t.me/';
let key: LangPackKey;
if(username) { if(username) {
url += username + '/' + msgId; url += username + '/' + msgId;
toast('Link copied to clipboard.'); key = 'LinkCopied';
} else { } else {
url += 'c/' + Math.abs(this.peerId) + '/' + msgId; url += 'c/' + Math.abs(this.peerId) + '/' + msgId;
toast('This link will only work for chat members.'); key = 'LinkCopiedPrivateInfo';
} }
toast(I18n.format(key, true));
copyTextToClipboard(url); copyTextToClipboard(url);
}; };

12
src/components/chat/selection.ts

@ -15,6 +15,7 @@ import ListenerSetter from "../../helpers/listenerSetter";
import PopupSendNow from "../popups/sendNow"; import PopupSendNow from "../popups/sendNow";
import appNavigationController from "../appNavigationController"; import appNavigationController from "../appNavigationController";
import { isMobileSafari } from "../../helpers/userAgent"; import { isMobileSafari } from "../../helpers/userAgent";
import I18n, { i18n, _i18n } from "../../lib/langPack";
const MAX_SELECTION_LENGTH = 100; const MAX_SELECTION_LENGTH = 100;
//const MIN_CLICK_MOVE = 32; // minimum bubble height //const MIN_CLICK_MOVE = 32; // minimum bubble height
@ -214,7 +215,8 @@ export default class ChatSelection {
private updateContainer(forceSelection = false) { private updateContainer(forceSelection = false) {
if(!this.selectedMids.size && !forceSelection) return; if(!this.selectedMids.size && !forceSelection) return;
this.selectionCountEl.innerText = this.selectedMids.size + ' Message' + (this.selectedMids.size === 1 ? '' : 's'); this.selectionCountEl.textContent = '';
this.selectionCountEl.append(i18n('Chat.Selection.MessagesCount', [this.selectedMids.size]));
let cantForward = !this.selectedMids.size, cantDelete = !this.selectedMids.size, cantSend = !this.selectedMids.size; let cantForward = !this.selectedMids.size, cantDelete = !this.selectedMids.size, cantSend = !this.selectedMids.size;
for(const mid of this.selectedMids.values()) { for(const mid of this.selectedMids.values()) {
@ -348,7 +350,7 @@ export default class ChatSelection {
if(this.chat.type === 'scheduled') { if(this.chat.type === 'scheduled') {
this.selectionSendNowBtn = Button('btn-primary btn-transparent btn-short text-bold selection-container-send', {icon: 'send2'}); this.selectionSendNowBtn = Button('btn-primary btn-transparent btn-short text-bold selection-container-send', {icon: 'send2'});
this.selectionSendNowBtn.append('Send Now'); _i18n(this.selectionSendNowBtn, 'Chat.Context.Scheduled.SendNow');
this.listenerSetter.add(this.selectionSendNowBtn, 'click', () => { this.listenerSetter.add(this.selectionSendNowBtn, 'click', () => {
new PopupSendNow(this.bubbles.peerId, [...this.selectedMids], () => { new PopupSendNow(this.bubbles.peerId, [...this.selectedMids], () => {
this.cancelSelection(); this.cancelSelection();
@ -356,7 +358,7 @@ export default class ChatSelection {
}); });
} else { } else {
this.selectionForwardBtn = Button('btn-primary btn-transparent text-bold selection-container-forward', {icon: 'forward'}); this.selectionForwardBtn = Button('btn-primary btn-transparent text-bold selection-container-forward', {icon: 'forward'});
this.selectionForwardBtn.append('Forward'); _i18n(this.selectionForwardBtn, 'Forward');
this.listenerSetter.add(this.selectionForwardBtn, 'click', () => { this.listenerSetter.add(this.selectionForwardBtn, 'click', () => {
new PopupForward(this.bubbles.peerId, [...this.selectedMids], () => { new PopupForward(this.bubbles.peerId, [...this.selectedMids], () => {
this.cancelSelection(); this.cancelSelection();
@ -365,7 +367,7 @@ export default class ChatSelection {
} }
this.selectionDeleteBtn = Button('btn-primary btn-transparent danger text-bold selection-container-delete', {icon: 'delete'}); this.selectionDeleteBtn = Button('btn-primary btn-transparent danger text-bold selection-container-delete', {icon: 'delete'});
this.selectionDeleteBtn.append('Delete'); _i18n(this.selectionDeleteBtn, 'Delete');
this.listenerSetter.add(this.selectionDeleteBtn, 'click', () => { this.listenerSetter.add(this.selectionDeleteBtn, 'click', () => {
new PopupDeleteMessages(this.bubbles.peerId, [...this.selectedMids], this.chat.type, () => { new PopupDeleteMessages(this.bubbles.peerId, [...this.selectedMids], this.chat.type, () => {
this.cancelSelection(); this.cancelSelection();
@ -461,7 +463,7 @@ export default class ChatSelection {
} else { } else {
const diff = MAX_SELECTION_LENGTH - this.selectedMids.size - 1; const diff = MAX_SELECTION_LENGTH - this.selectedMids.size - 1;
if(diff < 0) { if(diff < 0) {
toast('Max selection count reached.'); toast(I18n.format('Chat.Selection.LimitToast', true));
return; return;
/* const it = this.selectedMids.values(); /* const it = this.selectedMids.values();
do { do {

12
src/components/dialogsContextMenu.ts

@ -18,7 +18,7 @@ export default class DialogsContextMenu {
private init() { private init() {
this.buttons = [{ this.buttons = [{
icon: 'unread', icon: 'unread',
text: 'Mark as unread', text: 'MarkAsUnread',
onClick: this.onUnreadClick, onClick: this.onUnreadClick,
verify: () => { verify: () => {
const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count); const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count);
@ -26,7 +26,7 @@ export default class DialogsContextMenu {
} }
}, { }, {
icon: 'readchats', icon: 'readchats',
text: 'Mark as read', text: 'MarkAsRead',
onClick: this.onUnreadClick, onClick: this.onUnreadClick,
verify: () => { verify: () => {
const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count); const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count);
@ -34,7 +34,7 @@ export default class DialogsContextMenu {
} }
}, { }, {
icon: 'pin', icon: 'pin',
text: 'Pin', text: 'ChatList.Context.Pin',
onClick: this.onPinClick, onClick: this.onPinClick,
verify: () => { verify: () => {
const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.filters[this.filterId].pinned_peers.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned; const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.filters[this.filterId].pinned_peers.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned;
@ -42,7 +42,7 @@ export default class DialogsContextMenu {
} }
}, { }, {
icon: 'unpin', icon: 'unpin',
text: 'Unpin', text: 'ChatList.Context.Unpin',
onClick: this.onPinClick, onClick: this.onPinClick,
verify: () => { verify: () => {
const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.filters[this.filterId].pinned_peers.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned; const isPinned = this.filterId > 1 ? appMessagesManager.filtersStorage.filters[this.filterId].pinned_peers.includes(this.dialog.peerId) : !!this.dialog.pFlags?.pinned;
@ -50,7 +50,7 @@ export default class DialogsContextMenu {
} }
}, { }, {
icon: 'mute', icon: 'mute',
text: 'Mute', text: 'ChatList.Context.Mute',
onClick: this.onMuteClick, onClick: this.onMuteClick,
verify: () => { verify: () => {
const isMuted = this.dialog.notify_settings && this.dialog.notify_settings.mute_until > (Date.now() / 1000 | 0); const isMuted = this.dialog.notify_settings && this.dialog.notify_settings.mute_until > (Date.now() / 1000 | 0);
@ -58,7 +58,7 @@ export default class DialogsContextMenu {
} }
}, { }, {
icon: 'unmute', icon: 'unmute',
text: 'Unmute', text: 'ChatList.Context.Unmute',
onClick: this.onMuteClick, onClick: this.onMuteClick,
verify: () => { verify: () => {
const isMuted = this.dialog.notify_settings && this.dialog.notify_settings.mute_until > (Date.now() / 1000 | 0); const isMuted = this.dialog.notify_settings && this.dialog.notify_settings.mute_until > (Date.now() / 1000 | 0);

10
src/components/inputField.ts

@ -1,7 +1,7 @@
import { getRichValue, isInputEmpty } from "../helpers/dom"; import { getRichValue, isInputEmpty } from "../helpers/dom";
import { debounce } from "../helpers/schedulers"; import { debounce } from "../helpers/schedulers";
import { checkRTL } from "../helpers/string"; import { checkRTL } from "../helpers/string";
import { i18n, LangPackKey } from "../lib/langPack"; import { i18n, LangPackKey, _i18n } from "../lib/langPack";
import RichTextProcessor from "../lib/richtextprocessor"; import RichTextProcessor from "../lib/richtextprocessor";
let init = () => { let init = () => {
@ -59,7 +59,7 @@ export enum InputState {
}; };
export type InputFieldOptions = { export type InputFieldOptions = {
placeholder?: string, placeholder?: LangPackKey,
label?: LangPackKey, label?: LangPackKey,
name?: string, name?: string,
maxLength?: number, maxLength?: number,
@ -134,11 +134,15 @@ class InputField {
} }
} else { } else {
this.container.innerHTML = ` this.container.innerHTML = `
<input type="text" ${name ? `name="${name}"` : ''} ${placeholder ? `placeholder="${placeholder}"` : ''} autocomplete="off" ${label ? 'required=""' : ''} class="input-field-input"> <input type="text" ${name ? `name="${name}"` : ''} autocomplete="off" ${label ? 'required=""' : ''} class="input-field-input">
`; `;
input = this.container.firstElementChild as HTMLElement; input = this.container.firstElementChild as HTMLElement;
input.addEventListener('input', () => checkAndSetRTL(input)); input.addEventListener('input', () => checkAndSetRTL(input));
if(placeholder) {
_i18n(input, placeholder, undefined, 'placeholder');
}
} }
if(label) { if(label) {

3
src/components/inputSearch.ts

@ -1,4 +1,5 @@
//import { getRichValue } from "../helpers/dom"; //import { getRichValue } from "../helpers/dom";
import { LangPackKey } from "../lib/langPack";
import InputField from "./inputField"; import InputField from "./inputField";
export default class InputSearch { export default class InputSearch {
@ -12,7 +13,7 @@ export default class InputSearch {
public onChange: (value: string) => void; public onChange: (value: string) => void;
public onClear: () => void; public onClear: () => void;
constructor(placeholder: string, onChange?: (value: string) => void) { constructor(placeholder: LangPackKey, onChange?: (value: string) => void) {
this.inputField = new InputField({ this.inputField = new InputField({
placeholder, placeholder,
plainText: true plainText: true

4
src/components/misc.ts

@ -123,13 +123,13 @@ export function formatPhoneNumber(str: string) {
return {formatted: str, country}; return {formatted: str, country};
} }
export function parseMenuButtonsTo(to: {[name: string]: HTMLElement}, elements: HTMLCollection | NodeListOf<HTMLElement>) { /* export function parseMenuButtonsTo(to: {[name: string]: HTMLElement}, elements: HTMLCollection | NodeListOf<HTMLElement>) {
Array.from(elements).forEach(el => { Array.from(elements).forEach(el => {
const match = el.className.match(/(?:^|\s)menu-(.+?)(?:$|\s)/); const match = el.className.match(/(?:^|\s)menu-(.+?)(?:$|\s)/);
if(!match) return; if(!match) return;
to[match[1]] = el as HTMLElement; to[match[1]] = el as HTMLElement;
}); });
} } */
let onMouseMove = (e: MouseEvent) => { let onMouseMove = (e: MouseEvent) => {
let rect = openedMenu.getBoundingClientRect(); let rect = openedMenu.getBoundingClientRect();

2
src/components/popups/forward.ts

@ -17,7 +17,7 @@ export default class PopupForward extends PopupPickUser {
appImManager.chat.input.initMessagesForward(fromPeerId, mids.slice()); appImManager.chat.input.initMessagesForward(fromPeerId, mids.slice());
}, },
onClose, onClose,
placeholder: 'Forward to...', placeholder: 'ShareModal.Search.ForwardPlaceholder',
chatRightsAction: 'send_messages' chatRightsAction: 'send_messages'
}); });
} }

5
src/components/popups/pickUser.ts

@ -1,6 +1,7 @@
import { isTouchSupported } from "../../helpers/touchSupport"; import { isTouchSupported } from "../../helpers/touchSupport";
import AppSelectPeers from "../appSelectPeers"; import AppSelectPeers from "../appSelectPeers";
import PopupElement from "."; import PopupElement from ".";
import { LangPackKey, _i18n } from "../../lib/langPack";
export default class PopupPickUser extends PopupElement { export default class PopupPickUser extends PopupElement {
protected selector: AppSelectPeers; protected selector: AppSelectPeers;
@ -9,7 +10,7 @@ export default class PopupPickUser extends PopupElement {
peerTypes: AppSelectPeers['peerType'], peerTypes: AppSelectPeers['peerType'],
onSelect?: (peerId: number) => Promise<void> | void, onSelect?: (peerId: number) => Promise<void> | void,
onClose?: () => void, onClose?: () => void,
placeholder: string, placeholder: LangPackKey,
chatRightsAction?: AppSelectPeers['chatRightsAction'], chatRightsAction?: AppSelectPeers['chatRightsAction'],
peerId?: number, peerId?: number,
}) { }) {
@ -47,11 +48,11 @@ export default class PopupPickUser extends PopupElement {
rippleEnabled: false, rippleEnabled: false,
avatarSize: 46, avatarSize: 46,
peerId: options.peerId, peerId: options.peerId,
placeholder: options.placeholder
}); });
//this.scrollable = new Scrollable(this.body); //this.scrollable = new Scrollable(this.body);
this.selector.input.placeholder = options.placeholder;
this.title.append(this.selector.input); this.title.append(this.selector.input);
} }
} }

2
src/components/privacySection.ts

@ -117,7 +117,7 @@ export default class PrivacySection {
type: 'privacy', type: 'privacy',
skippable: true, skippable: true,
title: exception.titleLangKey, title: exception.titleLangKey,
placeholder: 'Add Users or Groups...', placeholder: 'PrivacyModal.Search.Placeholder',
takeOut: (newPeerIds) => { takeOut: (newPeerIds) => {
_peerIds.length = 0; _peerIds.length = 0;
_peerIds.push(...newPeerIds); _peerIds.push(...newPeerIds);

33
src/components/sidebarLeft/index.ts

@ -8,7 +8,6 @@ import rootScope from "../../lib/rootScope";
import { attachClickEvent, findUpClassName, findUpTag } from "../../helpers/dom"; import { attachClickEvent, findUpClassName, findUpTag } from "../../helpers/dom";
import { SearchGroup } from "../appSearch"; import { SearchGroup } from "../appSearch";
import "../avatar"; import "../avatar";
import { parseMenuButtonsTo } from "../misc";
import Scrollable, { ScrollableX } from "../scrollable"; import Scrollable, { ScrollableX } from "../scrollable";
import InputSearch from "../inputSearch"; import InputSearch from "../inputSearch";
import SidebarSlider from "../slider"; import SidebarSlider from "../slider";
@ -69,8 +68,8 @@ export class AppSidebarLeft extends SidebarSlider {
takeOut: (peerIds) => { takeOut: (peerIds) => {
new AppNewGroupTab(this).open(peerIds); new AppNewGroupTab(this).open(peerIds);
}, },
title: 'Add Members', title: 'GroupAddMembers',
placeholder: 'Add People...' placeholder: 'SendMessageTo'
}); });
}; };
@ -126,23 +125,33 @@ export class AppSidebarLeft extends SidebarSlider {
this.menuEl = this.toolsBtn.querySelector('.btn-menu'); this.menuEl = this.toolsBtn.querySelector('.btn-menu');
this.newBtnMenu = this.sidebarEl.querySelector('#new-menu'); this.newBtnMenu = this.sidebarEl.querySelector('#new-menu');
const _newBtnMenu = ButtonMenu([{
icon: 'newchannel',
text: 'NewChannel',
onClick: () => {
new AppNewChannelTab(this).open();
}
}, {
icon: 'newgroup',
text: 'NewGroup',
onClick: onNewGroupClick
}, {
icon: 'newprivate',
text: 'NewPrivateChat',
onClick: onContactsClick
}]);
_newBtnMenu.classList.add('top-left');
this.newBtnMenu.append(_newBtnMenu);
this.inputSearch.input.addEventListener('focus', () => this.initSearch(), {once: true}); this.inputSearch.input.addEventListener('focus', () => this.initSearch(), {once: true});
parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children); //parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
this.archivedCount = document.createElement('span'); this.archivedCount = document.createElement('span');
this.archivedCount.className = 'archived-count badge badge-24 badge-gray'; this.archivedCount.className = 'archived-count badge badge-24 badge-gray';
btnArchive.element.append(this.archivedCount); btnArchive.element.append(this.archivedCount);
attachClickEvent(this.newButtons.privateChat, onContactsClick);
attachClickEvent(this.newButtons.channel, (e) => {
new AppNewChannelTab(this).open();
});
attachClickEvent(this.newButtons.group, onNewGroupClick);
rootScope.on('dialogs_archived_unread', (e) => { rootScope.on('dialogs_archived_unread', (e) => {
this.archivedCount.innerText = '' + formatNumber(e.count, 1); this.archivedCount.innerText = '' + formatNumber(e.count, 1);
this.archivedCount.classList.toggle('hide', !e.count); this.archivedCount.classList.toggle('hide', !e.count);

11
src/components/sidebarLeft/tabs/addMembers.ts

@ -2,6 +2,7 @@ import { SliderSuperTab } from "../../slider";
import AppSelectPeers from "../../appSelectPeers"; import AppSelectPeers from "../../appSelectPeers";
import { putPreloader } from "../../misc"; import { putPreloader } from "../../misc";
import Button from "../../button"; import Button from "../../button";
import { LangPackKey, _i18n } from "../../../lib/langPack";
export default class AppAddMembersTab extends SliderSuperTab { export default class AppAddMembersTab extends SliderSuperTab {
private nextBtn: HTMLButtonElement; private nextBtn: HTMLButtonElement;
@ -40,8 +41,8 @@ export default class AppAddMembersTab extends SliderSuperTab {
} }
public open(options: { public open(options: {
title: string, title: LangPackKey,
placeholder: string, placeholder: LangPackKey,
peerId?: number, peerId?: number,
type: AppAddMembersTab['peerType'], type: AppAddMembersTab['peerType'],
takeOut?: AppAddMembersTab['takeOut'], takeOut?: AppAddMembersTab['takeOut'],
@ -50,7 +51,7 @@ export default class AppAddMembersTab extends SliderSuperTab {
}) { }) {
const ret = super.open(); const ret = super.open();
this.title.innerHTML = options.title; this.setTitle(options.title);
this.peerType = options.type; this.peerType = options.type;
this.takeOut = options.takeOut; this.takeOut = options.takeOut;
this.skippable = options.skippable; this.skippable = options.skippable;
@ -60,9 +61,9 @@ export default class AppAddMembersTab extends SliderSuperTab {
onChange: this.skippable ? null : (length) => { onChange: this.skippable ? null : (length) => {
this.nextBtn.classList.toggle('is-visible', !!length); this.nextBtn.classList.toggle('is-visible', !!length);
}, },
peerType: ['contacts'] peerType: ['contacts'],
placeholder: options.placeholder
}); });
this.selector.input.placeholder = options.placeholder;
if(options.selectedPeerIds) { if(options.selectedPeerIds) {
this.selector.addInitial(options.selectedPeerIds); this.selector.addInitial(options.selectedPeerIds);

2
src/components/sidebarLeft/tabs/blockedUsers.ts

@ -31,7 +31,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
attachClickEvent(btnAdd, (e) => { attachClickEvent(btnAdd, (e) => {
new PopupPickUser({ new PopupPickUser({
peerTypes: ['contacts'], peerTypes: ['contacts'],
placeholder: 'Block user...', placeholder: 'BlockModal.Search.Placeholder',
onSelect: (peerId) => { onSelect: (peerId) => {
//console.log('block', peerId); //console.log('block', peerId);
appUsersManager.toggleBlock(peerId, true); appUsersManager.toggleBlock(peerId, true);

9
src/components/sidebarLeft/tabs/editFolder.ts

@ -275,7 +275,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
} }
if(peers.length) { if(peers.length) {
showMore.innerHTML = `<div class="tgico-down"></div><div>Show ${Math.min(20, peers.length)} more chat${peers.length > 1 ? 's' : ''}</div>`; showMore.lastElementChild.replaceWith(i18n('FilterShowMoreChats', [peers.length]));
} else if(showMore) { } else if(showMore) {
showMore.remove(); showMore.remove();
} }
@ -286,11 +286,14 @@ export default class AppEditFolderTab extends SliderSuperTab {
let showMore: HTMLElement; let showMore: HTMLElement;
if(peers.length) { if(peers.length) {
showMore = document.createElement('div'); showMore = document.createElement('div');
showMore.classList.add('show-more'); showMore.classList.add('show-more', 'rp-overflow');
showMore.addEventListener('click', () => renderMore(20)); showMore.addEventListener('click', () => renderMore(20));
showMore.innerHTML = `<div class="tgico-down"></div><div>Show ${Math.min(20, peers.length)} more chat${peers.length > 1 ? 's' : ''}</div>`;
ripple(showMore); ripple(showMore);
const down = document.createElement('div');
down.classList.add('tgico-down');
showMore.append(down, i18n('FilterShowMoreChats', [peers.length]));
container.append(showMore); container.append(showMore);
} }

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

@ -211,10 +211,10 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
appendTo: this.container, appendTo: this.container,
onChange: this.onSelectChange, onChange: this.onSelectChange,
peerType: ['dialogs'], peerType: ['dialogs'],
renderResultsFunc: this.renderResults renderResultsFunc: this.renderResults,
placeholder: 'Search'
}); });
this.selector.selected = new Set(selectedPeers); this.selector.selected = new Set(selectedPeers);
this.selector.input.placeholder = 'Search';
const _add = this.selector.add.bind(this.selector); const _add = this.selector.add.bind(this.selector);
this.selector.add = (peerId, title, scroll) => { this.selector.add = (peerId, title, scroll) => {

13
src/components/sidebarLeft/tabs/newChannel.ts

@ -6,6 +6,7 @@ import InputField from "../../inputField";
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit"; import AvatarEdit from "../../avatarEdit";
import AppAddMembersTab from "./addMembers"; import AppAddMembersTab from "./addMembers";
import { _i18n } from "../../../lib/langPack";
export default class AppNewChannelTab extends SliderSuperTab { export default class AppNewChannelTab extends SliderSuperTab {
private uploadAvatar: () => Promise<InputFile> = null; private uploadAvatar: () => Promise<InputFile> = null;
@ -17,7 +18,7 @@ export default class AppNewChannelTab extends SliderSuperTab {
protected init() { protected init() {
this.container.classList.add('new-channel-container'); this.container.classList.add('new-channel-container');
this.title.innerText = 'New Channel'; this.setTitle('NewChannel');
this.avatarEdit = new AvatarEdit((_upload) => { this.avatarEdit = new AvatarEdit((_upload) => {
this.uploadAvatar = _upload; this.uploadAvatar = _upload;
@ -27,12 +28,12 @@ export default class AppNewChannelTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.channelNameInputField = new InputField({ this.channelNameInputField = new InputField({
label: 'Channel Name', label: 'Channel.ChannelNameHolder',
maxLength: 128 maxLength: 128
}); });
this.channelDescriptionInputField = new InputField({ this.channelDescriptionInputField = new InputField({
label: 'Description (optional)', label: 'Channel.DescriptionPlaceholder',
maxLength: 255 maxLength: 255
}); });
@ -49,7 +50,7 @@ export default class AppNewChannelTab extends SliderSuperTab {
const caption = document.createElement('div'); const caption = document.createElement('div');
caption.classList.add('caption'); caption.classList.add('caption');
caption.innerText = 'You can provide an optional description for your channel.'; _i18n(caption, 'Channel.DescriptionHolderDescrpiton');
this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'}); this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'});
@ -70,8 +71,8 @@ export default class AppNewChannelTab extends SliderSuperTab {
peerId: channelId, peerId: channelId,
type: 'channel', type: 'channel',
skippable: true, skippable: true,
title: 'Add Members', title: 'GroupAddMembers',
placeholder: 'Add People...', placeholder: 'SendMessageTo',
takeOut: (peerIds) => { takeOut: (peerIds) => {
return appChatsManager.inviteToChannel(Math.abs(channelId), peerIds); return appChatsManager.inviteToChannel(Math.abs(channelId), peerIds);
} }

8
src/components/sidebarLeft/tabs/newGroup.ts

@ -8,6 +8,7 @@ import Button from "../../button";
import InputField from "../../inputField"; import InputField from "../../inputField";
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit"; import AvatarEdit from "../../avatarEdit";
import { i18n } from "../../../lib/langPack";
export default class AppNewGroupTab extends SliderSuperTab { export default class AppNewGroupTab extends SliderSuperTab {
private searchGroup = new SearchGroup(' ', 'contacts', true, 'new-group-members disable-hover', false); private searchGroup = new SearchGroup(' ', 'contacts', true, 'new-group-members disable-hover', false);
@ -19,7 +20,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
protected init() { protected init() {
this.container.classList.add('new-group-container'); this.container.classList.add('new-group-container');
this.title.innerText = 'New Group'; this.setTitle('NewGroup');
this.avatarEdit = new AvatarEdit((_upload) => { this.avatarEdit = new AvatarEdit((_upload) => {
this.uploadAvatar = _upload; this.uploadAvatar = _upload;
@ -29,7 +30,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.groupNameInputField = new InputField({ this.groupNameInputField = new InputField({
label: 'Group Name', label: 'CreateGroup.NameHolder',
maxLength: 128 maxLength: 128
}); });
@ -99,7 +100,8 @@ export default class AppNewGroupTab extends SliderSuperTab {
} }
}); });
this.searchGroup.nameEl.innerText = this.userIds.length + ' members'; this.searchGroup.nameEl.textContent = '';
this.searchGroup.nameEl.append(i18n('Members', [this.userIds.length]));
this.searchGroup.setActive(); this.searchGroup.setActive();
}); });

2
src/components/sidebarRight/tabs/groupPermissions.ts

@ -154,7 +154,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
openPermissions(peerId); openPermissions(peerId);
}, 0); }, 0);
}, },
placeholder: 'Add Exception...', placeholder: 'ExceptionModal.Search.Placeholder',
peerId: -this.chatId, peerId: -this.chatId,
}); });
} }

2
src/config/app.ts

@ -2,7 +2,7 @@ const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.4.0', version: '0.4.0',
langPackVersion: '0.0.2', langPackVersion: '0.0.3',
domains: [] as string[], domains: [] as string[],
baseDcId: 2 baseDcId: 2
}; };

8
src/index.hbs

@ -117,13 +117,7 @@
</div> </div>
</div> </div>
<div class="transition-item sidebar-search" id="search-container"></div> <div class="transition-item sidebar-search" id="search-container"></div>
<button class="btn-circle rp btn-corner tgico-newchat_filled btn-menu-toggle" id="new-menu"> <button class="btn-circle rp btn-corner tgico-newchat_filled btn-menu-toggle" id="new-menu"></button>
<div class="btn-menu top-left">
<div class="btn-menu-item menu-channel tgico-newchannel rp">New Channel</div>
<div class="btn-menu-item menu-group tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-privateChat tgico-newprivate rp">New Private Chat</div>
</div>
</button>
</div> </div>
</div> </div>
</div> </div>

72
src/lang.ts

@ -1,4 +1,6 @@
const lang = { const lang = {
"AttachAlbum": "Album",
"BlockModal.Search.Placeholder": "Block user...",
"FilterIncludeExcludeInfo": "Choose chats and types of chats that will\nappear and never appear in this folder.", "FilterIncludeExcludeInfo": "Choose chats and types of chats that will\nappear and never appear in this folder.",
"FilterNameInputLabel": "Folder Name", "FilterNameInputLabel": "Folder Name",
"FilterMenuDelete": "Delete Folder", "FilterMenuDelete": "Delete Folder",
@ -20,7 +22,13 @@ const lang = {
"EditProfile.Username.Taken": "Username is already taken", "EditProfile.Username.Taken": "Username is already taken",
"EditProfile.Username.Invalid": "Username is invalid", "EditProfile.Username.Invalid": "Username is invalid",
"EditProfile.Username.Help": "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\nYou can use a–z, 0–9 and underscores. Minimum length is 5 characters.", "EditProfile.Username.Help": "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\nYou can use a–z, 0–9 and underscores. Minimum length is 5 characters.",
"ExceptionModal.Search.Placeholder": "Add exception...",
"ChatList.Menu.Archived": "Archived", "ChatList.Menu.Archived": "Archived",
"Chat.Selection.MessagesCount": {
"one_value": "%d Message",
"other_value": "%d Messages",
},
"Chat.Selection.LimitToast": "Max selection count reached.",
"Saved": "Saved", "Saved": "Saved",
"General.Keyboard": "Keyboard", "General.Keyboard": "Keyboard",
"General.SendShortcut.Enter": "Send by Enter", "General.SendShortcut.Enter": "Send by Enter",
@ -33,12 +41,19 @@ const lang = {
"ChatBackground.Blur": "Blur Wallpaper Image", "ChatBackground.Blur": "Blur Wallpaper Image",
"Notifications.Sound": "Notification Sound", "Notifications.Sound": "Notification Sound",
"Notifications.MessagePreview": "Message preview", "Notifications.MessagePreview": "Message preview",
"NewPrivateChat": "New Private Chat",
"Message.Context.Selection.Copy": "Copy selected",
"Message.Context.Selection.Clear": "Clear selection",
"Message.Context.Selection.Delete": "Delete selected",
"Message.Context.Selection.Forward": "Forward selected",
"Message.Context.Selection.SendNow": "Send Now selected",
"Checkbox.Enabled": "Enabled", "Checkbox.Enabled": "Enabled",
"Checkbox.Disabled": "Disabled", "Checkbox.Disabled": "Disabled",
"Privacy.Devices": { "Privacy.Devices": {
"one_value": "%1$d device", "one_value": "%1$d device",
"other_value": "%1$d devices" "other_value": "%1$d devices"
}, },
"PrivacyModal.Search.Placeholder": "Add Users or Groups...",
// * android // * android
"ActionCreateChannel": "Channel created", "ActionCreateChannel": "Channel created",
@ -56,8 +71,21 @@ const lang = {
"ActionInviteUser": "un1 joined the group via invite link", "ActionInviteUser": "un1 joined the group via invite link",
"ActionPinnedNoText": "un1 pinned a message", "ActionPinnedNoText": "un1 pinned a message",
"ActionMigrateFromGroup": "This group was upgraded to a supergroup", "ActionMigrateFromGroup": "This group was upgraded to a supergroup",
"AttachPhoto": "Photo",
"AttachVideo": "Video",
"AttachGif": "GIF",
"AttachLocation": "Location",
"AttachLiveLocation": "Live Location",
"AttachContact": "Contact",
"AttachDocument": "File",
"AttachSticker": "Sticker",
"AttachAudio": "Voice message",
"AttachRound": "Video message",
"AttachGame": "Game",
//"ChannelJoined": "You joined this channel", //"ChannelJoined": "You joined this channel",
"ChannelMegaJoined": "You joined this group", "ChannelMegaJoined": "You joined this group",
"Channel.DescriptionPlaceholder": "Description (optional)",
"Draft": "Draft",
"FilterAlwaysShow": "Include Chats", "FilterAlwaysShow": "Include Chats",
"FilterNeverShow": "Exclude Chats", "FilterNeverShow": "Exclude Chats",
"FilterInclude": "Included Chats", "FilterInclude": "Included Chats",
@ -67,6 +95,11 @@ const lang = {
"FilterNew": "New Folder", "FilterNew": "New Folder",
"Filters": "Folders", "Filters": "Folders",
"FilterRecommended": "Recommended Folders", "FilterRecommended": "Recommended Folders",
"FilterShowMoreChats": {
"one_value": "Show %1$d More Chat",
"other_value": "Show %1$d More Chats"
},
"FromYou": "You",
"Add": "Add", "Add": "Add",
"Chats": { "Chats": {
"one_value": "%1$d chat", "one_value": "%1$d chat",
@ -84,7 +117,12 @@ const lang = {
"one_value": "%1$d user", "one_value": "%1$d user",
"other_value": "%1$d users" "other_value": "%1$d users"
}, },
"Members": {
"one_value": "%1$d member",
"other_value": "%1$d members"
},
"UsernameHelpLink": "This link opens a chat with you:\n%1$s", "UsernameHelpLink": "This link opens a chat with you:\n%1$s",
"NewChannel": "New Channel",
"NewGroup": "New Group", "NewGroup": "New Group",
"Contacts": "Contacts", "Contacts": "Contacts",
"SavedMessages": "Saved Messages", "SavedMessages": "Saved Messages",
@ -108,6 +146,8 @@ const lang = {
"NotificationsGroups": "Groups", "NotificationsGroups": "Groups",
"NotificationsChannels": "Channels", "NotificationsChannels": "Channels",
"NotificationsOther": "Other", "NotificationsOther": "Other",
"MarkAsUnread": "Mark as unread",
"MarkAsRead": "Mark as read",
"ContactJoined": "Contact joined Telegram", "ContactJoined": "Contact joined Telegram",
"Loading": "Loading...", "Loading": "Loading...",
"Unblock": "Unblock", "Unblock": "Unblock",
@ -143,6 +183,20 @@ const lang = {
"ArchivedChats": "Archived Chats", "ArchivedChats": "Archived Chats",
"Cancel": "Cancel", "Cancel": "Cancel",
"HistoryCleared": "History was cleared", "HistoryCleared": "History was cleared",
"Archive": "Archive",
"Unarchive": "Unarchive",
"Delete": "Delete",
"Reply": "Reply",
"Edit": "Edit",
"Forward": "Forward",
"CopyLink": "Copy Link",
"Copy": "Copy",
"Search": "Search",
"LinkCopied": "Link copied to clipboard",
"LinkCopiedPrivateInfo": "This link will only work for members of this chat.",
"GroupAddMembers": "Add Members",
"SendMessageTo": "Add people...",
"SelectChat": "Select Chat",
// * macos // * macos
"AccountSettings.Filters": "Chat Folders", "AccountSettings.Filters": "Chat Folders",
@ -150,6 +204,7 @@ const lang = {
"AccountSettings.PrivacyAndSecurity": "Privacy and Security", "AccountSettings.PrivacyAndSecurity": "Privacy and Security",
"AccountSettings.Language": "Language", "AccountSettings.Language": "Language",
"Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco", "Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco",
"Chat.CopySelectedText": "Copy Selected Text",
"Chat.Date.ScheduledFor": "Scheduled for %@", "Chat.Date.ScheduledFor": "Scheduled for %@",
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online", //"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
"Chat.Date.ScheduledForToday": "Scheduled for today", "Chat.Date.ScheduledForToday": "Scheduled for today",
@ -159,6 +214,14 @@ const lang = {
"Chat.Service.Channel.RemovedPhoto": "Channel photo removed", "Chat.Service.Channel.RemovedPhoto": "Channel photo removed",
"Chat.Service.Channel.UpdatedVideo": "Channel video updated", "Chat.Service.Channel.UpdatedVideo": "Channel video updated",
"Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@", "Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@",
"Chat.Poll.Unvote": "Retract Vote",
"Chat.Poll.Stop": "Stop Poll",
"Chat.Poll.Stop.Confirm.Header": "Stop Poll?",
"Chat.Poll.Stop.Confirm.Text": "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone.",
"ChatList.Context.Mute": "Mute",
"ChatList.Context.Unmute": "Unmute",
"ChatList.Context.Pin": "Pin",
"ChatList.Context.Unpin": "Unpin",
"ChatList.Service.Call.incoming": "Incoming Call (%@)", "ChatList.Service.Call.incoming": "Incoming Call (%@)",
"ChatList.Service.Call.outgoing": "Outgoing Call (%@)", "ChatList.Service.Call.outgoing": "Outgoing Call (%@)",
"ChatList.Service.Call.Cancelled": "Cancelled Call", "ChatList.Service.Call.Cancelled": "Cancelled Call",
@ -177,6 +240,9 @@ const lang = {
"ChatList.Filter.MutedChats": "Muted", "ChatList.Filter.MutedChats": "Muted",
"ChatList.Filter.ReadChats": "Read", "ChatList.Filter.ReadChats": "Read",
"ChatList.Filter.Archive": "Archived", "ChatList.Filter.Archive": "Archived",
"Channel.ChannelNameHolder": "Channel Name",
"Channel.DescriptionHolderDescrpiton": "You can provide an optional description for your channel.",
"CreateGroup.NameHolder": "Group Name",
"Date.Today": "Today", "Date.Today": "Today",
"EditAccount.Username": "Username", "EditAccount.Username": "Username",
"EditAccount.Title": "Edit Profile", "EditAccount.Title": "Edit Profile",
@ -186,6 +252,7 @@ const lang = {
"Telegram.InstalledStickerPacksController": "Stickers", "Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications", "Telegram.NotificationSettingsViewController": "Notifications",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji", "Stickers.SuggestStickers": "Suggest Stickers by Emoji",
"ShareModal.Search.ForwardPlaceholder": "Forward to...",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers", "InstalledStickers.LoopAnimated": "Loop Animated Stickers",
"PrivacyAndSecurity.Item.On": "On", "PrivacyAndSecurity.Item.On": "On",
"PrivacyAndSecurity.Item.Off": "Off", "PrivacyAndSecurity.Item.Off": "Off",
@ -211,7 +278,10 @@ const lang = {
"one_value": "%d user", "one_value": "%d user",
"other_value": "%d users" "other_value": "%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." "RcentSessions.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.",
"Message.Context.Select": "Select",
"Message.Context.Pin": "Pin",
"Message.Context.Unpin": "Unpin",
}; };
export default lang; export default lang;

6
src/lib/appManagers/appDialogsManager.ts

@ -29,6 +29,7 @@ import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
import appNotificationsManager from "./appNotificationsManager"; import appNotificationsManager from "./appNotificationsManager";
import { InputNotifyPeer } from "../../layer"; import { InputNotifyPeer } from "../../layer";
import PeerTitle from "../../components/peerTitle"; import PeerTitle from "../../components/peerTitle";
import { i18n } from "../langPack";
type DialogDom = { type DialogDom = {
avatarEl: AvatarElement, avatarEl: AvatarElement,
@ -1069,7 +1070,8 @@ export class AppDialogsManager {
if(draftMessage) { if(draftMessage) {
const bold = document.createElement('b'); const bold = document.createElement('b');
bold.classList.add('danger'); bold.classList.add('danger');
bold.innerHTML = 'Draft: '; bold.append(i18n('Draft'));
bold.append(': ');
dom.lastMessageSpan.prepend(bold); dom.lastMessageSpan.prepend(bold);
} else if(peer._ !== 'peerUser' && peerId !== lastMessage.fromId && !lastMessage.action) { } else if(peer._ !== 'peerUser' && peerId !== lastMessage.fromId && !lastMessage.action) {
const sender = appPeersManager.getPeer(lastMessage.fromId); const sender = appPeersManager.getPeer(lastMessage.fromId);
@ -1077,7 +1079,7 @@ export class AppDialogsManager {
const senderBold = document.createElement('b'); const senderBold = document.createElement('b');
if(sender.id === rootScope.myId) { if(sender.id === rootScope.myId) {
senderBold.innerHTML = 'You'; senderBold.append(i18n('FromYou'));
} else { } else {
//str = sender.first_name || sender.last_name || sender.username; //str = sender.first_name || sender.last_name || sender.username;
senderBold.append(new PeerTitle({ senderBold.append(new PeerTitle({

26
src/lib/appManagers/appMessagesManager.ts

@ -8,7 +8,7 @@ import { randomLong } from "../../helpers/random";
import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string"; import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string";
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer"; import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer";
import { InvokeApiOptions } from "../../types"; import { InvokeApiOptions } from "../../types";
import I18n, { join, langPack, LangPackKey, _i18n } from "../langPack"; import I18n, { i18n, join, langPack, LangPackKey, _i18n } from "../langPack";
import { logger, LogLevels } from "../logger"; import { logger, LogLevels } from "../logger";
import type { ApiFileManager } from '../mtproto/apiFileManager'; import type { ApiFileManager } from '../mtproto/apiFileManager';
//import apiManager from '../mtproto/apiManager'; //import apiManager from '../mtproto/apiManager';
@ -2505,10 +2505,6 @@ export class AppMessagesManager {
const parts: (HTMLElement | string)[] = []; const parts: (HTMLElement | string)[] = [];
const addPart = (part: string | HTMLElement, text?: string) => { const addPart = (part: string | HTMLElement, text?: string) => {
if(text) {
part += ', ';
}
if(plain) { if(plain) {
parts.push(part); parts.push(part);
} else { } else {
@ -2517,6 +2513,10 @@ export class AppMessagesManager {
else el.append(part); else el.append(part);
parts.push(el); parts.push(el);
} }
if(text) {
parts.push(', ');
}
}; };
if(message.media) { if(message.media) {
@ -2538,7 +2538,7 @@ export class AppMessagesManager {
if(usingFullAlbum) { if(usingFullAlbum) {
text = this.getAlbumText(message.grouped_id).message; text = this.getAlbumText(message.grouped_id).message;
addPart('Album', text); addPart(i18n('AttachAlbum'), text);
} }
} else { } else {
usingFullAlbum = false; usingFullAlbum = false;
@ -2548,31 +2548,31 @@ export class AppMessagesManager {
const media = message.media; const media = message.media;
switch(media._) { switch(media._) {
case 'messageMediaPhoto': case 'messageMediaPhoto':
addPart('Photo', message.message); addPart(i18n('AttachPhoto'), message.message);
break; break;
case 'messageMediaDice': case 'messageMediaDice':
addPart(plain ? media.emoticon : RichTextProcessor.wrapEmojiText(media.emoticon)); addPart(plain ? media.emoticon : RichTextProcessor.wrapEmojiText(media.emoticon));
break; break;
case 'messageMediaGeo': case 'messageMediaGeo':
addPart('Geolocation'); addPart(i18n('AttachLiveLocation'));
break; break;
case 'messageMediaPoll': case 'messageMediaPoll':
addPart(plain ? '📊' + ' ' + (media.poll.question || 'poll') : media.poll.rReply); addPart(plain ? '📊' + ' ' + (media.poll.question || 'poll') : media.poll.rReply);
break; break;
case 'messageMediaContact': case 'messageMediaContact':
addPart('Contact'); addPart(i18n('AttachContact'));
break; break;
case 'messageMediaDocument': case 'messageMediaDocument':
let document = media.document; let document = media.document;
if(document.type === 'video') { if(document.type === 'video') {
addPart('Video', message.message); addPart(i18n('AttachVideo'), message.message);
} else if(document.type === 'voice') { } else if(document.type === 'voice') {
addPart('Voice message', message.message); addPart(i18n('AttachAudio'), message.message);
} else if(document.type === 'gif') { } else if(document.type === 'gif') {
addPart('GIF', message.message); addPart(i18n('AttachGif'), message.message);
} else if(document.type === 'round') { } else if(document.type === 'round') {
addPart('Video message', message.message); addPart(i18n('AttachRound'), message.message);
} else if(document.type === 'sticker') { } else if(document.type === 'sticker') {
addPart(((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker'); addPart(((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker');
text = ''; text = '';

6
src/scss/partials/_leftSidebar.scss

@ -459,6 +459,12 @@
} }
} }
.new-group-container {
.search-group {
margin-top: .5rem;
}
}
.edit-folder-container { .edit-folder-container {
.folder-category-button:nth-child(n+2) { .folder-category-button:nth-child(n+2) {
pointer-events: none; pointer-events: none;

Loading…
Cancel
Save