Browse Source

More and more translations

master
Eduard Kuzmenko 4 years ago
parent
commit
a90aae065e
  1. 6
      src/components/appMediaViewer.ts
  2. 6
      src/components/appMediaViewerNew.ts
  3. 6
      src/components/appSearchSuper..ts
  4. 15
      src/components/appSelectPeers.ts
  5. 3
      src/components/chat/topbar.ts
  6. 61
      src/components/poll.ts
  7. 9
      src/components/popups/peer.ts
  8. 33
      src/components/popups/stickers.ts
  9. 7
      src/components/sidebarLeft/tabs/blockedUsers.ts
  10. 2
      src/components/sidebarLeft/tabs/contacts.ts
  11. 10
      src/components/sidebarLeft/tabs/newGroup.ts
  12. 16
      src/components/sidebarRight/index.ts
  13. 40
      src/components/sidebarRight/tabs/editChannel.ts
  14. 15
      src/components/sidebarRight/tabs/editContact.ts
  15. 38
      src/components/sidebarRight/tabs/editGroup.ts
  16. 2
      src/components/sidebarRight/tabs/gifs.ts
  17. 36
      src/components/sidebarRight/tabs/groupPermissions.ts
  18. 33
      src/components/sidebarRight/tabs/groupType.ts
  19. 14
      src/components/sidebarRight/tabs/pollResults.ts
  20. 218
      src/components/sidebarRight/tabs/sharedMedia.ts
  21. 14
      src/components/sidebarRight/tabs/stickers.ts
  22. 19
      src/components/sidebarRight/tabs/userPermissions.ts
  23. 2
      src/config/app.ts
  24. 10
      src/helpers/dom.ts
  25. 2
      src/helpers/number.ts
  26. 42
      src/index.hbs
  27. 162
      src/lang.ts
  28. 5
      src/lib/appManagers/appChatsManager.ts
  29. 29
      src/lib/appManagers/appImManager.ts
  30. 136
      src/lib/appManagers/appUsersManager.ts
  31. 3
      src/lib/langPack.ts
  32. 7
      src/scss/partials/_avatar.scss
  33. 6
      src/scss/partials/_button.scss
  34. 8
      src/scss/partials/_chatBubble.scss
  35. 13
      src/scss/partials/_rightSidebar.scss
  36. 8
      src/scss/partials/popups/_peer.scss
  37. 1
      src/scss/partials/popups/_popup.scss
  38. 9
      src/scss/partials/popups/_stickers.scss

6
src/components/appMediaViewer.ts

@ -31,6 +31,7 @@ import DEBUG from "../config/debug"; @@ -31,6 +31,7 @@ import DEBUG from "../config/debug";
import appNavigationController from "./appNavigationController";
import { Message } from "../layer";
import { forEachReverse } from "../helpers/array";
import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -1257,7 +1258,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet @@ -1257,7 +1258,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
//.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve())
.then(() => {
if(mediaSizes.isMobile) {
appSidebarRight.closeTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
const tab = appSidebarRight.getTab(AppSharedMediaTab);
if(tab) {
tab.close();
}
}
const message = appMessagesManager.getMessageByPeer(peerId, mid);

6
src/components/appMediaViewerNew.ts

@ -29,6 +29,7 @@ import { months, ONE_DAY } from "../helpers/date"; @@ -29,6 +29,7 @@ import { months, ONE_DAY } from "../helpers/date";
import { SearchSuperContext } from "./appSearchSuper.";
import { Message, PhotoSize } from "../layer";
import { forEachReverse } from "../helpers/array";
import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -1159,7 +1160,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet @@ -1159,7 +1160,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
//.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve())
.then(() => {
if(mediaSizes.isMobile) {
appSidebarRight.closeTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
const tab = appSidebarRight.getTab(AppSharedMediaTab);
if(tab) {
tab.close();
}
}
const message = appMessagesManager.getMessageByPeer(peerId, mid);

6
src/components/appSearchSuper..ts

@ -676,9 +676,9 @@ export default class AppSearchSuper { @@ -676,9 +676,9 @@ export default class AppSearchSuper {
if(showMembersCount && (peer.participants_count || peer.participants)) {
const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(searchIndexManager.cleanSearchText(query))})`, 'gi');
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>');
dom.lastMessageSpan.innerText = appChatsManager.getChatMembersString(-peerId);
dom.lastMessageSpan.append(appChatsManager.getChatMembersString(-peerId));
} else if(peerId === rootScope.myId) {
dom.lastMessageSpan.innerHTML = 'chat with yourself';
dom.lastMessageSpan.append(i18n('Presence.YourChat'));
} else {
let username = appPeersManager.getPeerUsername(peerId);
if(!username) {
@ -769,7 +769,7 @@ export default class AppSearchSuper { @@ -769,7 +769,7 @@ export default class AppSearchSuper {
autonomous: true
});
dom.lastMessageSpan.innerText = peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appChatsManager.getChatMembersString(peerId);
dom.lastMessageSpan.append(peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appChatsManager.getChatMembersString(peerId));
});
if(!state.recentSearch.length) {

15
src/components/appSelectPeers.ts

@ -11,7 +11,7 @@ import { FocusDirection } from "../helpers/fastSmoothScroll"; @@ -11,7 +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";
import { i18n, LangPackKey, _i18n } from "../lib/langPack";
type PeerType = 'contacts' | 'dialogs' | 'channelParticipants';
@ -434,19 +434,16 @@ export default class AppSelectPeers { @@ -434,19 +434,16 @@ export default class AppSelectPeers {
dom.containerEl.prepend(checkboxField.label);
}
let subtitle = '';
let subtitleEl: HTMLElement;
if(peerId < 0) {
subtitle = appChatsManager.getChatMembersString(-peerId);
subtitleEl = appChatsManager.getChatMembersString(-peerId);
} else if(peerId === rootScope.myId) {
subtitle = 'chat with yourself';
subtitleEl = i18n('Presence.YourChat');
} else {
subtitle = appUsersManager.getUserStatusString(peerId);
if(subtitle === 'online') {
subtitle = `<i>${subtitle}</i>`;
}
subtitleEl = appUsersManager.getUserStatusString(peerId);
}
dom.lastMessageSpan.innerHTML = subtitle;
dom.lastMessageSpan.append(subtitleEl);
});
}

3
src/components/chat/topbar.ts

@ -555,7 +555,8 @@ export default class ChatTopbar { @@ -555,7 +555,8 @@ export default class ChatTopbar {
return;
}
this.subtitle.innerHTML = subtitle;
this.subtitle.textContent = '';
this.subtitle.append(subtitle);
});
};
}

61
src/components/poll.ts

@ -5,10 +5,11 @@ import appPollsManager, { Poll, PollResults } from "../lib/appManagers/appPollsM @@ -5,10 +5,11 @@ import appPollsManager, { Poll, PollResults } from "../lib/appManagers/appPollsM
import serverTimeManager from "../lib/mtproto/serverTimeManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
import rootScope from "../lib/rootScope";
import { attachClickEvent, cancelEvent, detachClickEvent, findUpClassName } from "../helpers/dom";
import { attachClickEvent, cancelEvent, detachClickEvent, findUpClassName, replaceContent } from "../helpers/dom";
import { ripple } from "./ripple";
import appSidebarRight from "./sidebarRight";
import AppPollResultsTab from "./sidebarRight/tabs/pollResults";
import { i18n, LangPackKey } from "../lib/langPack";
let lineTotalLength = 0;
const tailLength = 9;
@ -200,7 +201,7 @@ export default class PollElement extends HTMLElement { @@ -200,7 +201,7 @@ export default class PollElement extends HTMLElement {
//console.log('pollElement poll:', poll, results);
let desc = '';
let descKey: LangPackKey = '';
if(poll.pFlags) {
this.isPublic = !!poll.pFlags.public_voters;
this.isQuiz = !!poll.pFlags.quiz;
@ -208,11 +209,12 @@ export default class PollElement extends HTMLElement { @@ -208,11 +209,12 @@ export default class PollElement extends HTMLElement {
this.isMultiple = !!poll.pFlags.multiple_choice;
if(this.isClosed) {
desc = 'Final results';
descKey = 'Chat.Poll.Type.Closed';
this.classList.add('is-closed');
} else if(this.isQuiz) {
descKey = this.isPublic ? 'Chat.Poll.Type.Quiz' : 'Chat.Poll.Type.AnonymousQuiz';
} else {
let type = this.isQuiz ? 'Quiz' : 'Poll';
desc = (this.isPublic ? '' : 'Anonymous ') + type;
descKey = this.isPublic ? 'Chat.Poll.Type.Public' : 'Chat.Poll.Type.Anonymous';
}
}
@ -240,20 +242,19 @@ export default class PollElement extends HTMLElement { @@ -240,20 +242,19 @@ export default class PollElement extends HTMLElement {
this.innerHTML = `
<div class="poll-title">${poll.rQuestion}</div>
<div class="poll-desc">
<div class="poll-type">${desc}</div>
<div class="poll-type"></div>
<div class="poll-avatars"></div>
</div>
${votes}
<div class="poll-footer">
<div class="poll-footer-button poll-view-results hide">View Results</div>
<div class="poll-votes-count"></div>
</div>
`;
${votes}`;
this.descDiv = this.firstElementChild.nextElementSibling as HTMLElement;
this.typeDiv = this.descDiv.firstElementChild as HTMLElement;
this.avatarsDiv = this.descDiv.lastElementChild as HTMLElement;
if(descKey) {
this.typeDiv.append(i18n(descKey));
}
if(this.isQuiz) {
this.classList.add('is-quiz');
@ -332,9 +333,18 @@ export default class PollElement extends HTMLElement { @@ -332,9 +333,18 @@ export default class PollElement extends HTMLElement {
this.svgLines = Array.from(this.querySelectorAll('.poll-line')) as SVGSVGElement[];
this.numberDivs = Array.from(this.querySelectorAll('.poll-answer-percents')) as HTMLDivElement[];
const footerDiv = this.lastElementChild;
this.viewResults = footerDiv.firstElementChild as HTMLElement;
this.votersCountDiv = footerDiv.lastElementChild as HTMLDivElement;
const footerDiv = document.createElement('div');
footerDiv.classList.add('poll-footer');
this.viewResults = document.createElement('div');
this.viewResults.className = 'poll-footer-button poll-view-results hide';
this.viewResults.append(i18n('Chat.Poll.ViewResults'));
this.votersCountDiv = document.createElement('div');
this.votersCountDiv.className = 'poll-votes-count';
footerDiv.append(this.viewResults, this.votersCountDiv);
this.append(footerDiv);
this.viewResults.addEventListener('click', (e) => {
cancelEvent(e);
@ -348,7 +358,7 @@ export default class PollElement extends HTMLElement { @@ -348,7 +358,7 @@ export default class PollElement extends HTMLElement {
if(this.isMultiple) {
this.sendVoteBtn = document.createElement('div');
this.sendVoteBtn.classList.add('poll-footer-button', 'poll-send-vote');
this.sendVoteBtn.innerText = 'Vote';
this.sendVoteBtn.append(i18n('Chat.Poll.SubmitVote'));
ripple(this.sendVoteBtn);
if(!poll.chosenIndexes.length) {
@ -495,7 +505,7 @@ export default class PollElement extends HTMLElement { @@ -495,7 +505,7 @@ export default class PollElement extends HTMLElement {
if(this.isClosed) {
this.classList.add('is-closed');
this.typeDiv.innerText = 'Final results';
replaceContent(this.typeDiv, i18n('Chat.Poll.Type.Closed'));
}
// set chosen
@ -534,7 +544,7 @@ export default class PollElement extends HTMLElement { @@ -534,7 +544,7 @@ export default class PollElement extends HTMLElement {
*/
results.recent_voters/* .slice().reverse() */.forEach((userId, idx) => {
const style = idx === 0 ? '' : `style="transform: translateX(-${idx * 3}px);"`;
html += `<avatar-element class="avatar-18" dialog="0" peer="${userId}" ${style}></avatar-element>`;
html += `<avatar-element class="avatar-16" dialog="0" peer="${userId}" ${style}></avatar-element>`;
});
this.avatarsDiv.innerHTML = html;
}
@ -612,9 +622,16 @@ export default class PollElement extends HTMLElement { @@ -612,9 +622,16 @@ export default class PollElement extends HTMLElement {
setVotersCount(results: PollResults) {
const votersCount = results.total_voters || 0;
const votersOrAnswers = this.isQuiz ? (votersCount > 1 || !votersCount ? 'answers' : 'answer') : (votersCount > 1 || !votersCount ? 'votes' : 'vote');
this.votersCountDiv.innerText = `${results.total_voters ? results.total_voters + ' ' + votersOrAnswers : 'No ' + votersOrAnswers}`;
let key: LangPackKey, args = [votersCount];
if(this.isClosed) {
if(this.isQuiz) key = votersCount ? 'Chat.Quiz.TotalVotes' : 'Chat.Quiz.TotalVotesResultEmpty';
else key = votersCount ? 'Chat.Poll.TotalVotes1' : 'Chat.Poll.TotalVotesResultEmpty';
} else {
if(this.isQuiz) key = votersCount ? 'Chat.Quiz.TotalVotes' : 'Chat.Quiz.TotalVotesEmpty';
else key = votersCount ? 'Chat.Poll.TotalVotes1' : 'Chat.Poll.TotalVotesEmpty';
}
replaceContent(this.votersCountDiv, i18n(key, args));
}
setLineProgress(index: number, percents: number) {
@ -632,4 +649,4 @@ export default class PollElement extends HTMLElement { @@ -632,4 +649,4 @@ export default class PollElement extends HTMLElement {
// у элемента могут быть ещё другие методы и свойства
}
customElements.define("poll-element", PollElement);
customElements.define("poll-element", PollElement);

9
src/components/popups/peer.ts

@ -1,11 +1,14 @@ @@ -1,11 +1,14 @@
import AvatarElement from "../avatar";
import PopupElement, { PopupButton } from ".";
import { i18n, LangPackKey } from "../../lib/langPack";
export default class PopupPeer extends PopupElement {
constructor(private className: string, options: Partial<{
peerId: number,
title: string,
titleLangKey?: LangPackKey,
description: string,
descriptionLangKey?: LangPackKey,
buttons: Array<PopupButton>
}> = {}) {
super('popup-peer' + (className ? ' ' + className : ''), options.buttons, {overlayClosable: true});
@ -15,12 +18,14 @@ export default class PopupPeer extends PopupElement { @@ -15,12 +18,14 @@ export default class PopupPeer extends PopupElement {
avatarEl.setAttribute('peer', '' + options.peerId);
avatarEl.classList.add('avatar-32');
this.title.innerText = options.title || '';
if(options.descriptionLangKey) this.title.append(i18n(options.titleLangKey));
else this.title.innerText = options.title || '';
this.header.prepend(avatarEl);
let p = document.createElement('p');
p.classList.add('popup-description');
p.innerHTML = options.description;
if(options.descriptionLangKey) p.append(i18n(options.descriptionLangKey));
else p.innerHTML = options.description;
this.container.insertBefore(p, this.header.nextElementSibling);
}

33
src/components/popups/stickers.ts

@ -6,10 +6,12 @@ import { wrapSticker } from "../wrappers"; @@ -6,10 +6,12 @@ import { wrapSticker } from "../wrappers";
import LazyLoadQueue from "../lazyLoadQueue";
import { putPreloader } from "../misc";
import animationIntersector from "../animationIntersector";
import { findUpClassName } from "../../helpers/dom";
import { findUpClassName, toggleDisability } from "../../helpers/dom";
import appImManager from "../../lib/appManagers/appImManager";
import { StickerSet } from "../../layer";
import mediaSizes from "../../helpers/mediaSizes";
import { i18n } from "../../lib/langPack";
import Button from "../button";
const ANIMATION_GROUP = 'STICKERS-POPUP';
@ -28,7 +30,7 @@ export default class PopupStickers extends PopupElement { @@ -28,7 +30,7 @@ export default class PopupStickers extends PopupElement {
super('popup-stickers', null, {closable: true, overlayClosable: true, body: true});
this.h6 = document.createElement('h6');
this.h6.innerText = 'Loading...';
this.h6.append(i18n('Loading'));
this.header.append(this.h6);
@ -51,7 +53,7 @@ export default class PopupStickers extends PopupElement { @@ -51,7 +53,7 @@ export default class PopupStickers extends PopupElement {
div.append(this.stickersDiv);
this.stickersFooter.innerText = 'Loading...';
this.stickersFooter.append(i18n('Loading'));
this.body.append(div);
const scrollable = new Scrollable(this.body);
@ -66,12 +68,12 @@ export default class PopupStickers extends PopupElement { @@ -66,12 +68,12 @@ export default class PopupStickers extends PopupElement {
}
onFooterClick = () => {
this.stickersFooter.setAttribute('disabled', 'true');
const toggle = toggleDisability([this.stickersFooter], true);
appStickersManager.toggleStickerSet(this.set).then(() => {
this.btnClose.click();
this.hide();
}).catch(() => {
this.stickersFooter.removeAttribute('disabled');
toggle();
});
};
@ -81,7 +83,7 @@ export default class PopupStickers extends PopupElement { @@ -81,7 +83,7 @@ export default class PopupStickers extends PopupElement {
const fileId = target.dataset.docId;
if(appImManager.chat.input.sendMessageWithDocument(fileId)) {
this.btnClose.click();
this.hide();
} else {
console.warn('got no doc by id:', fileId);
}
@ -97,9 +99,20 @@ export default class PopupStickers extends PopupElement { @@ -97,9 +99,20 @@ export default class PopupStickers extends PopupElement {
this.h6.innerHTML = RichTextProcessor.wrapEmojiText(set.set.title);
!set.set.installed_date ? this.stickersFooter.classList.add('add') : this.stickersFooter.classList.remove('add');
this.stickersFooter.innerHTML = set.set.hasOwnProperty('installed_date') ? '<div style="cursor: pointer; margin: 0 auto; width: 150px;">Remove stickers</div>' : `<button class="btn-primary btn-color-primary">ADD ${set.set.count} STICKERS</button>`;
this.stickersFooter.addEventListener('click', this.onFooterClick);
let button: HTMLElement;
if(set.set.hasOwnProperty('installed_date')) {
button = Button('btn-primary btn-primary-transparent danger', {noRipple: true});
button.append(i18n('StickerPack.Remove', [set.set.count]));
} else {
button = Button('btn-primary btn-color-primary', {noRipple: true});
button.append(i18n('StickerPack.Add1', [set.set.count]));
}
this.stickersFooter.textContent = '';
this.stickersFooter.append(button);
button.addEventListener('click', this.onFooterClick);
if(set.documents.length) {
this.stickersDiv.addEventListener('click', this.onStickersClick);
@ -133,4 +146,4 @@ export default class PopupStickers extends PopupElement { @@ -133,4 +146,4 @@ export default class PopupStickers extends PopupElement {
}
});
}
}
}

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

@ -54,7 +54,12 @@ export default class AppBlockedUsersTab extends SliderSuperTab { @@ -54,7 +54,12 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
});
const user = appUsersManager.getUser(peerId);
dom.lastMessageSpan.innerHTML = user.pFlags.bot ? ('@' + user.username) : user.rPhone || (user.username ? '@' + user.username : appUsersManager.getUserStatusString(peerId));
if(user.pFlags.bot) {
dom.lastMessageSpan.append('@' + user.username);
} else {
if(user.rPhone) dom.lastMessageSpan.innerHTML = user.rPhone;
else dom.lastMessageSpan.append(user.username ? '@' + user.username : appUsersManager.getUserStatusString(peerId));
}
//dom.titleSpan.innerHTML = 'Raaid El Syed';
//dom.lastMessageSpan.innerHTML = '+1 234 567891';

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

@ -100,7 +100,7 @@ export default class AppContactsTab extends SliderSuperTab { @@ -100,7 +100,7 @@ export default class AppContactsTab extends SliderSuperTab {
});
let status = appUsersManager.getUserStatusString(user.id);
dom.lastMessageSpan.innerHTML = status === 'online' ? `<i>${status}</i>` : status;
dom.lastMessageSpan.append(status);
});
if(!sorted.length) renderPage = undefined;

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

@ -89,15 +89,7 @@ export default class AppNewGroupTab extends SliderSuperTab { @@ -89,15 +89,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
avatarSize: 48
});
let subtitle = '';
subtitle = appUsersManager.getUserStatusString(userId);
if(subtitle === 'online') {
subtitle = `<i>${subtitle}</i>`;
}
if(subtitle) {
dom.lastMessageSpan.innerHTML = subtitle;
}
dom.lastMessageSpan.append(appUsersManager.getUserStatusString(userId));
});
this.searchGroup.nameEl.textContent = '';

16
src/components/sidebarRight/index.ts

@ -6,32 +6,23 @@ import { MOUNT_CLASS_TO } from "../../config/debug"; @@ -6,32 +6,23 @@ import { MOUNT_CLASS_TO } from "../../config/debug";
export const RIGHT_COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown';
const sharedMediaTab = new AppSharedMediaTab();
export class AppSidebarRight extends SidebarSlider {
public static SLIDERITEMSIDS = {
sharedMedia: 0,
};
public sharedMediaTab: AppSharedMediaTab;
constructor() {
super({
sidebarEl: document.getElementById('column-right') as HTMLElement,
tabs: new Map([
[AppSidebarRight.SLIDERITEMSIDS.sharedMedia, sharedMediaTab],
] as any[]),
canHideFirst: true,
navigationType: 'right'
});
this.sharedMediaTab = sharedMediaTab;
mediaSizes.addEventListener('changeScreen', (from, to) => {
if(to === ScreenSize.medium && from !== ScreenSize.mobile) {
this.toggleSidebar(false);
}
});
this.sharedMediaTab = new AppSharedMediaTab(this);
}
public onCloseTab(id: number, animate: boolean, isNavigation?: boolean) {
@ -72,7 +63,8 @@ export class AppSidebarRight extends SidebarSlider { @@ -72,7 +63,8 @@ export class AppSidebarRight extends SidebarSlider {
if(!willChange) return Promise.resolve();
if(!active && !this.historyTabIds.length) {
this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
this.sharedMediaTab.open();
//this.selectTab(this.sharedMediaTab);
}
const animationPromise = appImManager.selectTab(active ? 1 : 2, animate);

40
src/components/sidebarRight/tabs/editChannel.ts

@ -10,6 +10,8 @@ import appProfileManager from "../../../lib/appManagers/appProfileManager"; @@ -10,6 +10,8 @@ import appProfileManager from "../../../lib/appManagers/appProfileManager";
import { attachClickEvent, toggleDisability } from "../../../helpers/dom";
import PopupPeer from "../../popups/peer";
import { addCancelButton } from "../../popups";
import { i18n } from "../../../lib/langPack";
import { numberThousandSplitter } from "../../../helpers/number";
export default class AppEditChannelTab extends SliderSuperTab {
private nameInputField: InputField;
@ -19,7 +21,7 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -19,7 +21,7 @@ export default class AppEditChannelTab extends SliderSuperTab {
protected async init() {
this.container.classList.add('edit-peer-container', 'edit-channel-container');
this.title.innerHTML = 'Edit';
this.setTitle('Edit');
const chatFull = await appProfileManager.getChannelFull(-this.peerId, true);
@ -33,12 +35,12 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -33,12 +35,12 @@ export default class AppEditChannelTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper');
this.nameInputField = new InputField({
label: 'Name',
label: 'Channel.ChannelNameHolder',
name: 'channel-name',
maxLength: 255
});
this.descriptionInputField = new InputField({
label: 'Description',
label: 'DescriptionPlaceholder',
name: 'channel-description',
maxLength: 255
});
@ -89,8 +91,8 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -89,8 +91,8 @@ export default class AppEditChannelTab extends SliderSuperTab {
if(appChatsManager.hasRights(-this.peerId, 'change_type')) {
const channelTypeRow = new Row({
title: 'Channel Type',
subtitle: 'Private',
titleLangKey: 'ChannelType',
subtitleLangKey: 'TypePrivate',
clickable: true,
icon: 'lock'
});
@ -100,8 +102,8 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -100,8 +102,8 @@ export default class AppEditChannelTab extends SliderSuperTab {
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
const discussionRow = new Row({
title: 'Discussion',
subtitle: 'Add',
titleLangKey: 'PeerInfo.Discussion',
subtitleLangKey: 'PeerInfo.Discussion.Add',
clickable: true,
icon: 'message'
});
@ -110,7 +112,7 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -110,7 +112,7 @@ export default class AppEditChannelTab extends SliderSuperTab {
}
const administratorsRow = new Row({
title: 'Administrators',
titleLangKey: 'PeerInfo.Administrators',
subtitle: '' + chatFull.admins_count,
icon: 'admin',
clickable: true
@ -120,7 +122,7 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -120,7 +122,7 @@ export default class AppEditChannelTab extends SliderSuperTab {
if(appChatsManager.hasRights(-this.peerId, 'change_info')) {
const signMessagesCheckboxField = new CheckboxField({
text: 'Sign Messages',
text: 'PeerInfo.SignMessages',
checked: false
});
@ -136,12 +138,13 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -136,12 +138,13 @@ export default class AppEditChannelTab extends SliderSuperTab {
});
const subscribersRow = new Row({
title: 'Subscribers',
subtitle: '335 356 subscribers',
titleLangKey: 'PeerInfo.Subscribers',
icon: 'newgroup',
clickable: true
});
subscribersRow.subtitle.append(i18n('Subscribers', [numberThousandSplitter(335356)]));
section.content.append(subscribersRow.container);
this.scrollable.append(section.container);
@ -152,15 +155,22 @@ export default class AppEditChannelTab extends SliderSuperTab { @@ -152,15 +155,22 @@ export default class AppEditChannelTab extends SliderSuperTab {
});
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'Delete Channel'});
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'PeerInfo.DeleteChannel'});
attachClickEvent(btnDelete, () => {
new PopupPeer('popup-delete-channel', {
peerId: this.peerId,
title: 'Delete Channel?',
description: `Are you sure you want to delete this channel? All subscribers will be removed and all messages will be lost.`,
titleLangKey: 'ChannelDeleteMenu',
descriptionLangKey: 'AreYouSureDeleteAndExitChannel',
buttons: addCancelButton([{
text: 'DELETE',
langKey: 'ChannelDeleteMenu',
callback: () => {
const toggle = toggleDisability([btnDelete], true);
},
isDanger: true
}, {
langKey: 'DeleteChannelForAll',
callback: () => {
const toggle = toggleDisability([btnDelete], true);

15
src/components/sidebarRight/tabs/editContact.ts

@ -14,6 +14,7 @@ import rootScope from "../../../lib/rootScope"; @@ -14,6 +14,7 @@ import rootScope from "../../../lib/rootScope";
import appPeersManager from "../../../lib/appManagers/appPeersManager";
import PopupPeer from "../../popups/peer";
import { addCancelButton } from "../../popups";
import { i18n } from "../../../lib/langPack";
export default class AppEditContactTab extends SliderSuperTab {
private nameInputField: InputField;
@ -23,7 +24,7 @@ export default class AppEditContactTab extends SliderSuperTab { @@ -23,7 +24,7 @@ export default class AppEditContactTab extends SliderSuperTab {
protected init() {
this.container.classList.add('edit-peer-container', 'edit-contact-container');
this.title.innerHTML = 'Edit';
this.setTitle('Edit');
{
const section = new SettingSection({noDelimiter: true});
@ -33,12 +34,12 @@ export default class AppEditContactTab extends SliderSuperTab { @@ -33,12 +34,12 @@ export default class AppEditContactTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper');
this.nameInputField = new InputField({
label: 'Name',
label: 'EditProfile.FirstNameLabel',
name: 'contact-name',
maxLength: 70
});
this.lastNameInputField = new InputField({
label: 'Last Name',
label: 'Login.Register.LastName.Placeholder',
name: 'contact-lastname',
maxLength: 70
});
@ -103,7 +104,7 @@ export default class AppEditContactTab extends SliderSuperTab { @@ -103,7 +104,7 @@ export default class AppEditContactTab extends SliderSuperTab {
const profileSubtitleDiv = document.createElement('div');
profileSubtitleDiv.classList.add('profile-subtitle');
profileSubtitleDiv.innerHTML = 'original name';
profileSubtitleDiv.append(i18n('EditContact.OriginalName'));
section.content.append(div, profileNameDiv, profileSubtitleDiv, inputWrapper, notificationsRow.container);
@ -130,10 +131,10 @@ export default class AppEditContactTab extends SliderSuperTab { @@ -130,10 +131,10 @@ export default class AppEditContactTab extends SliderSuperTab {
attachClickEvent(btnDelete, () => {
new PopupPeer('popup-delete-contact', {
peerId: this.peerId,
title: 'Delete Contact?',
description: `Are you sure you want to delete <b>${appPeersManager.getPeerTitle(this.peerId)}</b> from your contact list?`,
titleLangKey: 'DeleteContact',
descriptionLangKey: 'AreYouSureDeleteContact',
buttons: addCancelButton([{
text: 'DELETE',
langKey: 'Delete',
callback: () => {
const toggle = toggleDisability([btnDelete], true);

38
src/components/sidebarRight/tabs/editGroup.ts

@ -14,6 +14,7 @@ import { addCancelButton } from "../../popups"; @@ -14,6 +14,7 @@ import { addCancelButton } from "../../popups";
import AppGroupTypeTab from "./groupType";
import rootScope from "../../../lib/rootScope";
import AppGroupPermissionsTab from "./groupPermissions";
import { i18n } from "../../../lib/langPack";
export default class AppEditGroupTab extends SliderSuperTab {
private groupNameInputField: InputField;
@ -27,7 +28,7 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -27,7 +28,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
this.scrollable.container.innerHTML = '';
this.container.classList.add('edit-peer-container', 'edit-group-container');
this.title.innerHTML = 'Edit';
this.setTitle('Edit');
const chatFull = await appProfileManager.getChatFull(this.chatId, true);
@ -39,12 +40,12 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -39,12 +40,12 @@ export default class AppEditGroupTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper');
this.groupNameInputField = new InputField({
label: 'Group Name',
label: 'CreateGroup.NameHolder',
name: 'group-name',
maxLength: 255
});
this.descriptionInputField = new InputField({
label: 'Description',
label: 'DescriptionPlaceholder',
name: 'group-description',
maxLength: 255
});
@ -70,7 +71,7 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -70,7 +71,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
if(appChatsManager.hasRights(this.chatId, 'change_type')) {
const groupTypeRow = new Row({
title: 'Group Type',
titleLangKey: 'GroupType',
clickable: () => {
const tab = new AppGroupTypeTab(this.slider);
tab.peerId = -this.chatId;
@ -83,7 +84,8 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -83,7 +84,8 @@ export default class AppEditGroupTab extends SliderSuperTab {
});
const setGroupTypeSubtitle = () => {
groupTypeRow.subtitle.innerHTML = chat.username ? 'Public' : 'Private';
groupTypeRow.subtitle.textContent = '';
groupTypeRow.subtitle.append(i18n(chat.username ? 'TypePublicGroup' : 'TypePrivateGroup'));
};
setGroupTypeSubtitle();
@ -103,7 +105,7 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -103,7 +105,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
] as ChatRights[];
const permissionsRow = new Row({
title: 'Permissions',
titleLangKey: 'ChannelPermissions',
clickable: () => {
const tab = new AppGroupPermissionsTab(this.slider);
tab.chatId = this.chatId;
@ -127,7 +129,7 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -127,7 +129,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
}
const administratorsRow = new Row({
title: 'Administrators',
titleLangKey: 'PeerInfo.Administrators',
subtitle: '' + ((chatFull as ChatFull.channelFull).admins_count || 1),
icon: 'admin',
clickable: true
@ -170,7 +172,7 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -170,7 +172,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
});
const membersRow = new Row({
title: 'Members',
titleLangKey: 'GroupMembers',
subtitle: '2 500',
icon: 'newgroup',
clickable: true
@ -197,15 +199,21 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -197,15 +199,21 @@ export default class AppEditGroupTab extends SliderSuperTab {
if(appChatsManager.isChannel(this.chatId) && appChatsManager.hasRights(this.chatId, 'delete_chat')) {
const section = new SettingSection({});
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'Delete Group'});
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'DeleteMega'});
attachClickEvent(btnDelete, () => {
new PopupPeer('popup-delete-group', {
peerId: -this.chatId,
title: 'Delete Group?',
description: `Are you sure you want to delete this group? All members will be removed, and all messages will be lost.`,
titleLangKey: 'DeleteMegaMenu',
descriptionLangKey: 'AreYouSureDeleteAndExit',
buttons: addCancelButton([{
text: 'DELETE',
langKey: 'DeleteMegaMenu',
callback: () => {
const toggle = toggleDisability([btnDelete], true);
},
isDanger: true
}, {
langKey: 'DeleteChat.DeleteGroupForAll',
callback: () => {
const toggle = toggleDisability([btnDelete], true);
@ -214,6 +222,12 @@ export default class AppEditGroupTab extends SliderSuperTab { @@ -214,6 +222,12 @@ export default class AppEditGroupTab extends SliderSuperTab {
}, () => {
toggle();
});
/* appChatsManager.deleteChannel(this.chatId).then(() => {
this.close();
}, () => {
toggle();
}); */
},
isDanger: true
}])

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

@ -27,7 +27,7 @@ export default class AppGifsTab extends SliderSuperTab { @@ -27,7 +27,7 @@ export default class AppGifsTab extends SliderSuperTab {
protected init() {
this.container.id = 'search-gifs-container';
this.inputSearch = new InputSearch('Search GIFs', (value) => {
this.inputSearch = new InputSearch('SearchGifsTitle', (value) => {
this.reset();
this.search(value);
});

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

@ -5,6 +5,7 @@ import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../lay @@ -5,6 +5,7 @@ import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../lay
import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appProfileManager from "../../../lib/appManagers/appProfileManager";
import I18n, { i18n } from "../../../lib/langPack";
import rootScope from "../../../lib/rootScope";
import CheckboxField from "../../checkboxField";
import PopupPickUser from "../../popups/pickUser";
@ -29,14 +30,14 @@ export class ChatPermissions { @@ -29,14 +30,14 @@ export class ChatPermissions {
participant?: ChannelParticipant.channelParticipantBanned
}) {
this.v = [
{flags: ['send_messages'], text: 'Send Messages'},
{flags: ['send_media'], text: 'Send Media'},
{flags: ['send_stickers', 'send_gifs'], text: 'Send Stickers & GIFs'},
{flags: ['send_polls'], text: 'Send Polls'},
{flags: ['embed_links'], text: 'Send Links'},
{flags: ['invite_users'], text: 'Add Users'},
{flags: ['pin_messages'], text: 'Pin Messages'},
{flags: ['change_info'], text: 'Change Chat Info'}
{flags: ['send_messages'], text: 'UserRestrictionsSend'},
{flags: ['send_media'], text: 'UserRestrictionsSendMedia'},
{flags: ['send_stickers', 'send_gifs'], text: 'UserRestrictionsSendStickers'},
{flags: ['send_polls'], text: 'UserRestrictionsSendPolls'},
{flags: ['embed_links'], text: 'UserRestrictionsEmbedLinks'},
{flags: ['invite_users'], text: 'UserRestrictionsInviteUsers'},
{flags: ['pin_messages'], text: 'UserRestrictionsPinMessages'},
{flags: ['change_info'], text: 'UserRestrictionsChangeInfo'}
];
this.toggleWith = {
@ -71,7 +72,7 @@ export class ChatPermissions { @@ -71,7 +72,7 @@ export class ChatPermissions {
}); */
attachClickEvent(info.checkboxField.label, (e) => {
toast('This option is disabled for all members in Group Permissions.');
toast(I18n.format('UserRestrictionsDisabled', true));
}, {listenerSetter: options.listenerSetter});
}
@ -116,12 +117,12 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { @@ -116,12 +117,12 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
protected async init() {
this.container.classList.add('edit-peer-container', 'group-permissions-container');
this.title.innerHTML = 'Permissions';
this.setTitle('ChannelPermissions');
let chatPermissions: ChatPermissions;
{
const section = new SettingSection({
name: 'What can members of this group do?',
name: 'ChannelPermissionsHeader',
});
chatPermissions = new ChatPermissions({
@ -139,12 +140,12 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { @@ -139,12 +140,12 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
{
const section = new SettingSection({
name: 'Exceptions'
name: 'PrivacyExceptions'
});
const addExceptionRow = new Row({
title: 'Add Exception',
subtitle: 'Loading...',
titleLangKey: 'ChannelAddException',
subtitleLangKey: 'Loading',
icon: 'adduser',
clickable: () => {
new PopupPickUser({
@ -177,8 +178,8 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { @@ -177,8 +178,8 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
};
const removedUsersRow = new Row({
title: 'Removed Users',
subtitle: 'No removed users',
titleLangKey: 'ChannelBlockedUsers',
subtitleLangKey: 'NoBlockedUsers',
icon: 'deleteuser',
clickable: true
});
@ -277,7 +278,8 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { @@ -277,7 +278,8 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
});
const setLength = () => {
addExceptionRow.subtitle.innerHTML = exceptionsCount ? exceptionsCount + ' exceptions' : 'None';
addExceptionRow.subtitle.textContent = '';
addExceptionRow.subtitle.append(i18n(exceptionsCount ? 'Permissions.ExceptionsCount' : 'Permissions.NoExceptions', [exceptionsCount]));
};
let exceptionsCount = 0;

33
src/components/sidebarRight/tabs/groupType.ts

@ -13,6 +13,7 @@ import { SettingSection } from "../../sidebarLeft"; @@ -13,6 +13,7 @@ import { SettingSection } from "../../sidebarLeft";
import { toast } from "../../toast";
import { UsernameInputField } from "../../usernameInputField";
import { SliderSuperTabEventable } from "../../sliderTab";
import I18n from "../../../lib/langPack";
export default class AppGroupTypeTab extends SliderSuperTabEventable {
public peerId: number;
@ -20,28 +21,28 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable { @@ -20,28 +21,28 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
protected init() {
this.container.classList.add('edit-peer-container', 'group-type-container');
this.title.innerHTML = 'Group Type';
this.setTitle('GroupType');
const section = new SettingSection({
name: 'Group Type'
name: 'GroupType'
});
const random = randomLong();
const privateRow = new Row({
radioField: new RadioField({
text: 'Private Group',
langKey: 'MegaPrivate',
name: random,
value: 'private'
}),
subtitle: 'Private groups can only be joined if you were invited or have an invite link.'
subtitleLangKey: 'MegaPrivateInfo'
});
const publicRow = new Row({
radioField: new RadioField({
text: 'Public Group',
langKey: 'MegaPublic',
name: random,
value: 'public'
}),
subtitle: 'Public groups can be found in search, history is available to everyone and anyone can join.'
subtitleLangKey: 'MegaPublicInfo'
});
const form = RadioFormFromRows([privateRow, publicRow], (value) => {
const a = [privateSection, publicSection];
@ -62,18 +63,18 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable { @@ -62,18 +63,18 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
//let revoked = false;
const linkRow = new Row({
title: (this.chatFull.exported_invite as ExportedChatInvite.chatInviteExported).link,
subtitle: 'People can join your group by following this link. You can revoke the link at any time.',
subtitleLangKey: 'MegaPrivateLinkHelp',
clickable: () => {
copyTextToClipboard((this.chatFull.exported_invite as ExportedChatInvite.chatInviteExported).link);
toast('Invite link copied to clipboard.');
toast(I18n.format('LinkCopied', true));
}
});
const btnRevoke = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'Revoke Link'});
const btnRevoke = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'RevokeLink'});
attachClickEvent(btnRevoke, () => {
new PopupConfirmAction('revoke-link', [{
text: 'OK',
langKey: 'RevokeButton',
callback: () => {
const toggle = toggleDisability([btnRevoke], true);
@ -85,15 +86,15 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable { @@ -85,15 +86,15 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
});
}
}], {
title: 'Revoke Link?',
text: 'Your previous link will be deactivated and we\'ll generate a new invite link for you.'
title: 'RevokeLink',
text: 'RevokeAlert'
}).show();
}, {listenerSetter: this.listenerSetter});
privateSection.content.append(linkRow.container, btnRevoke);
const publicSection = new SettingSection({
caption: 'People can share this link with others and find your group using Telegram search.',
caption: 'Channel.UsernameAboutGroup',
noDelimiter: true
});
@ -109,13 +110,13 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable { @@ -109,13 +110,13 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
};
const linkInputField = new UsernameInputField({
label: 'Link',
label: 'SetUrlPlaceholder',
name: 'group-public-link',
plainText: true,
listenerSetter: this.listenerSetter,
availableText: 'Link is available',
availableText: 'Link.Available',
invalidText: 'Link is invalid',
takenText: 'Link is already taken',
takenText: 'Link.Taken',
onChange: onChange,
peerId: this.peerId,
head: placeholder

14
src/components/sidebarRight/tabs/pollResults.ts

@ -5,6 +5,7 @@ import { roundPercents } from "../../poll"; @@ -5,6 +5,7 @@ import { roundPercents } from "../../poll";
import { RichTextProcessor } from "../../../lib/richtextprocessor";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import { ripple } from "../../ripple";
import { i18n } from "../../../lib/langPack";
export default class AppPollResultsTab extends SliderSuperTab {
private resultsDiv: HTMLElement;
@ -13,8 +14,6 @@ export default class AppPollResultsTab extends SliderSuperTab { @@ -13,8 +14,6 @@ export default class AppPollResultsTab extends SliderSuperTab {
this.container.id = 'poll-results-container';
this.container.classList.add('chatlist-container');
this.title.innerHTML = 'Results';
this.resultsDiv = document.createElement('div');
this.resultsDiv.classList.add('poll-results');
this.scrollable.append(this.resultsDiv);
@ -24,6 +23,8 @@ export default class AppPollResultsTab extends SliderSuperTab { @@ -24,6 +23,8 @@ export default class AppPollResultsTab extends SliderSuperTab {
const ret = super.open();
const poll = appPollsManager.getPoll(message.media.poll.id);
this.setTitle(poll.poll.pFlags.quiz ? 'PollResults.Title.Quiz' : 'PollResults.Title.Poll');
const title = document.createElement('h3');
title.innerHTML = poll.poll.rQuestion;
@ -82,7 +83,7 @@ export default class AppPollResultsTab extends SliderSuperTab { @@ -82,7 +83,7 @@ export default class AppPollResultsTab extends SliderSuperTab {
if(offset) {
left -= votesList.votes.length;
(showMore.lastElementChild as HTMLElement).innerText = `Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}`;
(showMore.lastElementChild as HTMLElement).replaceWith(i18n('PollResults.LoadMore', [Math.min(20, left)]));
}
offset = votesList.next_offset;
@ -101,11 +102,12 @@ export default class AppPollResultsTab extends SliderSuperTab { @@ -101,11 +102,12 @@ export default class AppPollResultsTab extends SliderSuperTab {
if(left <= 0) return;
const showMore = document.createElement('div');
showMore.classList.add('poll-results-more', 'show-more');
showMore.classList.add('poll-results-more', 'show-more', 'rp-overflow');
showMore.addEventListener('click', load);
showMore.innerHTML = `<div class="tgico-down"></div><div>Show ${Math.min(20, left)} more voter${left > 1 ? 's' : ''}</div>`;
ripple(showMore);
const down = document.createElement('div');
down.classList.add('tgico-down');
showMore.append(down, i18n('PollResults.LoadMore', [Math.min(20, left)]));
fragment.append(showMore);
});

218
src/components/sidebarRight/tabs/sharedMedia.ts

@ -2,14 +2,14 @@ import appImManager from "../../../lib/appManagers/appImManager"; @@ -2,14 +2,14 @@ import appImManager from "../../../lib/appManagers/appImManager";
import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
import appPeersManager from "../../../lib/appManagers/appPeersManager";
import appProfileManager from "../../../lib/appManagers/appProfileManager";
import appUsersManager from "../../../lib/appManagers/appUsersManager";
import appUsersManager, { User } from "../../../lib/appManagers/appUsersManager";
import { logger } from "../../../lib/logger";
import { RichTextProcessor } from "../../../lib/richtextprocessor";
import rootScope from "../../../lib/rootScope";
import AppSearchSuper, { SearchSuperType } from "../../appSearchSuper.";
import AvatarElement from "../../avatar";
import Scrollable from "../../scrollable";
import { SliderTab } from "../../slider";
import SidebarSlider, { SliderSuperTab, SliderTab } from "../../slider";
import CheckboxField from "../../checkboxField";
import { attachClickEvent } from "../../../helpers/dom";
import appSidebarRight from "..";
@ -19,27 +19,25 @@ import AppEditGroupTab from "./editGroup"; @@ -19,27 +19,25 @@ import AppEditGroupTab from "./editGroup";
import PeerTitle from "../../peerTitle";
import AppEditChannelTab from "./editChannel";
import AppEditContactTab from "./editContact";
import appChatsManager from "../../../lib/appManagers/appChatsManager";
import appChatsManager, { Channel } from "../../../lib/appManagers/appChatsManager";
import { Chat } from "../../../layer";
let setText = (text: string, el: HTMLDivElement) => {
import Button from "../../button";
import ButtonIcon from "../../buttonIcon";
import I18n, { i18n } from "../../../lib/langPack";
import { SettingSection } from "../../sidebarLeft";
import Row from "../../row";
import { copyTextToClipboard } from "../../../helpers/clipboard";
import { toast } from "../../toast";
let setText = (text: string, row: Row) => {
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
row.title.innerHTML = text;
row.container.style.display = '';
});
};
// TODO: отредактированное сообщение не изменится
export default class AppSharedMediaTab implements SliderTab {
public container: HTMLElement;
public closeBtn: HTMLButtonElement;
export default class AppSharedMediaTab extends SliderSuperTab {
public editBtn: HTMLElement;
private peerId = 0;
@ -50,12 +48,10 @@ export default class AppSharedMediaTab implements SliderTab { @@ -50,12 +48,10 @@ export default class AppSharedMediaTab implements SliderTab {
avatar: AvatarElement,
name: HTMLDivElement,
subtitle: HTMLDivElement,
bio: HTMLDivElement,
username: HTMLDivElement,
phone: HTMLDivElement,
notificationsRow: HTMLDivElement,
notificationsCheckbox: HTMLInputElement,
notificationsStatus: HTMLParagraphElement
bio: Row,
username: Row,
phone: Row,
notifications: Row
} = {} as any;
public historiesStorage: {
@ -64,8 +60,6 @@ export default class AppSharedMediaTab implements SliderTab { @@ -64,8 +60,6 @@ export default class AppSharedMediaTab implements SliderTab {
}>
} = {};
public scroll: Scrollable = null;
private log = logger('SM'/* , LogLevels.error */);
setPeerStatusInterval: number;
cleaned: boolean;
@ -73,44 +67,118 @@ export default class AppSharedMediaTab implements SliderTab { @@ -73,44 +67,118 @@ export default class AppSharedMediaTab implements SliderTab {
private setBioTimeout: number;
public init() {
this.container = document.getElementById('shared-media-container');
this.closeBtn = this.container.querySelector('.sidebar-header .btn-icon');
this.closeBtn.classList.add('sidebar-close-button');
this.editBtn = this.container.querySelector('.sidebar-header .tgico-edit');
this.profileContentEl = this.container.querySelector('.profile-content');
this.profileElements = {
avatar: this.profileContentEl.querySelector('.profile-avatar'),
name: this.profileContentEl.querySelector('.profile-name'),
subtitle: this.profileContentEl.querySelector('.profile-subtitle'),
bio: this.profileContentEl.querySelector('.profile-row-bio'),
username: this.profileContentEl.querySelector('.profile-row-username'),
phone: this.profileContentEl.querySelector('.profile-row-phone'),
notificationsRow: this.profileContentEl.querySelector('.profile-row-notifications'),
notificationsCheckbox: null,
notificationsStatus: this.profileContentEl.querySelector('.profile-row-notifications > p')
};
constructor(slider: SidebarSlider) {
super(slider, false);
}
protected init() {
this.container.id = 'shared-media-container';
this.container.classList.add('profile-container');
// * header
const newCloseBtn = Button('btn-icon sidebar-close-button', {noRipple: true});
this.closeBtn.replaceWith(newCloseBtn);
this.closeBtn = newCloseBtn;
const animatedCloseIcon = document.createElement('div');
animatedCloseIcon.classList.add('animated-close-icon');
newCloseBtn.append(animatedCloseIcon);
const transitionContainer = document.createElement('div');
transitionContainer.className = 'transition slide-fade';
const transitionFirstItem = document.createElement('div');
transitionFirstItem.classList.add('transition-item');
this.title.append(i18n('Telegram.PeerInfoController'));
this.editBtn = ButtonIcon('edit');
const moreBtn = ButtonIcon('more');
transitionFirstItem.append(this.title, this.editBtn, moreBtn);
const transitionLastItem = document.createElement('div');
transitionLastItem.classList.add('transition-item');
const secondTitle: HTMLElement = this.title.cloneNode() as any;
secondTitle.append(i18n('PeerInfo.SharedMedia'));
transitionLastItem.append(secondTitle);
transitionContainer.append(transitionFirstItem, transitionLastItem);
const checkboxField = new CheckboxField({
text: 'Notifications',
name: 'notifications'
this.header.append(transitionContainer);
// * body
this.profileContentEl = document.createElement('div');
this.profileContentEl.classList.add('profile-content');
const section = new SettingSection({
noDelimiter: true
});
this.profileElements.notificationsCheckbox = checkboxField.input;
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsRow.prepend(checkboxField.label);
this.scroll = new Scrollable(this.container, 'SR', 400);
this.profileElements.avatar = new AvatarElement();
this.profileElements.avatar.classList.add('profile-avatar', 'avatar-120');
this.profileElements.avatar.setAttribute('dialog', '1');
this.profileElements.avatar.setAttribute('clickable', '');
this.profileElements.name = document.createElement('div');
this.profileElements.name.classList.add('profile-name');
this.profileElements.subtitle = document.createElement('div');
this.profileElements.subtitle.classList.add('profile-subtitle');
this.profileElements.bio = new Row({
title: ' ',
subtitleLangKey: 'UserBio',
icon: 'info',
clickable: () => {
appProfileManager.getProfileByPeerId(this.peerId).then(full => {
copyTextToClipboard(full.about);
toast(I18n.format('BioCopied', true));
});
}
});
this.profileElements.username = new Row({
title: ' ',
subtitleLangKey: 'Username',
icon: 'username',
clickable: () => {
const peer: Channel | User = appPeersManager.getPeer(this.peerId);
copyTextToClipboard('@' + peer.username);
toast(I18n.format('UsernameCopied', true));
}
});
this.profileElements.phone = new Row({
title: ' ',
subtitleLangKey: 'Phone',
icon: 'phone',
clickable: () => {
const peer: User = appUsersManager.getUser(this.peerId);
copyTextToClipboard('+' + peer.phone);
toast(I18n.format('PhoneCopied', true));
}
});
this.profileElements.notifications = new Row({
checkboxField: new CheckboxField({text: 'Notifications'})
});
section.content.append(this.profileElements.avatar, this.profileElements.name, this.profileElements.subtitle,
this.profileElements.bio.container, this.profileElements.username.container, this.profileElements.phone.container, this.profileElements.notifications.container);
this.profileContentEl.append(section.container);
this.scrollable.append(this.profileContentEl);
const HEADER_HEIGHT = 56;
const closeIcon = this.closeBtn.firstElementChild as HTMLElement;
this.scroll.onAdditionalScroll = () => {
this.scrollable.onAdditionalScroll = () => {
const rect = this.searchSuper.nav.getBoundingClientRect();
if(!rect.width) return;
const top = rect.top;
const isSharedMedia = top <= HEADER_HEIGHT;
closeIcon.classList.toggle('state-back', isSharedMedia);
animatedCloseIcon.classList.toggle('state-back', isSharedMedia);
transition(+isSharedMedia);
if(!isSharedMedia) {
@ -118,16 +186,16 @@ export default class AppSharedMediaTab implements SliderTab { @@ -118,16 +186,16 @@ export default class AppSharedMediaTab implements SliderTab {
}
};
const transition = TransitionSlider(this.closeBtn.nextElementSibling as HTMLElement, 'slide-fade', 400, null, false);
const transition = TransitionSlider(transitionContainer, 'slide-fade', 400, null, false);
transition(0);
attachClickEvent(this.closeBtn, (e) => {
if(this.closeBtn.firstElementChild.classList.contains('state-back')) {
this.scroll.scrollIntoViewNew(this.scroll.container.firstElementChild as HTMLElement, 'start');
this.scrollable.scrollIntoViewNew(this.scrollable.container.firstElementChild as HTMLElement, 'start');
transition(0);
closeIcon.classList.remove('state-back');
} else if(!this.scroll.isHeavyAnimationInProgress) {
animatedCloseIcon.classList.remove('state-back');
} else if(!this.scrollable.isHeavyAnimationInProgress) {
appSidebarRight.onCloseBtnClick();
}
});
@ -153,9 +221,13 @@ export default class AppSharedMediaTab implements SliderTab { @@ -153,9 +221,13 @@ export default class AppSharedMediaTab implements SliderTab {
}
});
this.container.prepend(this.closeBtn.parentElement);
//this.container.prepend(this.closeBtn.parentElement);
this.profileElements.notifications.checkboxField.input.addEventListener('change', (e) => {
if(!e.isTrusted) {
return;
}
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
//let checked = this.profileElements.notificationsCheckbox.checked;
appMessagesManager.mutePeer(this.peerId);
});
@ -163,8 +235,7 @@ export default class AppSharedMediaTab implements SliderTab { @@ -163,8 +235,7 @@ export default class AppSharedMediaTab implements SliderTab {
rootScope.on('dialog_notify_settings', (dialog) => {
if(this.peerId === dialog.peerId) {
const muted = appNotificationsManager.isPeerLocalMuted(this.peerId, false);
this.profileElements.notificationsCheckbox.checked = !muted;
this.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
this.profileElements.notifications.checkboxField.checked = !muted;
}
});
@ -208,7 +279,7 @@ export default class AppSharedMediaTab implements SliderTab { @@ -208,7 +279,7 @@ export default class AppSharedMediaTab implements SliderTab {
inputFilter: 'inputMessagesFilterMusic',
name: 'SharedMusicTab2',
type: 'music'
}], this.scroll/* , undefined, undefined, false */);
}], this.scrollable/* , undefined, undefined, false */);
this.profileContentEl.append(this.searchSuper.container);
}
@ -226,7 +297,8 @@ export default class AppSharedMediaTab implements SliderTab { @@ -226,7 +297,8 @@ export default class AppSharedMediaTab implements SliderTab {
return;
}
this.profileElements.subtitle.innerHTML = subtitle || '';
this.profileElements.subtitle.textContent = '';
this.profileElements.subtitle.append(subtitle || '');
});
};
@ -287,16 +359,15 @@ export default class AppSharedMediaTab implements SliderTab { @@ -287,16 +359,15 @@ export default class AppSharedMediaTab implements SliderTab {
}
}
this.scroll.onScroll();
this.scrollable.onScroll();
}
public cleanupHTML() {
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
this.profileElements.bio.container.style.display = 'none';
this.profileElements.phone.container.style.display = 'none';
this.profileElements.username.container.style.display = 'none';
this.profileElements.notifications.container.style.display = '';
this.profileElements.notifications.checkboxField.checked = true;
this.editBtn.style.display = 'none';
if(this.setBioTimeout) {
window.clearTimeout(this.setBioTimeout);
@ -351,11 +422,10 @@ export default class AppSharedMediaTab implements SliderTab { @@ -351,11 +422,10 @@ export default class AppSharedMediaTab implements SliderTab {
}
const muted = appNotificationsManager.isPeerLocalMuted(peerId, false);
this.profileElements.notificationsCheckbox.checked = !muted;
this.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
this.profileElements.notifications.checkboxField.checked = !muted;
} else {
window.requestAnimationFrame(() => {
this.profileElements.notificationsRow.style.display = 'none';
this.profileElements.notifications.container.style.display = 'none';
});
}
@ -446,6 +516,6 @@ export default class AppSharedMediaTab implements SliderTab { @@ -446,6 +516,6 @@ export default class AppSharedMediaTab implements SliderTab {
}
onOpenAfterTimeout() {
this.scroll.onScroll();
this.scrollable.onScroll();
}
}

14
src/components/sidebarRight/tabs/stickers.ts

@ -11,6 +11,7 @@ import { wrapSticker } from "../../wrappers"; @@ -11,6 +11,7 @@ import { wrapSticker } from "../../wrappers";
import appSidebarRight from "..";
import { StickerSet, StickerSetCovered } from "../../../layer";
import { forEachReverse } from "../../../helpers/array";
import { i18n } from "../../../lib/langPack";
export default class AppStickersTab extends SliderSuperTab {
private inputSearch: InputSearch;
@ -23,7 +24,7 @@ export default class AppStickersTab extends SliderSuperTab { @@ -23,7 +24,7 @@ export default class AppStickersTab extends SliderSuperTab {
this.lazyLoadQueue = new LazyLoadQueue();
this.inputSearch = new InputSearch('Search Stickers', (value) => {
this.inputSearch = new InputSearch('StickersTab.SearchPlaceholder', (value) => {
this.search(value);
});
@ -57,7 +58,8 @@ export default class AppStickersTab extends SliderSuperTab { @@ -57,7 +58,8 @@ export default class AppStickersTab extends SliderSuperTab {
appStickersManager.getStickerSet({id, access_hash}).then(full => {
appStickersManager.toggleStickerSet(full.set).then(changed => {
if(changed) {
button.innerText = full.set.installed_date ? 'Added' : 'Add';
button.textContent = '';
button.append(i18n(full.set.installed_date ? 'Stickers.SearchAdded' : 'Stickers.SearchAdd'));
button.classList.toggle('gray', !!full.set.installed_date);
}
}).finally(() => {
@ -91,12 +93,16 @@ export default class AppStickersTab extends SliderSuperTab { @@ -91,12 +93,16 @@ export default class AppStickersTab extends SliderSuperTab {
details.classList.add('sticker-set-details');
details.innerHTML = `
<div class="sticker-set-name">${RichTextProcessor.wrapEmojiText(set.title)}</div>
<div class="sticker-set-count">${set.count} stickers</div>
`;
const countDiv = document.createElement('div');
countDiv.classList.add('sticker-set-count');
countDiv.append(i18n('Stickers.Count', [set.count]));
details.append(countDiv);
const button = document.createElement('button');
button.classList.add('btn-primary', 'btn-color-primary', 'sticker-set-button');
button.innerText = set.installed_date ? 'Added' : 'Add';
button.append(i18n(set.installed_date ? 'Stickers.SearchAdded' : 'Stickers.SearchAdd'));
// button.style.width = set.installed_date ? '68px' : '52px';
if(set.installed_date) {

19
src/components/sidebarRight/tabs/userPermissions.ts

@ -19,13 +19,13 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { @@ -19,13 +19,13 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
protected init() {
this.container.classList.add('edit-peer-container', 'user-permissions-container');
this.title.innerHTML = 'User Permissions';
this.setTitle('UserRestrictions');
let destroyListener: () => void;
{
const section = new SettingSection({
name: 'What can this user do?',
name: 'UserRestrictionsCanDo',
});
const div = document.createElement('div');
@ -43,7 +43,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { @@ -43,7 +43,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
avatarSize: 48
});
dom.lastMessageSpan.innerHTML = appUsersManager.getUserStatusString(this.userId);
dom.lastMessageSpan.append(appUsersManager.getUserStatusString(this.userId));
const p = new ChatPermissions({
chatId: this.chatId,
@ -71,7 +71,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { @@ -71,7 +71,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
const section = new SettingSection({});
if(this.participant._ === 'channelParticipantBanned') {
const btnDeleteException = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'Delete Exception'});
const btnDeleteException = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'GroupPermission.Delete'});
attachClickEvent(btnDeleteException, () => {
const toggle = toggleDisability([btnDeleteException], true);
@ -86,10 +86,15 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { @@ -86,10 +86,15 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
section.content.append(btnDeleteException);
}
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'deleteuser', text: 'Ban and Remove From Group'});
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'deleteuser', text: 'UserRestrictionsBlock'});
attachClickEvent(btnDelete, () => {
new PopupPeer('popup-group-kick-user', {
const toggle = toggleDisability([btnDelete], true);
appChatsManager.kickFromChannel(this.chatId, this.participant).then(() => {
this.eventListener.removeEventListener('destroy', destroyListener);
this.close();
});
/* new PopupPeer('popup-group-kick-user', {
peerId: -this.chatId,
title: 'Ban User?',
description: `Are you sure you want to ban <b>${appPeersManager.getPeerTitle(this.userId)}</b>`,
@ -107,7 +112,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { @@ -107,7 +112,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
},
isDanger: true
}])
}).show();
}).show(); */
}, {listenerSetter: this.listenerSetter});
section.content.append(btnDelete);

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.4',
langPackVersion: '0.0.5',
domains: [] as string[],
baseDcId: 2
};

10
src/helpers/dom.ts

@ -811,3 +811,13 @@ export function htmlToSpan(html: string) { @@ -811,3 +811,13 @@ export function htmlToSpan(html: string) {
span.innerHTML = html;
return span;
}
export function replaceContent(elem: HTMLElement, node: string | Node) {
if(elem.children.length === 1) {
elem.firstChild.remove();
} else {
elem.textContent = '';
}
elem.append(node);
}

2
src/helpers/number.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
export function numberThousandSplitter(x: number, joiner = ',') {
export function numberThousandSplitter(x: number, joiner = ' ') {
const parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, joiner);
return parts.join(".");

42
src/index.hbs

@ -118,47 +118,7 @@ @@ -118,47 +118,7 @@
</div>
<div class="main-column" id="column-center"></div>
<div class="sidebar sidebar-right main-column" id="column-right">
<div class="sidebar-content sidebar-slider tabs-container">
<div class="sidebar-slider-item profile-container" id="shared-media-container">
<div class="sidebar-header">
<button class="btn-icon">
<div class="animated-close-icon"></div>
</button>
<div class="transition slide-fade">
<div class="transition-item">
<div class="sidebar-header__title">Info</div>
<div class="btn-icon tgico-edit"></div>
<div class="btn-icon tgico-more"></div>
</div>
<div class="transition-item">
<div class="sidebar-header__title">Shared Media</div>
</div>
</div>
</div>
<div class="profile-content">
<div class="profile-content-wrapper">
<avatar-element class="profile-avatar avatar-120" dialog="1" clickable></avatar-element>
<div class="profile-name"></div>
<div class="profile-subtitle"></div>
<div class="profile-row profile-row-bio tgico-info">
<p></p>
<p class="profile-row-label">Bio</p>
</div>
<div class="profile-row profile-row-username tgico-username">
<p></p>
<p class="profile-row-label">Username</p>
</div>
<div class="profile-row profile-row-phone tgico-phone">
<p></p>
<p class="profile-row-label">Phone</p>
</div>
<div class="profile-row profile-row-notifications">
<p class="profile-row-label">Enabled</p>
</div>
</div>
</div>
</div>
</div>
<div class="sidebar-content sidebar-slider tabs-container"></div>
</div>
</div>
<div class="emoji-dropdown" id="emoji-dropdown" style="display: none;">

162
src/lang.ts

@ -12,6 +12,7 @@ const lang = { @@ -12,6 +12,7 @@ const lang = {
"FilterAllBots": "All Bots",
"WordDelimiter": ", ",
"WordDelimiterLast": " and ",
"EditContact.OriginalName": "original name",
"EditProfile.FirstNameLabel": "Name",
"EditProfile.BioLabel": "Bio (optional)",
"EditProfile.Username.Label": "Username (optional)",
@ -61,11 +62,25 @@ const lang = { @@ -61,11 +62,25 @@ const lang = {
"one_value": "Send Album",
"other_value": "Send %d Albums"
},
"Presence.YourChat": "chat with yourself",
"Privacy.Devices": {
"one_value": "%1$d device",
"other_value": "%1$d devices"
},
"PrivacyModal.Search.Placeholder": "Add Users or Groups...",
"Permissions.NoExceptions": "No exceptions",
"Permissions.ExceptionsCount": {
"one_value": "%d exception",
"other_value": "%d exceptions",
},
"Link.Available": "Link is available",
"Link.Taken": "Link is already taken",
"Link.Invalid": "Link is invalid",
"StickersTab.SearchPlaceholder": "Search Stickers",
"StickerPack.Remove": {
"one_value": "Remove %d Sticker",
"other_value": "Remove %d Stickers"
},
// * android
"ActionCreateChannel": "Channel created",
@ -101,6 +116,7 @@ const lang = { @@ -101,6 +116,7 @@ const lang = {
//"ChannelJoined": "You joined this channel",
"ChannelMegaJoined": "You joined this group",
"Channel.DescriptionPlaceholder": "Description (optional)",
"DescriptionPlaceholder": "Description",
"Draft": "Draft",
"FilterAlwaysShow": "Include Chats",
"FilterNeverShow": "Exclude Chats",
@ -238,6 +254,66 @@ const lang = { @@ -238,6 +254,66 @@ const lang = {
"AskAQuestion": "Ask a Question",
"AddAnExplanationInfo": "Users will see this text after choosing the wrong answer, good for educational purposes.",
"AccDescrQuizExplanation": "Explanation",
"PhoneCopied": "Phone copied to clipboard",
"UsernameCopied": "Username copied to clipboard.",
//"HashtagCopied": "Hashtag copied to clipboard.",
"BioCopied": "Bio copied to clipboard.",
"UserBio": "Bio",
"Username": "Username",
"Phone": "Phone",
"Notifications": "Notifications",
"AreYouSureDeleteContact": "Are you sure you want to delete this contact?",
"AreYouSureDeleteAndExit": "Are you sure you want to delete and leave the group?",
"AreYouSureDeleteAndExitChannel": "Do you want to delete and leave the channel?",
"DeleteContact": "Delete contact",
"GroupType": "Group Type",
"ChannelType": "Channel Type",
"TypePrivate": "Private",
"TypePublic": "Public",
"TypePrivateGroup": "Private",
"TypePublicGroup": "Public",
"GroupMembers": "Members",
"DeleteMega": "Delete Group",
"DeleteMegaMenu": "Delete group",
"ChannelDeleteMenu": "Delete channel",
"ChannelPermissions": "Permissions",
"ChannelPermissionsHeader": "What can members of this group do?",
"ChannelAddException": "Add Exception",
"ChannelBlockedUsers": "Removed users",
"NoBlockedUsers": "No removed users",
"UserRestrictions": "User Permissions",
"UserRestrictionsDisabled": "This option is disabled for all members in Group Permissions",
"UserRestrictionsCanDo": "What can this user do?",
"UserRestrictionsRead": "Read Messages",
"UserRestrictionsSend": "Send Messages",
"UserRestrictionsSendMedia": "Send Media",
"UserRestrictionsSendPolls": "Send Polls",
"UserRestrictionsSendStickers": "Send Stickers and GIFs",
"UserRestrictionsEmbedLinks": "Embed Links",
"UserRestrictionsChangeInfo": "Change Chat Info",
"UserRestrictionsPinMessages": "Pin Messages",
"UserRestrictionsInviteUsers": "Add Users",
"UserRestrictionsBlock": "Ban and remove from group",
"ChannelPublic": "Public Channel",
"MegaPublic": "Public Group",
"MegaLocation": "Location-based Group",
"ChannelPublicInfo": "Public channels can be found in search, anyone can join them.",
"MegaPublicInfo": "Public groups can be found in search, chat history is available to everyone and anyone can join.",
"ChannelPrivate": "Private Channel",
"MegaPrivate": "Private Group",
"ChannelPrivateInfo": "Private channels can only be joined via an invite link.",
"MegaPrivateInfo": "Private groups can only be joined if you were invited or have an invite link.",
"ChannelPrivateLinkHelp": "People can join your channel by following this link. You can revoke the link any time.",
"MegaPrivateLinkHelp": "People can join your group by following this link. You can revoke the link any time.",
"RevokeButton": "Revoke",
"RevokeLink": "Revoke Link",
"RevokeAlert": "Are you sure you want to revoke this link? Once the link is revoked, no one will be able to join using it.",
"SetUrlPlaceholder": "Link",
"Subscribers": {
"one_value": "%1$d subscriber",
"other_value": "%1$d subscribers"
},
"SearchGifsTitle": "Search GIFs",
// * macos
"AccountSettings.Filters": "Chat Folders",
@ -245,6 +321,8 @@ const lang = { @@ -245,6 +321,8 @@ 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",
"Channel.UsernameAboutChannel": "People can share this link with others and can find your channel using Telegram search.",
"Channel.UsernameAboutGroup": "People can share this link with others and find your group using Telegram search.",
"Chat.CopySelectedText": "Copy Selected Text",
"Chat.Date.ScheduledFor": "Scheduled for %@",
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
@ -257,6 +335,25 @@ const lang = { @@ -257,6 +335,25 @@ const lang = {
"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.ViewResults": "View Results",
"Chat.Poll.SubmitVote": "Vote",
"Chat.Poll.Type.Anonymous": "Anonymous Poll",
"Chat.Poll.Type.Public": "Poll",
"Chat.Poll.Type.AnonymousQuiz": "Anonymous Quiz",
"Chat.Poll.Type.Quiz": "Quiz",
"Chat.Poll.Type.Closed": "Final Results",
"Chat.Poll.TotalVotes1": {
"one_value": "%d vote",
"other_value": "%d votes"
},
"Chat.Quiz.TotalVotes": {
"one_value": "%d answer",
"other_value": "%d answers"
},
"Chat.Poll.TotalVotesEmpty": "No votes yet",
"Chat.Poll.TotalVotesResultEmpty": "No votes",
"Chat.Quiz.TotalVotesEmpty": "No answers yet",
"Chat.Quiz.TotalVotesResultEmpty": "No answers",
// "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.",
// "Chat.Pinned.UnpinAll": {
@ -297,16 +394,76 @@ const lang = { @@ -297,16 +394,76 @@ const lang = {
"Channel.DescriptionHolderDescrpiton": "You can provide an optional description for your channel.",
"CreateGroup.NameHolder": "Group Name",
"Date.Today": "Today",
"DeleteChat.DeleteGroupForAll": "Delete for all members",
"DeleteChannelForAll": "Delete for all subscribers",
"EditAccount.Username": "Username",
"EditAccount.Title": "Edit Profile",
"EditAccount.Logout": "Log Out",
"LastSeen.HoursAgo": {
"one_value": "last seen %d hour ago",
"other_value": "last seen %d hours ago"
},
"Login.Register.LastName.Placeholder": "Last Name",
"Telegram.GeneralSettingsViewController": "General Settings",
"Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications",
"Telegram.PeerInfoController": "Info",
"StickerPack.Add1": {
"one_value": "Add %d Sticker",
"other_value": "Add %d Stickers"
},
"Stickers.Count": {
"one_value": "%d sticker",
"other_value": "%d stickers"
},
"Stickers.SearchAdd": "Add",
"Stickers.SearchAdded": "Added",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji",
"ShareModal.Search.ForwardPlaceholder": "Forward to...",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers",
// "Peer.Activity.User.PlayingGame": "playing a game",
"Peer.Activity.User.TypingText": "typing",
// "Peer.Activity.User.SendingPhoto": "sending a photo",
// "Peer.Activity.User.RecordingVideo": "recording video",
// "Peer.Activity.User.SendingVideo": "sending a video",
// "Peer.Activity.User.RecordingAudio": "recording voice",
// "Peer.Activity.User.SendingFile": "sending file",
"Peer.ServiceNotifications": "service notifications",
"Peer.RepliesNotifications": "Reply Notifications",
"Peer.Status.online": "online",
"Peer.Status.recently": "last seen recently",
"Peer.Status.justNow": "last seen just now",
"Peer.Status.lastWeek": "last seen within a week",
"Peer.Status.lastMonth": "last seen within a month",
"Peer.Status.longTimeAgo": "last seen a long time ago",
"Peer.Status.Today": "today",
"Peer.Status.Yesterday": "yesterday",
"Peer.Status.LastSeenAt": "last seen %@ at %@",
"Peer.Status.minAgo": {
"one_value": "last seen %d minute ago",
"other_value": "last seen %d minutes ago"
},
"Peer.Status.Member": {
"one_value": "%d member",
"other_value": "%d members"
},
"Peer.Status.Subscribers": {
"one_value": "%d subscriber",
"other_value": "%d subscribers"
},
"PeerInfo.Administrators": "Administrators",
"PeerInfo.DeleteChannel": "Delete Channel",
"PeerInfo.Discussion": "Discussion",
"PeerInfo.Discussion.Add": "Add",
"PeerInfo.SignMessages": "Sign Messages",
"PeerInfo.SharedMedia": "Shared Media",
"PeerInfo.Subscribers": "Subscribers",
"PollResults.Title.Poll": "Poll Results",
"PollResults.Title.Quiz": "Quiz Results",
"PollResults.LoadMore": {
"other_value": "Show More (%d)"
},
//"PeerInfo.Confirm.DeleteGroupConfirmation": "Wait! Deleting this group will remove all members and all messages will be lost. Delete the group anyway?",
"PreviewSender.CaptionPlaceholder": "Add a caption...",
"PreviewSender.SendFile": {
"one_value": "Send File",
@ -320,6 +477,8 @@ const lang = { @@ -320,6 +477,8 @@ const lang = {
"one_value": "Send Video",
"other_value": "Send %d Videos"
},
"Presence.bot": "bot",
"Presence.Support": "support",
"PrivacyAndSecurity.Item.On": "On",
"PrivacyAndSecurity.Item.Off": "Off",
"PrivacySettings.VoiceCalls": "Calls",
@ -352,7 +511,8 @@ const lang = { @@ -352,7 +511,8 @@ const lang = {
"NewPoll.Explanation.Placeholder": "Add a Comment (Optional)",
"NewPoll.OptionsAddOption": "Add an Option",
"NewPoll.MultipleChoice": "Multiple Answers",
"NewPoll.Quiz": "Quiz Mode"
"NewPoll.Quiz": "Quiz Mode",
"GroupPermission.Delete": "Delete Exception",
};
export default lang;

5
src/lib/appManagers/appChatsManager.ts

@ -2,6 +2,7 @@ import { MOUNT_CLASS_TO } from "../../config/debug"; @@ -2,6 +2,7 @@ import { MOUNT_CLASS_TO } from "../../config/debug";
import { numberThousandSplitter } from "../../helpers/number";
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object";
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatFull, ChatParticipant, ChatParticipants, InputChannel, InputChatPhoto, InputFile, InputPeer, SendMessageAction, Update, Updates } from "../../layer";
import { i18n, LangPackKey } from "../langPack";
import apiManagerProxy from "../mtproto/mtprotoworker";
import apiManager from '../mtproto/mtprotoworker';
import { RichTextProcessor } from "../richtextprocessor";
@ -402,7 +403,9 @@ export class AppChatsManager { @@ -402,7 +403,9 @@ export class AppChatsManager {
const isChannel = this.isBroadcast(id);
count = count || 1;
return numberThousandSplitter(count, ' ') + ' ' + (isChannel ? (count > 1 ? 'subscribers' : 'subscriber') : (count > 1 ? 'members' : 'member'));
let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member';
return i18n(key, [numberThousandSplitter(count)]);
}
public wrapForFull(id: number, fullChat: any) {

29
src/lib/appManagers/appImManager.ts

@ -38,6 +38,7 @@ import { MOUNT_CLASS_TO } from '../../config/debug'; @@ -38,6 +38,7 @@ import { MOUNT_CLASS_TO } from '../../config/debug';
import appNavigationController from '../../components/appNavigationController';
import appNotificationsManager from './appNotificationsManager';
import AppPrivateSearchTab from '../../components/sidebarRight/tabs/search';
import { i18n } from '../langPack';
//console.log('appImManager included33!');
@ -742,8 +743,8 @@ export class AppImManager { @@ -742,8 +743,8 @@ export class AppImManager {
}
public async getPeerStatus(peerId: number) {
let subtitle = '';
if(!peerId) return subtitle;
let subtitle: HTMLElement;
if(!peerId) return '';
if(peerId < 0) { // not human
const chat = appPeersManager.getPeer(peerId);
@ -756,10 +757,10 @@ export class AppImManager { @@ -756,10 +757,10 @@ export class AppImManager {
subtitle = appChatsManager.getChatMembersString(-peerId);
if(participants_count < 2) return subtitle;
const onlines = await appChatsManager.getOnlines(chat.id);
/* const onlines = await appChatsManager.getOnlines(chat.id);
if(onlines > 1) {
subtitle += ', ' + numberThousandSplitter(onlines, ' ') + ' online';
}
subtitle += ', ' + numberThousandSplitter(onlines) + ' online';
} */
return subtitle;
//}
@ -774,15 +775,19 @@ export class AppImManager { @@ -774,15 +775,19 @@ export class AppImManager {
if(!appUsersManager.isBot(peerId)) {
const typings = appChatsManager.typingsInPeer[peerId];
if(typings && typings.length) {
return '<span class="online">typing...</span>';
} else if(subtitle === 'online') {
return `<span class="online">${subtitle}</span>`;
} else {
return subtitle;
const span = document.createElement('span');
span.classList.add('online');
span.append(i18n('Peer.Activity.User.TypingText'));
return span;
} else if(user.status?._ === 'userStatusOnline') {
const span = document.createElement('span');
span.classList.add('online');
span.append(subtitle);
return span;
}
} else {
return subtitle;
}
return subtitle;
}
}
}

136
src/lib/appManagers/appUsersManager.ts

@ -3,6 +3,7 @@ import { MOUNT_CLASS_TO } from "../../config/debug"; @@ -3,6 +3,7 @@ import { MOUNT_CLASS_TO } from "../../config/debug";
import { tsNow } from "../../helpers/date";
import { safeReplaceObject, isObject } from "../../helpers/object";
import { InputUser, Update, User as MTUser, UserStatus } from "../../layer";
import I18n, { i18n, LangPackKey } from "../langPack";
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import { REPLIES_PEER_ID } from "../mtproto/mtproto_config";
@ -386,79 +387,92 @@ export class AppUsersManager { @@ -386,79 +387,92 @@ export class AppUsersManager {
return this.getUser(rootScope.myId);
}
public getUserStatusString(userId: number) {
public getUserStatusString(userId: number): HTMLElement {
let key: LangPackKey;
let args: any[];
switch(userId) {
case REPLIES_PEER_ID:
return 'reply notifications';
key = 'Peer.RepliesNotifications';
break;
case 777000:
return 'service notifications';
}
if(this.isBot(userId)) {
return 'bot';
}
const user = this.getUser(userId);
if(!user) {
return '';
}
if(user.pFlags.support) {
return 'support';
}
let str = '';
switch(user.status?._) {
case 'userStatusRecently': {
str = 'last seen recently';
key = 'Peer.ServiceNotifications';
break;
}
default: {
if(this.isBot(userId)) {
key = 'Presence.bot';
break;
}
case 'userStatusLastWeek': {
str = 'last seen last week';
break;
}
const user = this.getUser(userId);
if(!user) {
key = '';
break;
}
case 'userStatusLastMonth': {
str = 'last seen last month';
break;
}
case 'userStatusOffline': {
str = 'last seen ';
const date = user.status.was_online;
const now = Date.now() / 1000;
if((now - date) < 60) {
str += ' just now';
} else if((now - date) < 3600) {
const c = (now - date) / 60 | 0;
str += c + ' ' + (c === 1 ? 'minute' : 'minutes') + ' ago';
} else if(now - date < 86400) {
const c = (now - date) / 3600 | 0;
str += c + ' ' + (c === 1 ? 'hour' : 'hours') + ' ago';
} else {
const d = new Date(date * 1000);
str += ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + ' at ' +
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
if(user.pFlags.support) {
key = 'Presence.Support';
break;
}
break;
}
case 'userStatusOnline': {
str = 'online';
break;
}
switch(user.status?._) {
case 'userStatusRecently': {
key = 'Peer.Status.recently';
break;
}
case 'userStatusLastWeek': {
key = 'Peer.Status.lastWeek';
break;
}
case 'userStatusLastMonth': {
key = 'Peer.Status.lastMonth';
break;
}
case 'userStatusOffline': {
key = 'last seen ';
const date = user.status.was_online;
const now = Date.now() / 1000;
if((now - date) < 60) {
key = 'Peer.Status.justNow';
} else if((now - date) < 3600) {
key = 'Peer.Status.minAgo';
const c = (now - date) / 60 | 0;
args = [c];
} else if(now - date < 86400) {
key = 'LastSeen.HoursAgo';
const c = (now - date) / 3600 | 0;
args = [c];
} else {
key = 'Peer.Status.LastSeenAt';
const d = new Date(date * 1000);
args = [('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2),
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2)];
}
break;
}
case 'userStatusOnline': {
key = 'Peer.Status.online';
break;
}
default: {
key = 'Peer.Status.longTimeAgo';
break;
}
}
default: {
str = 'last seen a long time ago';
break;
}
}
return str;
return i18n(key, args);
}
public isBot(id: number) {

3
src/lib/langPack.ts

@ -197,7 +197,8 @@ namespace I18n { @@ -197,7 +197,8 @@ namespace I18n {
let input: string;
if(str) {
if(str._ === 'langPackStringPluralized' && args?.length) {
const v = args[0] as number;
let v = args[0] as number | string;
if(typeof(v) === 'string') v = +v.replace(/\D/g, '');
const s = pluralRules.select(v);
// @ts-ignore
input = str[s + '_value'] || str['other_value'];

7
src/scss/partials/_avatar.scss

@ -172,4 +172,9 @@ avatar-element { @@ -172,4 +172,9 @@ avatar-element {
--size: 18px;
--multiplier: 3;
}
}
&.avatar-16 {
--size: 16px;
--multiplier: 3.375;
}
}

6
src/scss/partials/_button.scss

@ -245,6 +245,12 @@ @@ -245,6 +245,12 @@
background: hover-color($color-blue);
}
&.danger {
@include hover() {
background: hover-color($color-red);
}
}
.preloader-circular .preloader-path {
stroke: $color-blue;
}

8
src/scss/partials/_chatBubble.scss

@ -2069,7 +2069,7 @@ poll-element { @@ -2069,7 +2069,7 @@ poll-element {
font-size: 14px;
color: #707579;
margin-top: 2px;
margin-bottom: 7px;
margin-bottom: 5px;
display: flex;
position: relative;
@ -2078,6 +2078,10 @@ poll-element { @@ -2078,6 +2078,10 @@ poll-element {
// }
}
&-type {
margin-top: 2px;
}
&-hint {
position: absolute;
font-size: 1.5rem;
@ -2296,6 +2300,8 @@ poll-element { @@ -2296,6 +2300,8 @@ poll-element {
avatar-element {
border: 1px solid #fff;
cursor: pointer;
width: 18px;
height: 18px;
}
.circle-hover {

13
src/scss/partials/_rightSidebar.scss

@ -111,6 +111,10 @@ @@ -111,6 +111,10 @@
padding-top: 15px;
}
}
.sidebar-left-section {
padding-bottom: 0;
}
}
&-container {
@ -142,7 +146,7 @@ @@ -142,7 +146,7 @@
text-align: center;
color: $color-gray;
font-size: 14px;
margin-bottom: 2px;
margin-bottom: .875rem;
margin-top: 1px;
@include respond-to(handhelds) {
@ -216,7 +220,8 @@ @@ -216,7 +220,8 @@
}
&-avatar {
margin: 1px auto 10px;
margin: .5rem auto 10px;
display: block;
//flex: 0 0 auto;
@include respond-to(handhelds) {
@ -650,14 +655,12 @@ @@ -650,14 +655,12 @@
font-size: 15px;
border-radius: 16px;
font-weight: 400;
width: 52px;
//width: auto; //68 - Added //52 - Add
width: auto;
transition: width 0.2s;
&.gray {
background: #F1F3F4;
color: #707579;
width: 68px;
}
}

8
src/scss/partials/popups/_peer.scss

@ -46,4 +46,10 @@ @@ -46,4 +46,10 @@
}
}
}
}
}
.popup-peer, .popup-confirm-action {
.popup-container {
max-width: unquote('min(400px, 100%)');
}
}

1
src/scss/partials/popups/_popup.scss

@ -129,7 +129,6 @@ @@ -129,7 +129,6 @@
margin-top: .5rem;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 286px;
overflow: hidden;
}
}

9
src/scss/partials/popups/_stickers.scss

@ -11,18 +11,19 @@ @@ -11,18 +11,19 @@
}
.sticker-set-footer {
padding: 19px 0 17px 0;
border-top: 1px solid $lightgrey;
text-align: center;
color: $color-blue;
.btn-primary {
width: 164px;
text-transform: uppercase;
width: auto;
padding: 0 1.0625rem;
height: 44px;
}
}
.sticker-set-footer.add {
.sticker-set-footer {
padding: 8px 0;
}
@ -83,4 +84,4 @@ @@ -83,4 +84,4 @@
}
}
}
}
}

Loading…
Cancel
Save