Browse Source

Better langpack formatting

Date formatting
Fix datepicker closening
Fix jumping by forwarded
master
Eduard Kuzmenko 3 years ago
parent
commit
ad9a6918d7
  1. 44
      src/components/chat/bubbles.ts
  2. 3
      src/components/popups/datePicker.ts
  3. 2
      src/components/sidebarLeft/tabs/activeSessions.ts
  4. 8
      src/components/sidebarLeft/tabs/editProfile.ts
  5. 26
      src/helpers/date.ts
  6. 4
      src/helpers/string.ts
  7. 264
      src/lang.ts
  8. 9
      src/lib/appManagers/appDialogsManager.ts
  9. 24
      src/lib/appManagers/appMessagesManager.ts
  10. 138
      src/lib/langPack.ts
  11. 37
      src/scripts/format_lang.js
  12. 190
      src/scripts/out/langPack.strings
  13. 4
      src/scss/partials/_chatBubble.scss

44
src/components/chat/bubbles.ts

@ -26,7 +26,7 @@ import { months } from "../../helpers/date";
import RichTextProcessor from "../../lib/richtextprocessor"; import RichTextProcessor from "../../lib/richtextprocessor";
import mediaSizes from "../../helpers/mediaSizes"; import mediaSizes from "../../helpers/mediaSizes";
import { isAndroid, isApple, isSafari } from "../../helpers/userAgent"; import { isAndroid, isApple, isSafari } from "../../helpers/userAgent";
import { langPack } from "../../lib/langPack"; import I18n, { i18n, langPack } from "../../lib/langPack";
import AvatarElement from "../avatar"; import AvatarElement from "../avatar";
import { formatPhoneNumber } from "../misc"; import { formatPhoneNumber } from "../misc";
import { ripple } from "../ripple"; import { ripple } from "../ripple";
@ -716,7 +716,7 @@ export default class ChatBubbles {
} }
//this.log('chatInner click:', target); //this.log('chatInner click:', target);
const isVideoComponentElement = target.tagName === 'SPAN'; const isVideoComponentElement = target.tagName === 'SPAN' && !target.classList.contains('peer-title');
/* if(isVideoComponentElement) { /* if(isVideoComponentElement) {
const video = target.parentElement.querySelector('video') as HTMLElement; const video = target.parentElement.querySelector('video') as HTMLElement;
if(video) { if(video) {
@ -808,9 +808,9 @@ export default class ChatBubbles {
return; return;
} }
if(['IMG', 'DIV', "AVATAR-ELEMENT"/* , 'A' */].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV'); if(['IMG', 'DIV', "AVATAR-ELEMENT", 'SPAN'/* , 'A' */].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV');
if(target.tagName === 'DIV' || target.tagName === "AVATAR-ELEMENT"/* || target.tagName === 'A' */) { if(['DIV', 'SPAN'].indexOf(target.tagName) !== -1/* || target.tagName === 'A' */) {
if(target.classList.contains('goto-original')) { if(target.classList.contains('goto-original')) {
const savedFrom = bubble.dataset.savedFrom; const savedFrom = bubble.dataset.savedFrom;
const splitted = savedFrom.split('_'); const splitted = savedFrom.split('_');
@ -824,7 +824,7 @@ export default class ChatBubbles {
new PopupForward(this.peerId, [mid]); new PopupForward(this.peerId, [mid]);
//appSidebarRight.forwardTab.open([mid]); //appSidebarRight.forwardTab.open([mid]);
return; return;
} else if(target.classList.contains('name')) { } else if(target.classList.contains('peer-title') || target.classList.contains('name')) {
const peerId = +target.dataset.peerId; const peerId = +target.dataset.peerId;
const savedFrom = target.dataset.savedFrom; const savedFrom = target.dataset.savedFrom;
@ -1251,28 +1251,48 @@ export default class ChatBubbles {
date.setHours(0, 0, 0); date.setHours(0, 0, 0);
const dateTimestamp = date.getTime(); const dateTimestamp = date.getTime();
if(!(dateTimestamp in this.dateMessages)) { if(!(dateTimestamp in this.dateMessages)) {
let str = ''; let dateElement: HTMLElement;
const today = new Date(); const today = new Date();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
if(today.getTime() === date.getTime()) { if(today.getTime() === date.getTime()) {
str = 'Today'; dateElement = i18n(this.chat.type === 'scheduled' ? 'Chat.Date.ScheduledForToday' : 'Date.Today');
} else { } else {
str = months[date.getMonth()] + ' ' + date.getDate(); const options: Intl.DateTimeFormatOptions = {
day: 'numeric',
month: 'long'
};
if(date.getFullYear() !== today.getFullYear()) { if(date.getFullYear() !== today.getFullYear()) {
str += ', ' + date.getFullYear(); options.year = 'numeric';
}
dateElement = new I18n.IntlDateElement({
date,
options
}).element;
if(this.chat.type === 'scheduled') {
dateElement = i18n('Chat.Date.ScheduledFor', [dateElement]);
} }
} }
if(this.chat.type === 'scheduled') { /* if(this.chat.type === 'scheduled') {
str = 'Scheduled for ' + str; str = 'Scheduled for ' + str;
} } */
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'bubble service is-date'; div.className = 'bubble service is-date';
div.innerHTML = `<div class="bubble-content"><div class="service-msg">${str}</div></div>`; const bubbleContent = document.createElement('div');
bubbleContent.classList.add('bubble-content');
const serviceMsg = document.createElement('div');
serviceMsg.classList.add('service-msg');
serviceMsg.append(dateElement);
bubbleContent.append(serviceMsg);
div.append(bubbleContent);
////////this.log('need to render date message', dateTimestamp, str); ////////this.log('need to render date message', dateTimestamp, str);
const container = document.createElement('div'); const container = document.createElement('div');

3
src/components/popups/datePicker.ts

@ -222,6 +222,8 @@ export default class PopupDatePicker extends PopupElement {
if(this.selectedEl === target) return; if(this.selectedEl === target) return;
this.selectedEl.classList.remove('active'); this.selectedEl.classList.remove('active');
} }
this.selectedEl = target;
target.classList.add('active'); target.classList.add('active');
const timestamp = +target.dataset.timestamp; const timestamp = +target.dataset.timestamp;
@ -229,7 +231,6 @@ export default class PopupDatePicker extends PopupElement {
this.selectedDate = new Date(timestamp); this.selectedDate = new Date(timestamp);
this.setTitle(); this.setTitle();
this.setMonth();
this.setTimeTitle(); this.setTimeTitle();
}; };

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

@ -98,7 +98,7 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
const onError = (err: any) => { const onError = (err: any) => {
if(err.type === 'FRESH_RESET_AUTHORISATION_FORBIDDEN') { if(err.type === 'FRESH_RESET_AUTHORISATION_FORBIDDEN') {
toast(I18n.getString('RecentSessions.Error.FreshReset')); toast(I18n.format('RecentSessions.Error.FreshReset', true));
} }
}; };

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

@ -98,20 +98,16 @@ export default class AppEditProfileTab extends SliderSuperTab {
const profileUrlContainer = this.profileUrlContainer = document.createElement('div'); const profileUrlContainer = this.profileUrlContainer = document.createElement('div');
profileUrlContainer.classList.add('profile-url-container'); profileUrlContainer.classList.add('profile-url-container');
profileUrlContainer.append(i18n('UsernameHelpLink', ['']));
const profileUrlAnchor = this.profileUrlAnchor = document.createElement('a'); const profileUrlAnchor = this.profileUrlAnchor = document.createElement('a');
profileUrlAnchor.classList.add('profile-url'); profileUrlAnchor.classList.add('profile-url');
profileUrlAnchor.href = '#'; profileUrlAnchor.href = '#';
profileUrlAnchor.target = '_blank'; profileUrlAnchor.target = '_blank';
profileUrlContainer.append(profileUrlAnchor); profileUrlContainer.append(i18n('UsernameHelpLink', [profileUrlAnchor]));
caption.append(profileUrlContainer); caption.append(profileUrlContainer);
this.profileUrlContainer = caption.querySelector('.profile-url-container');
this.profileUrlAnchor = this.profileUrlContainer.lastElementChild as HTMLAnchorElement;
inputFields.push(this.usernameInputField); inputFields.push(this.usernameInputField);
this.scrollable.append(h2, inputWrapper, caption); this.scrollable.append(h2, inputWrapper, caption);
} }

26
src/helpers/date.ts

@ -1,4 +1,5 @@
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
import I18n from "../lib/langPack";
export const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; export const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
export const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; export const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
@ -33,6 +34,31 @@ export const formatDateAccordingToToday = (time: Date) => {
return timeStr; return timeStr;
}; };
export function formatDateAccordingToTodayNew(time: Date) {
const today = new Date();
const now = today.getTime() / 1000 | 0;
const timestamp = time.getTime() / 1000 | 0;
const options: Intl.DateTimeFormatOptions = {};
if((now - timestamp) < ONE_DAY && today.getDate() === time.getDate()) { // if the same day
options.hour = options.minute = '2-digit';
options.hour12 = false;
} else if(today.getFullYear() !== time.getFullYear()) { // different year
options.year = options.day = 'numeric';
options.month = '2-digit';
} else if((now - timestamp) < (ONE_DAY * 7) && getWeekNumber(today) === getWeekNumber(time)) { // current week
options.weekday = 'short';
} else { // same year
options.month = 'short';
options.day = 'numeric';
}
return new I18n.IntlDateElement({
date: time,
options
}).element;
}
export const getFullDate = (date: Date, options: Partial<{ export const getFullDate = (date: Date, options: Partial<{
noTime: true, noTime: true,
noSeconds: true, noSeconds: true,

4
src/helpers/string.ts

@ -98,3 +98,7 @@ export function convertKeyToInputKey(key: string) {
key = 'input' + key; key = 'input' + key;
return key; return key;
} }
export function capitalizeFirstLetter(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

264
src/lang.ts

@ -1,27 +1,27 @@
const lang = { const lang = {
FilterIncludeExcludeInfo: 'Choose chats and types of chats that will\nappear and never appear in this folder.', "FilterIncludeExcludeInfo": "Choose chats and types of chats that will\nappear and never appear in this folder.",
FilterNameInputLabel: 'Folder Name', "FilterNameInputLabel": "Folder Name",
FilterMenuDelete: 'Delete Folder', "FilterMenuDelete": "Delete Folder",
FilterHeaderEdit: 'Edit Folder', "FilterHeaderEdit": "Edit Folder",
FilterAllGroups: 'All Groups', "FilterAllGroups": "All Groups",
FilterAllContacts: 'All Contacts', "FilterAllContacts": "All Contacts",
FilterAllNonContacts: 'All Non-Contacts', "FilterAllNonContacts": "All Non-Contacts",
FilterAllChannels: 'All Channels', "FilterAllChannels": "All Channels",
FilterAllBots: 'All Bots', "FilterAllBots": "All Bots",
FilterAllUnmuted: 'All Unmuted', "FilterAllUnmuted": "All Unmuted",
FilterAllUnread: 'All Unread', "FilterAllUnread": "All Unread",
FilterAllUnarchived: 'All Unarchived', "FilterAllUnarchived": "All Unarchived",
WordDelimiter: ', ', "WordDelimiter": ", ",
WordDelimiterLast: ' and ', "WordDelimiterLast": " and ",
"EditProfile.FirstNameLabel": 'Name', "EditProfile.FirstNameLabel": "Name",
"EditProfile.BioLabel": 'Bio (optional)', "EditProfile.BioLabel": "Bio (optional)",
"EditProfile.Username.Label": 'Username (optional)', "EditProfile.Username.Label": "Username (optional)",
"EditProfile.Username.Available": 'Username is available', "EditProfile.Username.Available": "Username is available",
"EditProfile.Username.Taken": 'Username is already taken', "EditProfile.Username.Taken": "Username is already taken",
"EditProfile.Username.Invalid": 'Username is invalid', "EditProfile.Username.Invalid": "Username is invalid",
"EditProfile.Username.Help": "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\nYou can use a–z, 0–9 and underscores. Minimum length is 5 characters.", "EditProfile.Username.Help": "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\nYou can use a–z, 0–9 and underscores. Minimum length is 5 characters.",
"ChatList.Menu.Archived": "Archived", "ChatList.Menu.Archived": "Archived",
Saved: "Saved", "Saved": "Saved",
"General.Keyboard": "Keyboard", "General.Keyboard": "Keyboard",
"General.SendShortcut.Enter": "Send by Enter", "General.SendShortcut.Enter": "Send by Enter",
"General.SendShortcut.CtrlEnter": "Send by %s + Enter", "General.SendShortcut.CtrlEnter": "Send by %s + Enter",
@ -29,120 +29,128 @@ const lang = {
"General.SendShortcut.NewLine.Enter": "New line by Enter", "General.SendShortcut.NewLine.Enter": "New line by Enter",
"General.AutoplayMedia": "Auto-Play Media", "General.AutoplayMedia": "Auto-Play Media",
"ChatBackground.UploadWallpaper": "Upload Wallpaper", "ChatBackground.UploadWallpaper": "Upload Wallpaper",
"ChatBackground.SetColor": "Upload Wallpaper", "ChatBackground.SetColor": "Set a Color",
"ChatBackground.Blur": "Upload Wallpaper", "ChatBackground.Blur": "Blur Wallpaper Image",
"Notifications.Sound": "Notification Sound", "Notifications.Sound": "Notification Sound",
"Notifications.MessagePreview": "Message preview", "Notifications.MessagePreview": "Message preview",
"Checkbox.Enabled": "Enabled", "Checkbox.Enabled": "Enabled",
"Checkbox.Disabled": "Disabled", "Checkbox.Disabled": "Disabled",
"Privacy.Devices": { "Privacy.Devices": {
one_value: '%1$d device', "one_value": "%1$d device",
other_value: '%1$d devices' "other_value": "%1$d devices"
}, },
// * android // * android
ActionCreateChannel: "Channel created", "ActionCreateChannel": "Channel created",
ActionCreateGroup: "un1 created the group", "ActionCreateGroup": "un1 created the group",
ActionChangedTitle: "un1 changed the group name to un2", "ActionChangedTitle": "un1 changed the group name to un2",
ActionRemovedPhoto: "un1 removed the group photo", "ActionRemovedPhoto": "un1 removed the group photo",
ActionChangedPhoto: "un1 changed the group photo", "ActionChangedPhoto": "un1 changed the group photo",
ActionChangedVideo: "un1 changed the group video", "ActionChangedVideo": "un1 changed the group video",
ActionAddUser: "un1 added un2", "ActionAddUser": "un1 added un2",
ActionAddUserSelf: "un1 returned to the group", "ActionAddUserSelf": "un1 returned to the group",
ActionAddUserSelfMega: "un1 joined the group", "ActionAddUserSelfMega": "un1 joined the group",
ActionAddUserSelfYou: "You returned to the group", "ActionAddUserSelfYou": "You returned to the group",
ActionLeftUser: "un1 left the group", "ActionLeftUser": "un1 left the group",
ActionKickUser: "un1 removed un2", "ActionKickUser": "un1 removed un2",
ActionInviteUser: "un1 joined the group via invite link", "ActionInviteUser": "un1 joined the group via invite link",
ActionPinnedNoText: "un1 pinned a message", "ActionPinnedNoText": "un1 pinned a message",
ActionMigrateFromGroup: "This group was upgraded to a supergroup", "ActionMigrateFromGroup": "This group was upgraded to a supergroup",
FilterAlwaysShow: 'Include Chats', "FilterAlwaysShow": "Include Chats",
FilterNeverShow: 'Exclude Chats', "FilterNeverShow": "Exclude Chats",
FilterInclude: 'Included Chats', "FilterInclude": "Included Chats",
FilterExclude: 'Excluded Chats', "FilterExclude": "Excluded Chats",
FilterChatTypes: 'Chat types', "FilterChatTypes": "Chat types",
FilterChats: 'Chats', "FilterChats": "Chats",
FilterNew: 'New Folder', "FilterNew": "New Folder",
Filters: 'Folders', "Filters": "Folders",
FilterRecommended: 'Recommended Folders', "FilterRecommended": "Recommended Folders",
Add: 'Add', "Add": "Add",
Chats: { "Chats": {
one_value: '%1$d chat', "one_value": "%1$d chat",
other_value: '%1$d chats' "other_value": "%1$d chats"
}, },
Channels: { "Channels": {
one_value: '%1$d channel', "one_value": "%1$d channel",
other_value: '%1$d channels' "other_value": "%1$d channels"
}, },
Groups: { "Groups": {
one_value: '%1$d group', "one_value": "%1$d group",
other_value: '%1$d groups' "other_value": "%1$d groups"
}, },
Users: { "Users": {
one_value: "%1$d user", "one_value": "%1$d user",
other_value: "%1$d users" "other_value": "%1$d users"
}, },
UsernameHelpLink: "This link opens a chat with you:\n%1$s", "UsernameHelpLink": "This link opens a chat with you:\n%1$s",
NewGroup: "New Group", "NewGroup": "New Group",
Contacts: "Contacts", "Contacts": "Contacts",
SavedMessages: "Saved Messages", "SavedMessages": "Saved Messages",
Settings: "Settings", "Settings": "Settings",
SettingsHelp: "Help", "SettingsHelp": "Help",
General: "General", "General": "General",
TextSize: "Message Text Size", "TextSize": "Message Text Size",
ChatBackground: "Chat Background", "ChatBackground": "Chat Background",
EnableAnimations: "Enable Animations", "EnableAnimations": "Enable Animations",
AutoDownloadMedia: "Auto-Download Media", "AutoDownloadMedia": "Auto-Download Media",
AutodownloadContacts: 'Contacts', "AutodownloadContacts": "Contacts",
AutodownloadPrivateChats: 'Private Chats', "AutodownloadPrivateChats": "Private Chats",
AutodownloadGroupChats: 'Group Chats', "AutodownloadGroupChats": "Group Chats",
AutodownloadChannels: 'Channels', "AutodownloadChannels": "Channels",
AutoplayGIF: 'GIFs', "AutoplayGIF": "GIFs",
AutoplayVideo: 'Videos', "AutoplayVideo": "Videos",
NotificationsForGroups: 'Notifications for groups', "NotificationsForGroups": "Notifications for groups",
NotificationsForPrivateChats: 'Notifications for private chats', "NotificationsForPrivateChats": "Notifications for private chats",
NotificationsForChannels: 'Notifications for channels', "NotificationsForChannels": "Notifications for channels",
NotificationsPrivateChats: "Private Chats", "NotificationsPrivateChats": "Private Chats",
NotificationsGroups: "Groups", "NotificationsGroups": "Groups",
NotificationsChannels: "Channels", "NotificationsChannels": "Channels",
NotificationsOther: 'Other', "NotificationsOther": "Other",
ContactJoined: 'Contact joined Telegram', "ContactJoined": "Contact joined Telegram",
Loading: "Loading...", "Loading": "Loading...",
Unblock: "Unblock", "Unblock": "Unblock",
BlockedUsers: "Blocked Users", "BlockedUsers": "Blocked Users",
BlockedUsersInfo: 'Blocked users will not be able to contact you and will not see your Last Seen time.', "BlockedUsersInfo": "Blocked users will not be able to contact you and will not see your Last Seen time.",
BlockedEmpty: "None", "BlockedEmpty": "None",
TwoStepVerification: "Two-Step Verification", "TwoStepVerification": "Two-Step Verification",
PrivacyExceptions: "Exceptions", "PrivacyExceptions": "Exceptions",
PrivacyLastSeen: "Last Seen & Online", "PrivacyLastSeen": "Last Seen & Online",
PrivacySettings: "Privacy and Security", "PrivacySettings": "Privacy and Security",
PrivacyTitle: "Privacy", "PrivacyTitle": "Privacy",
PrivacyPhone: "Phone Number", "PrivacyPhone": "Phone Number",
PrivacyPhoneTitle: "Who can see my phone number?", "PrivacyPhoneTitle": "Who can see my phone number?",
PrivacyPhoneTitle2: "Who can find me by my number?", "PrivacyPhoneTitle2": "Who can find me by my number?",
PrivacyPhoneInfo: "Users who have your number saved in their contacts will also see it on Telegram.", "PrivacyPhoneInfo": "Users who have your number saved in their contacts will also see it on Telegram.",
PrivacyPhoneInfo3: "Users who add your number to their contacts will see it on Telegram only if they are your contacts.", "PrivacyPhoneInfo3": "Users who add your number to their contacts will see it on Telegram only if they are your contacts.",
PrivacyProfilePhoto: "Profile Photos", "PrivacyProfilePhoto": "Profile Photos",
PrivacyProfilePhotoTitle: "Who can see my profile photos & videos?", "PrivacyProfilePhotoTitle": "Who can see my profile photos & videos?",
PrivacyP2PHeader: "Peer-to-Peer", "PrivacyP2PHeader": "Peer-to-Peer",
PrivacyForwardsTitle: "Who can add a link to my account when forwarding my messages?", "PrivacyForwardsTitle": "Who can add a link to my account when forwarding my messages?",
LastSeenTitle: "Who can see your Last Seen time?", "LastSeenTitle": "Who can see your Last Seen time?",
SessionsTitle: "Active Sessions", "SessionsTitle": "Active Sessions",
CurrentSession: "This device", "CurrentSession": "This device",
TerminateAllSessions: "Terminate All Other Sessions", "TerminateAllSessions": "Terminate All Other Sessions",
TerminateSessionText: "Are you sure you want to terminate this session?", "TerminateSessionText": "Are you sure you want to terminate this session?",
OtherSessions: "Active sessions", "OtherSessions": "Active sessions",
AreYouSureSessionTitle: "Terminate session", "AreYouSureSessionTitle": "Terminate session",
AreYouSureSessionsTitle: "Terminate sessions", "AreYouSureSessionsTitle": "Terminate sessions",
AreYouSureSessions: "Are you sure you want to terminate all other sessions?", "AreYouSureSessions": "Are you sure you want to terminate all other sessions?",
Terminate: "Terminate", "Terminate": "Terminate",
WhoCanCallMe: "Who can call me?", "WhoCanCallMe": "Who can call me?",
WhoCanAddMe: "Who can add me to group chats?", "WhoCanAddMe": "Who can add me to group chats?",
ArchivedChats: "Archived Chats", "ArchivedChats": "Archived Chats",
Cancel: "Cancel", "Cancel": "Cancel",
HistoryCleared: "History was cleared", "HistoryCleared": "History was cleared",
// * macos // * macos
"AccountSettings.Filters": "Chat Folders",
"AccountSettings.Notifications": "Notifications and Sounds",
"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.Date.ScheduledFor": "Scheduled for %@",
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
"Chat.Date.ScheduledForToday": "Scheduled for today",
"Chat.Service.PeerJoinedTelegram": "%@ joined Telegram", "Chat.Service.PeerJoinedTelegram": "%@ joined Telegram",
"Chat.Service.Channel.UpdatedTitle": "Channel renamed to \"%@\"", "Chat.Service.Channel.UpdatedTitle": "Channel renamed to \"%@\"",
"Chat.Service.Channel.UpdatedPhoto": "Channel photo updated", "Chat.Service.Channel.UpdatedPhoto": "Channel photo updated",
@ -166,15 +174,11 @@ const lang = {
"ChatList.Filter.MutedChats": "Muted", "ChatList.Filter.MutedChats": "Muted",
"ChatList.Filter.ReadChats": "Read", "ChatList.Filter.ReadChats": "Read",
"ChatList.Filter.Archive": "Archived", "ChatList.Filter.Archive": "Archived",
"Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco", "Date.Today": "Today",
"EditAccount.Username": "Username", "EditAccount.Username": "Username",
"EditAccount.Title": "Edit Profile", "EditAccount.Title": "Edit Profile",
"EditAccount.Logout": "Log Out", "EditAccount.Logout": "Log Out",
"Login.Register.LastName.Placeholder": "Last Name", "Login.Register.LastName.Placeholder": "Last Name",
"AccountSettings.Filters": "Chat Folders",
"AccountSettings.Notifications": "Notifications and Sounds",
"AccountSettings.PrivacyAndSecurity": "Privacy and Security",
"AccountSettings.Language": "Language",
"Telegram.GeneralSettingsViewController": "General Settings", "Telegram.GeneralSettingsViewController": "General Settings",
"Telegram.InstalledStickerPacksController": "Stickers", "Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications", "Telegram.NotificationSettingsViewController": "Notifications",
@ -191,7 +195,7 @@ const lang = {
"PrivacySettingsController.P2p.Desc": "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio and video quality.", "PrivacySettingsController.P2p.Desc": "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio and video quality.",
"PrivacySettingsController.PhoneCallDescription": "You can restrict who can call you with granular precision.", "PrivacySettingsController.PhoneCallDescription": "You can restrict who can call you with granular precision.",
"PrivacySettingsController.ProfilePhoto.CustomHelp": "You can restrict who can see your profile photo with granular precision.", "PrivacySettingsController.ProfilePhoto.CustomHelp": "You can restrict who can see your profile photo with granular precision.",
"PrivacySettingsController.LastSeenDescription": "You won't see Last Seen and Online statuses for people with whom you don't share yours. Approximate last seen will be shown instead (recently, within a week, within a month).", "PrivacySettingsController.LastSeenDescription": "You won\"t see Last Seen and Online statuses for people with whom you don\"t share yours. Approximate last seen will be shown instead (recently, within a week, within a month).",
"PrivacySettingsController.PeerInfo": "You can add users or entire groups as exceptions that will override the settings above.", "PrivacySettingsController.PeerInfo": "You can add users or entire groups as exceptions that will override the settings above.",
"PrivacySettingsController.Everbody": "Everybody", "PrivacySettingsController.Everbody": "Everybody",
"PrivacySettingsController.MyContacts": "My Contacts", "PrivacySettingsController.MyContacts": "My Contacts",
@ -201,10 +205,10 @@ const lang = {
"PrivacySettingsController.NeverAllow": "Never Allow", "PrivacySettingsController.NeverAllow": "Never Allow",
"PrivacySettingsController.AlwaysAllow": "Always Allow", "PrivacySettingsController.AlwaysAllow": "Always Allow",
"PrivacySettingsController.UserCount": { "PrivacySettingsController.UserCount": {
one_value: '%d user', "one_value": "%d user",
other_value: '%d users' "other_value": "%d users"
}, },
"RecentSessions.Error.FreshReset": "For security reasons, you can't terminate older sessions from a device that you've just connected. Please use an earlier connection or wait for a few hours.", "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."
}; };
export default lang; export default lang;

9
src/lib/appManagers/appDialogsManager.ts

@ -5,13 +5,13 @@ import { attachContextMenuListener, putPreloader } from "../../components/misc";
import { ripple } from "../../components/ripple"; import { ripple } from "../../components/ripple";
//import Scrollable from "../../components/scrollable"; //import Scrollable from "../../components/scrollable";
import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable"; import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable";
import { formatDateAccordingToToday } from "../../helpers/date"; import { formatDateAccordingToTodayNew } from "../../helpers/date";
import { escapeRegExp } from "../../helpers/string"; import { escapeRegExp } from "../../helpers/string";
import { isSafari } from "../../helpers/userAgent"; import { isSafari } from "../../helpers/userAgent";
import { logger, LogLevels } from "../logger"; import { logger, LogLevels } from "../logger";
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope"; import rootScope from "../rootScope";
import { findUpClassName, findUpTag, positionElementByIndex } from "../../helpers/dom"; import { findUpTag, positionElementByIndex } from "../../helpers/dom";
import appImManager from "./appImManager"; import appImManager from "./appImManager";
import appMessagesManager, { Dialog } from "./appMessagesManager"; import appMessagesManager, { Dialog } from "./appMessagesManager";
import {MyDialogFilter as DialogFilter} from "../storages/filters"; import {MyDialogFilter as DialogFilter} from "../storages/filters";
@ -1120,8 +1120,9 @@ export class AppDialogsManager {
if(!lastMessage.deleted || draftMessage/* && lastMessage._ !== 'draftMessage' */) { if(!lastMessage.deleted || draftMessage/* && lastMessage._ !== 'draftMessage' */) {
const date = draftMessage ? Math.max(draftMessage.date, lastMessage.date || 0) : lastMessage.date; const date = draftMessage ? Math.max(draftMessage.date, lastMessage.date || 0) : lastMessage.date;
dom.lastTimeSpan.innerHTML = formatDateAccordingToToday(new Date(date * 1000)); dom.lastTimeSpan.textContent = '';
} else dom.lastTimeSpan.innerHTML = ''; dom.lastTimeSpan.append(formatDateAccordingToTodayNew(new Date(date * 1000)));
} else dom.lastTimeSpan.textContent = '';
if(this.doms[peerId] === dom) { if(this.doms[peerId] === dom) {
this.setUnreadMessages(dialog); this.setUnreadMessages(dialog);

24
src/lib/appManagers/appMessagesManager.ts

@ -38,6 +38,7 @@ import appProfileManager from "./appProfileManager";
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug"; import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
import SlicedArray, { Slice, SliceEnd } from "../../helpers/slicedArray"; import SlicedArray, { Slice, SliceEnd } from "../../helpers/slicedArray";
import appNotificationsManager, { NotifyOptions } from "./appNotificationsManager"; import appNotificationsManager, { NotifyOptions } from "./appNotificationsManager";
import PeerTitle from "../../components/peerTitle";
//console.trace('include'); //console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -2687,6 +2688,8 @@ export class AppMessagesManager {
const element: HTMLElement = plain ? undefined : document.createElement('span'); const element: HTMLElement = plain ? undefined : document.createElement('span');
const action = message.action as MessageAction; const action = message.action as MessageAction;
// this.log('message action:', action);
if((action as MessageAction.messageActionCustomAction).message) { if((action as MessageAction.messageActionCustomAction).message) {
const richText = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true}); const richText = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true});
if(plain) { if(plain) {
@ -2701,9 +2704,8 @@ export class AppMessagesManager {
let langPackKey: LangPackKey = ''; let langPackKey: LangPackKey = '';
let args: any[]; let args: any[];
const getNameDivHTML = (peerId: number) => { const getNameDivHTML = (peerId: number, plain: boolean) => {
const title = appPeersManager.getPeerTitle(peerId); return plain ? appPeersManager.getPeerTitle(peerId) + ' ' : (new PeerTitle({peerId})).element;
return title ? (plain ? title + ' ' : `<div class="name inline" data-peer-id="${peerId}">${title}</div> `) : '';
}; };
switch(action._) { switch(action._) {
@ -2722,9 +2724,10 @@ export class AppMessagesManager {
break; break;
} }
case 'messageActionChatCreate':
case 'messageActionChatJoinedByLink': { case 'messageActionChatJoinedByLink': {
langPackKey = langPack[_]; langPackKey = langPack[_];
args = [getNameDivHTML(message.fromId)]; args = [getNameDivHTML(message.fromId, plain)];
break; break;
} }
@ -2736,7 +2739,12 @@ export class AppMessagesManager {
|| [(action as MessageAction.messageActionChatDeleteUser).user_id]; || [(action as MessageAction.messageActionChatDeleteUser).user_id];
langPackKey = langPack[_]; langPackKey = langPack[_];
args = [getNameDivHTML(message.fromId), users.map((userId: number) => getNameDivHTML(userId).trim()).join(', ')]; args = [
getNameDivHTML(message.fromId, plain),
users.length > 1 ?
users.map((userId: number) => (getNameDivHTML(userId, true) as string).trim()).join(', ') :
getNameDivHTML(users[0], plain)
];
break; break;
} }
@ -2767,17 +2775,13 @@ export class AppMessagesManager {
} }
if(plain) { if(plain) {
return I18n.getString(langPackKey, args); return I18n.format(langPackKey, true, args);
} else { } else {
return _i18n(element, langPackKey, args); return _i18n(element, langPackKey, args);
} }
//str = !langPackKey || langPackKey[0].toUpperCase() === langPackKey[0] ? langPackKey : getNameDivHTML(message.fromId) + langPackKey + (suffix ? ' ' : ''); //str = !langPackKey || langPackKey[0].toUpperCase() === langPackKey[0] ? langPackKey : getNameDivHTML(message.fromId) + langPackKey + (suffix ? ' ' : '');
} }
//this.log('message action:', action);
return element;
} }
public editPeerFolders(peerIds: number[], folderId: number) { public editPeerFolders(peerIds: number[], folderId: number) {

138
src/lib/langPack.ts

@ -1,5 +1,6 @@
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
import { safeAssign } from "../helpers/object"; import { safeAssign } from "../helpers/object";
import { capitalizeFirstLetter } from "../helpers/string";
import type lang from "../lang"; import type lang from "../lang";
import { LangPackDifference, LangPackString } from "../layer"; import { LangPackDifference, LangPackString } from "../layer";
import apiManager from "./mtproto/mtprotoworker"; import apiManager from "./mtproto/mtprotoworker";
@ -140,69 +141,138 @@ namespace I18n {
} }
}); });
} }
export function superFormatter(input: string, args?: any[], indexHolder = {i: 0}) {
let out: (string | HTMLElement)[] = [];
const regExp = /(\*\*)(.+?)\1|(\n)|un\d|%\d\$.|%./g;
let lastIndex = 0;
input.replace(regExp, (match, p1: any, p2: any, p3: any, offset: number, string: string) => {
//console.table({match, p1, p2, offset, string});
out.push(string.slice(lastIndex, offset));
if(p1) {
//offset += p1.length;
switch(p1) {
case '**': {
const b = document.createElement('b');
b.append(...superFormatter(p2, args, indexHolder));
out.push(b);
break;
}
}
} else if(p3) {
out.push(document.createElement('br'));
} else if(args) {
out.push(args[indexHolder.i++]);
}
lastIndex = offset + match.length;
return '';
});
export function getString(key: LangPackKey, args?: any[]) { if(lastIndex !== (input.length - 1)) {
const str = strings.get(key); out.push(input.slice(lastIndex));
let out = ''; }
return out;
}
export function format(key: LangPackKey, plain: true, args?: any[]): string;
export function format(key: LangPackKey, plain?: false, args?: any[]): (string | HTMLElement)[];
export function format(key: LangPackKey, plain = false, args?: any[]): (string | HTMLElement)[] | string {
const str = strings.get(key);
let input: string;
if(str) { if(str) {
if(str._ === 'langPackStringPluralized' && args?.length) { if(str._ === 'langPackStringPluralized' && args?.length) {
const v = args[0] as number; const v = args[0] as number;
const s = pluralRules.select(v); const s = pluralRules.select(v);
// @ts-ignore // @ts-ignore
out = str[s + '_value'] || str['other_value']; input = str[s + '_value'] || str['other_value'];
} else if(str._ === 'langPackString') { } else if(str._ === 'langPackString') {
out = str.value; input = str.value;
} else { } else {
out = '[' + key + ']'; input = '[' + key + ']';
//out = key; //input = key;
} }
} else { } else {
out = '[' + key + ']'; input = '[' + key + ']';
//out = key; //input = key;
} }
if(plain) {
if(args?.length) {
const regExp = /un\d|%\d\$.|%./g;
let i = 0;
input = input.replace(regExp, (match, offset, string) => {
return '' + args[i++];
});
}
out = out return input;
.replace(/\n/g, '<br>') } else {
.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>'); return superFormatter(input, args);
if(args?.length) {
let i = 0;
out = out.replace(/un\d|%\d\$.|%./g, (match, offset, string) => {
return '' + args[i++];
});
} }
return out;
} }
export const weakMap: WeakMap<HTMLElement, IntlElement> = new WeakMap(); export const weakMap: WeakMap<HTMLElement, IntlElementBase<IntlElementBaseOptions>> = new WeakMap();
export type IntlElementOptions = { export type IntlElementBaseOptions = {
element?: HTMLElement, element?: HTMLElement,
property?: /* 'innerText' | */'innerHTML' | 'placeholder' property?: /* 'innerText' | */'innerHTML' | 'placeholder',
key: LangPackKey,
args?: any[]
}; };
export class IntlElement {
public element: IntlElementOptions['element']; abstract class IntlElementBase<Options extends IntlElementBaseOptions> {
public key: IntlElementOptions['key']; public element: IntlElementBaseOptions['element'];
public args: IntlElementOptions['args']; public property: IntlElementBaseOptions['property'] = 'innerHTML';
public property: IntlElementOptions['property'] = 'innerHTML';
constructor(options: IntlElementOptions) { constructor(options: Options) {
this.element = options.element || document.createElement('span'); this.element = options.element || document.createElement('span');
this.element.classList.add('i18n'); this.element.classList.add('i18n');
this.update(options); this.update(options);
weakMap.set(this.element, this); weakMap.set(this.element, this);
} }
abstract update(options?: Options): void;
}
export type IntlElementOptions = IntlElementBaseOptions & {
key: LangPackKey,
args?: any[]
};
export class IntlElement extends IntlElementBase<IntlElementOptions> {
public key: IntlElementOptions['key'];
public args: IntlElementOptions['args'];
public update(options?: IntlElementOptions) { public update(options?: IntlElementOptions) {
safeAssign(this, options); safeAssign(this, options);
const str = getString(this.key, this.args); if(this.property === 'innerHTML') {
(this.element as any)[this.property] = str; this.element.textContent = '';
this.element.append(...format(this.key, false, this.args));
} else {
(this.element as HTMLInputElement)[this.property] = format(this.key, true, this.args);
}
}
}
export type IntlDateElementOptions = IntlElementBaseOptions & {
date: Date,
options: Intl.DateTimeFormatOptions
};
export class IntlDateElement extends IntlElementBase<IntlDateElementOptions> {
public date: IntlDateElementOptions['date'];
public options: IntlDateElementOptions['options'];
public update(options?: IntlDateElementOptions) {
safeAssign(this, options);
//var options = { month: 'long', day: 'numeric' };
const dateTimeFormat = new Intl.DateTimeFormat(lastRequestedLangCode, this.options);
(this.element as any)[this.property] = capitalizeFirstLetter(dateTimeFormat.format(this.date));
} }
} }

37
src/scripts/format_lang.js

@ -0,0 +1,37 @@
const fs = require('fs');
let str = fs.readFileSync('../lang.ts').toString().replace(/\s.+\/\/.+/g, '');
{
const pattern = '= {';
str = str.slice(str.indexOf(pattern) + pattern.length - 1);
}
{
const pattern = '};';
str = str.slice(0, str.indexOf(pattern) + pattern.length - 1);
}
//console.log(str);
const json = JSON.parse(str);
//console.log(json);
const f = (key, value, plural) => {
value = value
.replace(/\n/g, '\\n')
.replace(/"/g, '\\"');
return `"${key}${plural ? '_' + plural.replace('_value', '') : ''}" = "${value}";\n`;
};
let out = '';
for(const key in json) {
const value = json[key];
if(typeof(value) === 'string') {
out += f(key, value);
} else {
for(const plural in value) {
out += f(key, value[plural], plural);
}
}
}
fs.writeFileSync('./out/langPack.strings', out);

190
src/scripts/out/langPack.strings

@ -0,0 +1,190 @@
"FilterIncludeExcludeInfo" = "Choose chats and types of chats that will\nappear and never appear in this folder.";
"FilterNameInputLabel" = "Folder Name";
"FilterMenuDelete" = "Delete Folder";
"FilterHeaderEdit" = "Edit Folder";
"FilterAllGroups" = "All Groups";
"FilterAllContacts" = "All Contacts";
"FilterAllNonContacts" = "All Non-Contacts";
"FilterAllChannels" = "All Channels";
"FilterAllBots" = "All Bots";
"FilterAllUnmuted" = "All Unmuted";
"FilterAllUnread" = "All Unread";
"FilterAllUnarchived" = "All Unarchived";
"WordDelimiter" = ", ";
"WordDelimiterLast" = " and ";
"EditProfile.FirstNameLabel" = "Name";
"EditProfile.BioLabel" = "Bio (optional)";
"EditProfile.Username.Label" = "Username (optional)";
"EditProfile.Username.Available" = "Username is available";
"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.";
"ChatList.Menu.Archived" = "Archived";
"Saved" = "Saved";
"General.Keyboard" = "Keyboard";
"General.SendShortcut.Enter" = "Send by Enter";
"General.SendShortcut.CtrlEnter" = "Send by %s + Enter";
"General.SendShortcut.NewLine.ShiftEnter" = "New line by Shift + Enter";
"General.SendShortcut.NewLine.Enter" = "New line by Enter";
"General.AutoplayMedia" = "Auto-Play Media";
"ChatBackground.UploadWallpaper" = "Upload Wallpaper";
"ChatBackground.SetColor" = "Upload Wallpaper";
"ChatBackground.Blur" = "Upload Wallpaper";
"Notifications.Sound" = "Notification Sound";
"Notifications.MessagePreview" = "Message preview";
"Checkbox.Enabled" = "Enabled";
"Checkbox.Disabled" = "Disabled";
"Privacy.Devices_one" = "%1$d device";
"Privacy.Devices_other" = "%1$d devices";
"ActionCreateChannel" = "Channel created";
"ActionCreateGroup" = "un1 created the group";
"ActionChangedTitle" = "un1 changed the group name to un2";
"ActionRemovedPhoto" = "un1 removed the group photo";
"ActionChangedPhoto" = "un1 changed the group photo";
"ActionChangedVideo" = "un1 changed the group video";
"ActionAddUser" = "un1 added un2";
"ActionAddUserSelf" = "un1 returned to the group";
"ActionAddUserSelfMega" = "un1 joined the group";
"ActionAddUserSelfYou" = "You returned to the group";
"ActionLeftUser" = "un1 left the group";
"ActionKickUser" = "un1 removed un2";
"ActionInviteUser" = "un1 joined the group via invite link";
"ActionPinnedNoText" = "un1 pinned a message";
"ActionMigrateFromGroup" = "This group was upgraded to a supergroup";
"FilterAlwaysShow" = "Include Chats";
"FilterNeverShow" = "Exclude Chats";
"FilterInclude" = "Included Chats";
"FilterExclude" = "Excluded Chats";
"FilterChatTypes" = "Chat types";
"FilterChats" = "Chats";
"FilterNew" = "New Folder";
"Filters" = "Folders";
"FilterRecommended" = "Recommended Folders";
"Add" = "Add";
"Chats_one" = "%1$d chat";
"Chats_other" = "%1$d chats";
"Channels_one" = "%1$d channel";
"Channels_other" = "%1$d channels";
"Groups_one" = "%1$d group";
"Groups_other" = "%1$d groups";
"Users_one" = "%1$d user";
"Users_other" = "%1$d users";
"UsernameHelpLink" = "This link opens a chat with you:\n%1$s";
"NewGroup" = "New Group";
"Contacts" = "Contacts";
"SavedMessages" = "Saved Messages";
"Settings" = "Settings";
"SettingsHelp" = "Help";
"General" = "General";
"TextSize" = "Message Text Size";
"ChatBackground" = "Chat Background";
"EnableAnimations" = "Enable Animations";
"AutoDownloadMedia" = "Auto-Download Media";
"AutodownloadContacts" = "Contacts";
"AutodownloadPrivateChats" = "Private Chats";
"AutodownloadGroupChats" = "Group Chats";
"AutodownloadChannels" = "Channels";
"AutoplayGIF" = "GIFs";
"AutoplayVideo" = "Videos";
"NotificationsForGroups" = "Notifications for groups";
"NotificationsForPrivateChats" = "Notifications for private chats";
"NotificationsForChannels" = "Notifications for channels";
"NotificationsPrivateChats" = "Private Chats";
"NotificationsGroups" = "Groups";
"NotificationsChannels" = "Channels";
"NotificationsOther" = "Other";
"ContactJoined" = "Contact joined Telegram";
"Loading" = "Loading...";
"Unblock" = "Unblock";
"BlockedUsers" = "Blocked Users";
"BlockedUsersInfo" = "Blocked users will not be able to contact you and will not see your Last Seen time.";
"BlockedEmpty" = "None";
"TwoStepVerification" = "Two-Step Verification";
"PrivacyExceptions" = "Exceptions";
"PrivacyLastSeen" = "Last Seen & Online";
"PrivacySettings" = "Privacy and Security";
"PrivacyTitle" = "Privacy";
"PrivacyPhone" = "Phone Number";
"PrivacyPhoneTitle" = "Who can see my phone number?";
"PrivacyPhoneTitle2" = "Who can find me by my number?";
"PrivacyPhoneInfo" = "Users who have your number saved in their contacts will also see it on Telegram.";
"PrivacyPhoneInfo3" = "Users who add your number to their contacts will see it on Telegram only if they are your contacts.";
"PrivacyProfilePhoto" = "Profile Photos";
"PrivacyProfilePhotoTitle" = "Who can see my profile photos & videos?";
"PrivacyP2PHeader" = "Peer-to-Peer";
"PrivacyForwardsTitle" = "Who can add a link to my account when forwarding my messages?";
"LastSeenTitle" = "Who can see your Last Seen time?";
"SessionsTitle" = "Active Sessions";
"CurrentSession" = "This device";
"TerminateAllSessions" = "Terminate All Other Sessions";
"TerminateSessionText" = "Are you sure you want to terminate this session?";
"OtherSessions" = "Active sessions";
"AreYouSureSessionTitle" = "Terminate session";
"AreYouSureSessionsTitle" = "Terminate sessions";
"AreYouSureSessions" = "Are you sure you want to terminate all other sessions?";
"Terminate" = "Terminate";
"WhoCanCallMe" = "Who can call me?";
"WhoCanAddMe" = "Who can add me to group chats?";
"ArchivedChats" = "Archived Chats";
"Cancel" = "Cancel";
"HistoryCleared" = "History was cleared";
"Chat.Service.PeerJoinedTelegram" = "%@ joined Telegram";
"Chat.Service.Channel.UpdatedTitle" = "Channel renamed to \"%@\"";
"Chat.Service.Channel.UpdatedPhoto" = "Channel photo updated";
"Chat.Service.Channel.RemovedPhoto" = "Channel photo removed";
"Chat.Service.BotPermissionAllowed" = "You allowed this bot to message you when you logged in on %@";
"ChatList.Service.Call.incoming" = "Incoming Call (%@)";
"ChatList.Service.Call.outgoing" = "Outgoing Call (%@)";
"ChatList.Service.Call.Cancelled" = "Cancelled Call";
"ChatList.Service.Call.Missed" = "Missed Call";
"ChatList.Filter.Header" = "Create folders for different groups of chats and quickly switch between them.";
"ChatList.Filter.NewTitle" = "Create Folder";
"ChatList.Filter.List.Title" = "Chat Folders";
"ChatList.Filter.Include.AddChat" = "Add Chats";
"ChatList.Filter.Exclude.AddChat" = "Add Chats";
"ChatList.Filter.All" = "All";
"ChatList.Filter.Contacts" = "Contacts";
"ChatList.Filter.NonContacts" = "Non-Contacts";
"ChatList.Filter.Groups" = "Groups";
"ChatList.Filter.Channels" = "Channels";
"ChatList.Filter.Bots" = "Bots";
"ChatList.Filter.MutedChats" = "Muted";
"ChatList.Filter.ReadChats" = "Read";
"ChatList.Filter.Archive" = "Archived";
"Bio.Description" = "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco";
"EditAccount.Username" = "Username";
"EditAccount.Title" = "Edit Profile";
"EditAccount.Logout" = "Log Out";
"Login.Register.LastName.Placeholder" = "Last Name";
"AccountSettings.Filters" = "Chat Folders";
"AccountSettings.Notifications" = "Notifications and Sounds";
"AccountSettings.PrivacyAndSecurity" = "Privacy and Security";
"AccountSettings.Language" = "Language";
"Telegram.GeneralSettingsViewController" = "General Settings";
"Telegram.InstalledStickerPacksController" = "Stickers";
"Telegram.NotificationSettingsViewController" = "Notifications";
"Stickers.SuggestStickers" = "Suggest Stickers by Emoji";
"InstalledStickers.LoopAnimated" = "Loop Animated Stickers";
"PrivacyAndSecurity.Item.On" = "On";
"PrivacyAndSecurity.Item.Off" = "Off";
"PrivacySettings.VoiceCalls" = "Calls";
"PrivacySettings.Forwards" = "Forwarded Messages";
"PrivacySettings.Groups" = "Groups and Channels";
"PrivacySettingsController.AddUsers" = "Add Users";
"PrivacySettingsController.GroupDescription" = "You can restrict who can add you to groups and channels with granular precision.";
"PrivacySettingsController.Forwards.CustomHelp" = "You can restrict who can add a link to your account when forwarding your messages.";
"PrivacySettingsController.P2p.Desc" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio and video quality.";
"PrivacySettingsController.PhoneCallDescription" = "You can restrict who can call you with granular precision.";
"PrivacySettingsController.ProfilePhoto.CustomHelp" = "You can restrict who can see your profile photo with granular precision.";
"PrivacySettingsController.LastSeenDescription" = "You won\"t see Last Seen and Online statuses for people with whom you don\"t share yours. Approximate last seen will be shown instead (recently, within a week, within a month).";
"PrivacySettingsController.PeerInfo" = "You can add users or entire groups as exceptions that will override the settings above.";
"PrivacySettingsController.Everbody" = "Everybody";
"PrivacySettingsController.MyContacts" = "My Contacts";
"PrivacySettingsController.Nobody" = "Nobody";
"PrivacySettingsController.NeverShare" = "Never Share With";
"PrivacySettingsController.AlwaysShare" = "Always Share With";
"PrivacySettingsController.NeverAllow" = "Never Allow";
"PrivacySettingsController.AlwaysAllow" = "Always Allow";
"PrivacySettingsController.UserCount_one" = "%d user";
"PrivacySettingsController.UserCount_other" = "%d users";
"RecentSessions.Error.FreshReset" = "For security reasons, you can\"t terminate older sessions from a device that you\"ve just connected. Please use an earlier connection or wait for a few hours.";

4
src/scss/partials/_chatBubble.scss

@ -1572,13 +1572,13 @@ $bubble-margin: .25rem;
color: #fff; color: #fff;
} }
a, .name { a, .peer-title {
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
} }
} }
.name { .peer-title {
cursor: pointer; cursor: pointer;
//margin-right: 5px; //margin-right: 5px;
} }

Loading…
Cancel
Save