Browse Source

Group call service messages

master
Eduard Kuzmenko 4 years ago
parent
commit
04b6c71a39
  1. 10
      src/helpers/date.ts
  2. 25
      src/helpers/formatCallDuration.ts
  3. 38
      src/helpers/formatDuration.ts
  4. 36
      src/lang.ts
  5. 142
      src/lib/appManagers/appMessagesManager.ts
  6. 11
      src/lib/langPack.ts

10
src/helpers/date.ts

@ -64,6 +64,16 @@ export function formatDateAccordingToTodayNew(time: Date) {
}).element; }).element;
} }
export function formatTime(date: Date) {
return new I18n.IntlDateElement({
date,
options: {
hour: '2-digit',
minute: '2-digit'
}
}).element;
}
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.formatDateAccordingToTodayNew = formatDateAccordingToTodayNew); MOUNT_CLASS_TO && (MOUNT_CLASS_TO.formatDateAccordingToTodayNew = formatDateAccordingToTodayNew);
export const getFullDate = (date: Date, options: Partial<{ export const getFullDate = (date: Date, options: Partial<{

25
src/helpers/formatCallDuration.ts

@ -0,0 +1,25 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { i18n, join, LangPackKey } from "../lib/langPack";
import formatDuration, { DurationType } from "./formatDuration";
const CALL_DURATION_LANG_KEYS: {[type in DurationType]: LangPackKey} = {
s: 'Seconds',
m: 'Minutes',
h: 'Hours',
d: 'Days',
w: 'Weeks'
};
export default function formatCallDuration(duration: number) {
const a = formatDuration(duration, 2);
const elements = a.map(d => i18n(CALL_DURATION_LANG_KEYS[d.type], [d.duration]));
const fragment = document.createElement('span');
fragment.append(...join(elements, false));
return fragment;
}

38
src/helpers/formatDuration.ts

@ -0,0 +1,38 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
export type DurationType = 's' | 'm' | 'h' | 'd' | 'w';
export default function formatDuration(duration: number, showLast = 2) {
if(!duration) {
duration = 1;
}
let d: {duration: number, type: DurationType}[] = [];
const p = [
{m: 1, t: 's'},
{m: 60, t: 'm'},
{m: 60, t: 'h'},
{m: 24, t: 'd'},
{m: 7, t: 'w'}
] as Array<{m?: number, t: DurationType}>
const s = 1;
let t = s;
p.forEach((o, idx) => {
t *= o.m;
if(duration < t) {
return;
}
const modulus = p[idx === (p.length - 1) ? idx : idx + 1].m;
d.push({
duration: (duration / t % modulus | 0),
type: o.t
});
});
return d.slice(-showLast).reverse();
}

36
src/lang.ts

@ -441,6 +441,34 @@ const lang = {
"Emoji": "Emoji", "Emoji": "Emoji",
"AddContactTitle": "Add Contact", "AddContactTitle": "Add Contact",
"HiddenName": "Deleted Account", "HiddenName": "Deleted Account",
"ActionGroupCallStarted": "un1 started a voice chat",
"ActionGroupCallStartedByYou": "You started a voice chat",
"ActionGroupCallJustStarted": "Voice chat started",
"ActionGroupCallInvited": "un1 invited un2 to the voice chat",
"ActionGroupCallYouInvited": "You invited un2 to the voice chat",
"ActionGroupCallInvitedYou": "un1 invited you to the voice chat",
"Seconds": {
"one_value": "%1$d second",
"other_value": "%1$d seconds"
},
"Minutes": {
"one_value": "%1$d minute",
"other_value": "%1$d minutes"
},
"Hours": {
"one_value": "%1$d hour",
"other_value": "%1$d hours"
},
"Days": {
"one_value": "%1$d day",
"other_value": "%1$d days"
},
"Weeks": {
"one_value": "%1$d week",
"other_value": "%1$d weeks"
},
"TodayAtFormattedWithToday": "today at %1$s",
"formatDateAtTime": "%1$s at %2$s",
// * macos // * macos
"AccountSettings.Filters": "Chat Folders", "AccountSettings.Filters": "Chat Folders",
@ -465,6 +493,10 @@ const lang = {
"Chat.Service.Channel.RemovedPhoto": "Channel photo removed", "Chat.Service.Channel.RemovedPhoto": "Channel photo removed",
"Chat.Service.Channel.UpdatedVideo": "Channel video updated", "Chat.Service.Channel.UpdatedVideo": "Channel video updated",
"Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@", "Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@",
"Chat.Service.VoiceChatFinished": "%1$@ ended the voice chat (%2$@)",
"Chat.Service.VoiceChatFinishedYou": "You ended the voice chat (%@)",
//"Chat.Service.VoiceChatScheduled": "%1$@ scheduled a [voice chat](open) for %2$@",
//"Chat.Service.VoiceChatScheduledYou": "You scheduled a [voice chat](open) for %1$@",
"Chat.Poll.Unvote": "Retract Vote", "Chat.Poll.Unvote": "Retract Vote",
"Chat.Poll.Stop": "Stop Poll", "Chat.Poll.Stop": "Stop Poll",
"Chat.Poll.ViewResults": "View Results", "Chat.Poll.ViewResults": "View Results",
@ -516,6 +548,8 @@ const lang = {
"ChatList.Service.Call.outgoing": "Outgoing Call (%@)", "ChatList.Service.Call.outgoing": "Outgoing Call (%@)",
"ChatList.Service.Call.Cancelled": "Cancelled Call", "ChatList.Service.Call.Cancelled": "Cancelled Call",
"ChatList.Service.Call.Missed": "Missed Call", "ChatList.Service.Call.Missed": "Missed Call",
"ChatList.Service.VoiceChatScheduled": "%1$@ scheduled a voice chat for %2$@",
"ChatList.Service.VoiceChatScheduledYou": "You scheduled a voice chat for %2$@",
"ChatList.Filter.Header": "Create folders for different groups of chats and quickly switch between them.", "ChatList.Filter.Header": "Create folders for different groups of chats and quickly switch between them.",
"ChatList.Filter.NewTitle": "Create Folder", "ChatList.Filter.NewTitle": "Create Folder",
"ChatList.Filter.List.Title": "Chat Folders", "ChatList.Filter.List.Title": "Chat Folders",
@ -677,11 +711,13 @@ const lang = {
"GeneralSettings.BigEmoji": "Large Emoji", "GeneralSettings.BigEmoji": "Large Emoji",
"GeneralSettings.EmojiPrediction": "Suggest Emoji", "GeneralSettings.EmojiPrediction": "Suggest Emoji",
"GroupPermission.Delete": "Delete Exception", "GroupPermission.Delete": "Delete Exception",
"ScheduleController.at": "at",
"Schedule.SendToday": "Send today at %@", "Schedule.SendToday": "Send today at %@",
"Schedule.SendDate": "Send on %@ at %@", "Schedule.SendDate": "Send on %@ at %@",
//"Schedule.SendWhenOnline": "Send When Online", //"Schedule.SendWhenOnline": "Send When Online",
"Stickers.Recent": "Recent", "Stickers.Recent": "Recent",
//"Stickers.Favorite": "Favorite", //"Stickers.Favorite": "Favorite",
"Time.TomorrowAt": "tomorrow at %@",
"TwoStepAuth.SetPasswordHelp": "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.", "TwoStepAuth.SetPasswordHelp": "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.",
"TwoStepAuth.GenericHelp": "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.", "TwoStepAuth.GenericHelp": "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.",
"TwoStepAuth.ChangePassword": "Change Password", "TwoStepAuth.ChangePassword": "Change Password",

142
src/lib/appManagers/appMessagesManager.ts

@ -12,7 +12,7 @@
import { LazyLoadQueueBase } from "../../components/lazyLoadQueue"; import { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
import ProgressivePreloader from "../../components/preloader"; import ProgressivePreloader from "../../components/preloader";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise"; import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
import { tsNow } from "../../helpers/date"; import { formatTime, tsNow } from "../../helpers/date";
import { createPosterForVideo } from "../../helpers/files"; import { createPosterForVideo } from "../../helpers/files";
import { copy, getObjectKeysAndSort } from "../../helpers/object"; import { copy, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random"; import { randomLong } from "../../helpers/random";
@ -52,6 +52,7 @@ import { forEachReverse } from "../../helpers/array";
import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment"; import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment";
import htmlToSpan from "../../helpers/dom/htmlToSpan"; import htmlToSpan from "../../helpers/dom/htmlToSpan";
import { REPLIES_PEER_ID } from "../mtproto/mtproto_config"; import { REPLIES_PEER_ID } from "../mtproto/mtproto_config";
import formatCallDuration from "../../helpers/formatCallDuration";
//console.trace('include'); //console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -2311,69 +2312,88 @@ export class AppMessagesManager {
} }
if(message.action) { if(message.action) {
const action = message.action;
let migrateFrom: number; let migrateFrom: number;
let migrateTo: number; let migrateTo: number;
const suffix = message.fromId === appUsersManager.getSelf().id ? 'You' : ''; const suffix = message.fromId === appUsersManager.getSelf().id ? 'You' : '';
switch(message.action._) { switch(action._) {
//case 'messageActionChannelEditPhoto': //case 'messageActionChannelEditPhoto':
case 'messageActionChatEditPhoto': case 'messageActionChatEditPhoto':
message.action.photo = appPhotosManager.savePhoto(message.action.photo, mediaContext); action.photo = appPhotosManager.savePhoto(action.photo, mediaContext);
if(message.action.photo.video_sizes) { if(action.photo.video_sizes) {
message.action._ = isBroadcast ? 'messageActionChannelEditVideo' : 'messageActionChatEditVideo'; action._ = isBroadcast ? 'messageActionChannelEditVideo' : 'messageActionChatEditVideo';
} else { } else {
if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода. if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода.
message.action._ = 'messageActionChannelEditPhoto'; action._ = 'messageActionChannelEditPhoto';
}
}
break;
case 'messageActionGroupCall': {
//assumeType<MessageAction.messageActionGroupCall>(action);
let type: string;
if(action.duration === undefined) {
type = 'started';
if(message.peerId !== message.fromId) {
type += '_by' + suffix;
} }
} else {
type = 'ended_by' + suffix;
} }
action.type = type;
break; break;
}
case 'messageActionChatEditTitle': case 'messageActionChatEditTitle':
/* if(options.isNew) { /* if(options.isNew) {
const chat = appChatsManager.getChat(-peerId); const chat = appChatsManager.getChat(-peerId);
chat.title = message.action.title; chat.title = action.title;
appChatsManager.saveApiChat(chat, true); appChatsManager.saveApiChat(chat, true);
} */ } */
if(isBroadcast) { if(isBroadcast) {
message.action._ = 'messageActionChannelEditTitle'; action._ = 'messageActionChannelEditTitle';
} }
break; break;
case 'messageActionChatDeletePhoto': case 'messageActionChatDeletePhoto':
if(isBroadcast) { if(isBroadcast) {
message.action._ = 'messageActionChannelDeletePhoto'; action._ = 'messageActionChannelDeletePhoto';
} }
break; break;
case 'messageActionChatAddUser': case 'messageActionChatAddUser':
if(message.action.users.length === 1) { if(action.users.length === 1) {
message.action.user_id = message.action.users[0]; action.user_id = action.users[0];
if(message.fromId === message.action.user_id) { if(message.fromId === action.user_id) {
if(isChannel) { if(isChannel) {
message.action._ = 'messageActionChatJoined' + suffix; action._ = 'messageActionChatJoined' + suffix;
} else { } else {
message.action._ = 'messageActionChatReturn' + suffix; action._ = 'messageActionChatReturn' + suffix;
} }
} }
} else if(message.action.users.length > 1) { } else if(action.users.length > 1) {
message.action._ = 'messageActionChatAddUsers'; action._ = 'messageActionChatAddUsers';
} }
break; break;
case 'messageActionChatDeleteUser': case 'messageActionChatDeleteUser':
if(message.fromId === message.action.user_id) { if(message.fromId === action.user_id) {
message.action._ = 'messageActionChatLeave' + suffix; action._ = 'messageActionChatLeave' + suffix;
} }
break; break;
case 'messageActionChannelMigrateFrom': case 'messageActionChannelMigrateFrom':
migrateFrom = -message.action.chat_id; migrateFrom = -action.chat_id;
migrateTo = -channelId; migrateTo = -channelId;
break break
case 'messageActionChatMigrateTo': case 'messageActionChatMigrateTo':
migrateFrom = -channelId; migrateFrom = -channelId;
migrateTo = -message.action.channel_id; migrateTo = -action.channel_id;
break; break;
case 'messageActionHistoryClear': case 'messageActionHistoryClear':
@ -2384,11 +2404,11 @@ export class AppMessagesManager {
break; break;
case 'messageActionPhoneCall': case 'messageActionPhoneCall':
message.action.type = action.type =
(message.pFlags.out ? 'out_' : 'in_') + (message.pFlags.out ? 'out_' : 'in_') +
( (
message.action.reason._ === 'phoneCallDiscardReasonMissed' || action.reason._ === 'phoneCallDiscardReasonMissed' ||
message.action.reason._ === 'phoneCallDiscardReasonBusy' action.reason._ === 'phoneCallDiscardReasonBusy'
? 'missed' ? 'missed'
: 'ok' : 'ok'
); );
@ -2641,18 +2661,73 @@ export class AppMessagesManager {
}; };
switch(action._) { switch(action._) {
case "messageActionPhoneCall": { case 'messageActionPhoneCall': {
_ += '.' + (action as any).type; _ += '.' + (action as any).type;
const duration = action.duration || 1; args = [formatCallDuration(action.duration)];
const d: string[] = []; break;
}
d.push(duration % 60 + ' s'); case 'messageActionGroupCall': {
if(duration >= 60) d.push((duration / 60 | 0) + ' min'); _ += '.' + (action as any).type;
//if(duration >= 3600) d.push((duration / 3600 | 0) + ' h');
args = [];
if(!_.endsWith('You')) {
args.push(getNameDivHTML(message.fromId, plain));
}
args.push(formatCallDuration(action.duration));
break;
}
case 'messageActionInviteToGroupCall': {
const peerIds = [message.fromId, action.users[0]];
let a = 'ActionGroupCall';
const myId = appUsersManager.getSelf().id;
if(peerIds[0] === myId) a += 'You';
a += 'Invited';
if(peerIds[1] === myId) a += 'You';
peerIds.findAndSplice(peerId => peerId === myId);
langPackKey = a as LangPackKey;
args = peerIds.map(peerId => getNameDivHTML(peerId, plain));
break;
}
case 'messageActionGroupCallScheduled': {
const today = new Date();
const date = new Date(action.schedule_date * 1000);
const daysToStart = (date.getTime() - today.getTime()) / 86400e3;
const tomorrowDate = new Date(today);
tomorrowDate.setDate(tomorrowDate.getDate() + 1);
langPackKey = 'ChatList.Service.VoiceChatScheduled';
const myId = appUsersManager.getSelf().id;
if(message.fromId === myId) {
langPackKey += 'You';
}
let k: LangPackKey, _args: any[] = [];
if(daysToStart < 1 && date.getDate() === today.getDate()) {
k = 'TodayAtFormattedWithToday';
} else if(daysToStart < 2 && date.getDate() === tomorrowDate.getDate()) {
k = 'Time.TomorrowAt';
} else {
k = 'formatDateAtTime';
_args.push(new I18n.IntlDateElement({
date,
options: {
day: '2-digit',
month: '2-digit',
year: '2-digit'
}
}).element);
}
_args.push(formatTime(date));
const t = i18n(k, _args);
args = [t];
langPackKey = langPack[_];
args = [d.reverse().join(' ')];
break; break;
} }
@ -2668,15 +2743,12 @@ export class AppMessagesManager {
case 'messageActionChatJoinedByLink': case 'messageActionChatJoinedByLink':
case 'messageActionChannelEditVideo': case 'messageActionChannelEditVideo':
case 'messageActionChannelDeletePhoto': { case 'messageActionChannelDeletePhoto': {
langPackKey = langPack[_];
args = [getNameDivHTML(message.fromId, plain)]; args = [getNameDivHTML(message.fromId, plain)];
break; break;
} }
case 'messageActionChannelEditTitle': case 'messageActionChannelEditTitle':
case 'messageActionChatEditTitle': { case 'messageActionChatEditTitle': {
langPackKey = langPack[_];
args = []; args = [];
if(action._ === 'messageActionChatEditTitle') { if(action._ === 'messageActionChatEditTitle') {
args.push(getNameDivHTML(message.fromId, plain)); args.push(getNameDivHTML(message.fromId, plain));
@ -2692,7 +2764,6 @@ export class AppMessagesManager {
const users: number[] = (action as MessageAction.messageActionChatAddUser).users const users: number[] = (action as MessageAction.messageActionChatAddUser).users
|| [(action as MessageAction.messageActionChatDeleteUser).user_id]; || [(action as MessageAction.messageActionChatDeleteUser).user_id];
langPackKey = langPack[_];
args = [getNameDivHTML(message.fromId, plain)]; args = [getNameDivHTML(message.fromId, plain)];
if(users.length > 1) { if(users.length > 1) {
@ -2726,7 +2797,6 @@ export class AppMessagesManager {
const node = htmlToSpan(anchorHTML); const node = htmlToSpan(anchorHTML);
langPackKey = langPack[_];
args = [node]; args = [node];
break; break;
} }

11
src/lib/langPack.ts

@ -47,6 +47,12 @@ export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionPhoneCall.in_missed": "ChatList.Service.Call.Missed", "messageActionPhoneCall.in_missed": "ChatList.Service.Call.Missed",
"messageActionPhoneCall.out_missed": "ChatList.Service.Call.Cancelled", "messageActionPhoneCall.out_missed": "ChatList.Service.Call.Cancelled",
"messageActionGroupCall.started": "ActionGroupCallJustStarted",
"messageActionGroupCall.started_by": "ActionGroupCallStarted",
"messageActionGroupCall.started_byYou": "ActionGroupCallStartedByYou",
"messageActionGroupCall.ended_by": "Chat.Service.VoiceChatFinished",
"messageActionGroupCall.ended_byYou": "Chat.Service.VoiceChatFinishedYou",
"messageActionBotAllowed": "Chat.Service.BotPermissionAllowed" "messageActionBotAllowed": "Chat.Service.BotPermissionAllowed"
}; };
@ -198,7 +204,12 @@ namespace I18n {
return; return;
} }
try {
pluralRules = new Intl.PluralRules(langPack.lang_code); pluralRules = new Intl.PluralRules(langPack.lang_code);
} catch(err) {
console.error('pluralRules error', err);
pluralRules = new Intl.PluralRules(langPack.lang_code.split('-', 1)[0]);
}
strings.clear(); strings.clear();

Loading…
Cancel
Save