Browse Source

Better langpack formatting

Date formatting
Fix datepicker closening
Fix jumping by forwarded
master
Eduard Kuzmenko 4 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"; @@ -26,7 +26,7 @@ import { months } from "../../helpers/date";
import RichTextProcessor from "../../lib/richtextprocessor";
import mediaSizes from "../../helpers/mediaSizes";
import { isAndroid, isApple, isSafari } from "../../helpers/userAgent";
import { langPack } from "../../lib/langPack";
import I18n, { i18n, langPack } from "../../lib/langPack";
import AvatarElement from "../avatar";
import { formatPhoneNumber } from "../misc";
import { ripple } from "../ripple";
@ -716,7 +716,7 @@ export default class ChatBubbles { @@ -716,7 +716,7 @@ export default class ChatBubbles {
}
//this.log('chatInner click:', target);
const isVideoComponentElement = target.tagName === 'SPAN';
const isVideoComponentElement = target.tagName === 'SPAN' && !target.classList.contains('peer-title');
/* if(isVideoComponentElement) {
const video = target.parentElement.querySelector('video') as HTMLElement;
if(video) {
@ -808,9 +808,9 @@ export default class ChatBubbles { @@ -808,9 +808,9 @@ export default class ChatBubbles {
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')) {
const savedFrom = bubble.dataset.savedFrom;
const splitted = savedFrom.split('_');
@ -824,7 +824,7 @@ export default class ChatBubbles { @@ -824,7 +824,7 @@ export default class ChatBubbles {
new PopupForward(this.peerId, [mid]);
//appSidebarRight.forwardTab.open([mid]);
return;
} else if(target.classList.contains('name')) {
} else if(target.classList.contains('peer-title') || target.classList.contains('name')) {
const peerId = +target.dataset.peerId;
const savedFrom = target.dataset.savedFrom;
@ -1251,28 +1251,48 @@ export default class ChatBubbles { @@ -1251,28 +1251,48 @@ export default class ChatBubbles {
date.setHours(0, 0, 0);
const dateTimestamp = date.getTime();
if(!(dateTimestamp in this.dateMessages)) {
let str = '';
let dateElement: HTMLElement;
const today = new Date();
today.setHours(0, 0, 0, 0);
if(today.getTime() === date.getTime()) {
str = 'Today';
dateElement = i18n(this.chat.type === 'scheduled' ? 'Chat.Date.ScheduledForToday' : 'Date.Today');
} else {
str = months[date.getMonth()] + ' ' + date.getDate();
const options: Intl.DateTimeFormatOptions = {
day: 'numeric',
month: 'long'
};
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;
}
} */
const div = document.createElement('div');
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);
const container = document.createElement('div');

3
src/components/popups/datePicker.ts

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

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

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

26
src/helpers/date.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
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 days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
@ -33,6 +34,31 @@ export const formatDateAccordingToToday = (time: Date) => { @@ -33,6 +34,31 @@ export const formatDateAccordingToToday = (time: Date) => {
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<{
noTime: true,
noSeconds: true,

4
src/helpers/string.ts

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

264
src/lang.ts

@ -1,27 +1,27 @@ @@ -1,27 +1,27 @@
const lang = {
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',
"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",
"Saved": "Saved",
"General.Keyboard": "Keyboard",
"General.SendShortcut.Enter": "Send by Enter",
"General.SendShortcut.CtrlEnter": "Send by %s + Enter",
@ -29,120 +29,128 @@ const lang = { @@ -29,120 +29,128 @@ const lang = {
"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",
"ChatBackground.SetColor": "Set a Color",
"ChatBackground.Blur": "Blur Wallpaper Image",
"Notifications.Sound": "Notification Sound",
"Notifications.MessagePreview": "Message preview",
"Checkbox.Enabled": "Enabled",
"Checkbox.Disabled": "Disabled",
"Privacy.Devices": {
one_value: '%1$d device',
other_value: '%1$d devices'
"one_value": "%1$d device",
"other_value": "%1$d devices"
},
// * android
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_value: '%1$d chat',
other_value: '%1$d chats'
"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_value": "%1$d chat",
"other_value": "%1$d chats"
},
Channels: {
one_value: '%1$d channel',
other_value: '%1$d channels'
"Channels": {
"one_value": "%1$d channel",
"other_value": "%1$d channels"
},
Groups: {
one_value: '%1$d group',
other_value: '%1$d groups'
"Groups": {
"one_value": "%1$d group",
"other_value": "%1$d groups"
},
Users: {
one_value: "%1$d user",
other_value: "%1$d users"
"Users": {
"one_value": "%1$d user",
"other_value": "%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",
"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",
// * 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.Channel.UpdatedTitle": "Channel renamed to \"%@\"",
"Chat.Service.Channel.UpdatedPhoto": "Channel photo updated",
@ -166,15 +174,11 @@ const lang = { @@ -166,15 +174,11 @@ const lang = {
"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",
"Date.Today": "Today",
"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",
@ -191,7 +195,7 @@ const lang = { @@ -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.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.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",
@ -201,10 +205,10 @@ const lang = { @@ -201,10 +205,10 @@ const lang = {
"PrivacySettingsController.NeverAllow": "Never Allow",
"PrivacySettingsController.AlwaysAllow": "Always Allow",
"PrivacySettingsController.UserCount": {
one_value: '%d user',
other_value: '%d users'
"one_value": "%d user",
"other_value": "%d users"
},
"RecentSessions.Error.FreshReset": "For security reasons, you can't terminate older sessions from a device that you've just connected. Please use an earlier connection or wait for a few hours.",
"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;

9
src/lib/appManagers/appDialogsManager.ts

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

24
src/lib/appManagers/appMessagesManager.ts

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

138
src/lib/langPack.ts

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
import { MOUNT_CLASS_TO } from "../config/debug";
import { safeAssign } from "../helpers/object";
import { capitalizeFirstLetter } from "../helpers/string";
import type lang from "../lang";
import { LangPackDifference, LangPackString } from "../layer";
import apiManager from "./mtproto/mtprotoworker";
@ -140,69 +141,138 @@ namespace I18n { @@ -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[]) {
const str = strings.get(key);
let out = '';
if(lastIndex !== (input.length - 1)) {
out.push(input.slice(lastIndex));
}
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._ === 'langPackStringPluralized' && args?.length) {
const v = args[0] as number;
const s = pluralRules.select(v);
// @ts-ignore
out = str[s + '_value'] || str['other_value'];
input = str[s + '_value'] || str['other_value'];
} else if(str._ === 'langPackString') {
out = str.value;
input = str.value;
} else {
out = '[' + key + ']';
//out = key;
input = '[' + key + ']';
//input = key;
}
} else {
out = '[' + key + ']';
//out = key;
input = '[' + 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
.replace(/\n/g, '<br>')
.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
if(args?.length) {
let i = 0;
out = out.replace(/un\d|%\d\$.|%./g, (match, offset, string) => {
return '' + args[i++];
});
return input;
} else {
return superFormatter(input, args);
}
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,
property?: /* 'innerText' | */'innerHTML' | 'placeholder'
key: LangPackKey,
args?: any[]
property?: /* 'innerText' | */'innerHTML' | 'placeholder',
};
export class IntlElement {
public element: IntlElementOptions['element'];
public key: IntlElementOptions['key'];
public args: IntlElementOptions['args'];
public property: IntlElementOptions['property'] = 'innerHTML';
abstract class IntlElementBase<Options extends IntlElementBaseOptions> {
public element: IntlElementBaseOptions['element'];
public property: IntlElementBaseOptions['property'] = 'innerHTML';
constructor(options: IntlElementOptions) {
constructor(options: Options) {
this.element = options.element || document.createElement('span');
this.element.classList.add('i18n');
this.update(options);
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) {
safeAssign(this, options);
const str = getString(this.key, this.args);
(this.element as any)[this.property] = str;
if(this.property === 'innerHTML') {
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 @@ @@ -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 @@ @@ -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; @@ -1572,13 +1572,13 @@ $bubble-margin: .25rem;
color: #fff;
}
a, .name {
a, .peer-title {
&:hover {
text-decoration: underline;
}
}
.name {
.peer-title {
cursor: pointer;
//margin-right: 5px;
}

Loading…
Cancel
Save