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"; @@ -11,6 +11,7 @@ import { FocusDirection } from "../helpers/fastSmoothScroll";
import CheckboxField from "./checkboxField";
import appProfileManager from "../lib/appManagers/appProfileManager";
import { safeAssign } from "../helpers/object";
import { LangPackKey, _i18n } from "../lib/langPack";
type PeerType = 'contacts' | 'dialogs' | 'channelParticipants';
@ -57,6 +58,8 @@ export default class AppSelectPeers { @@ -57,6 +58,8 @@ export default class AppSelectPeers {
private tempIds: {[k in keyof AppSelectPeers['loadedWhat']]: number} = {};
private peerId = 0;
private placeholder: LangPackKey;
constructor(options: {
appendTo: AppSelectPeers['appendTo'],
@ -69,6 +72,7 @@ export default class AppSelectPeers { @@ -69,6 +72,7 @@ export default class AppSelectPeers {
multiSelect?: AppSelectPeers['multiSelect'],
rippleEnabled?: boolean,
avatarSize?: AppSelectPeers['avatarSize'],
placeholder?: LangPackKey
}) {
safeAssign(this, options);
@ -94,7 +98,12 @@ export default class AppSelectPeers { @@ -94,7 +98,12 @@ export default class AppSelectPeers {
this.input = document.createElement('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';
if(this.multiSelect) {

38
src/components/chat/contextMenu.ts

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

12
src/components/chat/selection.ts

@ -15,6 +15,7 @@ import ListenerSetter from "../../helpers/listenerSetter"; @@ -15,6 +15,7 @@ import ListenerSetter from "../../helpers/listenerSetter";
import PopupSendNow from "../popups/sendNow";
import appNavigationController from "../appNavigationController";
import { isMobileSafari } from "../../helpers/userAgent";
import I18n, { i18n, _i18n } from "../../lib/langPack";
const MAX_SELECTION_LENGTH = 100;
//const MIN_CLICK_MOVE = 32; // minimum bubble height
@ -214,7 +215,8 @@ export default class ChatSelection { @@ -214,7 +215,8 @@ export default class ChatSelection {
private updateContainer(forceSelection = false) {
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;
for(const mid of this.selectedMids.values()) {
@ -348,7 +350,7 @@ export default class ChatSelection { @@ -348,7 +350,7 @@ export default class ChatSelection {
if(this.chat.type === 'scheduled') {
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', () => {
new PopupSendNow(this.bubbles.peerId, [...this.selectedMids], () => {
this.cancelSelection();
@ -356,7 +358,7 @@ export default class ChatSelection { @@ -356,7 +358,7 @@ export default class ChatSelection {
});
} else {
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', () => {
new PopupForward(this.bubbles.peerId, [...this.selectedMids], () => {
this.cancelSelection();
@ -365,7 +367,7 @@ export default class ChatSelection { @@ -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.append('Delete');
_i18n(this.selectionDeleteBtn, 'Delete');
this.listenerSetter.add(this.selectionDeleteBtn, 'click', () => {
new PopupDeleteMessages(this.bubbles.peerId, [...this.selectedMids], this.chat.type, () => {
this.cancelSelection();
@ -461,7 +463,7 @@ export default class ChatSelection { @@ -461,7 +463,7 @@ export default class ChatSelection {
} else {
const diff = MAX_SELECTION_LENGTH - this.selectedMids.size - 1;
if(diff < 0) {
toast('Max selection count reached.');
toast(I18n.format('Chat.Selection.LimitToast', true));
return;
/* const it = this.selectedMids.values();
do {

12
src/components/dialogsContextMenu.ts

@ -18,7 +18,7 @@ export default class DialogsContextMenu { @@ -18,7 +18,7 @@ export default class DialogsContextMenu {
private init() {
this.buttons = [{
icon: 'unread',
text: 'Mark as unread',
text: 'MarkAsUnread',
onClick: this.onUnreadClick,
verify: () => {
const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count);
@ -26,7 +26,7 @@ export default class DialogsContextMenu { @@ -26,7 +26,7 @@ export default class DialogsContextMenu {
}
}, {
icon: 'readchats',
text: 'Mark as read',
text: 'MarkAsRead',
onClick: this.onUnreadClick,
verify: () => {
const isUnread = !!(this.dialog.pFlags?.unread_mark || this.dialog.unread_count);
@ -34,7 +34,7 @@ export default class DialogsContextMenu { @@ -34,7 +34,7 @@ export default class DialogsContextMenu {
}
}, {
icon: 'pin',
text: 'Pin',
text: 'ChatList.Context.Pin',
onClick: this.onPinClick,
verify: () => {
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 { @@ -42,7 +42,7 @@ export default class DialogsContextMenu {
}
}, {
icon: 'unpin',
text: 'Unpin',
text: 'ChatList.Context.Unpin',
onClick: this.onPinClick,
verify: () => {
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 { @@ -50,7 +50,7 @@ export default class DialogsContextMenu {
}
}, {
icon: 'mute',
text: 'Mute',
text: 'ChatList.Context.Mute',
onClick: this.onMuteClick,
verify: () => {
const isMuted = this.dialog.notify_settings && this.dialog.notify_settings.mute_until > (Date.now() / 1000 | 0);
@ -58,7 +58,7 @@ export default class DialogsContextMenu { @@ -58,7 +58,7 @@ export default class DialogsContextMenu {
}
}, {
icon: 'unmute',
text: 'Unmute',
text: 'ChatList.Context.Unmute',
onClick: this.onMuteClick,
verify: () => {
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 @@ @@ -1,7 +1,7 @@
import { getRichValue, isInputEmpty } from "../helpers/dom";
import { debounce } from "../helpers/schedulers";
import { checkRTL } from "../helpers/string";
import { i18n, LangPackKey } from "../lib/langPack";
import { i18n, LangPackKey, _i18n } from "../lib/langPack";
import RichTextProcessor from "../lib/richtextprocessor";
let init = () => {
@ -59,7 +59,7 @@ export enum InputState { @@ -59,7 +59,7 @@ export enum InputState {
};
export type InputFieldOptions = {
placeholder?: string,
placeholder?: LangPackKey,
label?: LangPackKey,
name?: string,
maxLength?: number,
@ -134,11 +134,15 @@ class InputField { @@ -134,11 +134,15 @@ class InputField {
}
} else {
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.addEventListener('input', () => checkAndSetRTL(input));
if(placeholder) {
_i18n(input, placeholder, undefined, 'placeholder');
}
}
if(label) {

3
src/components/inputSearch.ts

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

4
src/components/misc.ts

@ -123,13 +123,13 @@ export function formatPhoneNumber(str: string) { @@ -123,13 +123,13 @@ export function formatPhoneNumber(str: string) {
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 => {
const match = el.className.match(/(?:^|\s)menu-(.+?)(?:$|\s)/);
if(!match) return;
to[match[1]] = el as HTMLElement;
});
}
} */
let onMouseMove = (e: MouseEvent) => {
let rect = openedMenu.getBoundingClientRect();

2
src/components/popups/forward.ts

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

5
src/components/popups/pickUser.ts

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

2
src/components/privacySection.ts

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

33
src/components/sidebarLeft/index.ts

@ -8,7 +8,6 @@ import rootScope from "../../lib/rootScope"; @@ -8,7 +8,6 @@ import rootScope from "../../lib/rootScope";
import { attachClickEvent, findUpClassName, findUpTag } from "../../helpers/dom";
import { SearchGroup } from "../appSearch";
import "../avatar";
import { parseMenuButtonsTo } from "../misc";
import Scrollable, { ScrollableX } from "../scrollable";
import InputSearch from "../inputSearch";
import SidebarSlider from "../slider";
@ -69,8 +68,8 @@ export class AppSidebarLeft extends SidebarSlider { @@ -69,8 +68,8 @@ export class AppSidebarLeft extends SidebarSlider {
takeOut: (peerIds) => {
new AppNewGroupTab(this).open(peerIds);
},
title: 'Add Members',
placeholder: 'Add People...'
title: 'GroupAddMembers',
placeholder: 'SendMessageTo'
});
};
@ -126,23 +125,33 @@ export class AppSidebarLeft extends SidebarSlider { @@ -126,23 +125,33 @@ export class AppSidebarLeft extends SidebarSlider {
this.menuEl = this.toolsBtn.querySelector('.btn-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});
parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
//parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
this.archivedCount = document.createElement('span');
this.archivedCount.className = 'archived-count badge badge-24 badge-gray';
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) => {
this.archivedCount.innerText = '' + formatNumber(e.count, 1);
this.archivedCount.classList.toggle('hide', !e.count);

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

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

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

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

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

@ -275,7 +275,7 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -275,7 +275,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
}
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) {
showMore.remove();
}
@ -286,11 +286,14 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -286,11 +286,14 @@ export default class AppEditFolderTab extends SliderSuperTab {
let showMore: HTMLElement;
if(peers.length) {
showMore = document.createElement('div');
showMore.classList.add('show-more');
showMore.classList.add('show-more', 'rp-overflow');
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);
const down = document.createElement('div');
down.classList.add('tgico-down');
showMore.append(down, i18n('FilterShowMoreChats', [peers.length]));
container.append(showMore);
}

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

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

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

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

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

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

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

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

2
src/config/app.ts

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

8
src/index.hbs

@ -117,13 +117,7 @@ @@ -117,13 +117,7 @@
</div>
</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">
<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>
<button class="btn-circle rp btn-corner tgico-newchat_filled btn-menu-toggle" id="new-menu"></button>
</div>
</div>
</div>

72
src/lang.ts

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
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.",
"FilterNameInputLabel": "Folder Name",
"FilterMenuDelete": "Delete Folder",
@ -20,7 +22,13 @@ const lang = { @@ -20,7 +22,13 @@ const lang = {
"EditProfile.Username.Taken": "Username is already taken",
"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.",
"ExceptionModal.Search.Placeholder": "Add exception...",
"ChatList.Menu.Archived": "Archived",
"Chat.Selection.MessagesCount": {
"one_value": "%d Message",
"other_value": "%d Messages",
},
"Chat.Selection.LimitToast": "Max selection count reached.",
"Saved": "Saved",
"General.Keyboard": "Keyboard",
"General.SendShortcut.Enter": "Send by Enter",
@ -33,12 +41,19 @@ const lang = { @@ -33,12 +41,19 @@ const lang = {
"ChatBackground.Blur": "Blur Wallpaper Image",
"Notifications.Sound": "Notification Sound",
"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.Disabled": "Disabled",
"Privacy.Devices": {
"one_value": "%1$d device",
"other_value": "%1$d devices"
},
"PrivacyModal.Search.Placeholder": "Add Users or Groups...",
// * android
"ActionCreateChannel": "Channel created",
@ -56,8 +71,21 @@ const lang = { @@ -56,8 +71,21 @@ const lang = {
"ActionInviteUser": "un1 joined the group via invite link",
"ActionPinnedNoText": "un1 pinned a message",
"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",
"ChannelMegaJoined": "You joined this group",
"Channel.DescriptionPlaceholder": "Description (optional)",
"Draft": "Draft",
"FilterAlwaysShow": "Include Chats",
"FilterNeverShow": "Exclude Chats",
"FilterInclude": "Included Chats",
@ -67,6 +95,11 @@ const lang = { @@ -67,6 +95,11 @@ const lang = {
"FilterNew": "New Folder",
"Filters": "Folders",
"FilterRecommended": "Recommended Folders",
"FilterShowMoreChats": {
"one_value": "Show %1$d More Chat",
"other_value": "Show %1$d More Chats"
},
"FromYou": "You",
"Add": "Add",
"Chats": {
"one_value": "%1$d chat",
@ -84,7 +117,12 @@ const lang = { @@ -84,7 +117,12 @@ const lang = {
"one_value": "%1$d user",
"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",
"NewChannel": "New Channel",
"NewGroup": "New Group",
"Contacts": "Contacts",
"SavedMessages": "Saved Messages",
@ -108,6 +146,8 @@ const lang = { @@ -108,6 +146,8 @@ const lang = {
"NotificationsGroups": "Groups",
"NotificationsChannels": "Channels",
"NotificationsOther": "Other",
"MarkAsUnread": "Mark as unread",
"MarkAsRead": "Mark as read",
"ContactJoined": "Contact joined Telegram",
"Loading": "Loading...",
"Unblock": "Unblock",
@ -143,6 +183,20 @@ const lang = { @@ -143,6 +183,20 @@ const lang = {
"ArchivedChats": "Archived Chats",
"Cancel": "Cancel",
"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
"AccountSettings.Filters": "Chat Folders",
@ -150,6 +204,7 @@ const lang = { @@ -150,6 +204,7 @@ const lang = {
"AccountSettings.PrivacyAndSecurity": "Privacy and Security",
"AccountSettings.Language": "Language",
"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.ScheduledUntilOnline": "Scheduled until online",
"Chat.Date.ScheduledForToday": "Scheduled for today",
@ -159,6 +214,14 @@ const lang = { @@ -159,6 +214,14 @@ const lang = {
"Chat.Service.Channel.RemovedPhoto": "Channel photo removed",
"Chat.Service.Channel.UpdatedVideo": "Channel video updated",
"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.outgoing": "Outgoing Call (%@)",
"ChatList.Service.Call.Cancelled": "Cancelled Call",
@ -177,6 +240,9 @@ const lang = { @@ -177,6 +240,9 @@ const lang = {
"ChatList.Filter.MutedChats": "Muted",
"ChatList.Filter.ReadChats": "Read",
"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",
"EditAccount.Username": "Username",
"EditAccount.Title": "Edit Profile",
@ -186,6 +252,7 @@ const lang = { @@ -186,6 +252,7 @@ const lang = {
"Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji",
"ShareModal.Search.ForwardPlaceholder": "Forward to...",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers",
"PrivacyAndSecurity.Item.On": "On",
"PrivacyAndSecurity.Item.Off": "Off",
@ -211,7 +278,10 @@ const lang = { @@ -211,7 +278,10 @@ const lang = {
"one_value": "%d user",
"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;

6
src/lib/appManagers/appDialogsManager.ts

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

26
src/lib/appManagers/appMessagesManager.ts

@ -8,7 +8,7 @@ import { randomLong } from "../../helpers/random"; @@ -8,7 +8,7 @@ import { randomLong } from "../../helpers/random";
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 { 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 type { ApiFileManager } from '../mtproto/apiFileManager';
//import apiManager from '../mtproto/apiManager';
@ -2505,10 +2505,6 @@ export class AppMessagesManager { @@ -2505,10 +2505,6 @@ export class AppMessagesManager {
const parts: (HTMLElement | string)[] = [];
const addPart = (part: string | HTMLElement, text?: string) => {
if(text) {
part += ', ';
}
if(plain) {
parts.push(part);
} else {
@ -2517,6 +2513,10 @@ export class AppMessagesManager { @@ -2517,6 +2513,10 @@ export class AppMessagesManager {
else el.append(part);
parts.push(el);
}
if(text) {
parts.push(', ');
}
};
if(message.media) {
@ -2538,7 +2538,7 @@ export class AppMessagesManager { @@ -2538,7 +2538,7 @@ export class AppMessagesManager {
if(usingFullAlbum) {
text = this.getAlbumText(message.grouped_id).message;
addPart('Album', text);
addPart(i18n('AttachAlbum'), text);
}
} else {
usingFullAlbum = false;
@ -2548,31 +2548,31 @@ export class AppMessagesManager { @@ -2548,31 +2548,31 @@ export class AppMessagesManager {
const media = message.media;
switch(media._) {
case 'messageMediaPhoto':
addPart('Photo', message.message);
addPart(i18n('AttachPhoto'), message.message);
break;
case 'messageMediaDice':
addPart(plain ? media.emoticon : RichTextProcessor.wrapEmojiText(media.emoticon));
break;
case 'messageMediaGeo':
addPart('Geolocation');
addPart(i18n('AttachLiveLocation'));
break;
case 'messageMediaPoll':
addPart(plain ? '📊' + ' ' + (media.poll.question || 'poll') : media.poll.rReply);
break;
case 'messageMediaContact':
addPart('Contact');
addPart(i18n('AttachContact'));
break;
case 'messageMediaDocument':
let document = media.document;
if(document.type === 'video') {
addPart('Video', message.message);
addPart(i18n('AttachVideo'), message.message);
} else if(document.type === 'voice') {
addPart('Voice message', message.message);
addPart(i18n('AttachAudio'), message.message);
} else if(document.type === 'gif') {
addPart('GIF', message.message);
addPart(i18n('AttachGif'), message.message);
} else if(document.type === 'round') {
addPart('Video message', message.message);
addPart(i18n('AttachRound'), message.message);
} else if(document.type === 'sticker') {
addPart(((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker');
text = '';

6
src/scss/partials/_leftSidebar.scss

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

Loading…
Cancel
Save