Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
391b370b0f | ||
|
|
9b863fcf5b | ||
|
|
609868858b | ||
|
|
c291bf0861 | ||
|
|
bfd5482c15 | ||
|
|
52ea153c31 | ||
|
|
287c99ed2f | ||
|
|
8e60f54dac | ||
|
|
1f3fae53ca | ||
|
|
05ac97526e | ||
|
|
5fadfed47a | ||
|
|
ec1d547f27 | ||
|
|
9fee0ace4c | ||
|
|
84c2a33c18 | ||
|
|
83744e77d1 | ||
|
|
85635dbefd |
@@ -1,10 +1,10 @@
|
||||
@echo OFF
|
||||
|
||||
set "AppVersion=8024"
|
||||
set "AppVersionStrSmall=0.8.24"
|
||||
set "AppVersionStr=0.8.24"
|
||||
set "AppVersionStrFull=0.8.24.0"
|
||||
set "DevChannel=0"
|
||||
set "AppVersion=8027"
|
||||
set "AppVersionStrSmall=0.8.27"
|
||||
set "AppVersionStr=0.8.27"
|
||||
set "AppVersionStrFull=0.8.27.0"
|
||||
set "DevChannel=1"
|
||||
|
||||
if %DevChannel% neq 0 goto preparedev
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "Try now";
|
||||
|
||||
"lng_status_service_notifications" = "service notifications";
|
||||
"lng_status_bot" = "bot";
|
||||
"lng_status_bot_reads_all" = "has access to messages";
|
||||
"lng_status_bot_not_reads_all" = "has no access to messages";
|
||||
"lng_status_offline" = "last seen a long time ago";
|
||||
"lng_status_recently" = "last seen recently";
|
||||
"lng_status_last_week" = "last seen within a week";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "Group is unaccessible";
|
||||
"lng_topbar_info" = "Info";
|
||||
"lng_profile_about_section" = "About";
|
||||
"lng_profile_settings_section" = "Settings";
|
||||
"lng_profile_bot_settings" = "Settings";
|
||||
"lng_profile_bot_help" = "Help";
|
||||
"lng_profile_participants_section" = "Members";
|
||||
"lng_profile_info" = "Contact info";
|
||||
"lng_profile_group_info" = "Group info";
|
||||
@@ -343,6 +349,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_clear_history" = "Clear history";
|
||||
"lng_profile_send_message" = "Send Message";
|
||||
"lng_profile_share_contact" = "Share Contact";
|
||||
"lng_profile_invite_to_group" = "Add to Group";
|
||||
"lng_profile_delete_contact" = "Delete";
|
||||
"lng_profile_set_group_photo" = "Set Photo";
|
||||
"lng_profile_add_participant" = "Add Member";
|
||||
@@ -450,11 +457,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Send";
|
||||
"lng_message_ph" = "Write a message..";
|
||||
"lng_record_cancel" = "Release out of here to cancel";
|
||||
"lng_record_cancel" = "Release outside this field to cancel";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Please select chat to start messaging";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "You";
|
||||
"lng_bot_description" = "What can this bot do?";
|
||||
|
||||
"lng_bot_start" = "Start";
|
||||
"lng_bot_choose_group" = "Choose Group";
|
||||
"lng_bot_no_groups" = "You have no groups";
|
||||
"lng_bot_groups_not_found" = "No groups found";
|
||||
"lng_bot_sure_invite" = "Add the bot to «{group}»?";
|
||||
"lng_bot_already_in_group" = "The bot is already a member of the group.";
|
||||
|
||||
"lng_typing" = "typing";
|
||||
"lng_user_typing" = "{user} is typing";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
|
||||
"lng_new_version_text" = "— Improved sticker panel\n— Bug fixes and minor stuff";
|
||||
"lng_new_version_text" = "This new version includes support for bots using the new bot API, free for everyone. If you're an engineer, create your own bots for games, services or integrations.\n\nLearn more at {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insert Unicode control character";
|
||||
|
||||
|
||||
@@ -992,6 +992,18 @@ btnAttachEmoji: iconedButton(btnAttachDocument) {
|
||||
|
||||
width: 33px;
|
||||
}
|
||||
btnBotKbShow: iconedButton(btnAttachEmoji) {
|
||||
icon: sprite(375px, 74px, 21px, 16px);
|
||||
iconPos: point(6px, 16px);
|
||||
downIcon: sprite(375px, 74px, 21px, 16px);
|
||||
downIconPos: point(6px, 16px);
|
||||
}
|
||||
btnBotKbHide: iconedButton(btnAttachEmoji) {
|
||||
icon: sprite(352px, 74px, 23px, 14px);
|
||||
iconPos: point(5px, 17px);
|
||||
downIcon: sprite(352px, 74px, 23px, 14px);
|
||||
downIconPos: point(5px, 17px);
|
||||
}
|
||||
btnRecordAudio: sprite(363px, 366px, 16px, 24px);
|
||||
btnRecordAudioActive: sprite(379px, 366px, 16px, 24px);
|
||||
recordSignalColor: #f17077;
|
||||
@@ -999,8 +1011,8 @@ recordSignalMin: 5px;
|
||||
recordSignalMax: 10px;
|
||||
recordCancel: #aaa;
|
||||
recordCancelActive: #ec6466;
|
||||
recordFont: font(16px);
|
||||
recordTextTop: 12px;
|
||||
recordFont: font(13px);
|
||||
recordTextTop: 14px;
|
||||
|
||||
replySkip: 51px;
|
||||
replyColor: #377aae;
|
||||
@@ -1041,7 +1053,7 @@ textRectMargins: margins(-2px, -1px, -2px, -1px);
|
||||
taMsgField: flatTextarea(taDefFlat) {
|
||||
font: msgFont;
|
||||
}
|
||||
maxFieldHeight: 250px;
|
||||
maxFieldHeight: 265px;
|
||||
|
||||
newMsgSound: ':/gui/art/newmsg.wav';
|
||||
|
||||
@@ -1185,6 +1197,7 @@ btnShareContact: flatButton(btnDefNext, btnDefBig) {
|
||||
font: font(17px);
|
||||
overFont: font(17px);
|
||||
}
|
||||
profileMinBtnPadding: 10px;
|
||||
|
||||
forwardWidth: 364px;
|
||||
forwardMargins: margins(30px, 10px, 30px, 10px);
|
||||
@@ -1658,6 +1671,31 @@ stickerIconLeft: sprite(342px, 72px, 40px, 1px);
|
||||
stickerIconRight: sprite(342px, 73px, 40px, 1px);
|
||||
stickerIconMove: 400;
|
||||
|
||||
botKbDuration: 200;
|
||||
botKbBg: #f7f7f7;
|
||||
botKbOverBg: #e8ecef;
|
||||
botKbDownBg: #dfe3e6;
|
||||
botKbColor: #8a8a8f;
|
||||
botKbFont: font(16px);
|
||||
botKbButton: botKeyboardButton {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
height: 46px;
|
||||
textTop: 13px;
|
||||
downTextTop: 14px;
|
||||
}
|
||||
botKbTinyButton: botKeyboardButton {
|
||||
margin: 4px;
|
||||
padding: 3px;
|
||||
height: 25px;
|
||||
textTop: 2px;
|
||||
downTextTop: 3px;
|
||||
}
|
||||
botKbScroll: flatScroll(newScroll) {
|
||||
deltax: 3px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
mvBgColor: #222;
|
||||
mvBgOpacity: 0.92;
|
||||
mvThickFont: font(fsize semibold);
|
||||
@@ -1887,6 +1925,8 @@ mentionPadding: margins(8px, 5px, 8px, 5px);
|
||||
mentionTop: 11px;
|
||||
mentionFont: linkFont;
|
||||
mentionPhotoSize: msgPhotoSize;
|
||||
botCommandFont: font(fsize semibold);
|
||||
botDescFont: font(fsize italic);
|
||||
|
||||
sessionsHeight: 440px;
|
||||
sessionHeight: 70px;
|
||||
@@ -1915,3 +1955,5 @@ webPageDescriptionFont: font(fsize);
|
||||
webPagePhotoSkip: 5px;
|
||||
webPagePhotoSize: 100px;
|
||||
webPagePhotoDelta: 8px;
|
||||
|
||||
botDescSkip: 8px;
|
||||
|
||||
@@ -259,3 +259,11 @@ dropdown {
|
||||
duration: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
botKeyboardButton {
|
||||
margin: number;
|
||||
padding: number;
|
||||
height: number;
|
||||
textTop: number;
|
||||
downTextTop: number;
|
||||
}
|
||||
|
||||
@@ -1574,7 +1574,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
|
||||
tcpp << "\tFontFamilies _fontFamilies;\n";
|
||||
tcpp << "\tFontDatas _fontsMap;\n";
|
||||
tcpp << "\tColorDatas _colorsMap;\n";
|
||||
tcpp << "int _spriteWidth = " << spriteWidths[0] << ";\n\n";
|
||||
tcpp << "\tint _spriteWidth = " << spriteWidths[0] << ";\n\n";
|
||||
tcpp << "\tvoid startManager() {\n";
|
||||
|
||||
tcpp << "\n\t\tif (cRetina()) {\n";
|
||||
|
||||
@@ -119,6 +119,19 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
|
||||
App::feedUsers(d.vusers);
|
||||
App::feedChats(d.vchats);
|
||||
App::feedParticipants(f.vparticipants);
|
||||
const QVector<MTPBotInfo> &v(f.vbot_info.c_vector().v);
|
||||
for (QVector<MTPBotInfo>::const_iterator i = v.cbegin(), e = v.cend(); i < e; ++i) {
|
||||
switch (i->type()) {
|
||||
case mtpc_botInfo: {
|
||||
const MTPDbotInfo &b(i->c_botInfo());
|
||||
UserData *user = App::userLoaded(b.vuser_id.v);
|
||||
if (user) {
|
||||
user->setBotInfo(*i);
|
||||
emit fullPeerUpdated(user);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
PhotoData *photo = App::feedPhoto(f.vchat_photo);
|
||||
ChatData *chat = peer->asChat();
|
||||
if (chat) {
|
||||
@@ -132,7 +145,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
||||
|
||||
_fullRequests.remove(peer);
|
||||
emit fullPeerLoaded(peer);
|
||||
emit fullPeerUpdated(peer);
|
||||
}
|
||||
|
||||
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
|
||||
@@ -142,8 +155,10 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
|
||||
App::feedUserLink(MTP_int(App::userFromPeer(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
|
||||
|
||||
peer->asUser()->setBotInfo(d.vbot_info);
|
||||
|
||||
_fullRequests.remove(peer);
|
||||
emit fullPeerLoaded(peer);
|
||||
emit fullPeerUpdated(peer);
|
||||
}
|
||||
|
||||
bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
signals:
|
||||
|
||||
void fullPeerLoaded(PeerData *peer);
|
||||
void fullPeerUpdated(PeerData *peer);
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ namespace {
|
||||
typedef QHash<WebPageId, WebPageData*> WebPagesData;
|
||||
WebPagesData webPagesData;
|
||||
|
||||
typedef QMap<MsgId, ReplyMarkup> ReplyMarkups;
|
||||
ReplyMarkups replyMarkups;
|
||||
|
||||
VideoItems videoItems;
|
||||
AudioItems audioItems;
|
||||
DocumentItems documentItems;
|
||||
@@ -206,7 +209,11 @@ namespace App {
|
||||
return (peer_id & 0x100000000L) ? int32(peer_id & 0xFFFFFFFFL) : 0;
|
||||
}
|
||||
|
||||
int32 onlineForSort(int32 online, int32 now) {
|
||||
int32 onlineForSort(UserData *user, int32 now) {
|
||||
if (isServiceUser(user->id) || user->botInfo) {
|
||||
return -1;
|
||||
}
|
||||
int32 online = user->onlineTill;
|
||||
if (online <= 0) {
|
||||
switch (online) {
|
||||
case 0:
|
||||
@@ -232,8 +239,12 @@ namespace App {
|
||||
return online;
|
||||
}
|
||||
|
||||
int32 onlineWillChangeIn(int32 online, int32 now) {
|
||||
if (online <= 0) {
|
||||
int32 onlineWillChangeIn(UserData *user, int32 now) {
|
||||
if (isServiceUser(user->id) || user->botInfo) {
|
||||
return 86400;
|
||||
}
|
||||
int32 online = user->onlineTill;
|
||||
if (online <= 0) {
|
||||
if (-online > now) return -online - now;
|
||||
return 86400;
|
||||
}
|
||||
@@ -255,6 +266,8 @@ namespace App {
|
||||
QString onlineText(UserData *user, int32 now, bool precise) {
|
||||
if (isServiceUser(user->id)) {
|
||||
return lang(lng_status_service_notifications);
|
||||
} else if (user->botInfo) {
|
||||
return lang(lng_status_bot);
|
||||
}
|
||||
int32 online = user->onlineTill;
|
||||
if (online <= 0) {
|
||||
@@ -333,88 +346,63 @@ namespace App {
|
||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->access = UserNoAccess;
|
||||
data->setBotInfoVersion(-1);
|
||||
wasContact = (data->contact > 0);
|
||||
status = &emptyStatus;
|
||||
data->contact = -1;
|
||||
} break;
|
||||
case mtpc_userDeleted: {
|
||||
const MTPDuserDeleted &d(user.c_userDeleted());
|
||||
case mtpc_user: {
|
||||
const MTPDuser &d(user.c_user());
|
||||
|
||||
PeerId peer(peerFromUser(d.vid.v));
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerContact(d.vid);
|
||||
data->inputUser = MTP_inputUserContact(d.vid);
|
||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||
// data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->access = UserNoAccess;
|
||||
int32 flags = d.vflags.v;
|
||||
if (flags & MTPDuser_flag_self) {
|
||||
data->input = MTP_inputPeerSelf();
|
||||
data->inputUser = MTP_inputUserSelf();
|
||||
} else if ((flags & (MTPDuser_flag_contact | MTPDuser_flag_mutual_contact)) || !d.has_access_hash()) {
|
||||
data->input = MTP_inputPeerContact(d.vid);
|
||||
data->inputUser = MTP_inputUserContact(d.vid);
|
||||
} else {
|
||||
data->input = MTP_inputPeerForeign(d.vid, d.vaccess_hash);
|
||||
data->inputUser = MTP_inputUserForeign(d.vid, d.vaccess_hash);
|
||||
}
|
||||
if (flags & MTPDuser_flag_deleted) {
|
||||
data->setPhone(QString());
|
||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->access = UserNoAccess;
|
||||
status = &emptyStatus;
|
||||
} else {
|
||||
data->setPhone(d.has_phone() ? qs(d.vphone) : QString());
|
||||
QString fname = d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString();
|
||||
QString lname = d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString();
|
||||
QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString();
|
||||
bool showPhone = !isServiceUser(data->id) && !(flags & (MTPDuser_flag_self | MTPDuser_flag_contact | MTPDuser_flag_mutual_contact));
|
||||
QString pname = (showPhone && !data->phone.isEmpty()) ? formatPhone(data->phone) : QString();
|
||||
data->setName(fname, lname, QString(), uname);
|
||||
if (d.has_photo()) {
|
||||
data->setPhoto(d.vphoto);
|
||||
} else {
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
}
|
||||
if (d.has_access_hash()) data->access = d.vaccess_hash.v;
|
||||
status = d.has_status() ? &d.vstatus : &emptyStatus;
|
||||
}
|
||||
wasContact = (data->contact > 0);
|
||||
status = &emptyStatus;
|
||||
data->contact = -1;
|
||||
} break;
|
||||
case mtpc_userSelf: {
|
||||
const MTPDuserSelf &d(user.c_userSelf());
|
||||
|
||||
PeerId peer(peerFromUser(d.vid.v));
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerSelf();
|
||||
data->inputUser = MTP_inputUserSelf();
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->setPhone(qs(d.vphone));
|
||||
data->access = 0;
|
||||
wasContact = (data->contact > 0);
|
||||
status = &d.vstatus;
|
||||
|
||||
if (::self != data) {
|
||||
if (d.has_bot_info_version()) {
|
||||
data->setBotInfoVersion(d.vbot_info_version.v);
|
||||
data->botInfo->readsAllHistory = (d.vflags.v & MTPDuser_flag_bot_reads_all);
|
||||
data->botInfo->cantJoinGroups = (d.vflags.v & MTPDuser_flag_bot_cant_join);
|
||||
} else {
|
||||
data->setBotInfoVersion(-1);
|
||||
}
|
||||
data->contact = (flags & (MTPDuser_flag_contact | MTPDuser_flag_mutual_contact)) ? 1 : (data->phone.isEmpty() ? -1 : 0);
|
||||
if ((flags & MTPDuser_flag_self) && ::self != data) {
|
||||
::self = data;
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
} break;
|
||||
case mtpc_userContact: {
|
||||
const MTPDuserContact &d(user.c_userContact());
|
||||
|
||||
PeerId peer(peerFromUser(d.vid.v));
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerContact(d.vid);
|
||||
data->inputUser = MTP_inputUserContact(d.vid);
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->setPhone(qs(d.vphone));
|
||||
data->access = d.vaccess_hash.v;
|
||||
wasContact = (data->contact > 0);
|
||||
data->contact = 1;
|
||||
status = &d.vstatus;
|
||||
} break;
|
||||
case mtpc_userRequest: {
|
||||
const MTPDuserRequest &d(user.c_userRequest());
|
||||
|
||||
PeerId peer(peerFromUser(d.vid.v));
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerForeign(d.vid, d.vaccess_hash);
|
||||
data->inputUser = MTP_inputUserForeign(d.vid, d.vaccess_hash);
|
||||
data->setPhone(qs(d.vphone));
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), (!isServiceUser(data->id) && !data->phone.isEmpty()) ? formatPhone(data->phone) : QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->access = d.vaccess_hash.v;
|
||||
wasContact = (data->contact > 0);
|
||||
data->contact = 0;
|
||||
status = &d.vstatus;
|
||||
} break;
|
||||
case mtpc_userForeign: {
|
||||
const MTPDuserForeign &d(user.c_userForeign());
|
||||
|
||||
PeerId peer(peerFromUser(d.vid.v));
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerForeign(d.vid, d.vaccess_hash);
|
||||
data->inputUser = MTP_inputUserForeign(d.vid, d.vaccess_hash);
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->access = d.vaccess_hash.v;
|
||||
wasContact = (data->contact > 0);
|
||||
data->contact = -1;
|
||||
status = &d.vstatus;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!data) continue;
|
||||
@@ -472,6 +460,7 @@ namespace App {
|
||||
if (data->version < d.vversion.v) {
|
||||
data->version = d.vversion.v;
|
||||
data->participants = ChatData::Participants();
|
||||
data->botStatus = 0;
|
||||
}
|
||||
} break;
|
||||
case mtpc_chatForbidden: {
|
||||
@@ -505,6 +494,7 @@ namespace App {
|
||||
if (data->version < d.vversion.v) {
|
||||
data->version = d.vversion.v;
|
||||
data->participants = ChatData::Participants();
|
||||
data->botStatus = 0;
|
||||
}/**/
|
||||
} break;
|
||||
}
|
||||
@@ -518,7 +508,7 @@ namespace App {
|
||||
return data;
|
||||
}
|
||||
|
||||
void feedParticipants(const MTPChatParticipants &p) {
|
||||
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos) {
|
||||
switch (p.type()) {
|
||||
case mtpc_chatParticipantsForbidden: {
|
||||
const MTPDchatParticipantsForbidden &d(p.c_chatParticipantsForbidden());
|
||||
@@ -545,17 +535,24 @@ namespace App {
|
||||
}
|
||||
} else {
|
||||
chat->participants = ChatData::Participants();
|
||||
chat->botStatus = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!chat->participants.isEmpty()) {
|
||||
int32 botStatus = -1;
|
||||
for (ChatData::Participants::iterator i = chat->participants.begin(), e = chat->participants.end(); i != e;) {
|
||||
if (i.value() < pversion) {
|
||||
i = chat->participants.erase(i);
|
||||
} else {
|
||||
if (i.key()->botInfo) {
|
||||
botStatus = (botStatus > 0/* || i.key()->botInfo->readsAllHistory*/) ? 2 : 1;
|
||||
if (requestBotInfos && !i.key()->botInfo->inited) App::api()->requestFullPeer(i.key());
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
chat->botStatus = botStatus;
|
||||
}
|
||||
if (App::main()) App::main()->peerUpdated(chat);
|
||||
}
|
||||
@@ -571,6 +568,7 @@ namespace App {
|
||||
if (user) {
|
||||
if (chat->participants.isEmpty() && chat->count) {
|
||||
chat->count++;
|
||||
chat->botStatus = 0;
|
||||
} else if (chat->participants.find(user) == chat->participants.end()) {
|
||||
chat->participants[user] = (chat->participants.isEmpty() ? 1 : chat->participants.begin().value());
|
||||
if (d.vinviter_id.v == MTP::authedId()) {
|
||||
@@ -579,9 +577,14 @@ namespace App {
|
||||
chat->cankick.remove(user);
|
||||
}
|
||||
chat->count++;
|
||||
if (user->botInfo) {
|
||||
chat->botStatus = (chat->botStatus > 0/* || !user->botInfo->readsAllHistory*/) ? 2 : 1;
|
||||
if (!user->botInfo->inited) App::api()->requestFullPeer(user);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chat->participants = ChatData::Participants();
|
||||
chat->botStatus = 0;
|
||||
chat->count++;
|
||||
}
|
||||
if (App::main()) App::main()->peerUpdated(chat);
|
||||
@@ -602,9 +605,23 @@ namespace App {
|
||||
chat->participants.erase(i);
|
||||
chat->count--;
|
||||
}
|
||||
if (chat->botStatus > 0 && user->botInfo) {
|
||||
int32 botStatus = -1;
|
||||
for (ChatData::Participants::const_iterator j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) {
|
||||
if (j.key()->botInfo) {
|
||||
if (botStatus > 0/* || !j.key()->botInfo->readsAllHistory*/) {
|
||||
botStatus = 2;
|
||||
break;
|
||||
}
|
||||
botStatus = 1;
|
||||
}
|
||||
}
|
||||
chat->botStatus = botStatus;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chat->participants = ChatData::Participants();
|
||||
chat->botStatus = 0;
|
||||
chat->count--;
|
||||
}
|
||||
if (App::main()) App::main()->peerUpdated(chat);
|
||||
@@ -731,11 +748,7 @@ namespace App {
|
||||
MTPint userId(MTP_int(0));
|
||||
switch (dv.vuser.type()) {
|
||||
case mtpc_userEmpty: userId = dv.vuser.c_userEmpty().vid; break;
|
||||
case mtpc_userDeleted: userId = dv.vuser.c_userDeleted().vid; break;
|
||||
case mtpc_userContact: userId = dv.vuser.c_userContact().vid; break;
|
||||
case mtpc_userSelf: userId = dv.vuser.c_userSelf().vid; break;
|
||||
case mtpc_userRequest: userId = dv.vuser.c_userRequest().vid; break;
|
||||
case mtpc_userForeign: userId = dv.vuser.c_userForeign().vid; break;
|
||||
case mtpc_user: userId = dv.vuser.c_user().vid; break;
|
||||
}
|
||||
if (userId.v) {
|
||||
feedUserLink(userId, dv.vmy_link, dv.vforeign_link);
|
||||
@@ -1183,6 +1196,7 @@ namespace App {
|
||||
}
|
||||
|
||||
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation) {
|
||||
bool sentSticker = false;
|
||||
if (convert) {
|
||||
if (convert->id != document) {
|
||||
DocumentsData::iterator i = documentsData.find(convert->id);
|
||||
@@ -1191,6 +1205,7 @@ namespace App {
|
||||
}
|
||||
convert->id = document;
|
||||
convert->status = FileReady;
|
||||
sentSticker = !!convert->sticker;
|
||||
}
|
||||
convert->access = access;
|
||||
if (!convert->date && date) {
|
||||
@@ -1266,6 +1281,7 @@ namespace App {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sentSticker && App::main()) App::main()->incrementSticker(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1506,6 +1522,7 @@ namespace App {
|
||||
}
|
||||
::maxMsgId = 0;
|
||||
::hoveredItem = ::pressedItem = ::hoveredLinkItem = ::pressedLinkItem = ::contextItem = 0;
|
||||
replyMarkups.clear();
|
||||
}
|
||||
|
||||
void historyClearItems() {
|
||||
@@ -1661,6 +1678,9 @@ namespace App {
|
||||
prepareCorners(MediaviewSaveCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(EmojiHoverCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(StickerHoverCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(BotKeyboardCorners, st::msgRadius, st::botKbBg);
|
||||
prepareCorners(BotKeyboardOverCorners, st::msgRadius, st::botKbOverBg);
|
||||
prepareCorners(BotKeyboardDownCorners, st::msgRadius, st::botKbDownBg);
|
||||
|
||||
prepareCorners(MessageInCorners, st::msgRadius, st::msgInBg, &st::msgInShadow);
|
||||
prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInSelectBg, &st::msgInSelectShadow);
|
||||
@@ -1955,6 +1975,56 @@ namespace App {
|
||||
if (changeInMin) App::main()->updateMutedIn(changeInMin);
|
||||
}
|
||||
|
||||
void feedReplyMarkup(MsgId msgId, const MTPReplyMarkup &markup) {
|
||||
ReplyMarkup data;
|
||||
ReplyMarkup::Commands &commands(data.commands);
|
||||
switch (markup.type()) {
|
||||
case mtpc_replyKeyboardMarkup: {
|
||||
const MTPDreplyKeyboardMarkup &d(markup.c_replyKeyboardMarkup());
|
||||
data.flags = d.vflags.v;
|
||||
|
||||
const QVector<MTPKeyboardButtonRow> &v(d.vrows.c_vector().v);
|
||||
if (!v.isEmpty()) {
|
||||
commands.reserve(v.size());
|
||||
for (int32 i = 0, l = v.size(); i < l; ++i) {
|
||||
switch (v.at(i).type()) {
|
||||
case mtpc_keyboardButtonRow: {
|
||||
const MTPDkeyboardButtonRow &r(v.at(i).c_keyboardButtonRow());
|
||||
const QVector<MTPKeyboardButton> &b(r.vbuttons.c_vector().v);
|
||||
if (!b.isEmpty()) {
|
||||
QList<QString> btns;
|
||||
btns.reserve(b.size());
|
||||
for (int32 j = 0, s = b.size(); j < s; ++j) {
|
||||
switch (b.at(j).type()) {
|
||||
case mtpc_keyboardButton: {
|
||||
btns.push_back(qs(b.at(j).c_keyboardButton().vtext));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (!btns.isEmpty()) commands.push_back(btns);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (!commands.isEmpty()) {
|
||||
replyMarkups.insert(msgId, data);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void clearReplyMarkup(MsgId msgId) {
|
||||
replyMarkups.remove(msgId);
|
||||
}
|
||||
|
||||
const ReplyMarkup &replyMarkup(MsgId msgId) {
|
||||
static ReplyMarkup zeroMarkup(MTPDreplyKeyboardMarkup_flag_ZERO);
|
||||
ReplyMarkups::const_iterator i = replyMarkups.constFind(msgId);
|
||||
if (i == replyMarkups.cend()) return zeroMarkup;
|
||||
return i.value();
|
||||
}
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager) {
|
||||
if (cConnectionType() == dbictHttpProxy) {
|
||||
const ConnectionProxy &p(cConnectionProxy());
|
||||
@@ -1973,15 +2043,27 @@ namespace App {
|
||||
}
|
||||
}
|
||||
|
||||
void sendBotCommand(const QString &cmd, MsgId replyTo) {
|
||||
if (App::main()) {
|
||||
App::main()->sendBotCommand(cmd, replyTo);
|
||||
}
|
||||
}
|
||||
|
||||
void insertBotCommand(const QString &cmd) {
|
||||
if (App::main()) {
|
||||
App::main()->insertBotCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void searchByHashtag(const QString &tag) {
|
||||
if (App::main()) {
|
||||
App::main()->searchMessages(tag + ' ');
|
||||
}
|
||||
}
|
||||
|
||||
void openUserByName(const QString &username, bool toProfile) {
|
||||
void openUserByName(const QString &username, bool toProfile, const QString &startToken) {
|
||||
if (App::main()) {
|
||||
App::main()->openUserByName(username, toProfile);
|
||||
App::main()->openUserByName(username, toProfile, startToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ typedef QHash<VideoData*, HistoryItemsMap> VideoItems;
|
||||
typedef QHash<AudioData*, HistoryItemsMap> AudioItems;
|
||||
typedef QHash<DocumentData*, HistoryItemsMap> DocumentItems;
|
||||
typedef QHash<WebPageData*, HistoryItemsMap> WebPageItems;
|
||||
struct ReplyMarkup {
|
||||
ReplyMarkup(int32 flags = 0) : flags(flags) {
|
||||
}
|
||||
typedef QList<QList<QString> > Commands;
|
||||
Commands commands;
|
||||
int32 flags;
|
||||
};
|
||||
|
||||
enum RoundCorners {
|
||||
MaskCorners = 0x00, // for images
|
||||
@@ -48,6 +55,9 @@ enum RoundCorners {
|
||||
MediaviewSaveCorners,
|
||||
EmojiHoverCorners,
|
||||
StickerHoverCorners,
|
||||
BotKeyboardCorners,
|
||||
BotKeyboardOverCorners,
|
||||
BotKeyboardDownCorners,
|
||||
|
||||
InShadowCorners, // for photos without bg
|
||||
InSelectedShadowCorners,
|
||||
@@ -92,14 +102,14 @@ namespace App {
|
||||
int32 userFromPeer(const PeerId &peer_id);
|
||||
int32 chatFromPeer(const PeerId &peer_id);
|
||||
|
||||
int32 onlineForSort(int32 online, int32 now);
|
||||
int32 onlineWillChangeIn(int32 onlineOnServer, int32 nowOnServer);
|
||||
int32 onlineForSort(UserData *user, int32 now);
|
||||
int32 onlineWillChangeIn(UserData *user, int32 nowOnServer);
|
||||
QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
|
||||
bool onlineColorUse(int32 online, int32 now);
|
||||
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
|
||||
ChatData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
|
||||
void feedParticipants(const MTPChatParticipants &p);
|
||||
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos = false);
|
||||
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
|
||||
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
|
||||
void feedMsgs(const MTPVector<MTPMessage> &msgs, int msgsState = 0); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message
|
||||
@@ -222,11 +232,17 @@ namespace App {
|
||||
void unregMuted(PeerData *peer);
|
||||
void updateMuted();
|
||||
|
||||
void feedReplyMarkup(MsgId msgId, const MTPReplyMarkup &markup);
|
||||
void clearReplyMarkup(MsgId msgId);
|
||||
const ReplyMarkup &replyMarkup(MsgId msgId);
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager);
|
||||
void setProxySettings(QTcpSocket &socket);
|
||||
|
||||
void sendBotCommand(const QString &cmd, MsgId replyTo = 0);
|
||||
void insertBotCommand(const QString &cmd);
|
||||
void searchByHashtag(const QString &tag);
|
||||
void openUserByName(const QString &username, bool toProfile = false);
|
||||
void openUserByName(const QString &username, bool toProfile = false, const QString &startToken = QString());
|
||||
void joinGroupByHash(const QString &hash);
|
||||
void stickersBox(const QString &name);
|
||||
void openLocalUrl(const QString &url);
|
||||
|
||||
@@ -640,10 +640,10 @@ void Application::checkMapVersion() {
|
||||
psRegisterCustomScheme();
|
||||
if (Local::oldMapVersion()) {
|
||||
QString versionFeatures;
|
||||
if (DevChannel && Local::oldMapVersion() < 8023) {
|
||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Improved sticker panel\n\xe2\x80\x94 Bug fixes and minor stuff");// .replace('@', qsl("@") + QChar(0x200D));
|
||||
if (DevChannel && Local::oldMapVersion() < 8027) {
|
||||
versionFeatures = lang(lng_new_version_minor);// QString::fromUtf8("\xe2\x80\x94 IPv6 connections support\n\xe2\x80\x94 Bug fixes and minor stuff");// .replace('@', qsl("@") + QChar(0x200D));
|
||||
} else if (!DevChannel && Local::oldMapVersion() < 8024) {
|
||||
versionFeatures = lang(lng_new_version_text).trimmed();
|
||||
versionFeatures = lng_new_version_text(lt_blog_link, qsl("https://telegram.org/blog/bot-revolution"));// lang(lng_new_version_text).trimmed();
|
||||
}
|
||||
if (!versionFeatures.isEmpty()) {
|
||||
versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog"));
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 219 KiB |
@@ -23,7 +23,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
|
||||
ContactsInner::ContactsInner(bool creatingChat) : _chat(0), _creatingChat(creatingChat),
|
||||
#include "confirmbox.h"
|
||||
|
||||
ContactsInner::ContactsInner(bool creatingChat) : _chat(0), _bot(0), _creatingChat(creatingChat), _addToChat(0),
|
||||
_contacts(&App::main()->contactsList()),
|
||||
_sel(0),
|
||||
_filteredSel(-1),
|
||||
@@ -35,7 +37,7 @@ _addContactLnk(this, lang(lng_add_contact_button)) {
|
||||
init();
|
||||
}
|
||||
|
||||
ContactsInner::ContactsInner(ChatData *chat) : _chat(chat), _creatingChat(false),
|
||||
ContactsInner::ContactsInner(ChatData *chat) : _chat(chat), _bot(0), _creatingChat(false), _addToChat(0),
|
||||
_contacts(&App::main()->contactsList()),
|
||||
_sel(0),
|
||||
_filteredSel(-1),
|
||||
@@ -47,6 +49,24 @@ _addContactLnk(this, lang(lng_add_contact_button)) {
|
||||
init();
|
||||
}
|
||||
|
||||
ContactsInner::ContactsInner(UserData *bot) : _chat(0), _bot(bot), _creatingChat(false), _addToChat(0),
|
||||
_contacts(new DialogsIndexed(DialogsSortByAdd)),
|
||||
_sel(0),
|
||||
_filteredSel(-1),
|
||||
_mouseSel(false),
|
||||
_selCount(0),
|
||||
_searching(false),
|
||||
_byUsernameSel(-1),
|
||||
_addContactLnk(this, lang(lng_add_contact_button)) {
|
||||
DialogsIndexed &v(App::main()->dialogsList());
|
||||
for (DialogRow *r = v.list.begin; r != v.list.end; r = r->next) {
|
||||
if (r->history->peer->chat && !r->history->peer->asChat()->forbidden) {
|
||||
_contacts->addToEnd(r->history);
|
||||
}
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
void ContactsInner::init() {
|
||||
connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
|
||||
|
||||
@@ -59,10 +79,29 @@ void ContactsInner::init() {
|
||||
|
||||
connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow *, DialogRow *)), this, SLOT(onDialogRowReplaced(DialogRow *, DialogRow *)));
|
||||
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData *)));
|
||||
connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(peerUpdated(PeerData *)));
|
||||
connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(onPeerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)));
|
||||
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
|
||||
}
|
||||
|
||||
void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
|
||||
if (bot()) {
|
||||
_contacts->peerNameChanged(peer, oldNames, oldChars);
|
||||
}
|
||||
peerUpdated(peer);
|
||||
}
|
||||
|
||||
void ContactsInner::onAddBot() {
|
||||
if (_bot->botInfo && !_bot->botInfo->startGroupToken.isEmpty()) {
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
MTP::send(MTPmessages_StartBot(_bot->inputUser, MTP_int(App::chatFromPeer(_addToChat->id)), MTP_long(randomId), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot));
|
||||
|
||||
App::wnd()->hideLayer();
|
||||
App::main()->showPeer(_addToChat->id, 0, false);
|
||||
} else {
|
||||
App::main()->addParticipants(_addToChat, QVector<UserData*>(1, _bot));
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsInner::peerUpdated(PeerData *peer) {
|
||||
if (_chat && (!peer || peer == _chat)) {
|
||||
if (_chat->forbidden) {
|
||||
@@ -81,8 +120,8 @@ void ContactsInner::peerUpdated(PeerData *peer) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!peer->chat) {
|
||||
ContactsData::iterator i = _contactsData.find(peer->asUser());
|
||||
} else {
|
||||
ContactsData::iterator i = _contactsData.find(peer);
|
||||
if (i != _contactsData.cend()) {
|
||||
for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
|
||||
if (row->attached == i.value()) row->attached = 0;
|
||||
@@ -136,14 +175,23 @@ void ContactsInner::loadProfilePhotos(int32 yFrom) {
|
||||
ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) {
|
||||
ContactData *data = (ContactData*)row->attached;
|
||||
if (!data) {
|
||||
UserData *user = row->history->peer->asUser();
|
||||
ContactsData::const_iterator i = _contactsData.constFind(user);
|
||||
PeerData *peer = row->history->peer;
|
||||
ContactsData::const_iterator i = _contactsData.constFind(peer);
|
||||
if (i == _contactsData.cend()) {
|
||||
_contactsData.insert(user, data = new ContactData());
|
||||
data->inchat = _chat ? _chat->participants.contains(user) : false;
|
||||
_contactsData.insert(peer, data = new ContactData());
|
||||
data->inchat = (_chat && !peer->chat) ? _chat->participants.contains(peer->asUser()) : false;
|
||||
data->check = false;
|
||||
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
|
||||
data->online = App::onlineText(user, _time);
|
||||
data->name.setText(st::profileListNameFont, peer->name, _textNameOptions);
|
||||
if (peer->chat) {
|
||||
ChatData *chat = peer->asChat();
|
||||
if (chat->forbidden) {
|
||||
data->online = lang(lng_chat_status_unaccessible);
|
||||
} else {
|
||||
data->online = lng_chat_status_members(lt_count, chat->count);
|
||||
}
|
||||
} else {
|
||||
data->online = App::onlineText(peer->asUser(), _time);
|
||||
}
|
||||
} else {
|
||||
data = i.value();
|
||||
}
|
||||
@@ -152,9 +200,11 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) {
|
||||
return data;
|
||||
}
|
||||
|
||||
void ContactsInner::paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel) {
|
||||
void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data, bool sel) {
|
||||
int32 left = st::profileListPadding.width();
|
||||
|
||||
UserData *user = peer->chat ? 0 : peer->asUser();
|
||||
|
||||
if (data->inchat || data->check || _selCount + (_chat ? _chat->count : 0) >= cMaxGroupCount()) {
|
||||
sel = false;
|
||||
}
|
||||
@@ -163,7 +213,7 @@ void ContactsInner::paintDialog(QPainter &p, UserData *user, ContactData *data,
|
||||
p.fillRect(0, 0, width(), 2 * st::profileListPadding.height() + st::profileListPhotoSize, ((data->inchat || data->check) ? st::profileActiveBG : st::profileHoverBG)->b);
|
||||
}
|
||||
|
||||
p.drawPixmap(left, st::profileListPadding.height(), user->photo->pix(st::profileListPhotoSize));
|
||||
p.drawPixmap(left, st::profileListPadding.height(), peer->photo->pix(st::profileListPhotoSize));
|
||||
|
||||
if (data->inchat || data->check) {
|
||||
p.setPen(st::white->p);
|
||||
@@ -181,8 +231,7 @@ void ContactsInner::paintDialog(QPainter &p, UserData *user, ContactData *data,
|
||||
p.drawPixmap(QPoint(width() - st::contactsImg.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::contactsImg.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), st::contactsImg);
|
||||
}
|
||||
|
||||
|
||||
bool uname = (data->online.at(0) == '@');
|
||||
bool uname = user && (data->online.at(0) == '@');
|
||||
p.setFont(st::profileSubFont->f);
|
||||
if (uname && !data->inchat && !data->check && !_lastQuery.isEmpty() && user->username.startsWith(_lastQuery, Qt::CaseInsensitive)) {
|
||||
int32 availw = width() - (left + st::profileListPhotoSize + st::profileListPadding.width() * 2);
|
||||
@@ -200,8 +249,10 @@ void ContactsInner::paintDialog(QPainter &p, UserData *user, ContactData *data,
|
||||
} else {
|
||||
if (data->inchat || data->check) {
|
||||
p.setPen(st::white->p);
|
||||
} else if (user && (uname || App::onlineColorUse(user->onlineTill, _time))) {
|
||||
p.setPen(st::profileOnlineColor->p);
|
||||
} else {
|
||||
p.setPen(((uname || App::onlineColorUse(user->onlineTill, _time)) ? st::profileOnlineColor : st::profileOfflineColor)->p);
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
}
|
||||
p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online);
|
||||
}
|
||||
@@ -224,7 +275,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
|
||||
DialogRow *drawFrom = _contacts->list.current;
|
||||
p.translate(0, drawFrom->pos * rh);
|
||||
while (drawFrom != _contacts->list.end && drawFrom->pos * rh < yTo) {
|
||||
paintDialog(p, drawFrom->history->peer->asUser(), contactData(drawFrom), (drawFrom == _sel));
|
||||
paintDialog(p, drawFrom->history->peer, contactData(drawFrom), (drawFrom == _sel));
|
||||
p.translate(0, rh);
|
||||
drawFrom = drawFrom->next;
|
||||
}
|
||||
@@ -253,13 +304,29 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
|
||||
} else {
|
||||
p.setFont(st::noContactsFont->f);
|
||||
p.setPen(st::noContactsColor->p);
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight - ((cContactsReceived() && !_searching) ? st::noContactsFont->height : 0)), lang((cContactsReceived() && !_searching) ? lng_no_contacts : lng_contacts_loading), style::al_center);
|
||||
QString text;
|
||||
int32 skip = 0;
|
||||
if (bot()) {
|
||||
text = lang(cDialogsReceived() ? lng_bot_no_groups : lng_contacts_loading);
|
||||
} else if (cContactsReceived() && !_searching) {
|
||||
text = lang(lng_no_contacts);
|
||||
skip = st::noContactsFont->height;
|
||||
} else {
|
||||
text = lang(lng_contacts_loading);
|
||||
}
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight - skip), text, style::al_center);
|
||||
}
|
||||
} else {
|
||||
if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) {
|
||||
p.setFont(st::noContactsFont->f);
|
||||
p.setPen(st::noContactsColor->p);
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang((cContactsReceived() && !_searching) ? lng_contacts_not_found : lng_contacts_loading), style::al_center);
|
||||
QString text;
|
||||
if (bot()) {
|
||||
text = lang(cDialogsReceived() ? lng_bot_groups_not_found : lng_contacts_loading);
|
||||
} else {
|
||||
text = lang((cContactsReceived() && !_searching) ? lng_contacts_not_found : lng_contacts_loading);
|
||||
}
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), text, style::al_center);
|
||||
} else {
|
||||
if (!_filtered.isEmpty()) {
|
||||
int32 from = (yFrom >= 0) ? (yFrom / rh) : 0;
|
||||
@@ -269,7 +336,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
p.translate(0, from * rh);
|
||||
for (; from < to; ++from) {
|
||||
paintDialog(p, _filtered[from]->history->peer->asUser(), contactData(_filtered[from]), (_filteredSel == from));
|
||||
paintDialog(p, _filtered[from]->history->peer, contactData(_filtered[from]), (_filteredSel == from));
|
||||
p.translate(0, rh);
|
||||
}
|
||||
}
|
||||
@@ -371,25 +438,32 @@ void ContactsInner::chooseParticipant() {
|
||||
}
|
||||
} else {
|
||||
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
|
||||
PeerId peer = 0;
|
||||
PeerData *peer = 0;
|
||||
if (_filter.isEmpty()) {
|
||||
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) {
|
||||
peer = _byUsername[_byUsernameSel]->id;
|
||||
} else {
|
||||
peer = _sel ? _sel->history->peer->id : 0;
|
||||
peer = _byUsername[_byUsernameSel];
|
||||
} else if (_sel) {
|
||||
peer = _sel->history->peer;
|
||||
}
|
||||
} else {
|
||||
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) {
|
||||
peer = _byUsernameFiltered[_byUsernameSel]->id;
|
||||
peer = _byUsernameFiltered[_byUsernameSel];
|
||||
} else {
|
||||
if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return;
|
||||
peer = _filtered[_filteredSel]->history->peer->id;
|
||||
peer = _filtered[_filteredSel]->history->peer;
|
||||
}
|
||||
}
|
||||
if (peer) {
|
||||
App::wnd()->hideSettings(true);
|
||||
App::main()->showPeer(peer, 0, false, true);
|
||||
App::wnd()->hideLayer();
|
||||
if (bot() && peer->chat) {
|
||||
_addToChat = peer->asChat();
|
||||
ConfirmBox *box = new ConfirmBox(lng_bot_sure_invite(lt_group, peer->name));
|
||||
connect(box, SIGNAL(confirmed()), this, SLOT(onAddBot()));
|
||||
App::wnd()->replaceLayer(box);
|
||||
} else {
|
||||
App::wnd()->hideSettings(true);
|
||||
App::main()->showPeer(peer->id, 0, false, true);
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
parentWidget()->update();
|
||||
@@ -562,8 +636,10 @@ void ContactsInner::updateFilter(QString filter) {
|
||||
|
||||
refresh();
|
||||
|
||||
_searching = true;
|
||||
emit searchByUsername();
|
||||
if (!bot()) {
|
||||
_searching = true;
|
||||
emit searchByUsername();
|
||||
}
|
||||
}
|
||||
if (parentWidget()) parentWidget()->update();
|
||||
loadProfilePhotos(0);
|
||||
@@ -612,6 +688,8 @@ void ContactsInner::peopleReceived(const QString &query, const QVector<MTPContac
|
||||
}
|
||||
if (j == already) {
|
||||
UserData *u = App::user(uid);
|
||||
if (u->botInfo && u->botInfo->cantJoinGroups && (_chat || _creatingChat)) continue; // skip bot's that can't be invited to groups
|
||||
|
||||
ContactData *d = new ContactData();
|
||||
_byUsernameDatas.push_back(d);
|
||||
d->inchat = _chat ? _chat->participants.contains(u) : false;
|
||||
@@ -634,7 +712,7 @@ void ContactsInner::refresh() {
|
||||
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
|
||||
resize(width(), (_contacts->list.count * rh) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * rh)));
|
||||
} else {
|
||||
if (cContactsReceived()) {
|
||||
if (cContactsReceived() && !bot()) {
|
||||
if (_addContactLnk.isHidden()) _addContactLnk.show();
|
||||
} else {
|
||||
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
|
||||
@@ -656,6 +734,10 @@ ChatData *ContactsInner::chat() const {
|
||||
return _chat;
|
||||
}
|
||||
|
||||
UserData *ContactsInner::bot() const {
|
||||
return _bot;
|
||||
}
|
||||
|
||||
bool ContactsInner::creatingChat() const {
|
||||
return _creatingChat;
|
||||
}
|
||||
@@ -664,6 +746,10 @@ ContactsInner::~ContactsInner() {
|
||||
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
|
||||
delete *i;
|
||||
}
|
||||
if (_bot) {
|
||||
delete _contacts;
|
||||
if (_bot->botInfo) _bot->botInfo->startGroupToken = QString();
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsInner::resizeEvent(QResizeEvent *e) {
|
||||
@@ -796,8 +882,8 @@ QVector<UserData*> ContactsInner::selected() {
|
||||
QVector<UserData*> result;
|
||||
result.reserve(_contactsData.size());
|
||||
for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) {
|
||||
if (i.value()->check) {
|
||||
result.push_back(i.key());
|
||||
if (i.value()->check && !i.key()->chat) {
|
||||
result.push_back(i.key()->asUser());
|
||||
}
|
||||
}
|
||||
for (int32 i = 0, l = _byUsername.size(); i < l; ++i) {
|
||||
@@ -812,7 +898,7 @@ QVector<MTPInputUser> ContactsInner::selectedInputs() {
|
||||
QVector<MTPInputUser> result;
|
||||
result.reserve(_contactsData.size());
|
||||
for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) {
|
||||
if (i.value()->check) {
|
||||
if (i.value()->check && !i.key()->chat) {
|
||||
result.push_back(i.key()->inputUser);
|
||||
}
|
||||
}
|
||||
@@ -854,6 +940,14 @@ _cancel(this, lang(lng_cancel), st::btnSelectCancel) {
|
||||
init();
|
||||
}
|
||||
|
||||
ContactsBox::ContactsBox(UserData *bot) : ItemListBox(st::boxNoTopScroll), _inner(bot),
|
||||
_addContact(this, lang(lng_add_contact_button), st::contactsAdd),
|
||||
_filter(this, st::contactsFilter, lang(lng_participant_filter)),
|
||||
_next(this, lang(lng_create_group_next), st::btnSelectDone),
|
||||
_cancel(this, lang(lng_cancel), st::contactsClose) {
|
||||
init();
|
||||
}
|
||||
|
||||
void ContactsBox::init() {
|
||||
ItemListBox::init(&_inner, _cancel.height(), st::contactsAdd.height + st::newGroupNamePadding.top() + _filter.height() + st::newGroupNamePadding.bottom());
|
||||
|
||||
@@ -961,14 +1055,17 @@ void ContactsBox::hideAll() {
|
||||
|
||||
void ContactsBox::showAll() {
|
||||
ItemListBox::showAll();
|
||||
_addContact.show();
|
||||
_filter.show();
|
||||
if (_inner.chat() || _inner.creatingChat()) {
|
||||
_next.show();
|
||||
_addContact.hide();
|
||||
} else {
|
||||
_next.hide();
|
||||
_addContact.show();
|
||||
if (_inner.bot()) {
|
||||
_addContact.hide();
|
||||
} else {
|
||||
_addContact.show();
|
||||
}
|
||||
}
|
||||
_cancel.show();
|
||||
}
|
||||
@@ -1010,6 +1107,8 @@ void ContactsBox::paintEvent(QPaintEvent *e) {
|
||||
|
||||
// paint button sep
|
||||
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
|
||||
} else if (_inner.bot()) {
|
||||
paintTitle(p, lang(lng_bot_choose_group), true);
|
||||
} else {
|
||||
paintTitle(p, lang(lng_contacts_header), true);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
|
||||
ContactsInner(bool creatingChat);
|
||||
ContactsInner(ChatData *chat);
|
||||
ContactsInner(UserData *bot);
|
||||
void init();
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
@@ -39,7 +40,7 @@ public:
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
||||
void paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel);
|
||||
void paintDialog(QPainter &p, PeerData *peer, ContactData *data, bool sel);
|
||||
void updateFilter(QString filter = QString());
|
||||
|
||||
void selectSkip(int32 dir);
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
void refresh();
|
||||
|
||||
ChatData *chat() const;
|
||||
UserData *bot() const;
|
||||
bool creatingChat() const;
|
||||
|
||||
~ContactsInner();
|
||||
@@ -75,11 +77,17 @@ public slots:
|
||||
|
||||
void updateSel();
|
||||
void peerUpdated(PeerData *peer);
|
||||
void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
|
||||
|
||||
void onAddBot();
|
||||
|
||||
private:
|
||||
|
||||
ChatData *_chat;
|
||||
UserData *_bot;
|
||||
bool _creatingChat;
|
||||
|
||||
ChatData *_addToChat;
|
||||
|
||||
int32 _time;
|
||||
|
||||
@@ -99,7 +107,7 @@ private:
|
||||
bool inchat;
|
||||
bool check;
|
||||
};
|
||||
typedef QMap<UserData*, ContactData*> ContactsData;
|
||||
typedef QMap<PeerData*, ContactData*> ContactsData;
|
||||
ContactsData _contactsData;
|
||||
|
||||
ContactData *contactData(DialogRow *row);
|
||||
@@ -125,6 +133,7 @@ public:
|
||||
|
||||
ContactsBox(bool creatingChat = false);
|
||||
ContactsBox(ChatData *chat);
|
||||
ContactsBox(UserData *bot);
|
||||
void keyPressEvent(QKeyEvent *e);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
||||
@@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const int32 AppVersion = 8024;
|
||||
static const wchar_t *AppVersionStr = L"0.8.24";
|
||||
static const bool DevChannel = false;
|
||||
static const int32 AppVersion = 8027;
|
||||
static const wchar_t *AppVersionStr = L"0.8.27";
|
||||
static const bool DevChannel = true;
|
||||
|
||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||
static const wchar_t *AppName = L"Telegram Desktop";
|
||||
@@ -43,7 +43,8 @@ enum {
|
||||
MTPMinConnectDelay = 1000, // tcp connect should take less then 1 second
|
||||
MTPMaxConnectDelay = 8000, // tcp connect should take 8 seconds max
|
||||
MTPConnectionOldTimeout = 192000, // 192 seconds
|
||||
MTPTcpConnectionWaitTimeout = 3000, // 3 seconds waiting for tcp, until we accept http
|
||||
MTPTcpConnectionWaitTimeout = 2000, // 2 seconds waiting for tcp, until we accept http
|
||||
MTPIPv4ConnectionWaitTimeout = 1000, // 1 seconds waiting for ipv4, until we accept ipv6
|
||||
MTPMillerRabinIterCount = 30, // 30 Miller-Rabin iterations for dh_prime primality check
|
||||
|
||||
MTPUploadSessionsCount = 4, // max 4 upload sessions is created
|
||||
@@ -125,7 +126,7 @@ enum {
|
||||
SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text
|
||||
SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs
|
||||
|
||||
HiddenIsOnlineAfterMessage = 30, // user with hidden last seen stays online for such amount of seconds in the interface
|
||||
SetOnlineAfterActivity = 30, // user with hidden last seen stays online for such amount of seconds in the interface
|
||||
|
||||
ServiceUserId = 777000,
|
||||
WebPageUserId = 701000,
|
||||
@@ -186,10 +187,24 @@ static const BuiltInDc _builtInDcs[] = {
|
||||
{ 5, "149.154.171.5", 443 }
|
||||
};
|
||||
|
||||
static const BuiltInDc _builtInDcsIPv6[] = {
|
||||
{ 1, "2001:b28:f23d:f001::a", 443 },
|
||||
{ 2, "2001:67c:4e8:f002::a", 443 },
|
||||
{ 3, "2001:b28:f23d:f003::a", 443 },
|
||||
{ 4, "2001:67c:4e8:f004::a", 443 },
|
||||
{ 5, "2001:b28:f23f:f005::a", 443 }
|
||||
};
|
||||
|
||||
static const BuiltInDc _builtInTestDcs[] = {
|
||||
{ 1, "149.154.175.10", 443 },
|
||||
{ 2, "149.154.167.40", 443 },
|
||||
{ 3, "174.140.142.5", 443 }
|
||||
{ 3, "149.154.175.117", 443 }
|
||||
};
|
||||
|
||||
static const BuiltInDc _builtInTestDcsIPv6[] = {
|
||||
{ 1, "2001:b28:f23d:f001::e", 443 },
|
||||
{ 2, "2001:67c:4e8:f002::e", 443 },
|
||||
{ 3, "2001:b28:f23d:f003::e", 443 }
|
||||
};
|
||||
|
||||
inline const BuiltInDc *builtInDcs() {
|
||||
@@ -200,6 +215,14 @@ inline int builtInDcsCount() {
|
||||
return (cTestMode() ? sizeof(_builtInTestDcs) : sizeof(_builtInDcs)) / sizeof(BuiltInDc);
|
||||
}
|
||||
|
||||
inline const BuiltInDc *builtInDcsIPv6() {
|
||||
return cTestMode() ? _builtInTestDcsIPv6 : _builtInDcsIPv6;
|
||||
}
|
||||
|
||||
inline int builtInDcsCountIPv6() {
|
||||
return (cTestMode() ? sizeof(_builtInTestDcsIPv6) : sizeof(_builtInDcsIPv6)) / sizeof(BuiltInDc);
|
||||
}
|
||||
|
||||
static const char *UpdatesPublicKey = "\
|
||||
-----BEGIN RSA PUBLIC KEY-----\n\
|
||||
MIGJAoGBAMA4ViQrjkPZ9xj0lrer3r23JvxOnrtE8nI69XLGSr+sRERz9YnUptnU\n\
|
||||
|
||||
@@ -28,9 +28,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "localstorage.h"
|
||||
|
||||
DialogsListWidget::DialogsListWidget(QWidget *parent, MainWidget *main) : QWidget(parent),
|
||||
dialogs(false),
|
||||
contactsNoDialogs(true),
|
||||
contacts(true),
|
||||
dialogs(DialogsSortByDate),
|
||||
contactsNoDialogs(DialogsSortByName),
|
||||
contacts(DialogsSortByName),
|
||||
sel(0),
|
||||
contactSel(false),
|
||||
selByMouse(false),
|
||||
@@ -64,6 +64,8 @@ int32 DialogsListWidget::searchedOffset() const {
|
||||
}
|
||||
|
||||
void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
if (!App::main()) return;
|
||||
|
||||
QRect r(e->rect());
|
||||
bool trivial = (rect() == r);
|
||||
|
||||
@@ -95,7 +97,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
p.translate(0, from * st::mentionHeight);
|
||||
if (from < hashtagResults.size()) {
|
||||
int32 to = (r.bottom() / int32(st::mentionHeight)) + 1, w = width();
|
||||
int32 to = (r.bottom() / int32(st::mentionHeight)) + 1, w = width(), htagwidth = w - st::dlgPaddingHor * 2;
|
||||
if (to > hashtagResults.size()) to = hashtagResults.size();
|
||||
p.setFont(st::mentionFont->f);
|
||||
p.setPen(st::black->p);
|
||||
@@ -106,8 +108,27 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
|
||||
p.drawPixmap(QPoint(w - st::notifyClose.icon.pxWidth() - skip, skip), App::sprite(), st::notifyClose.icon);
|
||||
}
|
||||
QString tag = st::mentionFont->m.elidedText('#' + hashtagResults.at(from), Qt::ElideRight, w - st::dlgPaddingHor * 2);
|
||||
p.drawText(st::dlgPaddingHor, st::mentionTop + st::mentionFont->ascent, tag);
|
||||
|
||||
QString first = (_hashtagFilter.size() < 2) ? QString() : ('#' + hashtagResults.at(from).mid(0, _hashtagFilter.size() - 1)), second = (_hashtagFilter.size() < 2) ? ('#' + hashtagResults.at(from)) : hashtagResults.at(from).mid(_hashtagFilter.size() - 1);
|
||||
int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second);
|
||||
if (htagwidth < firstwidth + secondwidth) {
|
||||
if (htagwidth < firstwidth + st::mentionFont->elidew) {
|
||||
first = st::mentionFont->m.elidedText(first + second, Qt::ElideRight, htagwidth);
|
||||
second = QString();
|
||||
} else {
|
||||
second = st::mentionFont->m.elidedText(second, Qt::ElideRight, htagwidth - firstwidth);
|
||||
}
|
||||
}
|
||||
|
||||
p.setFont(st::mentionFont->f);
|
||||
if (!first.isEmpty()) {
|
||||
p.setPen(st::profileOnlineColor->p);
|
||||
p.drawText(st::dlgPaddingHor, st::mentionTop + st::mentionFont->ascent, first);
|
||||
}
|
||||
if (!second.isEmpty()) {
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
p.drawText(st::dlgPaddingHor + firstwidth, st::mentionTop + st::mentionFont->ascent, second);
|
||||
}
|
||||
p.translate(0, st::mentionHeight);
|
||||
}
|
||||
}
|
||||
@@ -607,8 +628,9 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsListWidget::onHashtagFilterUpdate(QString newFilter) {
|
||||
void DialogsListWidget::onHashtagFilterUpdate(QStringRef newFilter) {
|
||||
if (newFilter.isEmpty() || newFilter.at(0) != '#') {
|
||||
_hashtagFilter = QString();
|
||||
if (!hashtagResults.isEmpty()) {
|
||||
hashtagResults.clear();
|
||||
refresh(true);
|
||||
@@ -616,6 +638,7 @@ void DialogsListWidget::onHashtagFilterUpdate(QString newFilter) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
_hashtagFilter = newFilter.toString();
|
||||
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
|
||||
Local::readRecentHashtags();
|
||||
}
|
||||
@@ -624,7 +647,7 @@ void DialogsListWidget::onHashtagFilterUpdate(QString newFilter) {
|
||||
if (!recent.isEmpty()) {
|
||||
hashtagResults.reserve(qMin(recent.size(), 5));
|
||||
for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
|
||||
if (i->first.startsWith(newFilter.midRef(1), Qt::CaseInsensitive) && i->first.size() + 1 != newFilter.size()) {
|
||||
if (i->first.startsWith(_hashtagFilter.midRef(1), Qt::CaseInsensitive) && i->first.size() + 1 != newFilter.size()) {
|
||||
hashtagResults.push_back(i->first);
|
||||
if (hashtagResults.size() == 5) break;
|
||||
}
|
||||
@@ -1604,7 +1627,10 @@ void DialogsWidget::onSearchMore(MsgId minMsgId) {
|
||||
|
||||
void DialogsWidget::loadDialogs() {
|
||||
if (dlgPreloading) return;
|
||||
if (dlgCount >= 0 && dlgOffset >= dlgCount) return;
|
||||
if (dlgCount >= 0 && dlgOffset >= dlgCount) {
|
||||
cSetDialogsReceived(true);
|
||||
return;
|
||||
}
|
||||
|
||||
int32 loadCount = dlgOffset ? DialogsPerPage : DialogsFirstLoad;
|
||||
dlgPreloading = MTP::send(MTPmessages_GetDialogs(MTP_int(dlgOffset), MTP_int(0), MTP_int(loadCount)), rpcDone(&DialogsWidget::dialogsReceived), rpcFail(&DialogsWidget::dialogsFailed));
|
||||
@@ -1752,12 +1778,13 @@ void DialogsWidget::onFilterUpdate(bool force) {
|
||||
|
||||
void DialogsWidget::onFilterCursorMoved(int from, int to) {
|
||||
if (to < 0) to = _filter.cursorPosition();
|
||||
QString t = _filter.text(), r;
|
||||
QString t = _filter.text();
|
||||
QStringRef r;
|
||||
for (int start = to; start > 0;) {
|
||||
--start;
|
||||
if (t.size() <= start) break;
|
||||
if (t.at(start) == '#') {
|
||||
r = t.mid(start, to - start);
|
||||
r = t.midRef(start, to - start);
|
||||
break;
|
||||
}
|
||||
if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break;
|
||||
@@ -1886,6 +1913,10 @@ DialogsIndexed &DialogsWidget::contactsList() {
|
||||
return list.contactsList();
|
||||
}
|
||||
|
||||
DialogsIndexed &DialogsWidget::dialogsList() {
|
||||
return list.dialogsList();
|
||||
}
|
||||
|
||||
void DialogsWidget::onAddContact() {
|
||||
App::wnd()->replaceLayer(new AddContactBox());
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
bool hasFilteredResults() const;
|
||||
|
||||
void onFilterUpdate(QString newFilter, bool force = false);
|
||||
void onHashtagFilterUpdate(QString newFilter);
|
||||
void onHashtagFilterUpdate(QStringRef newFilter);
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
|
||||
@@ -130,7 +130,7 @@ private:
|
||||
bool contactSel;
|
||||
bool selByMouse;
|
||||
|
||||
QString filter;
|
||||
QString filter, _hashtagFilter;
|
||||
|
||||
QStringList hashtagResults;
|
||||
int32 hashtagSel;
|
||||
@@ -197,6 +197,7 @@ public:
|
||||
void removeContact(UserData *user);
|
||||
|
||||
DialogsIndexed &contactsList();
|
||||
DialogsIndexed &dialogsList();
|
||||
|
||||
void enableShadow(bool enable = true);
|
||||
|
||||
|
||||
@@ -779,7 +779,7 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
|
||||
int32 index = i * EmojiPanPerRow + j;
|
||||
if (index >= size) break;
|
||||
|
||||
float64 hover = (!_picker.isHidden() && c * emojiTabShift + index == _pickerSel) ? 1 : _hovers[c][index];
|
||||
float64 hover = (!_picker.isHidden() && c * MatrixRowShift + index == _pickerSel) ? 1 : _hovers[c][index];
|
||||
|
||||
QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height());
|
||||
if (hover > 0) {
|
||||
@@ -817,7 +817,7 @@ void EmojiPanInner::mousePressEvent(QMouseEvent *e) {
|
||||
_pressedSel = _selected;
|
||||
|
||||
if (_selected >= 0 && _selected != SwitcherSelected) {
|
||||
int tab = (_selected / emojiTabShift), sel = _selected % emojiTabShift;
|
||||
int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift;
|
||||
if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) {
|
||||
_pickerSel = _selected;
|
||||
if (cEmojiVariants().constFind(_emojis[tab][sel]->code) == cEmojiVariants().cend()) {
|
||||
@@ -838,7 +838,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) {
|
||||
return _picker.mouseReleaseEvent(0);
|
||||
} else if (_pickerSel >= 0) {
|
||||
int tab = (_pickerSel / emojiTabShift), sel = _pickerSel % emojiTabShift;
|
||||
int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift;
|
||||
if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) {
|
||||
if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) {
|
||||
_picker.hideStart();
|
||||
@@ -860,11 +860,11 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selected >= emojiTabCount * emojiTabShift) {
|
||||
if (_selected >= emojiTabCount * MatrixRowShift) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tab = (_selected / emojiTabShift), sel = _selected % emojiTabShift;
|
||||
int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift;
|
||||
if (sel < _emojis[tab].size()) {
|
||||
EmojiPtr emoji(_emojis[tab][sel]);
|
||||
if (emoji->color && !_picker.isHidden()) return;
|
||||
@@ -917,7 +917,7 @@ void EmojiPanInner::onSaveConfig() {
|
||||
}
|
||||
|
||||
void EmojiPanInner::onShowPicker() {
|
||||
int tab = (_pickerSel / emojiTabShift), sel = _pickerSel % emojiTabShift;
|
||||
int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift;
|
||||
if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) {
|
||||
int32 y = 0;
|
||||
for (int c = 0; c <= tab; ++c) {
|
||||
@@ -952,7 +952,7 @@ void EmojiPanInner::onColorSelected(EmojiPtr emoji) {
|
||||
cRefEmojiVariants().insert(emoji->code, emojiKey(emoji));
|
||||
}
|
||||
if (_pickerSel >= 0) {
|
||||
int tab = (_pickerSel / emojiTabShift), sel = _pickerSel % emojiTabShift;
|
||||
int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift;
|
||||
if (tab >= 0 && tab < emojiTabCount) {
|
||||
_emojis[tab][sel] = emoji;
|
||||
update();
|
||||
@@ -991,7 +991,7 @@ void EmojiPanInner::clearSelection(bool fast) {
|
||||
_lastMousePos = mapToGlobal(QPoint(-10, -10));
|
||||
if (fast) {
|
||||
for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) {
|
||||
int index = qAbs(i.key()) - 1, tab = (index / emojiTabShift), sel = index % emojiTabShift;
|
||||
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
|
||||
(index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = 0;
|
||||
}
|
||||
_animations.clear();
|
||||
@@ -1055,7 +1055,7 @@ void EmojiPanInner::updateSelected() {
|
||||
if (selIndex >= _emojis[c].size()) {
|
||||
selIndex = -1;
|
||||
} else {
|
||||
selIndex += c * emojiTabShift;
|
||||
selIndex += c * MatrixRowShift;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1098,7 +1098,7 @@ void EmojiPanInner::updateSelected() {
|
||||
bool EmojiPanInner::animStep(float64 ms) {
|
||||
uint64 now = getms();
|
||||
for (Animations::iterator i = _animations.begin(); i != _animations.end();) {
|
||||
int index = qAbs(i.key()) - 1, tab = (index / emojiTabShift), sel = index % emojiTabShift;
|
||||
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
|
||||
float64 dt = float64(now - i.value()) / st::emojiPanDuration;
|
||||
if (dt >= 1) {
|
||||
(index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = (i.key() > 0) ? 1 : 0;
|
||||
@@ -1293,11 +1293,11 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
emit switchToEmoji();
|
||||
return;
|
||||
}
|
||||
if (_selected >= emojiTabShift * _setIds.size()) {
|
||||
if (_selected >= MatrixRowShift * _setIds.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tab = (_selected / emojiTabShift), sel = _selected % emojiTabShift;
|
||||
int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift;
|
||||
if (_setIds[tab] == RecentStickerSetId && sel >= _sets[tab].size() && sel < _sets[tab].size() * 2 && _custom.at(sel - _sets[tab].size())) {
|
||||
clearSelection(true);
|
||||
bool refresh = false;
|
||||
@@ -1362,7 +1362,7 @@ void StickerPanInner::clearSelection(bool fast) {
|
||||
_lastMousePos = mapToGlobal(QPoint(-10, -10));
|
||||
if (fast) {
|
||||
for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) {
|
||||
int index = qAbs(i.key()) - 1, tab = (index / emojiTabShift), sel = index % emojiTabShift;
|
||||
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
|
||||
(index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = 0;
|
||||
}
|
||||
_animations.clear();
|
||||
@@ -1554,7 +1554,7 @@ void StickerPanInner::updateSelected() {
|
||||
ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height();
|
||||
if (p.y() >= y && p.y() < ytill) {
|
||||
if (!special && p.y() >= y && p.y() < y + st::emojiPanHeader && sx + st::stickerPanPadding >= width() - st::emojiPanHeaderLeft - st::notifyClose.icon.pxWidth() && sx + st::stickerPanPadding < width() - st::emojiPanHeaderLeft) {
|
||||
selIndex = c * emojiTabShift + _sets[c].size();
|
||||
selIndex = c * MatrixRowShift + _sets[c].size();
|
||||
} else {
|
||||
y += st::emojiPanHeader;
|
||||
if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) {
|
||||
@@ -1568,7 +1568,7 @@ void StickerPanInner::updateSelected() {
|
||||
selIndex += _sets[c].size();
|
||||
}
|
||||
}
|
||||
selIndex += c * emojiTabShift;
|
||||
selIndex += c * MatrixRowShift;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1578,12 +1578,12 @@ void StickerPanInner::updateSelected() {
|
||||
}
|
||||
|
||||
bool startanim = false;
|
||||
int oldSel = _selected, oldSelTab = oldSel / emojiTabShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / emojiTabShift, xNewSel = -1;
|
||||
if (oldSel >= 0 && oldSelTab < _setIds.size() && _setIds[oldSelTab] == RecentStickerSetId && oldSel >= oldSelTab * emojiTabShift + _sets[oldSelTab].size()) {
|
||||
int oldSel = _selected, oldSelTab = oldSel / MatrixRowShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / MatrixRowShift, xNewSel = -1;
|
||||
if (oldSel >= 0 && oldSelTab < _setIds.size() && _setIds[oldSelTab] == RecentStickerSetId && oldSel >= oldSelTab * MatrixRowShift + _sets[oldSelTab].size()) {
|
||||
xOldSel = oldSel;
|
||||
oldSel -= _sets[oldSelTab].size();
|
||||
}
|
||||
if (newSel >= 0 && newSelTab < _setIds.size() && _setIds[newSelTab] == RecentStickerSetId && newSel >= newSelTab * emojiTabShift + _sets[newSelTab].size()) {
|
||||
if (newSel >= 0 && newSelTab < _setIds.size() && _setIds[newSelTab] == RecentStickerSetId && newSel >= newSelTab * MatrixRowShift + _sets[newSelTab].size()) {
|
||||
xNewSel = newSel;
|
||||
newSel -= _sets[newSelTab].size();
|
||||
}
|
||||
@@ -1627,7 +1627,7 @@ void StickerPanInner::updateSelected() {
|
||||
bool StickerPanInner::animStep(float64 ms) {
|
||||
uint64 now = getms();
|
||||
for (Animations::iterator i = _animations.begin(); i != _animations.end();) {
|
||||
int index = qAbs(i.key()) - 1, tab = (index / emojiTabShift), sel = index % emojiTabShift;
|
||||
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
|
||||
float64 dt = float64(now - i.value()) / st::emojiPanDuration;
|
||||
if (dt >= 1) {
|
||||
(index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = (i.key() > 0) ? 1 : 0;
|
||||
@@ -2403,7 +2403,7 @@ void EmojiPan::onDelayedHide() {
|
||||
_removingSetId = 0;
|
||||
}
|
||||
|
||||
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows) : _parent(parent), _rows(rows), _hrows(hrows), _sel(-1), _mouseSel(false), _overDelete(false) {
|
||||
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows, BotCommandRows *crows) : _parent(parent), _rows(rows), _hrows(hrows), _crows(crows), _sel(-1), _mouseSel(false), _overDelete(false) {
|
||||
}
|
||||
|
||||
void MentionsInner::paintEvent(QPaintEvent *e) {
|
||||
@@ -2411,30 +2411,28 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
int32 atwidth = st::mentionFont->m.width('@'), hashwidth = st::mentionFont->m.width('#');
|
||||
int32 availwidth = width() - 2 * st::mentionPadding.left() - st::mentionPhotoSize - 2 * st::mentionPadding.right();
|
||||
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::dlgShadow, htagwidth = width() - st::mentionPadding.right() - htagleft;
|
||||
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::dlgShadow, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
|
||||
|
||||
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1, last = _rows->isEmpty() ? _hrows->size() : _rows->size();
|
||||
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
|
||||
int32 last = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size();
|
||||
bool hasUsername = _parent->filter().indexOf('@') > 1;
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
if (i >= last) break;
|
||||
|
||||
if (i == _sel) {
|
||||
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::dlgHoverBG->b);
|
||||
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
|
||||
if (_rows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
|
||||
if (!_hrows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
|
||||
}
|
||||
p.setPen(st::black->p);
|
||||
if (_rows->isEmpty()) {
|
||||
QString tag = st::mentionFont->m.elidedText('#' + _hrows->at(last - i - 1), Qt::ElideRight, htagwidth);
|
||||
p.setFont(st::mentionFont->f);
|
||||
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, tag);
|
||||
} else {
|
||||
if (!_rows->isEmpty()) {
|
||||
UserData *user = _rows->at(last - i - 1);
|
||||
QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
|
||||
int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
|
||||
if (availwidth < unamewidth + namewidth) {
|
||||
namewidth = (availwidth * namewidth) / (namewidth + unamewidth);
|
||||
unamewidth = availwidth - namewidth;
|
||||
if (firstwidth <= unamewidth) {
|
||||
if (firstwidth < unamewidth + st::mentionFont->elidew) {
|
||||
if (firstwidth < unamewidth) {
|
||||
first = st::mentionFont->m.elidedText(first, Qt::ElideRight, unamewidth);
|
||||
} else if (!second.isEmpty()) {
|
||||
@@ -2448,14 +2446,96 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
|
||||
user->photo->load();
|
||||
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
|
||||
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
|
||||
|
||||
p.setFont(st::mentionFont->f);
|
||||
|
||||
p.setPen(st::profileOnlineColor->p);
|
||||
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
|
||||
if (!second.isEmpty()) {
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
|
||||
}
|
||||
} else if (!_hrows->isEmpty()) {
|
||||
QString first = (_parent->filter().size() < 2) ? QString() : ('#' + _hrows->at(last - i - 1).mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('#' + _hrows->at(last - i - 1)) : _hrows->at(last - i - 1).mid(_parent->filter().size() - 1);
|
||||
int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second);
|
||||
if (htagwidth < firstwidth + secondwidth) {
|
||||
if (htagwidth < firstwidth + st::mentionFont->elidew) {
|
||||
first = st::mentionFont->m.elidedText(first + second, Qt::ElideRight, htagwidth);
|
||||
second = QString();
|
||||
} else {
|
||||
second = st::mentionFont->m.elidedText(second, Qt::ElideRight, htagwidth - firstwidth);
|
||||
}
|
||||
}
|
||||
|
||||
p.setFont(st::mentionFont->f);
|
||||
if (!first.isEmpty()) {
|
||||
p.setPen(st::profileOnlineColor->p);
|
||||
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
|
||||
}
|
||||
if (!second.isEmpty()) {
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
|
||||
}
|
||||
} else {
|
||||
UserData *user = _crows->at(last - i - 1).first;
|
||||
|
||||
const BotCommand &command = _crows->at(last - i - 1).second;
|
||||
QString toHighlight = command.command;
|
||||
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : -1;
|
||||
if (hasUsername || botStatus == 0 || botStatus == 2) {
|
||||
toHighlight += '@' + user->username;
|
||||
}
|
||||
if (_parent->chat() || botStatus == 0 || botStatus == 2) {
|
||||
user->photo->load();
|
||||
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
|
||||
}
|
||||
|
||||
int32 addleft = 0, widthleft = htagwidth;
|
||||
QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1);
|
||||
int32 firstwidth = st::botCommandFont->m.width(first), secondwidth = st::botCommandFont->m.width(second);
|
||||
if (htagwidth < firstwidth + secondwidth) {
|
||||
if (htagwidth < firstwidth + st::botCommandFont->elidew) {
|
||||
first = st::botCommandFont->m.elidedText(first + second, Qt::ElideRight, htagwidth);
|
||||
second = QString();
|
||||
} else {
|
||||
second = st::botCommandFont->m.elidedText(second, Qt::ElideRight, htagwidth - firstwidth);
|
||||
}
|
||||
}
|
||||
p.setFont(st::botCommandFont->f);
|
||||
if (!first.isEmpty()) {
|
||||
p.setPen(st::profileOnlineColor->p);
|
||||
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
|
||||
}
|
||||
if (!second.isEmpty()) {
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
|
||||
}
|
||||
addleft += firstwidth + secondwidth + st::mentionPadding.left();
|
||||
widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
|
||||
|
||||
QString params = command.params;
|
||||
if (widthleft > st::mentionFont->elidew && !params.isEmpty()) {
|
||||
p.setFont(st::mentionFont->f);
|
||||
int32 paramswidth = st::mentionFont->m.width(params);
|
||||
if (widthleft < paramswidth) {
|
||||
params = st::mentionFont->m.elidedText(params, Qt::ElideRight, widthleft);
|
||||
}
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
p.drawText(htagleft + addleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, params);
|
||||
|
||||
addleft += paramswidth + st::mentionPadding.left();
|
||||
widthleft -= paramswidth + st::mentionPadding.left();
|
||||
}
|
||||
QString description = command.description;
|
||||
if (widthleft > st::botDescFont->elidew && !description.isEmpty()) {
|
||||
p.setFont(st::botDescFont->f);
|
||||
int32 descwidth = st::botDescFont->m.width(description);
|
||||
if (widthleft < descwidth) {
|
||||
description = st::botDescFont->m.elidedText(description, Qt::ElideRight, widthleft);
|
||||
descwidth = st::botDescFont->m.width(description);
|
||||
}
|
||||
p.setPen(st::profileOfflineColor->p);
|
||||
p.drawText(htagleft + addleft + (widthleft - descwidth), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2476,7 +2556,7 @@ void MentionsInner::clearSel() {
|
||||
|
||||
bool MentionsInner::moveSel(int direction) {
|
||||
_mouseSel = false;
|
||||
int32 maxSel = (_rows->isEmpty() ? _hrows->size() : _rows->size());
|
||||
int32 maxSel = (_rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size());
|
||||
if (_sel >= maxSel || _sel < 0) {
|
||||
if (direction < 0) setSel(maxSel - 1, true);
|
||||
return (_sel >= 0 && _sel < maxSel);
|
||||
@@ -2488,9 +2568,23 @@ bool MentionsInner::moveSel(int direction) {
|
||||
}
|
||||
|
||||
bool MentionsInner::select() {
|
||||
int32 maxSel = (_rows->isEmpty() ? _hrows->size() : _rows->size());
|
||||
int32 maxSel = (_rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size());
|
||||
if (_sel >= 0 && _sel < maxSel) {
|
||||
QString result = _rows->isEmpty() ? ('#' + _hrows->at(_hrows->size() - _sel - 1)) : ('@' + _rows->at(_rows->size() - _sel - 1)->username);
|
||||
QString result;
|
||||
if (!_rows->isEmpty()) {
|
||||
result = '@' + _rows->at(_rows->size() - _sel - 1)->username;
|
||||
} else if (!_hrows->isEmpty()) {
|
||||
result = '#' + _hrows->at(_hrows->size() - _sel - 1);
|
||||
} else {
|
||||
UserData *user = _crows->at(_crows->size() - _sel - 1).first;
|
||||
const BotCommand &command(_crows->at(_crows->size() - _sel - 1).second);
|
||||
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : -1;
|
||||
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 1) {
|
||||
result = '/' + command.command + '@' + user->username;
|
||||
} else {
|
||||
result = '/' + command.command;
|
||||
}
|
||||
}
|
||||
emit chosen(result);
|
||||
return true;
|
||||
}
|
||||
@@ -2542,7 +2636,7 @@ void MentionsInner::leaveEvent(QEvent *e) {
|
||||
void MentionsInner::setSel(int sel, bool scroll) {
|
||||
_sel = sel;
|
||||
parentWidget()->update();
|
||||
int32 maxSel = _rows->isEmpty() ? _hrows->size() : _rows->size();
|
||||
int32 maxSel = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size();
|
||||
if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
|
||||
}
|
||||
|
||||
@@ -2552,7 +2646,7 @@ void MentionsInner::onUpdateSelected(bool force) {
|
||||
|
||||
int w = width(), mouseY = mouse.y();
|
||||
_overDelete = _rows->isEmpty() && (mouse.x() >= w - st::mentionHeight);
|
||||
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _rows->isEmpty() ? _hrows->size() : _rows->size();
|
||||
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size();
|
||||
if (sel < 0 || sel >= maxSel) {
|
||||
sel = -1;
|
||||
}
|
||||
@@ -2570,7 +2664,7 @@ void MentionsInner::onParentGeometryChanged() {
|
||||
}
|
||||
|
||||
MentionsDropdown::MentionsDropdown(QWidget *parent) : QWidget(parent),
|
||||
_scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows), _chat(0), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow) {
|
||||
_scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows, &_crows), _chat(0), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow) {
|
||||
_hideTimer.setSingleShot(true);
|
||||
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
|
||||
connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString)));
|
||||
@@ -2608,8 +2702,9 @@ void MentionsDropdown::paintEvent(QPaintEvent *e) {
|
||||
|
||||
}
|
||||
|
||||
void MentionsDropdown::showFiltered(ChatData *chat, QString start) {
|
||||
_chat = chat;
|
||||
void MentionsDropdown::showFiltered(PeerData *peer, QString start) {
|
||||
_chat = peer->chat ? peer->asChat() : 0;
|
||||
_user = peer->chat ? 0 : peer->asUser();
|
||||
start = start.toLower();
|
||||
bool toDown = (_filter != start);
|
||||
if (toDown) {
|
||||
@@ -2621,10 +2716,11 @@ void MentionsDropdown::showFiltered(ChatData *chat, QString start) {
|
||||
|
||||
void MentionsDropdown::updateFiltered(bool toDown) {
|
||||
int32 now = unixtime();
|
||||
QMultiMap<int32, UserData*> ordered;
|
||||
MentionRows rows;
|
||||
HashtagRows hrows;
|
||||
BotCommandRows crows;
|
||||
if (_filter.at(0) == '@') {
|
||||
QMultiMap<int32, UserData*> ordered;
|
||||
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
|
||||
if (_chat->participants.isEmpty()) {
|
||||
if (_chat->count > 0) {
|
||||
@@ -2635,7 +2731,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
||||
UserData *user = i.key();
|
||||
if (user->username.isEmpty()) continue;
|
||||
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||
ordered.insertMulti(App::onlineForSort(user->onlineTill, now), user);
|
||||
ordered.insertMulti(App::onlineForSort(user, now), user);
|
||||
}
|
||||
}
|
||||
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
|
||||
@@ -2644,7 +2740,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
||||
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||
rows.push_back(user);
|
||||
if (!ordered.isEmpty()) {
|
||||
ordered.remove(App::onlineForSort(user->onlineTill, now), user);
|
||||
ordered.remove(App::onlineForSort(user, now), user);
|
||||
}
|
||||
}
|
||||
if (!ordered.isEmpty()) {
|
||||
@@ -2653,23 +2749,79 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
||||
rows.push_back(i.value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (_filter.at(0) == '#') {
|
||||
const RecentHashtagPack &recent(cRecentWriteHashtags());
|
||||
hrows.reserve(recent.size());
|
||||
for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
|
||||
if (_filter.size() > 1 && (!i->first.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || i->first.size() + 1 == _filter.size())) continue;
|
||||
hrows.push_back(i->first);
|
||||
}
|
||||
} else if (_filter.at(0) == '/') {
|
||||
bool hasUsername = _filter.indexOf('@') > 1;
|
||||
QMap<UserData*, bool> bots;
|
||||
int32 cnt = 0;
|
||||
if (_chat) {
|
||||
if (_chat->participants.isEmpty()) {
|
||||
if (_chat->count > 0) {
|
||||
App::api()->requestFullPeer(_chat);
|
||||
}
|
||||
} else {
|
||||
int32 index = 0;
|
||||
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
|
||||
UserData *user = i.key();
|
||||
if (!user->botInfo || user->botInfo->commands.isEmpty()) continue;
|
||||
bots.insert(user, true);
|
||||
cnt += user->botInfo->commands.size();
|
||||
}
|
||||
}
|
||||
} else if (_user->botInfo) {
|
||||
cnt = _user->botInfo->commands.size();
|
||||
bots.insert(_user, true);
|
||||
}
|
||||
if (cnt) {
|
||||
crows.reserve(cnt);
|
||||
int32 botStatus = _chat ? _chat->botStatus : -1;
|
||||
if (_chat) {
|
||||
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
|
||||
UserData *user = *i;
|
||||
if (!user->botInfo || user->botInfo->commands.isEmpty()) continue;
|
||||
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
|
||||
if (_filter.size() > 1) {
|
||||
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
|
||||
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || toFilter.size() + 1 == _filter.size()) continue;
|
||||
}
|
||||
crows.push_back(qMakePair(user, user->botInfo->commands.at(j)));
|
||||
}
|
||||
if (!bots.isEmpty()) {
|
||||
bots.remove(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bots.isEmpty()) {
|
||||
for (QMap<UserData*, bool>::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
|
||||
UserData *user = i.key();
|
||||
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
|
||||
if (_filter.size() > 1) {
|
||||
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
|
||||
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || toFilter.size() + 1 == _filter.size()) continue;
|
||||
}
|
||||
crows.push_back(qMakePair(user, user->botInfo->commands.at(j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rows.isEmpty() && hrows.isEmpty()) {
|
||||
if (rows.isEmpty() && hrows.isEmpty() && crows.isEmpty()) {
|
||||
if (!isHidden()) {
|
||||
hideStart();
|
||||
_rows.clear();
|
||||
_hrows.clear();
|
||||
_crows.clear();
|
||||
}
|
||||
} else {
|
||||
_rows = rows;
|
||||
_hrows = hrows;
|
||||
_crows = crows;
|
||||
bool hidden = _hiding || isHidden();
|
||||
if (hidden) {
|
||||
show();
|
||||
@@ -2692,7 +2844,7 @@ void MentionsDropdown::setBoundings(QRect boundings) {
|
||||
}
|
||||
|
||||
void MentionsDropdown::recount(bool toDown) {
|
||||
int32 h = (_rows.isEmpty() ? _hrows.size() : _rows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
|
||||
int32 h = (_rows.isEmpty() ? (_hrows.isEmpty() ? _crows.size() : _hrows.size()) : _rows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
|
||||
|
||||
if (_inner.height() != h) {
|
||||
st += h - _inner.height();
|
||||
@@ -2780,6 +2932,10 @@ const QString &MentionsDropdown::filter() const {
|
||||
return _filter;
|
||||
}
|
||||
|
||||
ChatData *MentionsDropdown::chat() const {
|
||||
return _chat;
|
||||
}
|
||||
|
||||
int32 MentionsDropdown::innerTop() {
|
||||
return _scroll.scrollTop();
|
||||
}
|
||||
|
||||
@@ -467,6 +467,7 @@ private:
|
||||
|
||||
typedef QList<UserData*> MentionRows;
|
||||
typedef QList<QString> HashtagRows;
|
||||
typedef QList<QPair<UserData*, BotCommand> > BotCommandRows;
|
||||
|
||||
class MentionsDropdown;
|
||||
class MentionsInner : public QWidget {
|
||||
@@ -474,7 +475,7 @@ class MentionsInner : public QWidget {
|
||||
|
||||
public:
|
||||
|
||||
MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows);
|
||||
MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows, BotCommandRows *crows);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
@@ -505,6 +506,7 @@ private:
|
||||
MentionsDropdown *_parent;
|
||||
MentionRows *_rows;
|
||||
HashtagRows *_hrows;
|
||||
BotCommandRows *_crows;
|
||||
int32 _sel;
|
||||
bool _mouseSel;
|
||||
QPoint _mousePos;
|
||||
@@ -523,13 +525,14 @@ public:
|
||||
|
||||
void fastHide();
|
||||
|
||||
void showFiltered(ChatData *chat, QString start);
|
||||
void showFiltered(PeerData *peer, QString start);
|
||||
void updateFiltered(bool toDown = false);
|
||||
void setBoundings(QRect boundings);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
const QString &filter() const;
|
||||
ChatData *chat() const;
|
||||
|
||||
int32 innerTop();
|
||||
int32 innerBottom();
|
||||
@@ -556,11 +559,13 @@ private:
|
||||
QPixmap _cache;
|
||||
MentionRows _rows;
|
||||
HashtagRows _hrows;
|
||||
BotCommandRows _crows;
|
||||
|
||||
ScrollArea _scroll;
|
||||
MentionsInner _inner;
|
||||
|
||||
ChatData *_chat;
|
||||
UserData *_user;
|
||||
QString _filter;
|
||||
QRect _boundings;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent),
|
||||
_text(text),
|
||||
_st(st),
|
||||
_st(st), _autoFontPadding(0),
|
||||
a_bg(st.bgColor->c), a_text(st.color->c), _opacity(1) {
|
||||
if (_st.width < 0) {
|
||||
_st.width = textWidth() - _st.width;
|
||||
@@ -52,10 +52,35 @@ void FlatButton::setWidth(int32 w) {
|
||||
resize(_st.width, height());
|
||||
}
|
||||
|
||||
void FlatButton::setAutoFontSize(int32 padding, const QString &txt) {
|
||||
_autoFontPadding = padding;
|
||||
if (_autoFontPadding) {
|
||||
_textForAutoSize = txt;
|
||||
resizeEvent(0);
|
||||
} else {
|
||||
_textForAutoSize = QString();
|
||||
_autoFont = style::font();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
int32 FlatButton::textWidth() const {
|
||||
return _st.font->m.width(_text);
|
||||
}
|
||||
|
||||
void FlatButton::resizeEvent(QResizeEvent *e) {
|
||||
if (_autoFontPadding) {
|
||||
_autoFont = _st.font;
|
||||
for (int32 s = _st.font->f.pixelSize(); s >= st::fsize; --s) {
|
||||
_autoFont = style::font(s, _st.font->flags(), _st.font->family());
|
||||
if (2 * _autoFontPadding + _autoFont->m.width(_textForAutoSize) <= width()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Button::resizeEvent(e);
|
||||
}
|
||||
|
||||
bool FlatButton::animStep(float64 ms) {
|
||||
float64 dt = ms / _st.duration;
|
||||
bool res = true;
|
||||
@@ -95,12 +120,15 @@ void FlatButton::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(_opacity);
|
||||
p.fillRect(r, a_bg.current());
|
||||
|
||||
p.setFont(((_state & StateOver) ? _st.overFont : _st.font)->f);
|
||||
p.setFont((_autoFont ? _autoFont : ((_state & StateOver) ? _st.overFont : _st.font))->f);
|
||||
p.setRenderHint(QPainter::TextAntialiasing);
|
||||
p.setPen(a_text.current());
|
||||
|
||||
r.setTop((_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop);
|
||||
p.drawText(r, _text, QTextOption(Qt::AlignHCenter));
|
||||
int32 top = (_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop;
|
||||
if (_autoFont) top += (_st.font->height - _autoFont->height) / 2;
|
||||
r.setTop(top);
|
||||
|
||||
p.drawText(r, _text, style::al_top);
|
||||
}
|
||||
|
||||
BottomButton::BottomButton(QWidget *w, const QString &t, const style::flatButton &s) : FlatButton(w, t, s) {
|
||||
|
||||
@@ -29,12 +29,15 @@ public:
|
||||
|
||||
FlatButton(QWidget *parent, const QString &text, const style::flatButton &st);
|
||||
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void setOpacity(float64 o);
|
||||
|
||||
void setText(const QString &text);
|
||||
void setWidth(int32 w);
|
||||
void setAutoFontSize(int32 padding, const QString &txt);
|
||||
|
||||
int32 textWidth() const;
|
||||
|
||||
@@ -47,12 +50,15 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
QString _text;
|
||||
QString _text, _textForAutoSize;
|
||||
int32 _textWidth;
|
||||
|
||||
style::flatButton _st;
|
||||
anim::cvalue a_bg, a_text;
|
||||
|
||||
int32 _autoFontPadding;
|
||||
style::font _autoFont;
|
||||
|
||||
anim::cvalue a_bg, a_text;
|
||||
float64 _opacity;
|
||||
};
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FlatTextarea::getMentionHashtagStart(QString &start) const {
|
||||
void FlatTextarea::getMentionHashtagBotCommandStart(QString &start) const {
|
||||
int32 pos = textCursor().position();
|
||||
if (textCursor().anchor() != pos) return;
|
||||
|
||||
@@ -195,11 +195,16 @@ void FlatTextarea::getMentionHashtagStart(QString &start) const {
|
||||
QTextCharFormat f = fr.charFormat();
|
||||
if (f.isImageFormat()) continue;
|
||||
|
||||
bool mentionInCommand = false;
|
||||
QString t(fr.text());
|
||||
for (int i = pos - p; i > 0; --i) {
|
||||
if (t.at(i - 1) == '@') {
|
||||
if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
||||
start = t.mid(i - 1, pos - p - i + 1);
|
||||
} else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
|
||||
mentionInCommand = true;
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
} else if (t.at(i - 1) == '#') {
|
||||
@@ -207,15 +212,20 @@ void FlatTextarea::getMentionHashtagStart(QString &start) const {
|
||||
start = t.mid(i - 1, pos - p - i + 1);
|
||||
}
|
||||
return;
|
||||
} else if (t.at(i - 1) == '/') {
|
||||
if (i < 2) {
|
||||
start = t.mid(i - 1, pos - p - i + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (pos - p - i > 63) break;
|
||||
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break;
|
||||
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FlatTextarea::onMentionOrHashtagInsert(QString mentionOrHashtag) {
|
||||
void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) {
|
||||
QTextCursor c(textCursor());
|
||||
int32 pos = c.position();
|
||||
|
||||
@@ -231,31 +241,37 @@ void FlatTextarea::onMentionOrHashtagInsert(QString mentionOrHashtag) {
|
||||
QTextCharFormat f = fr.charFormat();
|
||||
if (f.isImageFormat()) continue;
|
||||
|
||||
bool mentionInCommand = false;
|
||||
QString t(fr.text());
|
||||
for (int i = pos - p; i > 0; --i) {
|
||||
if (t.at(i - 1) == '@' || t.at(i - 1) == '#') {
|
||||
if (t.at(i - 1) == '@' || t.at(i - 1) == '#' || t.at(i - 1) == '/') {
|
||||
if ((i == pos - p || t.at(i).isLetter() || t.at(i - 1) == '#') && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
||||
c.setPosition(p + i - 1, QTextCursor::MoveAnchor);
|
||||
int till = p + i;
|
||||
for (; (till < e) && (till - p - i + 1 < mentionOrHashtag.size()); ++till) {
|
||||
if (t.at(till - p).toLower() != mentionOrHashtag.at(till - p - i + 1).toLower()) {
|
||||
for (; (till < e) && (till - p - i + 1 < str.size()); ++till) {
|
||||
if (t.at(till - p).toLower() != str.at(till - p - i + 1).toLower()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (till - p - i + 1 == mentionOrHashtag.size() && till < e && t.at(till - p) == ' ') {
|
||||
if (till - p - i + 1 == str.size() && till < e && t.at(till - p) == ' ') {
|
||||
++till;
|
||||
}
|
||||
c.setPosition(till, QTextCursor::KeepAnchor);
|
||||
c.insertText(mentionOrHashtag + ' ');
|
||||
c.insertText(str + ' ');
|
||||
return;
|
||||
} else if ((i == pos - p || t.at(i).isLetter()) && t.at(i - 1) == '@' && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
|
||||
mentionInCommand = true;
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (pos - p - i > 63) break;
|
||||
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break;
|
||||
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
c.insertText(mentionOrHashtag + ' ');
|
||||
c.insertText(str + ' ');
|
||||
}
|
||||
|
||||
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
QSize minimumSizeHint() const;
|
||||
|
||||
EmojiPtr getSingleEmoji() const;
|
||||
void getMentionHashtagStart(QString &start) const;
|
||||
void getMentionHashtagBotCommandStart(QString &start) const;
|
||||
void removeSingleEmoji();
|
||||
QString getText(int32 start = 0, int32 end = -1) const;
|
||||
bool hasText() const;
|
||||
@@ -72,7 +72,7 @@ public slots:
|
||||
void onUndoAvailable(bool avail);
|
||||
void onRedoAvailable(bool avail);
|
||||
|
||||
void onMentionOrHashtagInsert(QString mentionOrHashtag);
|
||||
void onMentionHashtagOrBotCommandInsert(QString str);
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
@@ -661,3 +661,7 @@ void ScrollArea::updateColors(const style::color &bar, const style::color &bg, c
|
||||
hor.update();
|
||||
vert.update();
|
||||
}
|
||||
|
||||
ScrollArea::~ScrollArea() {
|
||||
takeWidget();
|
||||
}
|
||||
|
||||
@@ -131,6 +131,8 @@ public:
|
||||
|
||||
void updateColors(const style::color &bar, const style::color &bg, const style::color &barOver, const style::color &bgOver);
|
||||
|
||||
~ScrollArea();
|
||||
|
||||
public slots:
|
||||
|
||||
void scrollToY(int toTop, int toBottom = -1);
|
||||
|
||||
@@ -61,6 +61,10 @@ namespace style {
|
||||
return _flags;
|
||||
}
|
||||
|
||||
uint32 FontData::family() const {
|
||||
return _family;
|
||||
}
|
||||
|
||||
Font FontData::otherFlagsFont(uint32 flag, bool set) const {
|
||||
int32 newFlags = set ? (_flags | flag) : (_flags & ~flag);
|
||||
if (!modified[newFlags].v()) {
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace style {
|
||||
Font underline(bool set = true) const;
|
||||
|
||||
uint32 flags() const;
|
||||
uint32 family() const;
|
||||
|
||||
QFont f;
|
||||
QFontMetrics m;
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace {
|
||||
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
|
||||
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"));
|
||||
QSet<int32> _validProtocols, _validTopDomains;
|
||||
|
||||
const style::textStyle *_textStyle = 0;
|
||||
@@ -61,6 +62,10 @@ const QRegularExpression &reHashtag() {
|
||||
return _reHashtag;
|
||||
}
|
||||
|
||||
const QRegularExpression &reBotCommand() {
|
||||
return _reBotCommand;
|
||||
}
|
||||
|
||||
const style::textStyle *textstyleCurrent() {
|
||||
return _textStyle;
|
||||
}
|
||||
@@ -302,7 +307,10 @@ public:
|
||||
}
|
||||
|
||||
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
|
||||
if (!original.isEmpty() && original.at(0) == '@') {
|
||||
if (!original.isEmpty() && original.at(0) == '/') {
|
||||
result = original;
|
||||
fullDisplayed = -4; // bot command
|
||||
} else if (!original.isEmpty() && original.at(0) == '@') {
|
||||
result = original;
|
||||
fullDisplayed = -3; // mention
|
||||
} else if (!original.isEmpty() && original.at(0) == '#') {
|
||||
@@ -567,7 +575,9 @@ public:
|
||||
_t->_links.resize(lnkIndex);
|
||||
const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]);
|
||||
TextLinkPtr lnk;
|
||||
if (data.fullDisplayed < -2) { // mention
|
||||
if (data.fullDisplayed < -3) { // bot command
|
||||
lnk = TextLinkPtr(new BotCommandLink(data.url));
|
||||
} else if (data.fullDisplayed < -2) { // mention
|
||||
if (options.flags & TextTwitterMentions) {
|
||||
lnk = TextLinkPtr(new TextLink(qsl("https://twitter.com/") + data.url.mid(1), true));
|
||||
} else if (options.flags & TextInstagramMentions) {
|
||||
@@ -612,7 +622,7 @@ private:
|
||||
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
|
||||
}
|
||||
QString url;
|
||||
int32 fullDisplayed; // -3 - mention, -2 - hashtag, -1 - email
|
||||
int32 fullDisplayed; // -4 - bot command, -3 - mention, -2 - hashtag, -1 - email
|
||||
};
|
||||
typedef QVector<TextLinkData> TextLinks;
|
||||
TextLinks links;
|
||||
@@ -738,7 +748,15 @@ void TextLink::onClick(Qt::MouseButton button) const {
|
||||
QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
if (telegramMeUser.hasMatch()) {
|
||||
App::openUserByName(telegramMeUser.captured(1));
|
||||
QString params = url.mid(telegramMeUser.captured(0).size()), start, startToken;
|
||||
if (!params.isEmpty()) {
|
||||
QRegularExpressionMatch startParams = QRegularExpression(qsl("(^|&)(start|startgroup)=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), QRegularExpression::CaseInsensitiveOption).match(params);
|
||||
if (startParams.hasMatch()) {
|
||||
start = startParams.captured(2);
|
||||
startToken = startParams.captured(3);
|
||||
}
|
||||
}
|
||||
App::openUserByName(telegramMeUser.captured(1), start == qsl("startgroup"), startToken);
|
||||
} else if (telegramMeGroup.hasMatch()) {
|
||||
App::joinGroupByHash(telegramMeGroup.captured(1));
|
||||
} else if (telegramMeStickers.hasMatch()) {
|
||||
@@ -763,6 +781,13 @@ void HashtagLink::onClick(Qt::MouseButton button) const {
|
||||
}
|
||||
}
|
||||
|
||||
void BotCommandLink::onClick(Qt::MouseButton button) const {
|
||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||
App::insertBotCommand(_cmd);
|
||||
// App::sendBotCommand(_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
class TextPainter {
|
||||
public:
|
||||
|
||||
@@ -1088,9 +1113,33 @@ public:
|
||||
if (_yTo >= 0 && _y + _yDelta >= _yTo) return false;
|
||||
if (_y + _yDelta + _fontHeight <= _yFrom) return true;
|
||||
|
||||
uint16 trimmedLineEnd = _lineEnd;
|
||||
for (; trimmedLineEnd > _lineStart; --trimmedLineEnd) {
|
||||
QChar ch = _t->_text.at(trimmedLineEnd - 1);
|
||||
if ((ch != QChar::Space || trimmedLineEnd == _lineStart + 1) && ch != QChar::LineFeed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ITextBlock *_endBlock = (_endBlockIter == _end) ? 0 : (*_endBlockIter);
|
||||
bool elidedLine = _elideLast && _endBlock && (_y + _lineHeight >= _yTo);
|
||||
|
||||
int blockIndex = _lineStartBlock;
|
||||
ITextBlock *currentBlock = _t->_blocks[blockIndex];
|
||||
ITextBlock *nextBlock = (++blockIndex < _blocksSize) ? _t->_blocks[blockIndex] : 0;
|
||||
|
||||
int32 delta = (currentBlock->from() < _lineStart ? qMin(_lineStart - currentBlock->from(), 2) : 0);
|
||||
_localFrom = _lineStart - delta;
|
||||
int32 lineEnd = (_endBlock && _endBlock->from() < trimmedLineEnd && !elidedLine) ? qMin(uint16(trimmedLineEnd + 2), _blockEnd(_t, _endBlockIter, _end)) : trimmedLineEnd;
|
||||
|
||||
QString lineText = _t->_text.mid(_localFrom, lineEnd - _localFrom);
|
||||
int32 lineStart = delta, lineLength = trimmedLineEnd - _lineStart;
|
||||
|
||||
if (elidedLine) {
|
||||
initParagraphBidi();
|
||||
prepareElidedLine(lineText, lineStart, lineLength, _endBlock);
|
||||
}
|
||||
|
||||
QFixed x = _x;
|
||||
if (_align & Qt::AlignHCenter) {
|
||||
x += (_wLeft / 2).toInt();
|
||||
@@ -1138,34 +1187,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/* // lpadding is counted to _wLeft
|
||||
for (; _lineStart < _lineEnd; ++_lineStart) {
|
||||
if (_t->_text.at(_lineStart) != QChar::Space) {
|
||||
break;
|
||||
}
|
||||
}/**/
|
||||
for (; _lineEnd > _lineStart; --_lineEnd) {
|
||||
QChar ch = _t->_text.at(_lineEnd - 1);
|
||||
if ((ch != QChar::Space || _lineEnd == _lineStart + 1) && ch != QChar::LineFeed) {
|
||||
break;
|
||||
}
|
||||
}/**/
|
||||
if (_lineEnd == _lineStart && !elidedLine) return true;
|
||||
if (trimmedLineEnd == _lineStart && !elidedLine) return true;
|
||||
|
||||
initParagraphBidi(); // if was not inited
|
||||
|
||||
int blockIndex = _lineStartBlock;
|
||||
ITextBlock *currentBlock = _t->_blocks[blockIndex];
|
||||
ITextBlock *nextBlock = (++blockIndex < _blocksSize) ? _t->_blocks[blockIndex] : 0;
|
||||
|
||||
int32 delta = (currentBlock->from() < _lineStart ? qMin(_lineStart - currentBlock->from(), 2) : 0);
|
||||
_localFrom = _lineStart - delta;
|
||||
int32 lineEnd = (_endBlock && _endBlock->from() < _lineEnd && !elidedLine) ? qMin(uint16(_lineEnd + 2), _blockEnd(_t, _endBlockIter, _end)) : _lineEnd;
|
||||
|
||||
QString lineText = _t->_text.mid(_localFrom, lineEnd - _localFrom);
|
||||
int32 lineStart = delta, lineLength = _lineEnd - _lineStart;
|
||||
|
||||
if (elidedLine) prepareElidedLine(lineText, lineStart, lineLength, _endBlock);
|
||||
if (!elidedLine) initParagraphBidi(); // if was not inited
|
||||
|
||||
_f = _t->_font;
|
||||
QStackTextEngine engine(lineText, _f->f);
|
||||
@@ -1202,7 +1226,7 @@ public:
|
||||
}
|
||||
if (si.analysis.flags == QScriptAnalysis::Object) {
|
||||
if (_type == TextBlockEmoji || _type == TextBlockSkip) {
|
||||
si.width = currentBlock->f_width() + (nextBlock == _endBlock && (!nextBlock || nextBlock->from() >= _lineEnd) ? 0 : currentBlock->f_rpadding());
|
||||
si.width = currentBlock->f_width() + (nextBlock == _endBlock && (!nextBlock || nextBlock->from() >= trimmedLineEnd) ? 0 : currentBlock->f_rpadding());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1250,8 +1274,8 @@ public:
|
||||
*_getSymbolAfter = false;
|
||||
*_getSymbolUpon = false;
|
||||
} else {
|
||||
*_getSymbol = (_lineEnd > _lineStart) ? (_lineEnd - 1) : _lineStart;
|
||||
*_getSymbolAfter = (_lineEnd > _lineStart) ? true : false;
|
||||
*_getSymbol = (trimmedLineEnd > _lineStart) ? (trimmedLineEnd - 1) : _lineStart;
|
||||
*_getSymbolAfter = (trimmedLineEnd > _lineStart) ? true : false;
|
||||
*_getSymbolUpon = false;
|
||||
}
|
||||
return false;
|
||||
@@ -4071,7 +4095,9 @@ bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
|
||||
LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp!
|
||||
LinkRanges lnkRanges;
|
||||
|
||||
bool withHashtags = (flags & TextParseHashtags), withMentions = (flags & TextParseMentions);
|
||||
bool withHashtags = (flags & TextParseHashtags);
|
||||
bool withMentions = (flags & TextParseMentions);
|
||||
bool withBotCommands = (flags & TextParseBotCommands);
|
||||
|
||||
initLinkSets();
|
||||
int32 len = text.size(), nextCmd = rich ? 0 : len;
|
||||
@@ -4088,6 +4114,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
|
||||
QRegularExpressionMatch mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
|
||||
LinkRange link;
|
||||
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
|
||||
@@ -4097,7 +4124,9 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
|
||||
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX,
|
||||
mentionOffset = mMention.hasMatch() ? mMention.capturedStart() : INT_MAX,
|
||||
mentionEnd = mMention.hasMatch() ? mMention.capturedEnd() : INT_MAX;
|
||||
mentionEnd = mMention.hasMatch() ? mMention.capturedEnd() : INT_MAX,
|
||||
botCommandOffset = mBotCommand.hasMatch() ? mBotCommand.capturedStart() : INT_MAX,
|
||||
botCommandEnd = mBotCommand.hasMatch() ? mBotCommand.capturedEnd() : INT_MAX;
|
||||
if (mHashtag.hasMatch()) {
|
||||
if (!mHashtag.capturedRef(1).isEmpty()) {
|
||||
++hashtagOffset;
|
||||
@@ -4127,14 +4156,24 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mMention.hasMatch() && !mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
if (mBotCommand.hasMatch()) {
|
||||
if (!mBotCommand.capturedRef(1).isEmpty()) {
|
||||
++botCommandOffset;
|
||||
}
|
||||
if (!mBotCommand.capturedRef(3).isEmpty()) {
|
||||
--botCommandEnd;
|
||||
}
|
||||
}
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch() && !mBotCommand.hasMatch()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (explicitDomainOffset < domainOffset) {
|
||||
domainOffset = explicitDomainOffset;
|
||||
domainEnd = explicitDomainEnd;
|
||||
mDomain = mExplicitDomain;
|
||||
}
|
||||
if (mentionOffset < hashtagOffset && mentionOffset < domainOffset) {
|
||||
if (mentionOffset < hashtagOffset && mentionOffset < domainOffset && mentionOffset < botCommandOffset) {
|
||||
if (mentionOffset > nextCmd) {
|
||||
const QChar *after = textSkipCommand(start + nextCmd, start + len);
|
||||
if (after > start + nextCmd && mentionOffset < (after - start)) {
|
||||
@@ -4145,7 +4184,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
|
||||
link.from = start + mentionOffset;
|
||||
link.len = start + mentionEnd - link.from;
|
||||
} else if (hashtagOffset < domainOffset) {
|
||||
} else if (hashtagOffset < domainOffset && hashtagOffset < botCommandOffset) {
|
||||
if (hashtagOffset > nextCmd) {
|
||||
const QChar *after = textSkipCommand(start + nextCmd, start + len);
|
||||
if (after > start + nextCmd && hashtagOffset < (after - start)) {
|
||||
@@ -4156,6 +4195,17 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
|
||||
link.from = start + hashtagOffset;
|
||||
link.len = start + hashtagEnd - link.from;
|
||||
} else if (botCommandOffset < domainOffset) {
|
||||
if (botCommandOffset > nextCmd) {
|
||||
const QChar *after = textSkipCommand(start + nextCmd, start + len);
|
||||
if (after > start + nextCmd && botCommandOffset < (after - start)) {
|
||||
nextCmd = offset = matchOffset = after - start;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
link.from = start + botCommandOffset;
|
||||
link.len = start + botCommandEnd - link.from;
|
||||
} else {
|
||||
if (domainOffset > nextCmd) {
|
||||
const QChar *after = textSkipCommand(start + nextCmd, start + len);
|
||||
|
||||
@@ -31,11 +31,12 @@ enum {
|
||||
TextParseRichText = 0x004,
|
||||
TextParseMentions = 0x008,
|
||||
TextParseHashtags = 0x010,
|
||||
TextParseBotCommands = 0x020,
|
||||
|
||||
TextTwitterMentions = 0x020,
|
||||
TextTwitterHashtags = 0x040,
|
||||
TextInstagramMentions = 0x080,
|
||||
TextInstagramHashtags = 0x100,
|
||||
TextTwitterMentions = 0x040,
|
||||
TextTwitterHashtags = 0x080,
|
||||
TextInstagramMentions = 0x100,
|
||||
TextInstagramHashtags = 0x200,
|
||||
};
|
||||
|
||||
struct LinkRange {
|
||||
@@ -385,6 +386,32 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class BotCommandLink : public ITextLink {
|
||||
public:
|
||||
|
||||
BotCommandLink(const QString &cmd) : _cmd(cmd) {
|
||||
}
|
||||
|
||||
const QString &text() const {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
void onClick(Qt::MouseButton button) const;
|
||||
|
||||
const QString &readable() const {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
QString encoded() const {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QString _cmd;
|
||||
|
||||
};
|
||||
|
||||
static const QChar TextCommand(0x0010);
|
||||
enum TextCommands {
|
||||
TextCommandBold = 0x01,
|
||||
@@ -408,8 +435,7 @@ struct TextParseOptions {
|
||||
int32 maxh;
|
||||
Qt::LayoutDirection dir;
|
||||
};
|
||||
extern const TextParseOptions _defaultOptions;
|
||||
extern const TextParseOptions _textPlainOptions;
|
||||
extern const TextParseOptions _defaultOptions, _textPlainOptions;
|
||||
|
||||
enum TextSelectType {
|
||||
TextSelectLetters = 0x01,
|
||||
@@ -512,6 +538,7 @@ const QSet<int32> &validTopDomains();
|
||||
const QRegularExpression &reDomain();
|
||||
const QRegularExpression &reMailName();
|
||||
const QRegularExpression &reHashtag();
|
||||
const QRegularExpression &reBotCommand();
|
||||
|
||||
// text style
|
||||
const style::textStyle *textstyleCurrent();
|
||||
|
||||
@@ -41,14 +41,20 @@ TextParseOptions _textDlgOptions = {
|
||||
1, // maxh
|
||||
Qt::LayoutDirectionAuto, // lang-dependent
|
||||
};
|
||||
TextParseOptions _historyTextOptions = {
|
||||
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
|
||||
0, // maxw
|
||||
0, // maxh
|
||||
Qt::LayoutDirectionAuto, // dir
|
||||
};
|
||||
TextParseOptions _historyBotOptions = {
|
||||
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands | TextParseMultiline | TextParseRichText, // flags
|
||||
0, // maxw
|
||||
0, // maxh
|
||||
Qt::LayoutDirectionAuto, // dir
|
||||
};
|
||||
|
||||
namespace {
|
||||
TextParseOptions _historyTextOptions = {
|
||||
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
|
||||
0, // maxw
|
||||
0, // maxh
|
||||
Qt::LayoutDirectionAuto, // dir
|
||||
};
|
||||
TextParseOptions _historySrvOptions = {
|
||||
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
|
||||
0, // maxw
|
||||
@@ -103,6 +109,14 @@ namespace {
|
||||
inline const HistoryForwarded *toHistoryForwarded(const HistoryItem *item) {
|
||||
return item ? item->toHistoryForwarded() : 0;
|
||||
}
|
||||
inline const TextParseOptions &itemTextParseOptions(HistoryItem *item) {
|
||||
History *h = item->history();
|
||||
UserData *f = item->from();
|
||||
if ((!h->peer->chat && h->peer->asUser()->botInfo) || (!f->chat && f->asUser()->botInfo) || (h->peer->chat && h->peer->asChat()->botStatus >= 0)) {
|
||||
return _historyBotOptions;
|
||||
}
|
||||
return _historyTextOptions;
|
||||
}
|
||||
}
|
||||
|
||||
void historyInit() {
|
||||
@@ -149,7 +163,7 @@ void DialogRow::paint(QPainter &p, int32 w, bool act, bool sel) const {
|
||||
rectForName.setLeft(rectForName.left() + st::dlgChatImgSkip);
|
||||
}
|
||||
|
||||
HistoryItem *last = history->last;
|
||||
HistoryItem *last = history->lastMsg;
|
||||
if (!last) {
|
||||
p.setFont(st::dlgHistFont->f);
|
||||
p.setPen((act ? st::dlgActiveColor : st::dlgSystemColor)->p);
|
||||
@@ -290,12 +304,16 @@ History::History(const PeerId &peerId) : width(0), height(0)
|
||||
, peer(App::peer(peerId))
|
||||
, oldLoaded(false)
|
||||
, newLoaded(true)
|
||||
, last(0)
|
||||
, lastMsg(0)
|
||||
, activeMsgId(0)
|
||||
, draftToId(0)
|
||||
, lastWidth(0)
|
||||
, lastScrollTop(History::ScrollMax)
|
||||
, mute(isNotifyMuted(peer->notify))
|
||||
, lastKeyboardInited(false)
|
||||
, lastKeyboardUsed(false)
|
||||
, lastKeyboardId(0)
|
||||
, lastKeyboardFrom(0)
|
||||
, sendRequestId(0)
|
||||
, textCachedFor(0)
|
||||
, lastItemTextCache(st::dlgRichMinWidth)
|
||||
@@ -371,7 +389,7 @@ bool DialogsList::del(const PeerId &peerId, DialogRow *replacedBy) {
|
||||
}
|
||||
|
||||
void DialogsIndexed::peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
|
||||
if (byName) {
|
||||
if (sortMode == DialogsSortByName) {
|
||||
DialogRow *mainRow = list.adjustByName(peer);
|
||||
if (!mainRow) return;
|
||||
|
||||
@@ -400,7 +418,7 @@ void DialogsIndexed::peerNameChanged(PeerData *peer, const PeerData::Names &oldN
|
||||
for (PeerData::NameFirstChars::const_iterator i = toAdd.cbegin(), e = toAdd.cend(); i != e; ++i) {
|
||||
DialogsIndex::iterator j = index.find(*i);
|
||||
if (j == index.cend()) {
|
||||
j = index.insert(*i, new DialogsList(byName));
|
||||
j = index.insert(*i, new DialogsList(sortMode));
|
||||
}
|
||||
j.value()->addByName(history);
|
||||
}
|
||||
@@ -422,7 +440,7 @@ void DialogsIndexed::peerNameChanged(PeerData *peer, const PeerData::Names &oldN
|
||||
}
|
||||
}
|
||||
for (PeerData::NameFirstChars::const_iterator i = toRemove.cbegin(), e = toRemove.cend(); i != e; ++i) {
|
||||
history->dialogs.remove(*i);
|
||||
if (sortMode == DialogsSortByDate) history->dialogs.remove(*i);
|
||||
DialogsIndex::iterator j = index.find(*i);
|
||||
if (j != index.cend()) {
|
||||
j.value()->del(peer->id, mainRow);
|
||||
@@ -431,9 +449,13 @@ void DialogsIndexed::peerNameChanged(PeerData *peer, const PeerData::Names &oldN
|
||||
for (PeerData::NameFirstChars::const_iterator i = toAdd.cbegin(), e = toAdd.cend(); i != e; ++i) {
|
||||
DialogsIndex::iterator j = index.find(*i);
|
||||
if (j == index.cend()) {
|
||||
j = index.insert(*i, new DialogsList(byName));
|
||||
j = index.insert(*i, new DialogsList(sortMode));
|
||||
}
|
||||
if (sortMode == DialogsSortByDate) {
|
||||
history->dialogs.insert(*i, j.value()->addByPos(history));
|
||||
} else {
|
||||
j.value()->addToEnd(history);
|
||||
}
|
||||
history->dialogs.insert(*i, j.value()->addByPos(history));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -460,6 +482,8 @@ void Histories::regTyping(History *history, UserData *user) {
|
||||
uint64 ms = getms(true);
|
||||
history->typing[user] = ms + 6000;
|
||||
|
||||
user->madeAction();
|
||||
|
||||
TypingHistories::const_iterator i = typing.find(history);
|
||||
if (i == typing.cend()) {
|
||||
typing.insert(history, ms);
|
||||
@@ -527,7 +551,7 @@ HistoryItem *Histories::addToBack(const MTPmessage &msg, int msgState) {
|
||||
if (!h.value()->loadedAtBottom()) {
|
||||
HistoryItem *item = h.value()->addToHistory(msg);
|
||||
if (item) {
|
||||
h.value()->last = item;
|
||||
h.value()->lastMsg = item;
|
||||
if (msgState > 0) {
|
||||
h.value()->newItemAdded(item);
|
||||
}
|
||||
@@ -578,6 +602,9 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPmessage &msg, boo
|
||||
} else {
|
||||
result = new HistoryMessage(this, block, msg.c_message());
|
||||
}
|
||||
if (msg.c_message().has_reply_markup()) {
|
||||
App::feedReplyMarkup(msgId, msg.c_message().vreply_markup);
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_messageService: {
|
||||
@@ -754,7 +781,7 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *
|
||||
}
|
||||
}
|
||||
to->push_back(adding);
|
||||
last = adding;
|
||||
lastMsg = adding;
|
||||
adding->y = to->height;
|
||||
if (width) {
|
||||
int32 dh = adding->resize(width);
|
||||
@@ -777,12 +804,33 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *
|
||||
}
|
||||
}
|
||||
}
|
||||
if (peer->chat && adding->from()->id) {
|
||||
QList<UserData*> *lastAuthors = &(peer->asChat()->lastAuthors);
|
||||
int prev = lastAuthors->indexOf(adding->from());
|
||||
if (prev > 0) {
|
||||
lastAuthors->removeAt(prev);
|
||||
lastAuthors->push_front(adding->from());
|
||||
if (adding->from()->id) {
|
||||
if (peer->chat) {
|
||||
QList<UserData*> *lastAuthors = &(peer->asChat()->lastAuthors);
|
||||
int prev = lastAuthors->indexOf(adding->from());
|
||||
if (prev > 0) {
|
||||
lastAuthors->removeAt(prev);
|
||||
}
|
||||
if (prev) {
|
||||
lastAuthors->push_front(adding->from());
|
||||
}
|
||||
}
|
||||
if (adding->hasReplyMarkup()) {
|
||||
if (peer->chat) {
|
||||
peer->asChat()->markupSenders.insert(adding->from(), true);
|
||||
}
|
||||
if (App::replyMarkup(adding->id).flags & MTPDreplyKeyboardMarkup_flag_ZERO) { // zero markup means replyKeyboardHide
|
||||
if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->chat && !adding->out())) {
|
||||
lastKeyboardInited = true;
|
||||
lastKeyboardId = 0;
|
||||
lastKeyboardFrom = 0;
|
||||
}
|
||||
} else {
|
||||
lastKeyboardInited = true;
|
||||
lastKeyboardId = adding->id;
|
||||
lastKeyboardFrom = adding->from()->id;
|
||||
lastKeyboardUsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return adding;
|
||||
@@ -802,10 +850,7 @@ void History::newItemAdded(HistoryItem *item) {
|
||||
App::checkImageCacheSize();
|
||||
if (item->from()) {
|
||||
unregTyping(item->from());
|
||||
if (item->from()->onlineTill < 0) {
|
||||
item->from()->onlineTill = -unixtime() - HiddenIsOnlineAfterMessage; // pseudo-online
|
||||
if (App::main()) App::main()->peerUpdated(item->from());
|
||||
}
|
||||
item->from()->madeAction();
|
||||
}
|
||||
if (item->out()) {
|
||||
if (unreadBar) unreadBar->destroy();
|
||||
@@ -889,7 +934,43 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastAuthors && item->from()->id && !lastAuthors->contains(item->from())) lastAuthors->push_back(item->from());
|
||||
if (item->from()->id) {
|
||||
if (lastAuthors) { // chats
|
||||
if (!lastAuthors->contains(item->from())) {
|
||||
lastAuthors->push_back(item->from());
|
||||
}
|
||||
if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { // chats with bots
|
||||
bool wasKeyboardHide = peer->asChat()->markupSenders.contains(item->from());
|
||||
if (!wasKeyboardHide) {
|
||||
peer->asChat()->markupSenders.insert(item->from(), true);
|
||||
}
|
||||
if (!(App::replyMarkup(item->id).flags & MTPDreplyKeyboardMarkup_flag_ZERO)) {
|
||||
if (!lastKeyboardInited) {
|
||||
lastKeyboardInited = true;
|
||||
if (wasKeyboardHide) {
|
||||
lastKeyboardId = 0;
|
||||
lastKeyboardFrom = 0;
|
||||
} else {
|
||||
lastKeyboardId = item->id;
|
||||
lastKeyboardFrom = item->from()->id;
|
||||
lastKeyboardUsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { // conversations with bots
|
||||
lastKeyboardInited = true;
|
||||
if (App::replyMarkup(item->id).flags & MTPDreplyKeyboardMarkup_flag_ZERO) {
|
||||
lastKeyboardId = 0;
|
||||
lastKeyboardFrom = 0;
|
||||
} else {
|
||||
lastKeyboardInited = true;
|
||||
lastKeyboardId = item->id;
|
||||
lastKeyboardFrom = item->from()->id;
|
||||
lastKeyboardUsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer);
|
||||
}
|
||||
@@ -1138,9 +1219,9 @@ void History::fixLastMessage(bool wasAtBottom) {
|
||||
wasAtBottom = false;
|
||||
}
|
||||
if (wasAtBottom) {
|
||||
last = back()->back();
|
||||
lastMsg = back()->back();
|
||||
} else {
|
||||
last = 0;
|
||||
lastMsg = 0;
|
||||
if (App::main()) {
|
||||
App::main()->checkPeerHistory(peer);
|
||||
}
|
||||
@@ -1156,12 +1237,12 @@ void History::loadAround(MsgId msgId) {
|
||||
if (!item || !item->block()) {
|
||||
clear(true);
|
||||
}
|
||||
newLoaded = last && !last->detached();
|
||||
newLoaded = lastMsg && !lastMsg->detached();
|
||||
} else {
|
||||
if (!loadedAtBottom()) {
|
||||
clear(true);
|
||||
}
|
||||
newLoaded = isEmpty() || (last && !last->detached());
|
||||
newLoaded = isEmpty() || (lastMsg && !lastMsg->detached());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1245,13 +1326,19 @@ void History::clear(bool leaveItems) {
|
||||
}
|
||||
Parent::clear();
|
||||
setMsgCount(0);
|
||||
if (!leaveItems) {
|
||||
if (leaveItems) {
|
||||
lastKeyboardInited = false;
|
||||
} else {
|
||||
setUnreadCount(0);
|
||||
last = 0;
|
||||
lastMsg = 0;
|
||||
}
|
||||
height = 0;
|
||||
oldLoaded = false;
|
||||
if (peer->chat) peer->asChat()->lastAuthors.clear();
|
||||
if (peer->chat) {
|
||||
peer->asChat()->lastAuthors.clear();
|
||||
peer->asChat()->markupSenders.clear();
|
||||
}
|
||||
if (leaveItems && App::main()) App::main()->historyCleared(this);
|
||||
}
|
||||
|
||||
History::Parent::iterator History::erase(History::Parent::iterator i) {
|
||||
@@ -1473,7 +1560,7 @@ void HistoryItem::destroy() {
|
||||
bool wasAtBottom = history()->loadedAtBottom();
|
||||
_history->removeNotification(this);
|
||||
detach();
|
||||
if (history()->last == this) {
|
||||
if (history()->lastMsg == this) {
|
||||
history()->fixLastMessage(wasAtBottom);
|
||||
}
|
||||
HistoryMedia *m = getMedia(true);
|
||||
@@ -1549,7 +1636,7 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo
|
||||
, _caption(st::minPhotoSize)
|
||||
, openl(new PhotoLink(data)) {
|
||||
if (!caption.isEmpty()) {
|
||||
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
|
||||
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(parent));
|
||||
}
|
||||
init();
|
||||
}
|
||||
@@ -1949,7 +2036,7 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo
|
||||
, _uplDone(0)
|
||||
{
|
||||
if (!caption.isEmpty()) {
|
||||
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
|
||||
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(parent));
|
||||
}
|
||||
|
||||
_size = formatDurationAndSizeText(data->duration, data->size);
|
||||
@@ -3083,7 +3170,6 @@ void HistorySticker::updateFrom(const MTPMessageMedia &media) {
|
||||
if (!data->data.isEmpty()) {
|
||||
Local::writeStickerImage(mediaKey(mtpToLocationType(mtpc_inputDocumentFileLocation), data->dc, data->id), data->data);
|
||||
}
|
||||
if (App::main()) App::main()->incrementSticker(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3417,11 +3503,13 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) {
|
||||
}
|
||||
QString title(data->title.isEmpty() ? data->author : data->title);
|
||||
if (!title.isEmpty()) {
|
||||
_title.setText(st::webPageTitleFont, textClean(title), _webpageTitleOptions);
|
||||
title = textClean(title);
|
||||
if (!_asArticle && !data->photo && data->description.isEmpty()) title += textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y());
|
||||
_title.setText(st::webPageTitleFont, title, _webpageTitleOptions);
|
||||
if (_asArticle) {
|
||||
_maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth() + st::webPagePhotoDelta + st::webPagePhotoSize));
|
||||
} else {
|
||||
_maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth() + (data->photo ? parent->timeWidth(true) : 0)));
|
||||
_maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth()));
|
||||
_minh += qMin(_title.minHeight(), 2 * st::webPageTitleFont->height);
|
||||
}
|
||||
}
|
||||
@@ -4657,9 +4745,9 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
|
||||
void HistoryMessage::initDimensions(const QString &text) {
|
||||
if (!_media || !text.isEmpty()) { // !justMedia()
|
||||
if (_media && _media->isDisplayed()) {
|
||||
_text.setText(st::msgFont, text, _historyTextOptions);
|
||||
_text.setText(st::msgFont, text, itemTextParseOptions(this));
|
||||
} else {
|
||||
_text.setText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
|
||||
_text.setText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4678,14 +4766,14 @@ void HistoryMessage::initDimensions(const HistoryItem *parent) {
|
||||
if (_media->isDisplayed() && _text.hasSkipBlock()) {
|
||||
QString was = HistoryMessage::selectedText(FullItemSel);
|
||||
if (!was.isEmpty()) {
|
||||
_text.setText(st::msgFont, was, _historyTextOptions); // without date skip
|
||||
_text.setText(st::msgFont, was, itemTextParseOptions(this)); // without date skip
|
||||
_textWidth = 0;
|
||||
_textHeight = 0;
|
||||
}
|
||||
} else if (!_media->isDisplayed() && !_text.hasSkipBlock()) {
|
||||
QString was = HistoryMessage::selectedText(FullItemSel);
|
||||
if (!was.isEmpty()) {
|
||||
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip
|
||||
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this)); // without date skip
|
||||
_textWidth = 0;
|
||||
_textHeight = 0;
|
||||
}
|
||||
@@ -4742,14 +4830,14 @@ void HistoryMessage::setMedia(const MTPmessageMedia &media) {
|
||||
if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
|
||||
QString was = HistoryMessage::selectedText(FullItemSel);
|
||||
if (!was.isEmpty()) {
|
||||
_text.setText(st::msgFont, was, _historyTextOptions); // without date skip
|
||||
_text.setText(st::msgFont, was, itemTextParseOptions(this)); // without date skip
|
||||
_textWidth = 0;
|
||||
_textHeight = 0;
|
||||
}
|
||||
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
|
||||
QString was = HistoryMessage::selectedText(FullItemSel);
|
||||
if (!was.isEmpty()) {
|
||||
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip
|
||||
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this)); // without date skip
|
||||
_textWidth = 0;
|
||||
_textHeight = 0;
|
||||
}
|
||||
@@ -5058,6 +5146,9 @@ HistoryMessage::~HistoryMessage() {
|
||||
_media->unregItem(this);
|
||||
delete _media;
|
||||
}
|
||||
if (_flags & MTPDmessage::flag_reply_markup) {
|
||||
App::clearReplyMarkup(id);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryMessage(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.vfrom_id.v, textClean(qs(msg.vmessage)), msg.vmedia)
|
||||
|
||||
@@ -30,7 +30,7 @@ static const uint32 FullItemSel = 0xFFFFFFFF;
|
||||
|
||||
typedef QMap<int32, HistoryItem*> SelectedItemSet;
|
||||
|
||||
extern TextParseOptions _textNameOptions, _textDlgOptions;
|
||||
extern TextParseOptions _textNameOptions, _textDlgOptions, _historyTextOptions, _historyBotOptions;
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
@@ -198,7 +198,7 @@ struct History : public QList<HistoryBlock*> {
|
||||
|
||||
PeerData *peer;
|
||||
bool oldLoaded, newLoaded;
|
||||
HistoryItem *last;
|
||||
HistoryItem *lastMsg;
|
||||
MsgId activeMsgId;
|
||||
|
||||
typedef QList<HistoryItem*> NotifyQueue;
|
||||
@@ -238,8 +238,8 @@ struct History : public QList<HistoryBlock*> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last == old) {
|
||||
last = item;
|
||||
if (lastMsg == old) {
|
||||
lastMsg = item;
|
||||
}
|
||||
// showFrom can't be detached
|
||||
}
|
||||
@@ -251,6 +251,10 @@ struct History : public QList<HistoryBlock*> {
|
||||
int32 lastWidth, lastScrollTop;
|
||||
bool mute;
|
||||
|
||||
bool lastKeyboardInited, lastKeyboardUsed;
|
||||
MsgId lastKeyboardId;
|
||||
PeerId lastKeyboardFrom;
|
||||
|
||||
mtpRequestId sendRequestId;
|
||||
|
||||
// for dialog drawing
|
||||
@@ -284,8 +288,14 @@ struct History : public QList<HistoryBlock*> {
|
||||
static const int32 ScrollMax = INT_MAX;
|
||||
};
|
||||
|
||||
enum DialogsSortMode {
|
||||
DialogsSortByDate,
|
||||
DialogsSortByName,
|
||||
DialogsSortByAdd
|
||||
};
|
||||
|
||||
struct DialogsList {
|
||||
DialogsList(bool sortByName) : begin(&last), end(&last), byName(sortByName), count(0), current(&last) {
|
||||
DialogsList(DialogsSortMode sortMode) : begin(&last), end(&last), sortMode(sortMode), count(0), current(&last) {
|
||||
}
|
||||
|
||||
void adjustCurrent(int32 y, int32 h) const {
|
||||
@@ -323,10 +333,10 @@ struct DialogsList {
|
||||
end->pos++;
|
||||
if (begin == end) {
|
||||
begin = current = result;
|
||||
if (!byName && updatePos) history->posInDialogs = 0;
|
||||
if (sortMode == DialogsSortByDate && updatePos) history->posInDialogs = 0;
|
||||
} else {
|
||||
end->prev->next = result;
|
||||
if (!byName && updatePos) history->posInDialogs = end->prev->history->posInDialogs + 1;
|
||||
if (sortMode == DialogsSortByDate && updatePos) history->posInDialogs = end->prev->history->posInDialogs + 1;
|
||||
}
|
||||
rowByPeer.insert(history->peer->id, result);
|
||||
++count;
|
||||
@@ -334,7 +344,7 @@ struct DialogsList {
|
||||
}
|
||||
|
||||
void bringToTop(DialogRow *row, bool updatePos = true) {
|
||||
if (!byName && updatePos && row != begin) {
|
||||
if (sortMode == DialogsSortByDate && updatePos && row != begin) {
|
||||
row->history->posInDialogs = begin->history->posInDialogs - 1;
|
||||
}
|
||||
insertBefore(row, begin);
|
||||
@@ -389,7 +399,7 @@ struct DialogsList {
|
||||
}
|
||||
|
||||
DialogRow *adjustByName(const PeerData *peer) {
|
||||
if (!byName) return 0;
|
||||
if (sortMode != DialogsSortByName) return 0;
|
||||
|
||||
RowByPeer::iterator i = rowByPeer.find(peer->id);
|
||||
if (i == rowByPeer.cend()) return 0;
|
||||
@@ -408,7 +418,7 @@ struct DialogsList {
|
||||
}
|
||||
|
||||
DialogRow *addByName(History *history) {
|
||||
if (!byName) return 0;
|
||||
if (sortMode != DialogsSortByName) return 0;
|
||||
|
||||
DialogRow *row = addToEnd(history), *change = row;
|
||||
const QString &peerName(history->peer->name);
|
||||
@@ -425,7 +435,7 @@ struct DialogsList {
|
||||
}
|
||||
|
||||
void adjustByPos(DialogRow *row) {
|
||||
if (byName) return;
|
||||
if (sortMode != DialogsSortByDate) return;
|
||||
|
||||
DialogRow *change = row;
|
||||
while (change->prev && change->prev->history->posInDialogs > row->history->posInDialogs) {
|
||||
@@ -440,7 +450,7 @@ struct DialogsList {
|
||||
}
|
||||
|
||||
DialogRow *addByPos(History *history) {
|
||||
if (byName) return 0;
|
||||
if (sortMode != DialogsSortByDate) return 0;
|
||||
|
||||
DialogRow *row = addToEnd(history, false);
|
||||
adjustByPos(row);
|
||||
@@ -475,7 +485,7 @@ struct DialogsList {
|
||||
|
||||
DialogRow last;
|
||||
DialogRow *begin, *end;
|
||||
bool byName;
|
||||
DialogsSortMode sortMode;
|
||||
int32 count;
|
||||
|
||||
typedef QHash<PeerId, DialogRow*> RowByPeer;
|
||||
@@ -485,7 +495,7 @@ struct DialogsList {
|
||||
};
|
||||
|
||||
struct DialogsIndexed {
|
||||
DialogsIndexed(bool sortByName) : byName(sortByName), list(byName) {
|
||||
DialogsIndexed(DialogsSortMode sortMode) : sortMode(sortMode), list(sortMode) {
|
||||
}
|
||||
|
||||
History::DialogLinks addToEnd(History *history) {
|
||||
@@ -499,7 +509,7 @@ struct DialogsIndexed {
|
||||
for (PeerData::NameFirstChars::const_iterator i = history->peer->chars.cbegin(), e = history->peer->chars.cend(); i != e; ++i) {
|
||||
DialogsIndex::iterator j = index.find(*i);
|
||||
if (j == index.cend()) {
|
||||
j = index.insert(*i, new DialogsList(byName));
|
||||
j = index.insert(*i, new DialogsList(sortMode));
|
||||
}
|
||||
result.insert(*i, j.value()->addToEnd(history));
|
||||
}
|
||||
@@ -517,7 +527,7 @@ struct DialogsIndexed {
|
||||
for (PeerData::NameFirstChars::const_iterator i = history->peer->chars.cbegin(), e = history->peer->chars.cend(); i != e; ++i) {
|
||||
DialogsIndex::iterator j = index.find(*i);
|
||||
if (j == index.cend()) {
|
||||
j = index.insert(*i, new DialogsList(byName));
|
||||
j = index.insert(*i, new DialogsList(sortMode));
|
||||
}
|
||||
j.value()->addByName(history);
|
||||
}
|
||||
@@ -556,7 +566,7 @@ struct DialogsIndexed {
|
||||
|
||||
void clear();
|
||||
|
||||
bool byName;
|
||||
DialogsSortMode sortMode;
|
||||
DialogsList list;
|
||||
typedef QMap<QChar, DialogsList*> DialogsIndex;
|
||||
DialogsIndex index;
|
||||
@@ -678,6 +688,9 @@ public:
|
||||
void markMediaRead() {
|
||||
_flags &= ~MTPDmessage_flag_media_unread;
|
||||
}
|
||||
bool hasReplyMarkup() const {
|
||||
return _flags & MTPDmessage::flag_reply_markup;
|
||||
}
|
||||
virtual bool needCheck() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -79,6 +79,8 @@ public:
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
~HistoryList();
|
||||
|
||||
public slots:
|
||||
@@ -116,6 +118,12 @@ private:
|
||||
void applyDragSelection();
|
||||
|
||||
History *hist;
|
||||
|
||||
int32 ySkip;
|
||||
BotInfo *botInfo;
|
||||
int32 botDescWidth, botDescHeight;
|
||||
QRect botDescRect;
|
||||
|
||||
HistoryWidget *historyWidget;
|
||||
ScrollArea *scrollArea;
|
||||
int32 currentBlock, currentItem;
|
||||
@@ -172,6 +180,7 @@ public:
|
||||
void insertFromMimeData(const QMimeData *source);
|
||||
|
||||
void focusInEvent(QFocusEvent *e);
|
||||
void setMaxHeight(int32 maxHeight);
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -185,6 +194,72 @@ signals:
|
||||
|
||||
private:
|
||||
HistoryWidget *history;
|
||||
int32 _maxHeight;
|
||||
|
||||
};
|
||||
|
||||
class BotKeyboard : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
BotKeyboard();
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
|
||||
bool updateMarkup(HistoryItem *last);
|
||||
bool hasMarkup() const;
|
||||
|
||||
bool hoverStep(float64 ms);
|
||||
void resizeToWidth(int32 width, int32 maxOuterHeight);
|
||||
|
||||
bool maximizeSize() const;
|
||||
bool singleUse() const;
|
||||
|
||||
MsgId forMsgId() const {
|
||||
return _wasForMsgId;
|
||||
}
|
||||
|
||||
public slots:
|
||||
|
||||
void showCommandTip();
|
||||
void updateSelected();
|
||||
|
||||
private:
|
||||
|
||||
void updateStyle(int32 w = -1);
|
||||
void clearSelection();
|
||||
|
||||
MsgId _wasForMsgId;
|
||||
int32 _height, _maxOuterHeight;
|
||||
bool _maximizeSize, _singleUse;
|
||||
QTimer _cmdTipTimer;
|
||||
|
||||
QPoint _lastMousePos;
|
||||
struct Button {
|
||||
Button(const QString &str = QString()) : cmd(str), text(1), cwidth(0), hover(0), full(true) {
|
||||
}
|
||||
QRect rect;
|
||||
QString cmd;
|
||||
Text text;
|
||||
int32 cwidth;
|
||||
float64 hover;
|
||||
bool full;
|
||||
};
|
||||
int32 _sel, _down;
|
||||
QList<QList<Button> > _btns;
|
||||
|
||||
typedef QMap<int32, uint64> Animations;
|
||||
Animations _animations;
|
||||
Animation _hoverAnim;
|
||||
|
||||
const style::botKeyboardButton *_st;
|
||||
|
||||
};
|
||||
|
||||
class HistoryHider : public QWidget, public Animated {
|
||||
@@ -294,6 +369,7 @@ public:
|
||||
void newUnreadMsg(History *history, HistoryItem *item);
|
||||
void historyToDown(History *history);
|
||||
void historyWasRead(bool force = true);
|
||||
void historyCleared(History *history);
|
||||
|
||||
QRect historyRect() const;
|
||||
|
||||
@@ -371,6 +447,11 @@ public:
|
||||
bool recordingStep(float64 ms);
|
||||
void stopRecording(bool send);
|
||||
|
||||
void onListEscapePressed();
|
||||
|
||||
void sendBotCommand(const QString &cmd, MsgId replyTo);
|
||||
void insertBotCommand(const QString &cmd);
|
||||
|
||||
~HistoryWidget();
|
||||
|
||||
signals:
|
||||
@@ -391,7 +472,7 @@ public slots:
|
||||
void onPreviewTimeout();
|
||||
|
||||
void peerUpdated(PeerData *data);
|
||||
void onPeerLoaded(PeerData *data);
|
||||
void onFullPeerUpdated(PeerData *data);
|
||||
|
||||
void cancelTyping();
|
||||
|
||||
@@ -409,12 +490,15 @@ public slots:
|
||||
void onListScroll();
|
||||
void onHistoryToEnd();
|
||||
void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1);
|
||||
void onBotStart();
|
||||
|
||||
void onPhotoSelect();
|
||||
void onDocumentSelect();
|
||||
void onPhotoDrop(QDropEvent *e);
|
||||
void onDocumentDrop(QDropEvent *e);
|
||||
|
||||
void onKbToggle(bool manual = true);
|
||||
|
||||
void onPhotoReady();
|
||||
void onSendConfirmed();
|
||||
void onSendCancelled();
|
||||
@@ -462,7 +546,11 @@ private:
|
||||
int32 _replyToNameVersion;
|
||||
IconedButton _replyForwardPreviewCancel;
|
||||
void updateReplyToName();
|
||||
void drawFieldBackground(QPainter &p);
|
||||
|
||||
void drawField(Painter &p);
|
||||
void drawRecordButton(Painter &p);
|
||||
void drawRecording(Painter &p);
|
||||
void updateField();
|
||||
|
||||
QString _previewLinks;
|
||||
WebPageData *_previewData;
|
||||
@@ -484,6 +572,8 @@ private:
|
||||
void addMessagesToFront(const QVector<MTPMessage> &messages);
|
||||
void addMessagesToBack(const QVector<MTPMessage> &messages);
|
||||
|
||||
void updateBotKeyboard();
|
||||
|
||||
void stickersGot(const MTPmessages_AllStickers &stickers);
|
||||
bool stickersFailed(const RPCError &error);
|
||||
|
||||
@@ -511,14 +601,14 @@ private:
|
||||
ScrollArea _scroll;
|
||||
HistoryList *_list;
|
||||
History *hist;
|
||||
bool _histInited; // initial updateListSize() called
|
||||
bool _histInited, _histNeedUpdate; // initial updateListSize() called
|
||||
|
||||
IconedButton _toHistoryEnd;
|
||||
|
||||
MentionsDropdown _attachMention;
|
||||
|
||||
FlatButton _send;
|
||||
IconedButton _attachDocument, _attachPhoto, _attachEmoji;
|
||||
FlatButton _send, _botStart;
|
||||
IconedButton _attachDocument, _attachPhoto, _attachEmoji, _kbShow, _kbHide;
|
||||
MessageField _field;
|
||||
Animation _recordAnim, _recordingAnim;
|
||||
bool _recording, _inRecord, _inField;
|
||||
@@ -528,6 +618,11 @@ private:
|
||||
anim::cvalue a_recordCancel;
|
||||
int32 _recordCancelWidth;
|
||||
|
||||
bool _kbShown, _kbWasHidden;
|
||||
HistoryItem *_kbReplyTo;
|
||||
ScrollArea _kbScroll;
|
||||
BotKeyboard _keyboard;
|
||||
|
||||
Dropdown _attachType;
|
||||
EmojiPan _emojiPan;
|
||||
DragState _attachDrag;
|
||||
|
||||
@@ -202,7 +202,7 @@ void IntroCode::codeSubmitDone(const MTPauth_Authorization &result) {
|
||||
stopCheck();
|
||||
code.setDisabled(false);
|
||||
const MTPDauth_authorization &d(result.c_auth_authorization());
|
||||
if (d.vuser.type() != mtpc_userSelf) { // wtf?
|
||||
if (d.vuser.type() != mtpc_user || !(d.vuser.c_user().vflags.v & MTPDuser_flag_self)) { // wtf?
|
||||
showError(lang(lng_server_error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &res
|
||||
_pwdField.setDisabled(false);
|
||||
_codeField.setDisabled(false);
|
||||
const MTPDauth_authorization &d(result.c_auth_authorization());
|
||||
if (d.vuser.type() != mtpc_userSelf) { // wtf?
|
||||
if (d.vuser.type() != mtpc_user || !(d.vuser.c_user().vflags.v & MTPDuser_flag_self)) { // wtf?
|
||||
showError(lang(lng_server_error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) {
|
||||
first.setDisabled(false);
|
||||
last.setDisabled(false);
|
||||
const MTPDauth_authorization &d(result.c_auth_authorization());
|
||||
if (d.vuser.type() != mtpc_userSelf) { // wtf?
|
||||
if (d.vuser.type() != mtpc_user || !(d.vuser.c_user().vflags.v & MTPDuser_flag_self)) { // wtf?
|
||||
showError(lang(lng_server_error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "Jetzt versuchen";
|
||||
|
||||
"lng_status_service_notifications" = "Servicemeldungen";
|
||||
"lng_status_bot" = "Bot";
|
||||
"lng_status_bot_reads_all" = "hat Zugriff auf Nachrichten";
|
||||
"lng_status_bot_not_reads_all" = "hat keinen Zugriff auf Nachrichten";
|
||||
"lng_status_offline" = "vor langer Zeit gesehen";
|
||||
"lng_status_recently" = "kürzlich gesehen";
|
||||
"lng_status_last_week" = "innerhalb einer Woche gesehen";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "Gruppe nicht verfügbar";
|
||||
"lng_topbar_info" = "Info";
|
||||
"lng_profile_about_section" = "Info";
|
||||
"lng_profile_settings_section" = "Einstellungen";
|
||||
"lng_profile_bot_settings" = "Einstellungen";
|
||||
"lng_profile_bot_help" = "Hilfe";
|
||||
"lng_profile_participants_section" = "Teilnehmer";
|
||||
"lng_profile_info" = "Kontaktinfo";
|
||||
"lng_profile_group_info" = "Gruppeninfo";
|
||||
@@ -343,6 +349,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_clear_history" = "Chatverlauf löschen";
|
||||
"lng_profile_send_message" = "Nachricht senden";
|
||||
"lng_profile_share_contact" = "Kontakt teilen";
|
||||
"lng_profile_invite_to_group" = "In eine Gruppe einladen";
|
||||
"lng_profile_delete_contact" = "Löschen";
|
||||
"lng_profile_set_group_photo" = "Bild festlegen";
|
||||
"lng_profile_add_participant" = "Neues Mitglied";
|
||||
@@ -455,6 +462,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_willbe_history" = "Wähle einen Chat aus, um zu schreiben";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "Ich";
|
||||
"lng_bot_description" = "Was kann dieser Bot?";
|
||||
|
||||
"lng_bot_start" = "Starten";
|
||||
"lng_bot_choose_group" = "Gruppe auswählen";
|
||||
"lng_bot_no_groups" = "Du hast keine Gruppen";
|
||||
"lng_bot_groups_not_found" = "Keine Gruppen gefunden";
|
||||
"lng_bot_sure_invite" = "Den Bot zu «{group}» hinzufügen?";
|
||||
"lng_bot_already_in_group" = "Der Bot ist schon in dieser Gruppe.";
|
||||
|
||||
"lng_typing" = "tippt";
|
||||
"lng_user_typing" = "{user} tippt";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop wurde aktualisiert auf Version {version}\n\n{changes}\n\nGesamter Versionsverlauf:\n{link}";
|
||||
"lng_new_version_minor" = "— Fehlerbehebungen und Softwareoptimierungen";
|
||||
"lng_new_version_text" = "— Sticker Panel wurde verbessert\n— Fehlerbehebungen und sonstige Kleinigkeiten";
|
||||
"lng_new_version_text" = "Diese neue Version enthält Unterstützung für die neue Bot API, welche für alle kostenlos verfügbar ist. Kannst du programmieren? Erstelle deine eigenen Bots für Spiele, Dienste oder Integrationen.\n\nMehr dazu unter {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "Intentar ahora";
|
||||
|
||||
"lng_status_service_notifications" = "servicio de notificaciones";
|
||||
"lng_status_bot" = "bot";
|
||||
"lng_status_bot_reads_all" = "tiene acceso a los mensajes";
|
||||
"lng_status_bot_not_reads_all" = "no tiene acceso a los mensajes";
|
||||
"lng_status_offline" = "última vez hace mucho tiempo";
|
||||
"lng_status_recently" = "última vez recientemente";
|
||||
"lng_status_last_week" = "última vez hace unos días";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "El grupo es inaccesible";
|
||||
"lng_topbar_info" = "Información";
|
||||
"lng_profile_about_section" = "Acerca de";
|
||||
"lng_profile_settings_section" = "Ajustes";
|
||||
"lng_profile_bot_settings" = "Ajustes";
|
||||
"lng_profile_bot_help" = "Ayuda";
|
||||
"lng_profile_participants_section" = "Miembros";
|
||||
"lng_profile_info" = "Información";
|
||||
"lng_profile_group_info" = "Información";
|
||||
@@ -343,6 +349,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_clear_history" = "Borrar historial";
|
||||
"lng_profile_send_message" = "Enviar mensaje";
|
||||
"lng_profile_share_contact" = "Compartir contacto";
|
||||
"lng_profile_invite_to_group" = "Añadir al grupo";
|
||||
"lng_profile_delete_contact" = "Eliminar";
|
||||
"lng_profile_set_group_photo" = "Poner foto";
|
||||
"lng_profile_add_participant" = "Añadir miembro";
|
||||
@@ -420,13 +427,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Vídeo";
|
||||
"lng_media_audio" = "Mensaje de voz";
|
||||
|
||||
"lng_emoji_category0" = "Usados frecuentemente";
|
||||
"lng_emoji_category1" = "Gente";
|
||||
"lng_emoji_category0" = "Usados con frecuencia";
|
||||
"lng_emoji_category1" = "Personas";
|
||||
"lng_emoji_category2" = "Naturaleza";
|
||||
"lng_emoji_category3" = "Comida y bebidas";
|
||||
"lng_emoji_category3" = "Comida y bebida";
|
||||
"lng_emoji_category4" = "Celebración";
|
||||
"lng_emoji_category5" = "Actividad";
|
||||
"lng_emoji_category6" = "Viajes y lugares";
|
||||
"lng_emoji_category6" = "Viajes y destinos";
|
||||
"lng_emoji_category7" = "Objetos y símbolos";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
@@ -455,6 +462,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_willbe_history" = "Por favor, elige un chat para comenzar a conversar";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "Tú";
|
||||
"lng_bot_description" = "¿Qué puede hacer este bot?";
|
||||
|
||||
"lng_bot_start" = "Empezar";
|
||||
"lng_bot_choose_group" = "Elegir grupo";
|
||||
"lng_bot_no_groups" = "No tienes grupos";
|
||||
"lng_bot_groups_not_found" = "No se encontraron grupos";
|
||||
"lng_bot_sure_invite" = "¿Añadir el bot a «{group}»?";
|
||||
"lng_bot_already_in_group" = "El bot ya es un miembro del grupo.";
|
||||
|
||||
"lng_typing" = "escribiendo";
|
||||
"lng_user_typing" = "{user} está escribiendo";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop fue actualizada a la versión {version}\n\n{changes}\n\nEl historial completo está disponible aquí:\n{link}";
|
||||
"lng_new_version_minor" = "— Corrección de errores y otras mejoras menores";
|
||||
"lng_new_version_text" = "— Pestaña de stickers mejorada\n— Corrección de errores y elementos menores";
|
||||
"lng_new_version_text" = "Esta nueva versión incluye el soporte para bots, usando la nueva API para bots, gratis para todos. Si eres un ingeniero, crea tus propios bots para juegos, servicios o integraciones .\n\nConoce más en {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "Prova ora";
|
||||
|
||||
"lng_status_service_notifications" = "notifiche di servizio";
|
||||
"lng_status_bot" = "bot";
|
||||
"lng_status_bot_reads_all" = "ha accesso ai messaggi";
|
||||
"lng_status_bot_not_reads_all" = "non ha accesso ai messaggi";
|
||||
"lng_status_offline" = "ultimo accesso molto tempo fa";
|
||||
"lng_status_recently" = "ultimo accesso di recente";
|
||||
"lng_status_last_week" = "ultimo accesso entro una settimana";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "Gruppo non accessibile";
|
||||
"lng_topbar_info" = "Info";
|
||||
"lng_profile_about_section" = "Info";
|
||||
"lng_profile_settings_section" = "Impostazioni";
|
||||
"lng_profile_bot_settings" = "Impostazioni";
|
||||
"lng_profile_bot_help" = "Aiuto";
|
||||
"lng_profile_participants_section" = "Membri";
|
||||
"lng_profile_info" = "Info contatto";
|
||||
"lng_profile_group_info" = "Nome gruppo";
|
||||
@@ -343,12 +349,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_clear_history" = "Cancella la cronologia";
|
||||
"lng_profile_send_message" = "Invia messaggio";
|
||||
"lng_profile_share_contact" = "Condividi contatto";
|
||||
"lng_profile_invite_to_group" = "Aggiungi a un gruppo";
|
||||
"lng_profile_delete_contact" = "Elimina";
|
||||
"lng_profile_set_group_photo" = "Imposta foto";
|
||||
"lng_profile_add_participant" = "Aggiungi membro";
|
||||
"lng_profile_delete_and_exit" = "Esci";
|
||||
"lng_profile_kick" = "Rimuovi";
|
||||
"lng_profile_sure_kick" = "Espellere {user} dal gruppo?";
|
||||
"lng_profile_sure_kick" = "Rimuovere {user} dal gruppo?";
|
||||
"lng_profile_loading" = "Caricamento..";
|
||||
"lng_profile_shared_media" = "Media condivisi";
|
||||
"lng_profile_no_media" = "Nessun media in questa chat.";
|
||||
@@ -383,7 +390,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_user_left" = "{from} ha lasciato il gruppo";
|
||||
"lng_action_user_joined" = "{from} si è unito al gruppo";
|
||||
"lng_action_user_joined_by_link" = "{from} si è unito al gruppo tramite link di invito";
|
||||
"lng_action_user_registered" = "{from} ha iniziato a usare Telegram";
|
||||
"lng_action_user_registered" = "{from} si è unito a Telegram";
|
||||
"lng_action_removed_photo" = "{from} ha rimosso la foto del gruppo";
|
||||
"lng_action_changed_photo" = "{from} ha cambiato la foto del gruppo";
|
||||
"lng_action_changed_title" = "{from} ha cambiato il nome del gruppo in «{title}»";
|
||||
@@ -450,11 +457,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Invia";
|
||||
"lng_message_ph" = "Scrivi un messaggio..";
|
||||
"lng_record_cancel" = "Rilascia fuori per annullare";
|
||||
"lng_record_cancel" = "Rilascia fuori da qui per annullare";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Seleziona una chat per iniziare a messaggiare";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "Tu";
|
||||
"lng_bot_description" = "Cosa può fare questo bot?";
|
||||
|
||||
"lng_bot_start" = "Avvia";
|
||||
"lng_bot_choose_group" = "Scegli gruppo";
|
||||
"lng_bot_no_groups" = "Non hai gruppi";
|
||||
"lng_bot_groups_not_found" = "Nessun gruppo trovato";
|
||||
"lng_bot_sure_invite" = "Aggiungere il bot a «{group}»?";
|
||||
"lng_bot_already_in_group" = "Questo bot è già membro del gruppo.";
|
||||
|
||||
"lng_typing" = "sta scrivendo";
|
||||
"lng_user_typing" = "{user} sta scrivendo";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli update è disponibile qui:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fix e altri miglioramenti minori";
|
||||
"lng_new_version_text" = "— Pannello sticker migliorato\n— Risoluzione bug e miglioramenti minori";
|
||||
"lng_new_version_text" = "Questa nuova versione include il support per i bot usando la nuova API per i bot. Se sei un ingegnere, crea i tuoi bot per giochi, servizi o integrazioni. Scopri di più su {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "다시 시도";
|
||||
|
||||
"lng_status_service_notifications" = "서비스 알림";
|
||||
"lng_status_bot" = "bot";
|
||||
"lng_status_bot_reads_all" = "메시지 접근 권한이 있습니다.";
|
||||
"lng_status_bot_not_reads_all" = "메시지 접근 권한이 없습니다.";
|
||||
"lng_status_offline" = "마지막으로 접속한 지 오래됨";
|
||||
"lng_status_recently" = "최근에 접속";
|
||||
"lng_status_last_week" = "일주일 이내 마지막으로 접속";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "그룹에 접근할 수 없습니다.";
|
||||
"lng_topbar_info" = "정보";
|
||||
"lng_profile_about_section" = "취소";
|
||||
"lng_profile_settings_section" = "환경설정";
|
||||
"lng_profile_bot_settings" = "환경설정";
|
||||
"lng_profile_bot_help" = "도움말";
|
||||
"lng_profile_participants_section" = "사용자";
|
||||
"lng_profile_info" = "연락처 정보";
|
||||
"lng_profile_group_info" = "그룹 정보";
|
||||
@@ -343,6 +349,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_clear_history" = "히스토리 초기화";
|
||||
"lng_profile_send_message" = "메세지 전송";
|
||||
"lng_profile_share_contact" = "연락처 공유";
|
||||
"lng_profile_invite_to_group" = "그룹에 추가";
|
||||
"lng_profile_delete_contact" = "삭제";
|
||||
"lng_profile_set_group_photo" = "사진 설정";
|
||||
"lng_profile_add_participant" = "대화상대 추가";
|
||||
@@ -450,11 +457,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "보내기";
|
||||
"lng_message_ph" = "메시지 쓰기";
|
||||
"lng_record_cancel" = "이 영역에서 마우스 클릭을 해제하시면 취소가 됩니다.";
|
||||
"lng_record_cancel" = "Release outside this field to cancel";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "대화하실 방을 선택해주세요.";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "회원님";
|
||||
"lng_bot_description" = "봇이 할 수 있는 일은 무엇일까요?";
|
||||
|
||||
"lng_bot_start" = "Start";
|
||||
"lng_bot_choose_group" = "그룹 선택";
|
||||
"lng_bot_no_groups" = "그룹이 존재하지 않습니다.";
|
||||
"lng_bot_groups_not_found" = "그룹을 찾을 수 없습니다.";
|
||||
"lng_bot_sure_invite" = "<<{group}>>에 봇을 추가 하시겠습니까?";
|
||||
"lng_bot_already_in_group" = "봇이 이미 그룹의 멤버입니다.";
|
||||
|
||||
"lng_typing" = "입력중";
|
||||
"lng_user_typing" = "{user}님이 입력중입니다.";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}";
|
||||
"lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상";
|
||||
"lng_new_version_text" = "— 스티커 패널 향상\n— 버그 픽스 및 세부 수정등";
|
||||
"lng_new_version_text" = "This new version includes support for bots using the new bot API, free for everyone. If you're an engineer, create your own bots for games, services or integrations.\n\nLearn more at {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "Probeer nu";
|
||||
|
||||
"lng_status_service_notifications" = "servicemeldingen";
|
||||
"lng_status_bot" = "bot";
|
||||
"lng_status_bot_reads_all" = "toegang tot berichten";
|
||||
"lng_status_bot_not_reads_all" = "geen toegang tot berichten";
|
||||
"lng_status_offline" = "lang geleden gezien";
|
||||
"lng_status_recently" = "recent gezien";
|
||||
"lng_status_last_week" = "afgelopen week gezien";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "Groep is ontoegankelijk";
|
||||
"lng_topbar_info" = "Info";
|
||||
"lng_profile_about_section" = "Over";
|
||||
"lng_profile_settings_section" = "Instellingen";
|
||||
"lng_profile_bot_settings" = "Instellingen";
|
||||
"lng_profile_bot_help" = "Help";
|
||||
"lng_profile_participants_section" = "Deelnemers";
|
||||
"lng_profile_info" = "Contactinformatie";
|
||||
"lng_profile_group_info" = "Groepsinformatie";
|
||||
@@ -343,6 +349,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_clear_history" = "Geschiedenis wissen";
|
||||
"lng_profile_send_message" = "Bericht sturen";
|
||||
"lng_profile_share_contact" = "Contact delen";
|
||||
"lng_profile_invite_to_group" = "Groepslid maken";
|
||||
"lng_profile_delete_contact" = "Verwijder";
|
||||
"lng_profile_set_group_photo" = "Foto instellen";
|
||||
"lng_profile_add_participant" = "Lid toevoegen";
|
||||
@@ -455,6 +462,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_willbe_history" = "Kies een chat om te beginnen";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "Jij";
|
||||
"lng_bot_description" = "Wat kan deze bot? ";
|
||||
|
||||
"lng_bot_start" = "Begin";
|
||||
"lng_bot_choose_group" = "Groep kiezen";
|
||||
"lng_bot_no_groups" = "Je hebt geen groepen";
|
||||
"lng_bot_groups_not_found" = "Geen groepen gevonden";
|
||||
"lng_bot_sure_invite" = "De bot toevoegen aan «{group}»?";
|
||||
"lng_bot_already_in_group" = "De bot neemt al deel aan de groep.";
|
||||
|
||||
"lng_typing" = "aan het typen";
|
||||
"lng_user_typing" = "{user} is aan het typen";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram is bijgewerkt naar versie {version}\n\n{changes} \n\nVolledige versiegeschiedenis is hier te vinden:\n{link}";
|
||||
"lng_new_version_minor" = "— Probleemoplossing en andere kleine verbeteringen";
|
||||
"lng_new_version_text" = "— Verbeterd stickerpaneel\n— Probleemoplossing en kleine wijzigingen";
|
||||
"lng_new_version_text" = "Deze versie heeft ondersteuning voor de nieuwe bot-API, gratis voor iedereen. Handig met programmeren? Maak dan je eigen bots voor spelletjes, diensten en integraties. \n\nMeer weten? kijk op: {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_reconnecting_try_now" = "Tentar agora";
|
||||
|
||||
"lng_status_service_notifications" = "notificações de serviço";
|
||||
"lng_status_bot" = "bot";
|
||||
"lng_status_bot_reads_all" = "tem acesso as mensagens";
|
||||
"lng_status_bot_not_reads_all" = "não tem acesso as mensagens";
|
||||
"lng_status_offline" = "visto há muito tempo";
|
||||
"lng_status_recently" = "visto recentemente";
|
||||
"lng_status_last_week" = "visto há uma semana";
|
||||
@@ -333,7 +336,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_profile_chat_unaccessible" = "Grupo inacessível";
|
||||
"lng_topbar_info" = "Info";
|
||||
"lng_profile_about_section" = "Sobre";
|
||||
"lng_profile_settings_section" = "Configurações";
|
||||
"lng_profile_bot_settings" = "Configurações";
|
||||
"lng_profile_bot_help" = "Ajuda";
|
||||
"lng_profile_participants_section" = "Membros";
|
||||
"lng_profile_info" = "Informação de contato";
|
||||
"lng_profile_group_info" = "Informação do grupo";
|
||||
@@ -342,7 +348,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_enable_notifications" = "Notificações";
|
||||
"lng_profile_clear_history" = "Limpar histórico";
|
||||
"lng_profile_send_message" = "Enviar Mensagem";
|
||||
"lng_profile_share_contact" = "Compartilhar Contato";
|
||||
"lng_profile_share_contact" = "Compartilhar";
|
||||
"lng_profile_invite_to_group" = "Adicionar ao Grupo";
|
||||
"lng_profile_delete_contact" = "Apagar";
|
||||
"lng_profile_set_group_photo" = "Definir Foto";
|
||||
"lng_profile_add_participant" = "Adicionar Membro";
|
||||
@@ -450,11 +457,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Enviar";
|
||||
"lng_message_ph" = "Escrever a mensagem..";
|
||||
"lng_record_cancel" = "Solte fora daqui para cancelar";
|
||||
"lng_record_cancel" = "Solte fora desse campo para cancelar";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Selecione um chat para começar a conversar";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
"lng_from_you" = "Você";
|
||||
"lng_bot_description" = "O que esse bot pode fazer?";
|
||||
|
||||
"lng_bot_start" = "Iniciar";
|
||||
"lng_bot_choose_group" = "Escolher Grupo";
|
||||
"lng_bot_no_groups" = "Você não possui grupos";
|
||||
"lng_bot_groups_not_found" = "Nenhum grupo encontrado";
|
||||
"lng_bot_sure_invite" = "Adicionar bot ao «{group}»?";
|
||||
"lng_bot_already_in_group" = "O bot já é um membro do grupo.";
|
||||
|
||||
"lng_typing" = "escrevendo";
|
||||
"lng_user_typing" = "{user} está escrevendo";
|
||||
@@ -585,7 +600,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop foi atualizado para a versão {version}\n\n{changes}\n\nHistórico completo de mudanças disponível aqui:\n{link}";
|
||||
"lng_new_version_minor" = "— Resolução de bugs e outras menores melhorias";
|
||||
"lng_new_version_text" = "— Painel de sticker melhorado\n— Resolução de bugs e pequenas melhorias";
|
||||
"lng_new_version_text" = "Essa nova versão inclui suporte para bots usando a nova API de bots. Se você for um engenheiro, crie seus próprios bots para jogos, serviços ou integrações.\n\nLeia mais em {blog_link}";
|
||||
|
||||
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";
|
||||
|
||||
|
||||
@@ -609,13 +609,22 @@ namespace {
|
||||
mtpDcOptions *_dcOpts = 0;
|
||||
bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
|
||||
switch (blockId) {
|
||||
case dbiDcOption: {
|
||||
case dbiDcOptionOld: {
|
||||
quint32 dcId, port;
|
||||
QString host, ip;
|
||||
stream >> dcId >> host >> ip >> port;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
if (_dcOpts) _dcOpts->insert(dcId, mtpDcOption(dcId, host.toUtf8().constData(), ip.toUtf8().constData(), port));
|
||||
if (_dcOpts) _dcOpts->insert(dcId, mtpDcOption(dcId, 0, ip.toUtf8().constData(), port));
|
||||
} break;
|
||||
|
||||
case dbiDcOption: {
|
||||
quint32 dcIdWithShift, flags, port;
|
||||
QString ip;
|
||||
stream >> dcIdWithShift >> flags >> ip >> port;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
if (_dcOpts) _dcOpts->insert(dcIdWithShift, mtpDcOption(dcIdWithShift % _mtp_internal::dcShift, flags, ip.toUtf8().constData(), port));
|
||||
} break;
|
||||
|
||||
case dbiMaxGroupCount: {
|
||||
@@ -1723,9 +1732,16 @@ namespace Local {
|
||||
if (dcOpts.isEmpty()) {
|
||||
const BuiltInDc *bdcs = builtInDcs();
|
||||
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
|
||||
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port));
|
||||
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, 0, bdcs[i].ip, bdcs[i].port));
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
|
||||
}
|
||||
|
||||
const BuiltInDc *bdcsipv6 = builtInDcsIPv6();
|
||||
for (int i = 0, l = builtInDcsCountIPv6(); i < l; ++i) {
|
||||
int32 flags = MTPDdcOption_flag_ipv6, idWithShift = bdcsipv6[i].id + (flags * _mtp_internal::dcShift);
|
||||
dcOpts.insert(idWithShift, mtpDcOption(bdcsipv6[i].id, flags, bdcsipv6[i].ip, bdcsipv6[i].port));
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 IPv6 connect option: %2:%3").arg(bdcsipv6[i].id).arg(bdcsipv6[i].ip).arg(bdcsipv6[i].port));
|
||||
}
|
||||
}
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
@@ -1759,10 +1775,16 @@ namespace Local {
|
||||
if (dcOpts.isEmpty()) {
|
||||
const BuiltInDc *bdcs = builtInDcs();
|
||||
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
|
||||
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port));
|
||||
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, 0, bdcs[i].ip, bdcs[i].port));
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
|
||||
}
|
||||
|
||||
const BuiltInDc *bdcsipv6 = builtInDcsIPv6();
|
||||
for (int i = 0, l = builtInDcsCountIPv6(); i < l; ++i) {
|
||||
dcOpts.insert(bdcsipv6[i].id + (MTPDdcOption_flag_ipv6 * _mtp_internal::dcShift), mtpDcOption(bdcsipv6[i].id, MTPDdcOption_flag_ipv6, bdcsipv6[i].ip, bdcsipv6[i].port));
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 IPv6 connect option: %2:%3").arg(bdcsipv6[i].id).arg(bdcsipv6[i].ip).arg(bdcsipv6[i].port));
|
||||
}
|
||||
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
@@ -1770,7 +1792,7 @@ namespace Local {
|
||||
quint32 size = 10 * (sizeof(quint32) + sizeof(qint32));
|
||||
for (mtpDcOptions::const_iterator i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) {
|
||||
size += sizeof(quint32) + sizeof(quint32) + sizeof(quint32);
|
||||
size += _stringSize(QString::fromUtf8(i->host.data(), i->host.size())) + _stringSize(QString::fromUtf8(i->ip.data(), i->ip.size()));
|
||||
size += sizeof(quint32) + _stringSize(QString::fromUtf8(i->ip.data(), i->ip.size()));
|
||||
}
|
||||
size += sizeof(quint32) + _stringSize(cLangFile());
|
||||
|
||||
@@ -1794,8 +1816,8 @@ namespace Local {
|
||||
data.stream << quint32(dbiScale) << qint32(cConfigScale());
|
||||
data.stream << quint32(dbiLang) << qint32(cLang());
|
||||
for (mtpDcOptions::const_iterator i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) {
|
||||
data.stream << quint32(dbiDcOption) << quint32(i->id);
|
||||
data.stream << QString::fromUtf8(i->host.data(), i->host.size()) << QString::fromUtf8(i->ip.data(), i->ip.size());
|
||||
data.stream << quint32(dbiDcOption) << quint32(i.key());
|
||||
data.stream << quint32(i->flags) << QString::fromUtf8(i->ip.data(), i->ip.size());
|
||||
data.stream << quint32(i->port);
|
||||
}
|
||||
data.stream << quint32(dbiLangFile) << cLangFile();
|
||||
|
||||
@@ -26,6 +26,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "boxes/stickersetbox.h"
|
||||
#include "boxes/contactsbox.h"
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
@@ -494,9 +495,13 @@ void MainWidget::finishForwarding(History *hist) {
|
||||
if (_toForward.size() < 2) {
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
MsgId newId = clientMsgId();
|
||||
hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(_toForward.cbegin().value()));
|
||||
HistoryMessage *msg = static_cast<HistoryMessage*>(_toForward.cbegin().value());
|
||||
hist->addToBackForwarded(newId, msg);
|
||||
App::historyRegRandom(randomId, newId);
|
||||
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(hist->peer->input, MTP_int(_toForward.cbegin().key()), MTP_long(randomId)), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg->getMedia())) {
|
||||
App::main()->incrementSticker(sticker->document());
|
||||
}
|
||||
} else {
|
||||
QVector<MTPint> ids;
|
||||
QVector<MTPlong> randomIds;
|
||||
@@ -762,19 +767,21 @@ void MainWidget::removeContact(UserData *user) {
|
||||
|
||||
void MainWidget::addParticipants(ChatData *chat, const QVector<UserData*> &users) {
|
||||
for (QVector<UserData*>::const_iterator i = users.cbegin(), e = users.cend(); i != e; ++i) {
|
||||
MTP::send(MTPmessages_AddChatUser(MTP_int(chat->id & 0xFFFFFFFF), (*i)->inputUser, MTP_int(ForwardOnAdd)), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::addParticipantFail, chat), 0, 5);
|
||||
MTP::send(MTPmessages_AddChatUser(MTP_int(chat->id & 0xFFFFFFFF), (*i)->inputUser, MTP_int(ForwardOnAdd)), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::addParticipantFail, *i), 0, 5);
|
||||
}
|
||||
App::wnd()->hideLayer();
|
||||
showPeer(chat->id, 0, false);
|
||||
}
|
||||
|
||||
bool MainWidget::addParticipantFail(ChatData *chat, const RPCError &error) {
|
||||
bool MainWidget::addParticipantFail(UserData *user, const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
ConfirmBox *box = new ConfirmBox(lang(lng_failed_add_participant), true);
|
||||
App::wnd()->showLayer(box);
|
||||
QString text = lang(lng_failed_add_participant);
|
||||
if (error.type() == "USER_LEFT_CHAT") { // trying to return banned user to his group
|
||||
} else if (error.type() == "USER_ALREADY_PARTICIPANT" && user->botInfo) {
|
||||
text = lang(lng_bot_already_in_group);
|
||||
}
|
||||
App::wnd()->showLayer(new ConfirmBox(text, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -817,7 +824,7 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
|
||||
dialogs.removePeer(peer);
|
||||
} else {
|
||||
History *h = App::historyLoaded(peer->id);
|
||||
if (!h->last) {
|
||||
if (!h->lastMsg) {
|
||||
h->addToBack((*v)[0], 0);
|
||||
}
|
||||
}
|
||||
@@ -929,6 +936,10 @@ DialogsIndexed &MainWidget::contactsList() {
|
||||
return dialogs.contactsList();
|
||||
}
|
||||
|
||||
DialogsIndexed &MainWidget::dialogsList() {
|
||||
return dialogs.dialogsList();
|
||||
}
|
||||
|
||||
void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo, WebPageId webPageId) {
|
||||
saveRecentHashtags(text);
|
||||
QString sendingText, leftText = text;
|
||||
@@ -940,7 +951,7 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
|
||||
App::historyRegRandom(randomId, newId);
|
||||
|
||||
MTPstring msgText(MTP_string(sendingText));
|
||||
int32 flags = (hist->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out);
|
||||
int32 flags = newMessageFlags(hist->peer); // unread, out
|
||||
int32 sendFlags = 0;
|
||||
if (replyTo) {
|
||||
flags |= MTPDmessage::flag_reply_to_msg_id;
|
||||
@@ -953,8 +964,8 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
|
||||
WebPageData *page = App::webPage(webPageId);
|
||||
media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill)));
|
||||
}
|
||||
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media));
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup));
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
|
||||
finishForwarding(hist);
|
||||
@@ -963,6 +974,7 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
|
||||
void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo) {
|
||||
readServerHistory(hist, false);
|
||||
hist->loadAround(0);
|
||||
if (history.peer())
|
||||
sendPreparedText(hist, history.prepareMessage(text), replyTo);
|
||||
}
|
||||
|
||||
@@ -1012,6 +1024,14 @@ void MainWidget::stopAnimActive() {
|
||||
history.stopAnimActive();
|
||||
}
|
||||
|
||||
void MainWidget::sendBotCommand(const QString &cmd, MsgId replyTo) {
|
||||
history.sendBotCommand(cmd, replyTo);
|
||||
}
|
||||
|
||||
void MainWidget::insertBotCommand(const QString &cmd) {
|
||||
history.insertBotCommand(cmd);
|
||||
}
|
||||
|
||||
void MainWidget::searchMessages(const QString &query) {
|
||||
App::wnd()->hideMediaview();
|
||||
dialogs.searchMessages(query);
|
||||
@@ -1575,7 +1595,7 @@ void MainWidget::serviceNotification(const QString &msg, const MTPMessageMedia &
|
||||
QString sendingText, leftText = msg;
|
||||
HistoryItem *item = 0;
|
||||
while (textSplit(sendingText, leftText, MaxMessageSize)) {
|
||||
item = App::histories().addToBack(MTP_message(MTP_int(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(MTP::authedId())), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(sendingText), media), unread ? 1 : 2);
|
||||
item = App::histories().addToBack(MTP_message(MTP_int(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(MTP::authedId())), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(sendingText), media, MTPnullMarkup), unread ? 1 : 2);
|
||||
}
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
@@ -1765,6 +1785,7 @@ void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) {
|
||||
if (profile || overview) {
|
||||
if (profile) {
|
||||
profile->hide();
|
||||
profile->clear();
|
||||
profile->deleteLater();
|
||||
profile->rpcInvalidate();
|
||||
profile = 0;
|
||||
@@ -1888,6 +1909,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool
|
||||
}
|
||||
if (profile) {
|
||||
profile->hide();
|
||||
profile->clear();
|
||||
profile->deleteLater();
|
||||
profile->rpcInvalidate();
|
||||
profile = 0;
|
||||
@@ -1935,6 +1957,7 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop,
|
||||
}
|
||||
if (profile) {
|
||||
profile->hide();
|
||||
profile->clear();
|
||||
profile->deleteLater();
|
||||
profile->rpcInvalidate();
|
||||
}
|
||||
@@ -2059,6 +2082,10 @@ void MainWidget::historyWasRead() {
|
||||
history.historyWasRead(false);
|
||||
}
|
||||
|
||||
void MainWidget::historyCleared(History *hist) {
|
||||
history.historyCleared(hist);
|
||||
}
|
||||
|
||||
void MainWidget::animShow(const QPixmap &bgAnimCache, bool back) {
|
||||
_bgAnimCache = bgAnimCache;
|
||||
|
||||
@@ -2491,7 +2518,7 @@ void MainWidget::mtpPing() {
|
||||
}
|
||||
|
||||
void MainWidget::start(const MTPUser &user) {
|
||||
int32 uid = user.c_userSelf().vid.v;
|
||||
int32 uid = user.c_user().vid.v;
|
||||
if (MTP::authedId() != uid) {
|
||||
MTP::authed(uid);
|
||||
Local::writeMtpData();
|
||||
@@ -2519,35 +2546,42 @@ bool MainWidget::started() {
|
||||
void MainWidget::openLocalUrl(const QString &url) {
|
||||
QString u(url.trimmed());
|
||||
if (u.startsWith(QLatin1String("tg://resolve"), Qt::CaseInsensitive)) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://resolve/?\\?domain=([a-zA-Z0-9\\.\\_]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://resolve/?\\?domain=([a-zA-Z0-9\\.\\_]+)(&(start|startgroup)=([a-zA-Z0-9\\.\\_\\-]+))?(&|$)"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
if (m.hasMatch()) {
|
||||
openUserByName(m.captured(1));
|
||||
QString start = m.captured(3), startToken = m.captured(4);
|
||||
openUserByName(m.captured(1), (start == qsl("startgroup")), startToken);
|
||||
}
|
||||
} else if (u.startsWith(QLatin1String("tg://join"), Qt::CaseInsensitive)) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
if (m.hasMatch()) {
|
||||
joinGroupByHash(m.captured(1));
|
||||
}
|
||||
} else if (u.startsWith(QLatin1String("tg://addstickers"), Qt::CaseInsensitive)) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)(&|$)"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
if (m.hasMatch()) {
|
||||
stickersBox(MTP_inputStickerSetShortName(MTP_string(m.captured(1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::openUserByName(const QString &username, bool toProfile) {
|
||||
void MainWidget::openUserByName(const QString &username, bool toProfile, const QString &startToken) {
|
||||
App::wnd()->hideMediaview();
|
||||
|
||||
UserData *user = App::userByName(username);
|
||||
if (user) {
|
||||
if (toProfile) {
|
||||
showPeerProfile(user);
|
||||
if (user->botInfo && !user->botInfo->cantJoinGroups && !startToken.isEmpty()) {
|
||||
user->botInfo->startGroupToken = startToken;
|
||||
App::wnd()->showLayer(new ContactsBox(user));
|
||||
} else {
|
||||
showPeerProfile(user);
|
||||
}
|
||||
} else {
|
||||
if (user->botInfo) user->botInfo->startToken = startToken;
|
||||
emit showPeerAsync(user->id, 0, false, true);
|
||||
}
|
||||
} else {
|
||||
MTP::send(MTPcontacts_ResolveUsername(MTP_string(username)), rpcDone(&MainWidget::usernameResolveDone, toProfile), rpcFail(&MainWidget::usernameResolveFail, username));
|
||||
MTP::send(MTPcontacts_ResolveUsername(MTP_string(username)), rpcDone(&MainWidget::usernameResolveDone, qMakePair(toProfile, startToken)), rpcFail(&MainWidget::usernameResolveFail, username));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2568,13 +2602,19 @@ void MainWidget::onStickersInstalled(uint64 setId) {
|
||||
history.stickersInstalled(setId);
|
||||
}
|
||||
|
||||
void MainWidget::usernameResolveDone(bool toProfile, const MTPUser &user) {
|
||||
void MainWidget::usernameResolveDone(QPair<bool, QString> toProfileStartToken, const MTPUser &result) {
|
||||
App::wnd()->hideLayer();
|
||||
UserData *u = App::feedUsers(MTP_vector<MTPUser>(1, user));
|
||||
if (toProfile) {
|
||||
showPeerProfile(u);
|
||||
UserData *user = App::feedUsers(MTP_vector<MTPUser>(1, result));
|
||||
if (toProfileStartToken.first) {
|
||||
if (user->botInfo && !user->botInfo->cantJoinGroups && !toProfileStartToken.second.isEmpty()) {
|
||||
user->botInfo->startGroupToken = toProfileStartToken.second;
|
||||
App::wnd()->showLayer(new ContactsBox(user));
|
||||
} else {
|
||||
showPeerProfile(user);
|
||||
}
|
||||
} else {
|
||||
showPeer(u->id, 0, false, true);
|
||||
if (user->botInfo) user->botInfo->startToken = toProfileStartToken.second;
|
||||
showPeer(user->id, 0, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2666,7 +2706,7 @@ bool MainWidget::inviteImportFail(const RPCError &error) {
|
||||
|
||||
void MainWidget::startFull(const MTPVector<MTPUser> &users) {
|
||||
const QVector<MTPUser> &v(users.c_vector().v);
|
||||
if (v.isEmpty() || v[0].type() != mtpc_userSelf) { // wtf?..
|
||||
if (v.isEmpty() || v[0].type() != mtpc_user || !(v[0].c_user().vflags.v & MTPDuser_flag_self)) { // wtf?..
|
||||
return App::logOut();
|
||||
}
|
||||
start(v[0]);
|
||||
@@ -3054,7 +3094,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
|
||||
return;
|
||||
}
|
||||
bool out = (d.vflags.v & MTPDmessage_flag_out);
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(d.vflags, d.vid, out ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(out ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty()));
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(d.vflags, d.vid, out ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(out ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup));
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
@@ -3074,7 +3114,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
|
||||
_byPtsUpdates.insert(ptsKey(SkippedUpdates), updates);
|
||||
return;
|
||||
}
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(d.vflags, d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty()));
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(d.vflags, d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup));
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
@@ -3219,7 +3259,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateChatParticipants: {
|
||||
const MTPDupdateChatParticipants &d(update.c_updateChatParticipants());
|
||||
App::feedParticipants(d.vparticipants);
|
||||
App::feedParticipants(d.vparticipants, true);
|
||||
} break;
|
||||
|
||||
case mtpc_updateChatParticipantAdd: {
|
||||
|
||||
@@ -187,7 +187,7 @@ public:
|
||||
void start(const MTPUser &user);
|
||||
|
||||
void openLocalUrl(const QString &str);
|
||||
void openUserByName(const QString &name, bool toProfile = false);
|
||||
void openUserByName(const QString &name, bool toProfile = false, const QString &startToken = QString());
|
||||
void joinGroupByHash(const QString &hash);
|
||||
void stickersBox(const MTPInputStickerSet &set);
|
||||
|
||||
@@ -216,6 +216,7 @@ public:
|
||||
void dialogsToUp();
|
||||
void newUnreadMsg(History *history, HistoryItem *item);
|
||||
void historyWasRead();
|
||||
void historyCleared(History *history);
|
||||
|
||||
void peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg);
|
||||
void peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg);
|
||||
@@ -270,7 +271,7 @@ public:
|
||||
void removeContact(UserData *user);
|
||||
|
||||
void addParticipants(ChatData *chat, const QVector<UserData*> &users);
|
||||
bool addParticipantFail(ChatData *chat, const RPCError &e);
|
||||
bool addParticipantFail(UserData *user, const RPCError &e);
|
||||
|
||||
void kickParticipant(ChatData *chat, UserData *user);
|
||||
bool kickParticipantFail(ChatData *chat, const RPCError &e);
|
||||
@@ -285,6 +286,7 @@ public:
|
||||
void clearSelectedItems();
|
||||
|
||||
DialogsIndexed &contactsList();
|
||||
DialogsIndexed &dialogsList();
|
||||
|
||||
void sendMessage(History *history, const QString &text, MsgId replyTo);
|
||||
void sendPreparedText(History *hist, const QString &text, MsgId replyTo, WebPageId webPageId = 0);
|
||||
@@ -295,6 +297,9 @@ public:
|
||||
uint64 animActiveTime() const;
|
||||
void stopAnimActive();
|
||||
|
||||
void sendBotCommand(const QString &cmd, MsgId msgId);
|
||||
void insertBotCommand(const QString &cmd);
|
||||
|
||||
void searchMessages(const QString &query);
|
||||
void preloadOverviews(PeerData *peer);
|
||||
void mediaOverviewUpdated(PeerData *peer);
|
||||
@@ -447,7 +452,7 @@ private:
|
||||
void handleUpdates(const MTPUpdates &updates);
|
||||
bool updateFail(const RPCError &e);
|
||||
|
||||
void usernameResolveDone(bool toProfile, const MTPUser &user);
|
||||
void usernameResolveDone(QPair<bool, QString> toProfileStartToken, const MTPUser &result);
|
||||
bool usernameResolveFail(QString name, const RPCError &error);
|
||||
|
||||
void inviteCheckDone(QString hash, const MTPChatInvite &invite);
|
||||
|
||||
@@ -743,8 +743,9 @@ void MTPautoConnection::disconnectFromServer() {
|
||||
status = FinishedWork;
|
||||
}
|
||||
|
||||
void MTPautoConnection::connectToServer(const QString &addr, int32 port) {
|
||||
address = QUrl(qsl("http://%1:%2/api").arg(addr).arg(80));//not port - always 80 port for http transport
|
||||
void MTPautoConnection::connectToServer(const QString &addr, int32 port, int32 flags) {
|
||||
address = QUrl(((flags & MTPDdcOption_flag_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport
|
||||
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
|
||||
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
|
||||
|
||||
_addr = addr;
|
||||
@@ -943,7 +944,7 @@ void MTPtcpConnection::disconnectFromServer() {
|
||||
sock.close();
|
||||
}
|
||||
|
||||
void MTPtcpConnection::connectToServer(const QString &addr, int32 port) {
|
||||
void MTPtcpConnection::connectToServer(const QString &addr, int32 port, int32 flags) {
|
||||
connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead()));
|
||||
sock.connectToHost(QHostAddress(addr), port);
|
||||
}
|
||||
@@ -1013,8 +1014,9 @@ void MTPhttpConnection::disconnectFromServer() {
|
||||
address = QUrl();
|
||||
}
|
||||
|
||||
void MTPhttpConnection::connectToServer(const QString &addr, int32 p) {
|
||||
address = QUrl(qsl("http://%1:%2/api").arg(addr).arg(80));//not p - always 80 port for http transport
|
||||
void MTPhttpConnection::connectToServer(const QString &addr, int32 p, int32 flags) {
|
||||
address = QUrl(((flags & MTPDdcOption_flag_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport
|
||||
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
|
||||
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
|
||||
emit connected();
|
||||
}
|
||||
@@ -1062,19 +1064,30 @@ QString MTPhttpConnection::transport() const {
|
||||
return qsl("HTTP");
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::createConn() {
|
||||
if (conn) {
|
||||
conn->deleteLater();
|
||||
void MTProtoConnectionPrivate::createConn(bool createIPv4, bool createIPv6) {
|
||||
destroyConn();
|
||||
if (createIPv4) {
|
||||
if (cConnectionType() == dbictAuto) {
|
||||
_conn4 = new MTPautoConnection(thread());
|
||||
} else if (cConnectionType() == dbictTcpProxy) {
|
||||
_conn4 = new MTPtcpConnection(thread());
|
||||
} else {
|
||||
_conn4 = new MTPhttpConnection(thread());
|
||||
}
|
||||
connect(_conn4, SIGNAL(error(bool)), this, SLOT(onError4(bool)));
|
||||
connect(_conn4, SIGNAL(receivedSome()), this, SLOT(onReceivedSome()));
|
||||
}
|
||||
if (cConnectionType() == dbictAuto) {
|
||||
conn = new MTPautoConnection(thread());
|
||||
} else if (cConnectionType() == dbictTcpProxy) {
|
||||
conn = new MTPtcpConnection(thread());
|
||||
} else {
|
||||
conn = new MTPhttpConnection(thread());
|
||||
if (createIPv6) {
|
||||
if (cConnectionType() == dbictAuto) {
|
||||
_conn6 = new MTPautoConnection(thread());
|
||||
} else if (cConnectionType() == dbictTcpProxy) {
|
||||
_conn6 = new MTPtcpConnection(thread());
|
||||
} else {
|
||||
_conn6 = new MTPhttpConnection(thread());
|
||||
}
|
||||
connect(_conn6, SIGNAL(error(bool)), this, SLOT(onError6(bool)));
|
||||
connect(_conn6, SIGNAL(receivedSome()), this, SLOT(onReceivedSome()));
|
||||
}
|
||||
connect(conn, SIGNAL(error(bool)), this, SLOT(onError(bool)));
|
||||
connect(conn, SIGNAL(receivedSome()), this, SLOT(onReceivedSome()));
|
||||
firstSentAt = 0;
|
||||
if (oldConnection) {
|
||||
oldConnection = false;
|
||||
@@ -1083,17 +1096,35 @@ void MTProtoConnectionPrivate::createConn() {
|
||||
oldConnectionTimer.start(MTPConnectionOldTimeout);
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::destroyConn(MTPabstractConnection **conn) {
|
||||
if (conn) {
|
||||
if (*conn) {
|
||||
disconnect(*conn, SIGNAL(disconnected()), 0, 0);
|
||||
disconnect(*conn, SIGNAL(receivedData()), 0, 0);
|
||||
disconnect(*conn, SIGNAL(receivedSome()), 0, 0);
|
||||
|
||||
(*conn)->disconnectFromServer();
|
||||
(*conn)->deleteLater();
|
||||
*conn = 0;
|
||||
}
|
||||
} else {
|
||||
destroyConn(&_conn4);
|
||||
destroyConn(&_conn6);
|
||||
_conn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConnection *owner, MTPSessionData *data, uint32 _dc)
|
||||
: QObject(0)
|
||||
, _state(MTProtoConnection::Disconnected)
|
||||
, _needSessionReset(false)
|
||||
, dc(_dc)
|
||||
, _owner(owner)
|
||||
, conn(0)
|
||||
, _conn(0), _conn4(0), _conn6(0)
|
||||
, retryTimeout(1)
|
||||
, oldConnection(true)
|
||||
, receiveDelay(MTPMinReceiveDelay)
|
||||
, connectDelay(MTPMinConnectDelay)
|
||||
, _waitForReceived(MTPMinReceiveDelay)
|
||||
, _waitForConnected(MTPMinConnectDelay)
|
||||
, firstSentAt(-1)
|
||||
, _pingId(0)
|
||||
, _pingIdToSend(0)
|
||||
@@ -1107,14 +1138,13 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
|
||||
, authKeyStrings(0) {
|
||||
|
||||
oldConnectionTimer.moveToThread(thread);
|
||||
cantConnectTimer.moveToThread(thread);
|
||||
connCheckTimer.moveToThread(thread);
|
||||
_waitForConnectedTimer.moveToThread(thread);
|
||||
_waitForReceivedTimer.moveToThread(thread);
|
||||
_waitForIPv4Timer.moveToThread(thread);
|
||||
_pingSender.moveToThread(thread);
|
||||
retryTimer.moveToThread(thread);
|
||||
moveToThread(thread);
|
||||
|
||||
// createConn();
|
||||
|
||||
if (!dc) {
|
||||
QReadLocker lock(mtpDcOptionsMutex());
|
||||
const mtpDcOptions &options(cDcOptions());
|
||||
@@ -1131,8 +1161,9 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
|
||||
connect(thread, SIGNAL(finished()), this, SLOT(doFinish()));
|
||||
|
||||
connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer()));
|
||||
connect(&connCheckTimer, SIGNAL(timeout()), this, SLOT(onBadConnection()));
|
||||
connect(&cantConnectTimer, SIGNAL(timeout()), this, SLOT(onCantConnect()));
|
||||
connect(&_waitForConnectedTimer, SIGNAL(timeout()), this, SLOT(onWaitConnectedFailed()));
|
||||
connect(&_waitForReceivedTimer, SIGNAL(timeout()), this, SLOT(onWaitReceivedFailed()));
|
||||
connect(&_waitForIPv4Timer, SIGNAL(timeout()), this, SLOT(onWaitIPv4Failed()));
|
||||
connect(&oldConnectionTimer, SIGNAL(timeout()), this, SLOT(onOldConnection()));
|
||||
connect(&_pingSender, SIGNAL(timeout()), this, SLOT(onPingSender()));
|
||||
connect(sessionData->owner(), SIGNAL(authKeyCreated()), this, SLOT(updateAuthKey()), Qt::QueuedConnection);
|
||||
@@ -1183,10 +1214,10 @@ int32 MTProtoConnectionPrivate::getState() const {
|
||||
}
|
||||
|
||||
QString MTProtoConnectionPrivate::transport() const {
|
||||
if (!conn || _state < 0) {
|
||||
if ((!_conn4 && !_conn6) || _state < 0) {
|
||||
return QString();
|
||||
}
|
||||
return conn->transport();
|
||||
return (_conn4 ? _conn4 : _conn6)->transport();
|
||||
}
|
||||
|
||||
bool MTProtoConnectionPrivate::setState(int32 state, int32 ifState) {
|
||||
@@ -1421,7 +1452,7 @@ mtpMsgId MTProtoConnectionPrivate::placeToContainer(mtpRequest &toSendRequest, m
|
||||
|
||||
void MTProtoConnectionPrivate::tryToSend() {
|
||||
QReadLocker lockFinished(&sessionDataMutex);
|
||||
if (!sessionData || !conn) {
|
||||
if (!sessionData || !_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1513,7 +1544,7 @@ void MTProtoConnectionPrivate::tryToSend() {
|
||||
stateRequest->msDate = getms(true); // > 0 - can send without container
|
||||
stateRequest->requestId = reqid();// add to haveSent / wereAcked maps, but don't add to requestMap
|
||||
}
|
||||
if (conn->usingHttpWait()) {
|
||||
if (_conn->usingHttpWait()) {
|
||||
MTPHttpWait req(MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000)));
|
||||
|
||||
httpWaitRequest = mtpRequestData::prepare(req.innerLength() >> 2);
|
||||
@@ -1734,47 +1765,63 @@ void MTProtoConnectionPrivate::restartNow() {
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::socketStart(bool afterConfig) {
|
||||
if (!conn) createConn();
|
||||
retryTimer.stop();
|
||||
cantConnectTimer.stop();
|
||||
|
||||
if (conn->isConnected()) {
|
||||
onConnected();
|
||||
return;
|
||||
}
|
||||
setState(MTProtoConnection::Connecting);
|
||||
_pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0;
|
||||
_pingSender.stop();
|
||||
|
||||
std::string ip;
|
||||
uint32 port = 0;
|
||||
int32 flags4 = 0, flags6 = 0;
|
||||
std::string ip4, ip6;
|
||||
uint32 port4 = 0, port6 = 0;
|
||||
{
|
||||
QReadLocker lock(mtpDcOptionsMutex());
|
||||
const mtpDcOptions &options(cDcOptions());
|
||||
mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift);
|
||||
DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc));
|
||||
if (dcIndex != options.cend()) {
|
||||
ip = dcIndex->ip;
|
||||
port = dcIndex->port;
|
||||
mtpDcOptions::const_iterator dcIndex4 = options.constFind(dc % _mtp_internal::dcShift);
|
||||
if (dcIndex4 != options.cend()) {
|
||||
ip4 = dcIndex4->ip;
|
||||
flags4 = dcIndex4->flags;
|
||||
port4 = dcIndex4->port;
|
||||
}
|
||||
mtpDcOptions::const_iterator dcIndex6 = options.constFind((dc % _mtp_internal::dcShift) + (_mtp_internal::dcShift * MTPDdcOption_flag_ipv6));
|
||||
if (dcIndex6 != options.cend()) {
|
||||
ip6 = dcIndex6->ip;
|
||||
flags6 = dcIndex6->flags;
|
||||
port6 = dcIndex6->port;
|
||||
}
|
||||
}
|
||||
if (!port || ip.empty()) {
|
||||
bool noIPv4 = (!port4 || ip4.empty()), noIPv6 = (!port6 || ip6.empty());
|
||||
if (noIPv4 && noIPv6) {
|
||||
if (afterConfig) {
|
||||
LOG(("MTP Error: DC %1 options not found right after config load!").arg(dc));
|
||||
if (noIPv4) LOG(("MTP Error: DC %1 options for IPv4 not found right after config load!").arg(dc));
|
||||
if (noIPv6) LOG(("MTP Error: DC %1 options for IPv6 not found right after config load!").arg(dc));
|
||||
return restart();
|
||||
}
|
||||
DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc));
|
||||
if (noIPv4) DEBUG_LOG(("MTP Info: DC %1 options for IPv4 not found, waiting for config").arg(dc));
|
||||
if (noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 not found, waiting for config").arg(dc));
|
||||
connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded()));
|
||||
mtpConfigLoader()->load();
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip.c_str()).arg(port));
|
||||
|
||||
connect(conn, SIGNAL(connected()), this, SLOT(onConnected()));
|
||||
connect(conn, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
if (afterConfig && (_conn4 || _conn6)) return;
|
||||
|
||||
cantConnectTimer.start(connectDelay);
|
||||
conn->connectToServer(ip.c_str(), port);
|
||||
createConn(!noIPv4, !noIPv6);
|
||||
retryTimer.stop();
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
setState(MTProtoConnection::Connecting);
|
||||
_pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0;
|
||||
_pingSender.stop();
|
||||
|
||||
if (!noIPv4) DEBUG_LOG(("MTP Info: creating IPv4 connection to %1:%2..").arg(ip4.c_str()).arg(port4));
|
||||
if (!noIPv6) DEBUG_LOG(("MTP Info: creating IPv6 connection to [%1]:%2..").arg(ip6.c_str()).arg(port6));
|
||||
|
||||
_waitForConnectedTimer.start(_waitForConnected);
|
||||
if (_conn4) {
|
||||
connect(_conn4, SIGNAL(connected()), this, SLOT(onConnected4()));
|
||||
connect(_conn4, SIGNAL(disconnected()), this, SLOT(onDisconnected4()));
|
||||
_conn4->connectToServer(ip4.c_str(), port4, flags4);
|
||||
}
|
||||
if (_conn6) {
|
||||
connect(_conn6, SIGNAL(connected()), this, SLOT(onConnected6()));
|
||||
connect(_conn6, SIGNAL(disconnected()), this, SLOT(onDisconnected6()));
|
||||
_conn6->connectToServer(ip6.c_str(), port6, flags6);
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::restart(bool maybeBadKey) {
|
||||
@@ -1783,8 +1830,8 @@ void MTProtoConnectionPrivate::restart(bool maybeBadKey) {
|
||||
|
||||
DEBUG_LOG(("MTP Info: restarting MTProtoConnection, maybe bad key = %1").arg(logBool(maybeBadKey)));
|
||||
|
||||
connCheckTimer.stop();
|
||||
cantConnectTimer.stop();
|
||||
_waitForReceivedTimer.stop();
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
mtpAuthKeyPtr key(sessionData->getKey());
|
||||
if (key) {
|
||||
@@ -1812,12 +1859,12 @@ void MTProtoConnectionPrivate::restart(bool maybeBadKey) {
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onSentSome(uint64 size) {
|
||||
if (!connCheckTimer.isActive()) {
|
||||
uint64 remain = receiveDelay;
|
||||
if (!_waitForReceivedTimer.isActive()) {
|
||||
uint64 remain = _waitForReceived;
|
||||
if (!oldConnection) {
|
||||
uint64 remainBySize = size * receiveDelay / 8192; // 8kb / sec, so 512 kb give 64 sec
|
||||
uint64 remainBySize = size * _waitForReceived / 8192; // 8kb / sec, so 512 kb give 64 sec
|
||||
remain = snap(remainBySize, remain, uint64(MTPMaxReceiveDelay));
|
||||
if (remain != receiveDelay) {
|
||||
if (remain != _waitForReceived) {
|
||||
DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain));
|
||||
}
|
||||
}
|
||||
@@ -1826,7 +1873,7 @@ void MTProtoConnectionPrivate::onSentSome(uint64 size) {
|
||||
} else if (dc >= MTP::dld[0] && dc < MTP::dld[MTPDownloadSessionsCount - 1] + _mtp_internal::dcShift) {
|
||||
remain *= MTPDownloadSessionsCount;
|
||||
}
|
||||
connCheckTimer.start(remain);
|
||||
_waitForReceivedTimer.start(remain);
|
||||
}
|
||||
if (!firstSentAt) firstSentAt = getms(true);
|
||||
}
|
||||
@@ -1837,20 +1884,20 @@ void MTProtoConnectionPrivate::onReceivedSome() {
|
||||
DEBUG_LOG(("This connection marked as not old!"));
|
||||
}
|
||||
oldConnectionTimer.start(MTPConnectionOldTimeout);
|
||||
connCheckTimer.stop();
|
||||
_waitForReceivedTimer.stop();
|
||||
if (firstSentAt > 0) {
|
||||
int32 ms = getms(true) - firstSentAt;
|
||||
DEBUG_LOG(("MTP Info: response in %1ms, receiveDelay: %2ms").arg(ms).arg(receiveDelay));
|
||||
DEBUG_LOG(("MTP Info: response in %1ms, _waitForReceived: %2ms").arg(ms).arg(_waitForReceived));
|
||||
|
||||
if (ms > 0 && ms * 2 < int32(receiveDelay)) receiveDelay = qMax(ms * 2, int32(MTPMinReceiveDelay));
|
||||
if (ms > 0 && ms * 2 < int32(_waitForReceived)) _waitForReceived = qMax(ms * 2, int32(MTPMinReceiveDelay));
|
||||
firstSentAt = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onOldConnection() {
|
||||
oldConnection = true;
|
||||
receiveDelay = MTPMinReceiveDelay;
|
||||
DEBUG_LOG(("This connection marked as old! delay now %1ms").arg(receiveDelay));
|
||||
_waitForReceived = MTPMinReceiveDelay;
|
||||
DEBUG_LOG(("This connection marked as old! _waitForReceived now %1ms").arg(_waitForReceived));
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onPingSender() {
|
||||
@@ -1874,14 +1921,14 @@ void MTProtoConnectionPrivate::onPingSendForce() {
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onBadConnection() {
|
||||
void MTProtoConnectionPrivate::onWaitReceivedFailed() {
|
||||
if (cConnectionType() != dbictAuto && cConnectionType() != dbictTcpProxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("MTP Info: bad connection, delay: %1ms").arg(receiveDelay));
|
||||
if (receiveDelay < MTPMaxReceiveDelay) {
|
||||
receiveDelay *= 2;
|
||||
DEBUG_LOG(("MTP Info: bad connection, _waitForReceived: %1ms").arg(_waitForReceived));
|
||||
if (_waitForReceived < MTPMaxReceiveDelay) {
|
||||
_waitForReceived *= 2;
|
||||
}
|
||||
doDisconnect();
|
||||
restarted = true;
|
||||
@@ -1891,9 +1938,9 @@ void MTProtoConnectionPrivate::onBadConnection() {
|
||||
QTimer::singleShot(0, this, SLOT(socketStart()));
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onCantConnect() {
|
||||
DEBUG_LOG(("MTP Info: can't connect in %1ms").arg(connectDelay));
|
||||
if (connectDelay < MTPMaxConnectDelay) connectDelay *= 2;
|
||||
void MTProtoConnectionPrivate::onWaitConnectedFailed() {
|
||||
DEBUG_LOG(("MTP Info: can't connect in %1ms").arg(_waitForConnected));
|
||||
if (_waitForConnected < MTPMaxConnectDelay) _waitForConnected *= 2;
|
||||
|
||||
doDisconnect();
|
||||
restarted = true;
|
||||
@@ -1902,16 +1949,21 @@ void MTProtoConnectionPrivate::onCantConnect() {
|
||||
QTimer::singleShot(0, this, SLOT(socketStart()));
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::doDisconnect() {
|
||||
if (conn) {
|
||||
disconnect(conn, SIGNAL(disconnected()), 0, 0);
|
||||
disconnect(conn, SIGNAL(receivedData()), 0, 0);
|
||||
disconnect(conn, SIGNAL(receivedSome()), 0, 0);
|
||||
void MTProtoConnectionPrivate::onWaitIPv4Failed() {
|
||||
_conn = _conn6;
|
||||
destroyConn(&_conn4);
|
||||
|
||||
if (_conn) {
|
||||
DEBUG_LOG(("MTP Info: can't connect through IPv4, using IPv6 connection."));
|
||||
|
||||
conn->disconnectFromServer();
|
||||
conn->deleteLater();
|
||||
conn = 0;
|
||||
updateAuthKey();
|
||||
} else {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::doDisconnect() {
|
||||
destroyConn();
|
||||
|
||||
unlockKey();
|
||||
|
||||
@@ -1946,9 +1998,8 @@ void MTProtoConnectionPrivate::handleReceived() {
|
||||
return restart();
|
||||
}
|
||||
|
||||
|
||||
while (conn->received().size()) {
|
||||
const mtpBuffer &encryptedBuf(conn->received().front());
|
||||
while (_conn->received().size()) {
|
||||
const mtpBuffer &encryptedBuf(_conn->received().front());
|
||||
uint32 len = encryptedBuf.size();
|
||||
const mtpPrime *encrypted(encryptedBuf.data());
|
||||
if (len < 18) { // 2 auth_key_id, 4 msg_key, 2 salt, 2 session, 2 msg_id, 1 seq_no, 1 length, (1 data + 3 padding) min
|
||||
@@ -1976,14 +2027,14 @@ void MTProtoConnectionPrivate::handleReceived() {
|
||||
if (uint32(dataBuffer.size()) < msgLen + 8 * sizeof(mtpPrime) || (msgLen & 0x03)) {
|
||||
LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(msgLen).arg(dataBuffer.size()));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(mb(encrypted, len * sizeof(mtpPrime)).str()));
|
||||
conn->received().pop_front();
|
||||
_conn->received().pop_front();
|
||||
return restart();
|
||||
}
|
||||
uchar sha1Buffer[20];
|
||||
if (memcmp(&msgKey, hashSha1(data, msgLen + 8 * sizeof(mtpPrime), sha1Buffer) + 1, sizeof(msgKey))) {
|
||||
LOG(("TCP Error: bad SHA1 hash after aesDecrypt in message"));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(mb(encrypted, len * sizeof(mtpPrime)).str()));
|
||||
conn->received().pop_front();
|
||||
_conn->received().pop_front();
|
||||
return restart();
|
||||
}
|
||||
TCP_LOG(("TCP Info: decrypted message %1,%2,%3 is %4 len").arg(msgId).arg(seqNo).arg(logBool(needAck)).arg(msgLen + 8 * sizeof(mtpPrime)));
|
||||
@@ -1992,11 +2043,11 @@ void MTProtoConnectionPrivate::handleReceived() {
|
||||
if (session != serverSession) {
|
||||
LOG(("MTP Error: bad server session received"));
|
||||
TCP_LOG(("MTP Error: bad server session %1 instead of %2 in message received").arg(session).arg(serverSession));
|
||||
conn->received().pop_front();
|
||||
_conn->received().pop_front();
|
||||
return restart();
|
||||
}
|
||||
|
||||
conn->received().pop_front();
|
||||
_conn->received().pop_front();
|
||||
|
||||
int32 serverTime((int32)(msgId >> 32)), clientTime(unixtime());
|
||||
bool isReply = ((msgId & 0x03) == 1);
|
||||
@@ -2079,6 +2130,7 @@ void MTProtoConnectionPrivate::handleReceived() {
|
||||
_needSessionReset = (res < -1);
|
||||
return restart();
|
||||
}
|
||||
retryTimeout = 1; // reset restart() timer
|
||||
|
||||
if (!sessionData->isCheckedKey()) {
|
||||
DEBUG_LOG(("MTP Info: marked auth key as checked"));
|
||||
@@ -2091,7 +2143,7 @@ void MTProtoConnectionPrivate::handleReceived() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (conn->needHttpWait()) {
|
||||
if (_conn->needHttpWait()) {
|
||||
emit sendHttpWaitAsync();
|
||||
}
|
||||
}
|
||||
@@ -2588,8 +2640,6 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
|
||||
}
|
||||
}
|
||||
requestsAcked(ids, true);
|
||||
|
||||
retryTimeout = 1; // reset restart() timer
|
||||
} return 1;
|
||||
|
||||
}
|
||||
@@ -2836,27 +2886,72 @@ void MTProtoConnectionPrivate::resendMany(QVector<quint64> msgIds, quint64 msCan
|
||||
emit resendManyAsync(msgIds, msCanWait, forceContainer, sendMsgStateInfo);
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onConnected() {
|
||||
connectDelay = MTPMinConnectDelay;
|
||||
cantConnectTimer.stop();
|
||||
void MTProtoConnectionPrivate::onConnected4() {
|
||||
_waitForConnected = MTPMinConnectDelay;
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
_waitForIPv4Timer.stop();
|
||||
|
||||
QReadLocker lockFinished(&sessionDataMutex);
|
||||
if (!sessionData) return;
|
||||
|
||||
disconnect(conn, SIGNAL(connected()), this, SLOT(onConnected()));
|
||||
if (!conn->isConnected()) {
|
||||
LOG(("Connection Error: not connected in onConnected(), state: %1").arg(conn->debugState()));
|
||||
disconnect(_conn4, SIGNAL(connected()), this, SLOT(onConnected4()));
|
||||
if (!_conn4->isConnected()) {
|
||||
LOG(("Connection Error: not connected in onConnected4(), state: %1").arg(_conn4->debugState()));
|
||||
return restart();
|
||||
}
|
||||
|
||||
TCP_LOG(("Connection Info: connection succeed."));
|
||||
_conn = _conn4;
|
||||
destroyConn(&_conn6);
|
||||
|
||||
DEBUG_LOG(("MTP Info: connection through IPv4 succeed."));
|
||||
|
||||
updateAuthKey();
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onConnected6() {
|
||||
_waitForConnected = MTPMinConnectDelay;
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
QReadLocker lockFinished(&sessionDataMutex);
|
||||
if (!sessionData) return;
|
||||
|
||||
disconnect(_conn6, SIGNAL(connected()), this, SLOT(onConnected()));
|
||||
if (!_conn6->isConnected()) {
|
||||
LOG(("Connection Error: not connected in onConnected(), state: %1").arg(_conn6->debugState()));
|
||||
return restart();
|
||||
}
|
||||
|
||||
DEBUG_LOG(("MTP Info: connection through IPv6 succeed, waiting IPv4 for %1ms.").arg(MTPIPv4ConnectionWaitTimeout));
|
||||
|
||||
_waitForIPv4Timer.start(MTPIPv4ConnectionWaitTimeout);
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onDisconnected4() {
|
||||
if (_conn && _conn == _conn6) return; // disconnected the unused
|
||||
|
||||
if (_conn || !_conn6) {
|
||||
destroyConn();
|
||||
restart();
|
||||
} else {
|
||||
destroyConn(&_conn4);
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onDisconnected6() {
|
||||
if (_conn && _conn == _conn4) return; // disconnected the unused
|
||||
|
||||
if (_conn || !_conn4) {
|
||||
destroyConn();
|
||||
restart();
|
||||
} else {
|
||||
destroyConn(&_conn6);
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::updateAuthKey() {
|
||||
QReadLocker lockFinished(&sessionDataMutex);
|
||||
if (!sessionData || !conn) return;
|
||||
if (!sessionData || !_conn) return;
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc));
|
||||
uint64 newKeyId = 0;
|
||||
@@ -2899,20 +2994,20 @@ void MTProtoConnectionPrivate::updateAuthKey() {
|
||||
MTPReq_pq req_pq;
|
||||
req_pq.vnonce = authKeyData->nonce;
|
||||
|
||||
connect(conn, SIGNAL(receivedData()), this, SLOT(pqAnswered()));
|
||||
connect(_conn, SIGNAL(receivedData()), this, SLOT(pqAnswered()));
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: sending Req_pq.."));
|
||||
sendRequestNotSecure(req_pq);
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::clearMessages() {
|
||||
if (keyId && keyId != mtpAuthKey::RecreateKeyId && conn) {
|
||||
conn->received().clear();
|
||||
if (keyId && keyId != mtpAuthKey::RecreateKeyId && _conn) {
|
||||
_conn->received().clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::pqAnswered() {
|
||||
disconnect(conn, SIGNAL(receivedData()), this, SLOT(pqAnswered()));
|
||||
disconnect(_conn, SIGNAL(receivedData()), this, SLOT(pqAnswered()));
|
||||
DEBUG_LOG(("AuthKey Info: receiving Req_pq answer.."));
|
||||
|
||||
MTPReq_pq::ResponseType res_pq;
|
||||
@@ -3008,14 +3103,14 @@ void MTProtoConnectionPrivate::pqAnswered() {
|
||||
return restart();
|
||||
}
|
||||
|
||||
connect(conn, SIGNAL(receivedData()), this, SLOT(dhParamsAnswered()));
|
||||
connect(_conn, SIGNAL(receivedData()), this, SLOT(dhParamsAnswered()));
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: sending Req_DH_params.."));
|
||||
sendRequestNotSecure(req_DH_params);
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::dhParamsAnswered() {
|
||||
disconnect(conn, SIGNAL(receivedData()), this, SLOT(dhParamsAnswered()));
|
||||
disconnect(_conn, SIGNAL(receivedData()), this, SLOT(dhParamsAnswered()));
|
||||
DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer.."));
|
||||
|
||||
MTPReq_DH_params::ResponseType res_DH_params;
|
||||
@@ -3190,7 +3285,7 @@ void MTProtoConnectionPrivate::dhClientParamsSend() {
|
||||
|
||||
aesEncrypt(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), authKeyData->aesKey, authKeyData->aesIV);
|
||||
|
||||
connect(conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered()));
|
||||
connect(_conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered()));
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params.."));
|
||||
sendRequestNotSecure(req_client_DH_params);
|
||||
@@ -3200,7 +3295,7 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
|
||||
QReadLocker lockFinished(&sessionDataMutex);
|
||||
if (!sessionData) return;
|
||||
|
||||
disconnect(conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered()));
|
||||
disconnect(_conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered()));
|
||||
DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer.."));
|
||||
|
||||
MTPSet_client_DH_params::ResponseType res_client_DH_params;
|
||||
@@ -3295,7 +3390,7 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
|
||||
void MTProtoConnectionPrivate::authKeyCreated() {
|
||||
clearAuthKeyData();
|
||||
|
||||
connect(conn, SIGNAL(receivedData()), this, SLOT(handleReceived()));
|
||||
connect(_conn, SIGNAL(receivedData()), this, SLOT(handleReceived()));
|
||||
|
||||
if (sessionData->getSalt()) { // else receive salt in bad_server_salt first, then try to send all the requests
|
||||
setState(MTProtoConnection::Connected);
|
||||
@@ -3328,11 +3423,32 @@ void MTProtoConnectionPrivate::clearAuthKeyData() {
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onError(bool mayBeBadKey) {
|
||||
cantConnectTimer.stop();
|
||||
void MTProtoConnectionPrivate::onError4(bool mayBeBadKey) {
|
||||
if (_conn && _conn == _conn6) return; // error in the unused
|
||||
|
||||
MTP_LOG(dc, ("Restarting after error, maybe bad key: %1..").arg(logBool(mayBeBadKey)));
|
||||
return restart(mayBeBadKey);
|
||||
if (_conn || !_conn6) {
|
||||
destroyConn();
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
MTP_LOG(dc, ("Restarting after error in IPv4 connection, maybe bad key: %1..").arg(logBool(mayBeBadKey)));
|
||||
return restart(mayBeBadKey);
|
||||
} else {
|
||||
destroyConn(&_conn4);
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onError6(bool mayBeBadKey) {
|
||||
if (_conn && _conn == _conn4) return; // error in the unused
|
||||
|
||||
if (_conn || !_conn4) {
|
||||
destroyConn();
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
MTP_LOG(dc, ("Restarting after error in IPv6 connection, maybe bad key: %1..").arg(logBool(mayBeBadKey)));
|
||||
return restart(mayBeBadKey);
|
||||
} else {
|
||||
destroyConn(&_conn6);
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoConnectionPrivate::onReadyData() {
|
||||
@@ -3359,7 +3475,7 @@ void MTProtoConnectionPrivate::sendRequestNotSecure(const TRequest &request) {
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: sending request, size: %1, num: %2, time: %3").arg(requestSize).arg(authKeyData->req_num).arg(buffer[5]));
|
||||
|
||||
conn->sendData(buffer);
|
||||
_conn->sendData(buffer);
|
||||
|
||||
onSentSome(buffer.size() * sizeof(mtpPrime));
|
||||
|
||||
@@ -3373,12 +3489,12 @@ bool MTProtoConnectionPrivate::readResponseNotSecure(TResponse &response) {
|
||||
onReceivedSome();
|
||||
|
||||
try {
|
||||
if (conn->received().isEmpty()) {
|
||||
if (_conn->received().isEmpty()) {
|
||||
LOG(("AuthKey Error: trying to read response from empty received list"));
|
||||
return false;
|
||||
}
|
||||
mtpBuffer buffer(conn->received().front());
|
||||
conn->received().pop_front();
|
||||
mtpBuffer buffer(_conn->received().front());
|
||||
_conn->received().pop_front();
|
||||
|
||||
const mtpPrime *answer(buffer.constData());
|
||||
uint32 len = buffer.size();
|
||||
@@ -3449,8 +3565,8 @@ bool MTProtoConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResp
|
||||
|
||||
DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5]));
|
||||
|
||||
conn->setSentEncrypted();
|
||||
conn->sendData(result);
|
||||
_conn->setSentEncrypted();
|
||||
_conn->sendData(result);
|
||||
|
||||
if (needAnyResponse) {
|
||||
onSentSome(result.size() * sizeof(mtpPrime));
|
||||
|
||||
@@ -25,9 +25,27 @@ enum {
|
||||
MTPDmessage_flag_out = (1 << 1),
|
||||
MTPDmessage_flag_notify_by_from = (1 << 4),
|
||||
MTPDmessage_flag_media_unread = (1 << 5),
|
||||
|
||||
MTPmessages_SendMessage_flag_skipWebPage = (1 << 1),
|
||||
|
||||
MTPDdcOption_flag_ipv6 = (1 << 0),
|
||||
MTPDdcOption_flag_files = (1 << 1),
|
||||
|
||||
MTPDuser_flag_self = (1 << 10),
|
||||
MTPDuser_flag_contact = (1 << 11),
|
||||
MTPDuser_flag_mutual_contact = (1 << 12),
|
||||
MTPDuser_flag_deleted = (1 << 13),
|
||||
MTPDuser_flag_bot = (1 << 14),
|
||||
MTPDuser_flag_bot_reads_all = (1 << 15),
|
||||
MTPDuser_flag_bot_cant_join = (1 << 16),
|
||||
|
||||
MTPDreplyKeyboardMarkup_flag_resize = (1 << 0),
|
||||
MTPDreplyKeyboardMarkup_flag_single_use = (1 << 1),
|
||||
MTPDreplyKeyboardMarkup_flag_ZERO = (1 << 31) // client side flag for zeroMarkup
|
||||
};
|
||||
|
||||
static const MTPReplyMarkup MTPnullMarkup = MTP_replyKeyboardMarkup(MTP_int(0), MTP_vector<MTPKeyboardButtonRow>(0));
|
||||
|
||||
#include "mtproto/mtpPublicRSA.h"
|
||||
#include "mtproto/mtpAuthKey.h"
|
||||
|
||||
@@ -97,11 +115,6 @@ public:
|
||||
int32 state() const;
|
||||
QString transport() const;
|
||||
|
||||
/*template <typename TRequest> // not used
|
||||
uint64 sendAsync(const TRequest &request) {
|
||||
return data->sendAsync(request);
|
||||
}*/
|
||||
|
||||
private:
|
||||
|
||||
QThread *thread;
|
||||
@@ -125,7 +138,7 @@ public:
|
||||
|
||||
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
|
||||
virtual void disconnectFromServer() = 0;
|
||||
virtual void connectToServer(const QString &addr, int32 port) = 0;
|
||||
virtual void connectToServer(const QString &addr, int32 port, int32 flags) = 0;
|
||||
virtual bool isConnected() = 0;
|
||||
virtual bool usingHttpWait() {
|
||||
return false;
|
||||
@@ -193,7 +206,7 @@ public:
|
||||
|
||||
void sendData(mtpBuffer &buffer);
|
||||
void disconnectFromServer();
|
||||
void connectToServer(const QString &addr, int32 port);
|
||||
void connectToServer(const QString &addr, int32 port, int32 flags);
|
||||
bool isConnected();
|
||||
bool usingHttpWait();
|
||||
bool needHttpWait();
|
||||
@@ -255,7 +268,7 @@ public:
|
||||
|
||||
void sendData(mtpBuffer &buffer);
|
||||
void disconnectFromServer();
|
||||
void connectToServer(const QString &addr, int32 port);
|
||||
void connectToServer(const QString &addr, int32 port, int32 flags);
|
||||
bool isConnected();
|
||||
|
||||
int32 debugState() const;
|
||||
@@ -281,7 +294,7 @@ public:
|
||||
|
||||
void sendData(mtpBuffer &buffer);
|
||||
void disconnectFromServer();
|
||||
void connectToServer(const QString &addr, int32 port);
|
||||
void connectToServer(const QString &addr, int32 port, int32 flags);
|
||||
bool isConnected();
|
||||
bool usingHttpWait();
|
||||
bool needHttpWait();
|
||||
@@ -343,16 +356,25 @@ public slots:
|
||||
|
||||
void onPingSender();
|
||||
void onPingSendForce();
|
||||
void onBadConnection();
|
||||
void onCantConnect();
|
||||
|
||||
void onWaitConnectedFailed();
|
||||
void onWaitReceivedFailed();
|
||||
void onWaitIPv4Failed();
|
||||
|
||||
void onOldConnection();
|
||||
void onSentSome(uint64 size);
|
||||
void onReceivedSome();
|
||||
|
||||
void onError(bool maybeBadKey = false);
|
||||
void onReadyData();
|
||||
void socketStart(bool afterConfig = false);
|
||||
void onConnected();
|
||||
|
||||
void onConnected4();
|
||||
void onConnected6();
|
||||
void onDisconnected4();
|
||||
void onDisconnected6();
|
||||
void onError4(bool maybeBadKey = false);
|
||||
void onError6(bool maybeBadKey = false);
|
||||
|
||||
void doDisconnect();
|
||||
void doFinish();
|
||||
|
||||
@@ -373,7 +395,8 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
void createConn();
|
||||
void createConn(bool createIPv4, bool createIPv6);
|
||||
void destroyConn(MTPabstractConnection **conn = 0); // 0 - destory all
|
||||
|
||||
mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
|
||||
mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
|
||||
@@ -397,7 +420,7 @@ private:
|
||||
|
||||
uint32 dc;
|
||||
MTProtoConnection *_owner;
|
||||
MTPabstractConnection *conn;
|
||||
MTPabstractConnection *_conn, *_conn4, *_conn6;
|
||||
|
||||
SingleTimer retryTimer; // exp retry timer
|
||||
uint32 retryTimeout;
|
||||
@@ -406,8 +429,8 @@ private:
|
||||
SingleTimer oldConnectionTimer;
|
||||
bool oldConnection;
|
||||
|
||||
SingleTimer connCheckTimer, cantConnectTimer;
|
||||
uint32 receiveDelay, connectDelay;
|
||||
SingleTimer _waitForConnectedTimer, _waitForReceivedTimer, _waitForIPv4Timer;
|
||||
uint32 _waitForReceived, _waitForConnected;
|
||||
int64 firstSentAt;
|
||||
|
||||
QVector<MTPlong> ackRequestData, resendRequestData;
|
||||
|
||||
@@ -366,7 +366,7 @@ static const mtpTypeId mtpLayers[] = {
|
||||
mtpc_invokeWithLayer17,
|
||||
mtpc_invokeWithLayer18,
|
||||
}, mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]);
|
||||
static const mtpPrime mtpCurrentLayer = 29;
|
||||
static const mtpPrime mtpCurrentLayer = 31;
|
||||
|
||||
template <typename bareT>
|
||||
class MTPBoxed : public bareT {
|
||||
|
||||
@@ -175,15 +175,16 @@ void mtpUpdateDcOptions(const QVector<MTPDcOption> &options) {
|
||||
}
|
||||
for (QVector<MTPDcOption>::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) {
|
||||
const MTPDdcOption &optData(i->c_dcOption());
|
||||
if (already.constFind(optData.vid.v) == already.cend()) {
|
||||
already.insert(optData.vid.v);
|
||||
mtpDcOptions::const_iterator a = opts.constFind(optData.vid.v);
|
||||
int32 id = optData.vid.v, idWithShift = id + (optData.vflags.v * _mtp_internal::dcShift);
|
||||
if (already.constFind(idWithShift) == already.cend()) {
|
||||
already.insert(idWithShift);
|
||||
mtpDcOptions::const_iterator a = opts.constFind(idWithShift);
|
||||
if (a != opts.cend()) {
|
||||
if (a.value().ip != optData.vip_address.c_string().v || a.value().port != optData.vport.v) {
|
||||
restart.insert(optData.vid.v);
|
||||
restart.insert(id);
|
||||
}
|
||||
}
|
||||
opts.insert(optData.vid.v, mtpDcOption(optData.vid.v, optData.vhostname.c_string().v, optData.vip_address.c_string().v, optData.vport.v));
|
||||
opts.insert(idWithShift, mtpDcOption(id, optData.vflags.v, optData.vip_address.c_string().v, optData.vport.v));
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
@@ -1109,96 +1109,24 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_userSelf:
|
||||
case mtpc_user:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ userSelf");
|
||||
to.add("{ user");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" first_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" last_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" username: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" phone: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 6: to.add(" status: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_userContact:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ userContact");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" first_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" last_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" username: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" phone: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 6: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 7: to.add(" status: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_userRequest:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ userRequest");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" first_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" last_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" username: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" phone: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 6: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 7: to.add(" status: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_userForeign:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ userForeign");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" first_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" last_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" username: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 6: to.add(" status: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_userDeleted:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ userDeleted");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" first_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" last_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" username: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" access_hash: "); ++stages.back(); if (flag & MTPDuser::flag_access_hash) { types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
|
||||
case 3: to.add(" first_name: "); ++stages.back(); if (flag & MTPDuser::flag_first_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
|
||||
case 4: to.add(" last_name: "); ++stages.back(); if (flag & MTPDuser::flag_last_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break;
|
||||
case 5: to.add(" username: "); ++stages.back(); if (flag & MTPDuser::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 3 IN FIELD flags ]"); } break;
|
||||
case 6: to.add(" phone: "); ++stages.back(); if (flag & MTPDuser::flag_phone) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 4 IN FIELD flags ]"); } break;
|
||||
case 7: to.add(" photo: "); ++stages.back(); if (flag & MTPDuser::flag_photo) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break;
|
||||
case 8: to.add(" status: "); ++stages.back(); if (flag & MTPDuser::flag_status) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
|
||||
case 9: to.add(" bot_info_version: "); ++stages.back(); if (flag & MTPDuser::flag_bot_info_version) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 14 IN FIELD flags ]"); } break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -1347,6 +1275,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
case 2: to.add(" chat_photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" notify_settings: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" exported_invite: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" bot_info: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -1444,6 +1373,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
case 7: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 8: to.add(" message: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 9: to.add(" media: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 10: to.add(" reply_markup: "); ++stages.back(); if (flag & MTPDmessage::flag_reply_markup) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -1893,8 +1823,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" expires: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" user: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 0: to.add(" user: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -2048,8 +1977,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
case 2: to.add(" profile_photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" notify_settings: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" blocked: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" real_first_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 6: to.add(" real_last_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" bot_info: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -2404,6 +2332,10 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
to.add("{ inputMessagesFilterAudio }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
|
||||
break;
|
||||
|
||||
case mtpc_inputMessagesFilterAudioDocuments:
|
||||
to.add("{ inputMessagesFilterAudioDocuments }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
|
||||
break;
|
||||
|
||||
case mtpc_updateNewMessage:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
@@ -3057,8 +2989,8 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" hostname: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" ip_address: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" port: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
@@ -4353,6 +4285,86 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_botCommand:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ botCommand");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" command: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" params: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" description: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_botInfoEmpty:
|
||||
to.add("{ botInfoEmpty }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
|
||||
break;
|
||||
|
||||
case mtpc_botInfo:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ botInfo");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" share_text: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" description: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" commands: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_keyboardButton:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ keyboardButton");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" text: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_keyboardButtonRow:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ keyboardButtonRow");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" buttons: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_replyKeyboardHide:
|
||||
to.add("{ replyKeyboardHide }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
|
||||
break;
|
||||
|
||||
case mtpc_replyKeyboardMarkup:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ replyKeyboardMarkup");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" rows: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_req_pq:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
@@ -5018,6 +5030,22 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_auth_importBotAuthorization:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ auth_importBotAuthorization");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" flags: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" api_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" api_hash: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" bot_auth_token: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_auth_checkPassword:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
@@ -5388,6 +5416,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
case 2: to.add(" reply_to_msg_id: "); ++stages.back(); if (flag & MTPmessages_sendMessage::flag_reply_to_msg_id) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
|
||||
case 3: to.add(" message: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" random_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" reply_markup: "); ++stages.back(); if (flag & MTPmessages_sendMessage::flag_reply_markup) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -5405,6 +5434,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
case 2: to.add(" reply_to_msg_id: "); ++stages.back(); if (flag & MTPmessages_sendMedia::flag_reply_to_msg_id) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
|
||||
case 3: to.add(" media: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 4: to.add(" random_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 5: to.add(" reply_markup: "); ++stages.back(); if (flag & MTPmessages_sendMedia::flag_reply_markup) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
@@ -5539,6 +5569,22 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_messages_startBot:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
} else {
|
||||
to.add("{ messages_startBot");
|
||||
to.add("\n").addSpaces(lev);
|
||||
}
|
||||
switch (stage) {
|
||||
case 0: to.add(" bot: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 1: to.add(" chat_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 2: to.add(" random_id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
case 3: to.add(" start_param: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
|
||||
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mtpc_messages_getChats:
|
||||
if (stage) {
|
||||
to.add(",\n").addSpaces(lev);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -193,11 +193,6 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL
|
||||
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
|
||||
|
||||
userEmpty#200250ba id:int = User;
|
||||
userSelf#1c60e608 id:int first_name:string last_name:string username:string phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userContact#cab35e18 id:int first_name:string last_name:string username:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userRequest#d9ccc4ef id:int first_name:string last_name:string username:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userForeign#75cf7a8 id:int first_name:string last_name:string username:string access_hash:long photo:UserProfilePhoto status:UserStatus = User;
|
||||
userDeleted#d6016d7a id:int first_name:string last_name:string username:string = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
|
||||
@@ -210,7 +205,7 @@ chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#6e9c9bc7 id:int title:string photo:ChatPhoto participants_count:int date:int left:Bool version:int = Chat;
|
||||
chatForbidden#fb0ccc41 id:int title:string date:int = Chat;
|
||||
|
||||
chatFull#cade0791 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite = ChatFull;
|
||||
chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
|
||||
@@ -221,7 +216,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#a7ab1991 flags:# id:int from_id:int to_id:Peer fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int date:int message:string media:MessageMedia = Message;
|
||||
message#c3060325 flags:# id:int from_id:int to_id:Peer fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int date:int message:string media:MessageMedia reply_markup:flags.6?ReplyMarkup = Message;
|
||||
messageService#1d86f70e flags:int id:int from_id:int to_id:Peer date:int action:MessageAction = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
@@ -258,7 +253,7 @@ auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
|
||||
|
||||
auth.sentCode#efed51d9 phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode;
|
||||
|
||||
auth.authorization#f6b673a4 expires:int user:User = auth.Authorization;
|
||||
auth.authorization#ff036af1 user:User = auth.Authorization;
|
||||
|
||||
auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
|
||||
|
||||
@@ -280,7 +275,7 @@ peerNotifySettings#8d5e11ee mute_until:int sound:string show_previews:Bool event
|
||||
|
||||
wallPaper#ccb03657 id:int title:string sizes:Vector<PhotoSize> color:int = WallPaper;
|
||||
|
||||
userFull#771095da user:User link:contacts.Link profile_photo:Photo notify_settings:PeerNotifySettings blocked:Bool real_first_name:string real_last_name:string = UserFull;
|
||||
userFull#5a89ac5b user:User link:contacts.Link profile_photo:Photo notify_settings:PeerNotifySettings blocked:Bool bot_info:BotInfo = UserFull;
|
||||
|
||||
contact#f911c994 user_id:int mutual:Bool = Contact;
|
||||
|
||||
@@ -329,6 +324,7 @@ inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
|
||||
inputMessagesFilterPhotoVideoDocuments#d95e73bb = MessagesFilter;
|
||||
inputMessagesFilterDocument#9eddf188 = MessagesFilter;
|
||||
inputMessagesFilterAudio#cfc87522 = MessagesFilter;
|
||||
inputMessagesFilterAudioDocuments#5afbf764 = MessagesFilter;
|
||||
|
||||
updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
|
||||
updateMessageID#4e90bfd6 id:int random_id:long = Update;
|
||||
@@ -363,7 +359,7 @@ photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
|
||||
|
||||
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
|
||||
|
||||
dcOption#2ec2a43c id:int hostname:string ip_address:string port:int = DcOption;
|
||||
dcOption#5d8c6cc flags:# id:int ip_address:string port:int = DcOption;
|
||||
|
||||
config#4e32b894 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int broadcast_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int disabled_features:Vector<DisabledFeature> = Config;
|
||||
|
||||
@@ -596,6 +592,20 @@ stickerSet#a7a43b17 id:long access_hash:long title:string short_name:string = St
|
||||
|
||||
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
|
||||
|
||||
user#22e49072 flags:# id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int = User;
|
||||
|
||||
botCommand#b79d22ab command:string params:string description:string = BotCommand;
|
||||
|
||||
botInfoEmpty#bb2e37ce = BotInfo;
|
||||
botInfo#9cf585d user_id:int version:int share_text:string description:string commands:Vector<BotCommand> = BotInfo;
|
||||
|
||||
keyboardButton#a2fa4880 text:string = KeyboardButton;
|
||||
|
||||
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
|
||||
|
||||
replyKeyboardHide#ced6ebbc = ReplyMarkup;
|
||||
replyKeyboardMarkup#3502758c flags:# rows:Vector<KeyboardButtonRow> = ReplyMarkup;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -647,8 +657,8 @@ messages.deleteHistory#f4f8fb61 peer:InputPeer offset:int = messages.AffectedHis
|
||||
messages.deleteMessages#a5f18925 id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.sendMessage#9add8f26 flags:# peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long = messages.SentMessage;
|
||||
messages.sendMedia#2d7923b1 flags:# peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long = Updates;
|
||||
messages.sendMessage#fc55e6b5 flags:# peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup = messages.SentMessage;
|
||||
messages.sendMedia#c8f16791 flags:# peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates;
|
||||
messages.forwardMessages#55e1728d peer:InputPeer id:Vector<int> random_id:Vector<long> = Updates;
|
||||
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
|
||||
messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
|
||||
@@ -736,6 +746,8 @@ messages.getAllStickers#aa3bc868 hash:string = messages.AllStickers;
|
||||
|
||||
account.updateDeviceLocked#38df3532 period:int = Bool;
|
||||
|
||||
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
|
||||
|
||||
messages.getWebPagePreview#25223e24 message:string = MessageMedia;
|
||||
|
||||
account.getAuthorizations#e320c158 = account.Authorizations;
|
||||
@@ -756,3 +768,4 @@ messages.importChatInvite#6c50051c hash:string = Updates;
|
||||
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
|
||||
messages.installStickerSet#efbbfae9 stickerset:InputStickerSet = Bool;
|
||||
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
|
||||
messages.startBot#1b3e0ffc bot:InputUser chat_id:int random_id:long start_param:string = Updates;
|
||||
|
||||
@@ -39,9 +39,16 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
|
||||
_addParticipant(this, lang(lng_profile_add_participant), st::btnShareContact),
|
||||
_sendMessage(this, lang(lng_profile_send_message), st::btnShareContact),
|
||||
_shareContact(this, lang(lng_profile_share_contact), st::btnShareContact),
|
||||
_inviteToGroup(this, lang(lng_profile_invite_to_group), st::btnShareContact),
|
||||
_cancelPhoto(this, lang(lng_cancel)),
|
||||
_createInvitationLink(this, lang(lng_group_invite_create)),
|
||||
_invitationLink(this, qsl("telegram.me/joinchat/")),
|
||||
_botSettings(this, lang(lng_profile_bot_settings)),
|
||||
_botHelp(this, lang(lng_profile_bot_help)),
|
||||
|
||||
// about
|
||||
_about(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()),
|
||||
_aboutTop(0), _aboutHeight(0),
|
||||
|
||||
a_photo(0),
|
||||
_photoOver(false),
|
||||
@@ -66,7 +73,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
|
||||
|
||||
_menu(0) {
|
||||
|
||||
connect(App::api(), SIGNAL(fullPeerLoaded(PeerData*)), this, SLOT(onFullPeerLoaded(PeerData*)));
|
||||
connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*)));
|
||||
|
||||
if (_peerUser) {
|
||||
_phoneText = _peerUser->phone.isEmpty() ? QString() : App::formatPhone(_peerUser->phone);
|
||||
@@ -87,12 +94,34 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
|
||||
connect(&_addParticipant, SIGNAL(clicked()), this, SLOT(onAddParticipant()));
|
||||
connect(&_sendMessage, SIGNAL(clicked()), this, SLOT(onSendMessage()));
|
||||
connect(&_shareContact, SIGNAL(clicked()), this, SLOT(onShareContact()));
|
||||
connect(&_inviteToGroup, SIGNAL(clicked()), this, SLOT(onInviteToGroup()));
|
||||
connect(&_cancelPhoto, SIGNAL(clicked()), this, SLOT(onUpdatePhotoCancel()));
|
||||
connect(&_createInvitationLink, SIGNAL(clicked()), this, SLOT(onCreateInvitationLink()));
|
||||
connect(&_invitationLink, SIGNAL(clicked()), this, SLOT(onInvitationLink()));
|
||||
_invitationLink.setAcceptBoth(true);
|
||||
updateInvitationLink();
|
||||
|
||||
if (_peerChat) {
|
||||
QString maxStr = lang(_uploadPhoto.textWidth() > _addParticipant.textWidth() ? lng_profile_set_group_photo : lng_profile_add_participant);
|
||||
_uploadPhoto.setAutoFontSize(st::profileMinBtnPadding, maxStr);
|
||||
_uploadPhoto.setAutoFontSize(st::profileMinBtnPadding, maxStr);
|
||||
} else if (_peerUser) {
|
||||
QString maxStr;
|
||||
if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) {
|
||||
maxStr = lang(_sendMessage.textWidth() > _inviteToGroup.textWidth() ? lng_profile_send_message : lng_profile_invite_to_group);
|
||||
} else if (!_peerUser->phone.isEmpty()) {
|
||||
maxStr = lang(_sendMessage.textWidth() > _shareContact.textWidth() ? lng_profile_send_message : lng_profile_share_contact);
|
||||
} else {
|
||||
maxStr = lang(lng_profile_send_message);
|
||||
}
|
||||
_sendMessage.setAutoFontSize(st::profileMinBtnPadding, maxStr);
|
||||
_shareContact.setAutoFontSize(st::profileMinBtnPadding, maxStr);
|
||||
_inviteToGroup.setAutoFontSize(st::profileMinBtnPadding, maxStr);
|
||||
}
|
||||
|
||||
connect(&_botSettings, SIGNAL(clicked()), this, SLOT(onBotSettings()));
|
||||
connect(&_botHelp, SIGNAL(clicked()), this, SLOT(onBotHelp()));
|
||||
|
||||
connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId)));
|
||||
connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId)));
|
||||
|
||||
@@ -100,6 +129,17 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
|
||||
connect(App::main(), SIGNAL(peerUpdated(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
|
||||
connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(peerUpdated(PeerData *)));
|
||||
|
||||
// about
|
||||
if (_peerUser && _peerUser->botInfo) {
|
||||
if (!_peerUser->botInfo->shareText.isEmpty()) {
|
||||
_about.setText(st::linkFont, _peerUser->botInfo->shareText, _historyBotOptions);
|
||||
}
|
||||
updateBotLinksVisibility();
|
||||
} else {
|
||||
_botSettings.hide();
|
||||
_botHelp.hide();
|
||||
}
|
||||
|
||||
// settings
|
||||
connect(&_enableNotifications, SIGNAL(clicked()), this, SLOT(onEnableNotifications()));
|
||||
connect(&_clearHistory, SIGNAL(clicked()), this, SLOT(onClearHistory()));
|
||||
@@ -126,6 +166,10 @@ void ProfileInner::onShareContact() {
|
||||
App::main()->shareContactLayer(_peerUser);
|
||||
}
|
||||
|
||||
void ProfileInner::onInviteToGroup() {
|
||||
App::wnd()->showLayer(new ContactsBox(_peerUser));
|
||||
}
|
||||
|
||||
void ProfileInner::onSendMessage() {
|
||||
App::main()->showPeer(_peer->id);
|
||||
}
|
||||
@@ -279,7 +323,7 @@ void ProfileInner::chatInviteDone(const MTPExportedChatInvite &result) {
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
void ProfileInner::onFullPeerLoaded(PeerData *peer) {
|
||||
void ProfileInner::onFullPeerUpdated(PeerData *peer) {
|
||||
if (peer != _peer) return;
|
||||
if (_peerUser) {
|
||||
PhotoData *userPhoto = _peerUser->photoId ? App::photo(_peerUser->photoId) : 0;
|
||||
@@ -288,6 +332,15 @@ void ProfileInner::onFullPeerLoaded(PeerData *peer) {
|
||||
} else {
|
||||
_photoLink = TextLinkPtr();
|
||||
}
|
||||
if (_peerUser->botInfo) {
|
||||
if (_peerUser->botInfo->shareText.isEmpty()) {
|
||||
_about = Text(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right());
|
||||
} else {
|
||||
_about.setText(st::linkFont, _peerUser->botInfo->shareText, _historyBotOptions);
|
||||
}
|
||||
updateBotLinksVisibility();
|
||||
resizeEvent(0);
|
||||
}
|
||||
} else if (_peerChat) {
|
||||
updateInvitationLink();
|
||||
showAll();
|
||||
@@ -295,6 +348,30 @@ void ProfileInner::onFullPeerLoaded(PeerData *peer) {
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileInner::onBotSettings() {
|
||||
for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) {
|
||||
QString cmd = _peerUser->botInfo->commands.at(i).command;
|
||||
if (!cmd.compare(qsl("settings"), Qt::CaseInsensitive)) {
|
||||
App::main()->showPeer(_peer->id);
|
||||
App::main()->sendBotCommand('/' + cmd, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateBotLinksVisibility();
|
||||
}
|
||||
|
||||
void ProfileInner::onBotHelp() {
|
||||
for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) {
|
||||
QString cmd = _peerUser->botInfo->commands.at(i).command;
|
||||
if (!cmd.compare(qsl("help"), Qt::CaseInsensitive)) {
|
||||
App::main()->showPeer(_peer->id);
|
||||
App::main()->sendBotCommand('/' + cmd, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateBotLinksVisibility();
|
||||
}
|
||||
|
||||
void ProfileInner::peerUpdated(PeerData *data) {
|
||||
if (data == _peer) {
|
||||
PhotoData *photo = 0;
|
||||
@@ -325,13 +402,13 @@ void ProfileInner::updateOnlineDisplayTimer() {
|
||||
if (_peerChat->participants.isEmpty()) return;
|
||||
|
||||
for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) {
|
||||
int32 onlineWillChangeIn = App::onlineWillChangeIn(i.key()->onlineTill, t);
|
||||
int32 onlineWillChangeIn = App::onlineWillChangeIn(i.key(), t);
|
||||
if (onlineWillChangeIn < minIn) {
|
||||
minIn = onlineWillChangeIn;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
minIn = App::onlineWillChangeIn(_peerUser->onlineTill, t);
|
||||
minIn = App::onlineWillChangeIn(_peerUser, t);
|
||||
}
|
||||
App::main()->updateOnlineDisplayIn(minIn * 1000);
|
||||
}
|
||||
@@ -354,13 +431,13 @@ void ProfileInner::reorderParticipants() {
|
||||
bool onlyMe = true;
|
||||
for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) {
|
||||
UserData *user = i.key();
|
||||
int32 until = App::onlineForSort(user->onlineTill, t);
|
||||
int32 until = App::onlineForSort(user, t);
|
||||
Participants::iterator before = _participants.begin();
|
||||
if (user != self) {
|
||||
if (before != _participants.end() && (*before) == self) {
|
||||
++before;
|
||||
}
|
||||
while (before != _participants.end() && App::onlineForSort((*before)->onlineTill, t) >= until) {
|
||||
while (before != _participants.end() && App::onlineForSort(*before, t) >= until) {
|
||||
++before;
|
||||
}
|
||||
if (until > t && onlyMe) onlyMe = false;
|
||||
@@ -392,6 +469,9 @@ void ProfileInner::reorderParticipants() {
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileInner::start() {
|
||||
}
|
||||
|
||||
bool ProfileInner::event(QEvent *e) {
|
||||
if (e->type() == QEvent::MouseMove) {
|
||||
_lastPos = static_cast<QMouseEvent*>(e)->globalPos();
|
||||
@@ -446,7 +526,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
|
||||
if (!_errorText.isEmpty()) {
|
||||
p.setFont(st::setErrFont->f);
|
||||
p.setPen(st::setErrColor->p);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + addbyname + st::profilePhoneTop + st::profilePhoneFont->ascent, _errorText);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + addbyname + st::profilePhoneFont->ascent, _errorText);
|
||||
}
|
||||
if (!_phoneText.isEmpty()) {
|
||||
p.setPen(st::black->p);
|
||||
@@ -464,6 +544,17 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
top += _shareContact.height();
|
||||
|
||||
// about
|
||||
if (!_about.isEmpty()) {
|
||||
p.setFont(st::profileHeaderFont->f);
|
||||
p.setPen(st::profileHeaderColor->p);
|
||||
p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_about_section));
|
||||
top += st::profileHeaderSkip;
|
||||
|
||||
_about.draw(p, _left, top, _width);
|
||||
top += _aboutHeight;
|
||||
}
|
||||
|
||||
// settings
|
||||
p.setFont(st::profileHeaderFont->f);
|
||||
p.setPen(st::profileHeaderColor->p);
|
||||
@@ -537,7 +628,15 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
|
||||
if (!data) {
|
||||
data = _participantsData[cnt] = new ParticipantData();
|
||||
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
|
||||
data->online = App::onlineText(user, l_time);
|
||||
if (user->botInfo) {
|
||||
if (user->botInfo->readsAllHistory) {
|
||||
data->online = lang(lng_status_bot_reads_all);
|
||||
} else {
|
||||
data->online = lang(lng_status_bot_not_reads_all);
|
||||
}
|
||||
} else {
|
||||
data->online = App::onlineText(user, l_time);
|
||||
}
|
||||
data->cankick = (user != App::self()) && (_chatAdmin || (_peerChat->cankick.constFind(user) != _peerChat->cankick.cend()));
|
||||
}
|
||||
p.setPen(st::profileListNameColor->p);
|
||||
@@ -555,7 +654,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
|
||||
} else {
|
||||
p.setPen(st::btnDefLink.color->p);
|
||||
}
|
||||
p.drawText(_left + _width - _kickWidth, top + (_pHeight - st::linkFont->height) / 2 + st::linkFont->ascent, lang(lng_profile_kick));
|
||||
p.drawText(_left + _width - _kickWidth, top + st::profileListNameTop + st::linkFont->ascent, lang(lng_profile_kick));
|
||||
}
|
||||
}
|
||||
top += fullCnt * _pHeight;
|
||||
@@ -579,9 +678,9 @@ void ProfileInner::mouseMoveEvent(QMouseEvent *e) {
|
||||
}
|
||||
}
|
||||
if (!_photoLink && (!_peerChat || _peerChat->forbidden)) {
|
||||
setCursor((_kickOver || _kickDown) ? style::cur_pointer : style::cur_default);
|
||||
setCursor((_kickOver || _kickDown || textlnkOver()) ? style::cur_pointer : style::cur_default);
|
||||
} else {
|
||||
setCursor((_kickOver || _kickDown || _photoOver) ? style::cur_pointer : style::cur_default);
|
||||
setCursor((_kickOver || _kickDown || _photoOver || textlnkOver()) ? style::cur_pointer : style::cur_default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,6 +689,16 @@ void ProfileInner::updateSelected() {
|
||||
|
||||
QPoint lp = mapFromGlobal(_lastPos);
|
||||
|
||||
TextLinkPtr lnk;
|
||||
bool inText = false;
|
||||
if (!_about.isEmpty() && lp.y() >= _aboutTop && lp.y() < _aboutTop + _aboutHeight && lp.x() >= _left && lp.x() < _left + _width) {
|
||||
_about.getState(lnk, inText, lp.x() - _left, lp.y() - _aboutTop, _width);
|
||||
}
|
||||
if (textlnkOver() != lnk) {
|
||||
textlnkOver(lnk);
|
||||
update(QRect(_left, _aboutTop, _width, _aboutHeight));
|
||||
}
|
||||
|
||||
int32 partfrom = _mediaAudios.y() + _mediaAudios.height() + st::profileHeaderSkip;
|
||||
int32 newSelected = (lp.x() >= _left - st::profileListPadding.width() && lp.x() < _left + _width + st::profileListPadding.width() && lp.y() >= partfrom) ? (lp.y() - partfrom) / _pHeight : -1;
|
||||
|
||||
@@ -597,7 +706,7 @@ void ProfileInner::updateSelected() {
|
||||
if (newSelected >= 0 && newSelected < _participants.size()) {
|
||||
ParticipantData *data = _participantsData[newSelected];
|
||||
if (data && data->cankick) {
|
||||
int32 top = partfrom + newSelected * _pHeight + (_pHeight - st::linkFont->height) / 2;
|
||||
int32 top = partfrom + newSelected * _pHeight + st::profileListNameTop;
|
||||
if ((lp.x() >= _left + _width - _kickWidth) && (lp.x() < _left + _width) && (lp.y() >= top) && (lp.y() < top + st::linkFont->height)) {
|
||||
newKickOver = _participants[newSelected];
|
||||
}
|
||||
@@ -631,18 +740,31 @@ void ProfileInner::mousePressEvent(QMouseEvent *e) {
|
||||
onUpdatePhoto();
|
||||
}
|
||||
}
|
||||
textlnkDown(textlnkOver());
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileInner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
_lastPos = e->globalPos();
|
||||
updateSelected();
|
||||
if (_kickDown && _kickDown == _kickOver) {
|
||||
_kickConfirm = _kickOver;
|
||||
ConfirmBox *box = new ConfirmBox(lng_profile_sure_kick(lt_user, _kickOver->firstName));
|
||||
connect(box, SIGNAL(confirmed()), this, SLOT(onKickConfirm()));
|
||||
App::wnd()->showLayer(box);
|
||||
}
|
||||
if (textlnkDown()) {
|
||||
TextLinkPtr lnk = textlnkDown();
|
||||
textlnkDown(TextLinkPtr());
|
||||
if (lnk == textlnkOver()) {
|
||||
if (reBotCommand().match(lnk->encoded()).hasMatch()) {
|
||||
App::main()->showPeer(_peer->id);
|
||||
}
|
||||
lnk->onClick(e->button());
|
||||
}
|
||||
}
|
||||
_kickDown = 0;
|
||||
setCursor(_kickOver ? style::cur_pointer : style::cur_default);
|
||||
setCursor((_kickOver || textlnkOver()) ? style::cur_pointer : style::cur_default);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -697,16 +819,30 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
|
||||
_cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhotoSize - st::linkFont->height);
|
||||
} else {
|
||||
_cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhoneTop);
|
||||
_botSettings.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent) + st::profilePhoneTop);
|
||||
_botHelp.move(_botSettings.x() + (_botSettings.isHidden() ? 0 : _botSettings.width() + st::profilePhoneLeft), _botSettings.y());
|
||||
}
|
||||
top += st::profilePhotoSize;
|
||||
|
||||
top += st::profileButtonTop;
|
||||
|
||||
_uploadPhoto.setGeometry(_left, top, btnWidth, _uploadPhoto.height());
|
||||
_sendMessage.setGeometry(_left, top, btnWidth, _sendMessage.height());
|
||||
_addParticipant.setGeometry(_left + _width - btnWidth, top, btnWidth, _addParticipant.height());
|
||||
|
||||
_sendMessage.setGeometry(_left, top, btnWidth, _sendMessage.height());
|
||||
_shareContact.setGeometry(_left + _width - btnWidth, top, btnWidth, _shareContact.height());
|
||||
_inviteToGroup.setGeometry(_left + _width - btnWidth, top, btnWidth, _inviteToGroup.height());
|
||||
|
||||
top += _shareContact.height();
|
||||
|
||||
// about
|
||||
if (!_about.isEmpty()) {
|
||||
top += st::profileHeaderSkip;
|
||||
_aboutTop = top; _aboutHeight = _about.countHeight(_width); top += _aboutHeight;
|
||||
} else {
|
||||
_aboutTop = _aboutHeight = 0;
|
||||
}
|
||||
|
||||
// settings
|
||||
top += st::profileHeaderSkip;
|
||||
_enableNotifications.move(_left, top); top += _enableNotifications.height();
|
||||
@@ -829,6 +965,7 @@ void ProfileInner::showAll() {
|
||||
if (_peerChat) {
|
||||
_sendMessage.hide();
|
||||
_shareContact.hide();
|
||||
_inviteToGroup.hide();
|
||||
if (_peerChat->forbidden) {
|
||||
_uploadPhoto.hide();
|
||||
_cancelPhoto.hide();
|
||||
@@ -871,8 +1008,14 @@ void ProfileInner::showAll() {
|
||||
_sendMessage.show();
|
||||
if (_peerUser->phone.isEmpty()) {
|
||||
_shareContact.hide();
|
||||
if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) {
|
||||
_inviteToGroup.show();
|
||||
} else {
|
||||
_inviteToGroup.hide();
|
||||
}
|
||||
} else {
|
||||
_shareContact.show();
|
||||
_inviteToGroup.hide();
|
||||
}
|
||||
_enableNotifications.show();
|
||||
_clearHistory.show();
|
||||
@@ -944,6 +1087,23 @@ void ProfileInner::updateInvitationLink() {
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileInner::updateBotLinksVisibility() {
|
||||
if (!_peerUser || !_peerUser->botInfo || _peerUser->botInfo->commands.isEmpty()) {
|
||||
_botSettings.hide();
|
||||
_botHelp.hide();
|
||||
return;
|
||||
}
|
||||
bool hasSettings = false, hasHelp = false;
|
||||
for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) {
|
||||
QString cmd = _peerUser->botInfo->commands.at(i).command;
|
||||
hasSettings |= !cmd.compare(qsl("settings"), Qt::CaseInsensitive);
|
||||
hasHelp |= !cmd.compare(qsl("help"), Qt::CaseInsensitive);
|
||||
if (hasSettings && hasHelp) break;
|
||||
}
|
||||
_botSettings.setVisible(hasSettings);
|
||||
_botHelp.setVisible(hasHelp);
|
||||
}
|
||||
|
||||
QString ProfileInner::overviewLinkText(int32 type, int32 count) {
|
||||
switch (type) {
|
||||
case OverviewPhotos: return lng_profile_photos(lt_count, count);
|
||||
@@ -1063,6 +1223,7 @@ bool ProfileWidget::animStep(float64 ms) {
|
||||
_bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap();
|
||||
App::main()->topBar()->stopAnim();
|
||||
_scroll.show();
|
||||
_inner.start();
|
||||
activate();
|
||||
} else {
|
||||
a_bgCoord.update(dt1, st::introHideFunc);
|
||||
@@ -1092,6 +1253,12 @@ void ProfileWidget::mediaOverviewUpdated(PeerData *peer) {
|
||||
_inner.mediaOverviewUpdated(peer);
|
||||
}
|
||||
|
||||
void ProfileWidget::clear() {
|
||||
if (_inner.peer() && !_inner.peer()->chat && _inner.peer()->asUser()->botInfo) {
|
||||
_inner.peer()->asUser()->botInfo->startGroupToken = QString();
|
||||
}
|
||||
}
|
||||
|
||||
ProfileWidget::~ProfileWidget() {
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ public:
|
||||
|
||||
ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const PeerData *peer);
|
||||
|
||||
void start();
|
||||
|
||||
bool event(QEvent *e);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
@@ -64,6 +66,7 @@ public slots:
|
||||
void deleteContextImage();
|
||||
|
||||
void onShareContact();
|
||||
void onInviteToGroup();
|
||||
void onSendMessage();
|
||||
void onEnableNotifications();
|
||||
|
||||
@@ -94,12 +97,16 @@ public slots:
|
||||
void onCreateInvitationLink();
|
||||
void onCreateInvitationLinkSure();
|
||||
|
||||
void onFullPeerLoaded(PeerData *peer);
|
||||
void onFullPeerUpdated(PeerData *peer);
|
||||
|
||||
void onBotSettings();
|
||||
void onBotHelp();
|
||||
|
||||
private:
|
||||
|
||||
void showAll();
|
||||
void updateInvitationLink();
|
||||
void updateBotLinksVisibility();
|
||||
|
||||
void chatInviteDone(const MTPExportedChatInvite &result);
|
||||
|
||||
@@ -120,9 +127,13 @@ private:
|
||||
QString _phoneText;
|
||||
TextLinkPtr _photoLink;
|
||||
FlatButton _uploadPhoto, _addParticipant;
|
||||
FlatButton _sendMessage, _shareContact;
|
||||
FlatButton _sendMessage, _shareContact, _inviteToGroup;
|
||||
LinkButton _cancelPhoto, _createInvitationLink, _invitationLink;
|
||||
QString _invitationText;
|
||||
LinkButton _botSettings, _botHelp;
|
||||
|
||||
Text _about;
|
||||
int32 _aboutTop, _aboutHeight;
|
||||
|
||||
anim::fvalue a_photo;
|
||||
bool _photoOver;
|
||||
@@ -191,6 +202,7 @@ public:
|
||||
void updateNotifySettings();
|
||||
void mediaOverviewUpdated(PeerData *peer);
|
||||
|
||||
void clear();
|
||||
~ProfileWidget();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -1101,7 +1101,7 @@ void psOpenFile(const QString &name, bool openWith) {
|
||||
|
||||
void psShowInFolder(const QString &name) {
|
||||
App::wnd()->layerHidden();
|
||||
system((qsl("nautilus ") + escapeShell(QFileInfo(name).absoluteDir().absolutePath())).toUtf8().constData());
|
||||
system((qsl("xdg-open ") + escapeShell(QFileInfo(name).absoluteDir().absolutePath())).toUtf8().constData());
|
||||
}
|
||||
|
||||
void psStart() {
|
||||
|
||||
@@ -139,6 +139,7 @@ QUrl gUpdateURL = QUrl(qsl("http://tdesktop.com/linux/tupdates/current"));
|
||||
#endif
|
||||
|
||||
bool gContactsReceived = false;
|
||||
bool gDialogsReceived = false;
|
||||
|
||||
bool gWideMode = true;
|
||||
|
||||
|
||||
@@ -53,11 +53,11 @@ inline bool rtl() {
|
||||
}
|
||||
|
||||
struct mtpDcOption {
|
||||
mtpDcOption(int _id, const string &_host, const string &_ip, int _port) : id(_id), host(_host), ip(_ip), port(_port) {
|
||||
mtpDcOption(int id, int flags, const string &ip, int port) : id(id), flags(flags), ip(ip), port(port) {
|
||||
}
|
||||
|
||||
int id;
|
||||
string host;
|
||||
int flags;
|
||||
string ip;
|
||||
int port;
|
||||
};
|
||||
@@ -290,6 +290,7 @@ DeclareReadSetting(DBIPlatform, Platform);
|
||||
DeclareReadSetting(QUrl, UpdateURL);
|
||||
|
||||
DeclareSetting(bool, ContactsReceived);
|
||||
DeclareSetting(bool, DialogsReceived);
|
||||
|
||||
DeclareSetting(bool, WideMode);
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
|
||||
Q_IMPORT_PLUGIN(QWebpPlugin)
|
||||
#elif defined Q_OS_MAC
|
||||
Q_IMPORT_PLUGIN(QCoreWlanEnginePlugin)
|
||||
Q_IMPORT_PLUGIN(QGenericEnginePlugin)
|
||||
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)
|
||||
Q_IMPORT_PLUGIN(QDDSPlugin)
|
||||
|
||||
@@ -201,10 +201,73 @@ void UserData::setPhone(const QString &newPhone) {
|
||||
++nameVersion;
|
||||
}
|
||||
|
||||
void UserData::setBotInfoVersion(int32 version) {
|
||||
if (version < 0) {
|
||||
delete botInfo;
|
||||
botInfo = 0;
|
||||
} else if (!botInfo) {
|
||||
botInfo = new BotInfo();
|
||||
botInfo->version = version;
|
||||
} else if (botInfo->version < version) {
|
||||
botInfo->commands.clear();
|
||||
botInfo->description.clear();
|
||||
botInfo->shareText.clear();
|
||||
botInfo->version = version;
|
||||
botInfo->inited = false;
|
||||
}
|
||||
}
|
||||
void UserData::setBotInfo(const MTPBotInfo &info) {
|
||||
switch (info.type()) {
|
||||
case mtpc_botInfoEmpty:
|
||||
delete botInfo;
|
||||
botInfo = 0;
|
||||
break;
|
||||
case mtpc_botInfo: {
|
||||
const MTPDbotInfo &d(info.c_botInfo());
|
||||
if (App::peerFromUser(d.vuser_id.v) != id) return;
|
||||
|
||||
if (botInfo) {
|
||||
botInfo->version = d.vversion.v;
|
||||
} else {
|
||||
setBotInfoVersion(d.vversion.v);
|
||||
}
|
||||
|
||||
QString desc = qs(d.vdescription);
|
||||
if (botInfo->description != desc) {
|
||||
botInfo->description = desc;
|
||||
botInfo->text = Text(st::msgMinWidth);
|
||||
}
|
||||
botInfo->shareText = qs(d.vshare_text);
|
||||
|
||||
const QVector<MTPBotCommand> &v(d.vcommands.c_vector().v);
|
||||
botInfo->commands.clear();
|
||||
botInfo->commands.reserve(v.size());
|
||||
for (int32 i = 0, l = v.size(); i < l; ++i) {
|
||||
if (v.at(i).type() == mtpc_botCommand) {
|
||||
botInfo->commands.push_back(BotCommand(qs(v.at(i).c_botCommand().vcommand), qs(v.at(i).c_botCommand().vparams), qs(v.at(i).c_botCommand().vdescription)));
|
||||
}
|
||||
}
|
||||
|
||||
botInfo->inited = true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void UserData::nameUpdated() {
|
||||
nameText.setText(st::msgNameFont, name, _textNameOptions);
|
||||
}
|
||||
|
||||
void UserData::madeAction() {
|
||||
int32 t = unixtime();
|
||||
if (onlineTill <= 0 && -onlineTill < t) {
|
||||
onlineTill = -t - SetOnlineAfterActivity;
|
||||
if (App::main()) App::main()->peerUpdated(this);
|
||||
} else if (onlineTill > 0 && onlineTill < t + 1) {
|
||||
onlineTill = t + SetOnlineAfterActivity;
|
||||
if (App::main()) App::main()->peerUpdated(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) {
|
||||
switch (p.type()) {
|
||||
case mtpc_chatPhoto: {
|
||||
|
||||
@@ -118,15 +118,37 @@ private:
|
||||
PeerData *_peer;
|
||||
};
|
||||
|
||||
struct BotCommand {
|
||||
BotCommand(const QString &command, const QString ¶ms, const QString &description) : command(command), params(params), description(description) {
|
||||
}
|
||||
QString command, params, description;
|
||||
};
|
||||
struct BotInfo {
|
||||
BotInfo() : inited(false), readsAllHistory(false), cantJoinGroups(false), version(0), text(st::msgMinWidth) {
|
||||
}
|
||||
bool inited;
|
||||
bool readsAllHistory, cantJoinGroups;
|
||||
int32 version;
|
||||
QString shareText, description;
|
||||
QList<BotCommand> commands;
|
||||
Text text; // description
|
||||
|
||||
QString startToken, startGroupToken;
|
||||
};
|
||||
|
||||
struct PhotoData;
|
||||
struct UserData : public PeerData {
|
||||
UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1) {
|
||||
UserData(const PeerId &id) : PeerData(id), photoId(0), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1), botInfo(0) {
|
||||
}
|
||||
void setPhoto(const MTPUserProfilePhoto &photo);
|
||||
void setName(const QString &first, const QString &last, const QString &phoneName, const QString &username);
|
||||
void setPhone(const QString &newPhone);
|
||||
void setBotInfoVersion(int32 version);
|
||||
void setBotInfo(const MTPBotInfo &info);
|
||||
void nameUpdated();
|
||||
|
||||
void madeAction(); // pseudo-online
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
QString username;
|
||||
@@ -140,10 +162,12 @@ struct UserData : public PeerData {
|
||||
typedef QList<PhotoData*> Photos;
|
||||
Photos photos;
|
||||
int32 photosCount; // -1 not loaded, 0 all loaded
|
||||
|
||||
BotInfo *botInfo;
|
||||
};
|
||||
|
||||
struct ChatData : public PeerData {
|
||||
ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), photoId(0) {
|
||||
ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), botStatus(0), photoId(0) {
|
||||
}
|
||||
void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = 0);
|
||||
int32 count;
|
||||
@@ -158,12 +182,19 @@ struct ChatData : public PeerData {
|
||||
CanKick cankick;
|
||||
typedef QList<UserData*> LastAuthors;
|
||||
LastAuthors lastAuthors;
|
||||
typedef QMap<UserData*, bool> MarkupSenders;
|
||||
MarkupSenders markupSenders;
|
||||
int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other
|
||||
ImagePtr photoFull;
|
||||
PhotoId photoId;
|
||||
QString invitationUrl;
|
||||
// geo
|
||||
};
|
||||
|
||||
inline int32 newMessageFlags(PeerData *p) {
|
||||
return (p->input.type() == mtpc_inputPeerSelf) ? 0 : (((p->chat || !p->asUser()->botInfo) ? MTPDmessage_flag_unread : 0) | MTPDmessage_flag_out);
|
||||
}
|
||||
|
||||
typedef QMap<char, QPixmap> PreparedPhotoThumbs;
|
||||
struct PhotoData {
|
||||
PhotoData(const PhotoId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr()) :
|
||||
|
||||
@@ -233,7 +233,7 @@ QString rusKeyboardLayoutSwitch(const QString &from);
|
||||
enum DataBlockId {
|
||||
dbiKey = 0x00,
|
||||
dbiUser = 0x01,
|
||||
dbiDcOption = 0x02,
|
||||
dbiDcOptionOld = 0x02,
|
||||
dbiMaxGroupCount = 0x03,
|
||||
dbiMutePeer = 0x04,
|
||||
dbiSendKey = 0x05,
|
||||
@@ -270,6 +270,7 @@ enum DataBlockId {
|
||||
dbiRecentEmojis = 0x24,
|
||||
dbiEmojiVariants = 0x25,
|
||||
dbiRecentStickers = 0x26,
|
||||
dbiDcOption = 0x27,
|
||||
|
||||
dbiEncryptedWithSalt = 333,
|
||||
dbiEncrypted = 444,
|
||||
@@ -326,6 +327,8 @@ enum DBIScale {
|
||||
dbisScaleCount = 5,
|
||||
};
|
||||
|
||||
static const int MatrixRowShift = 40000;
|
||||
|
||||
enum DBIEmojiTab {
|
||||
dbietRecent = -1,
|
||||
dbietPeople = 0,
|
||||
@@ -338,7 +341,6 @@ enum DBIEmojiTab {
|
||||
dbietStickers = 666,
|
||||
};
|
||||
static const int emojiTabCount = 8;
|
||||
static const int emojiTabShift = 100000;
|
||||
inline DBIEmojiTab emojiTabAtIndex(int index) {
|
||||
return (index < 0 || index >= emojiTabCount) ? dbietRecent : DBIEmojiTab(index - 1);
|
||||
}
|
||||
|
||||
@@ -557,6 +557,7 @@ void Window::checkAutoLock() {
|
||||
|
||||
void Window::setupIntro(bool anim) {
|
||||
cSetContactsReceived(false);
|
||||
cSetDialogsReceived(false);
|
||||
if (intro && (intro->animating() || intro->isVisible()) && !main) return;
|
||||
|
||||
QPixmap bg = anim ? myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)) : QPixmap();
|
||||
@@ -606,7 +607,8 @@ void Window::sendServiceHistoryRequest() {
|
||||
|
||||
UserData *user = App::userLoaded(ServiceUserId);
|
||||
if (!user) {
|
||||
user = App::feedUsers(MTP_vector<MTPUser>(1, MTP_userRequest(MTP_int(ServiceUserId), MTP_string("Telegram"), MTP_string(""), MTP_string(""), MTP_long(-1), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently())));
|
||||
int32 userFlags = MTPDuser::flag_first_name | MTPDuser::flag_phone | MTPDuser::flag_status;
|
||||
user = App::feedUsers(MTP_vector<MTPUser>(1, MTP_user(MTP_int(userFlags), MTP_int(ServiceUserId), MTPlong(), MTP_string("Telegram"), MTPstring(), MTPstring(), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently(), MTPint())));
|
||||
}
|
||||
_serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(1)), main->rpcDone(&MainWidget::serviceHistoryDone), main->rpcFail(&MainWidget::serviceHistoryFail));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.8.24</string>
|
||||
<string>0.8.27</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
|
||||
Binary file not shown.
@@ -214,7 +214,6 @@
|
||||
E8D95529CED88F18818C9A8B /* intro.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0771C4C94B623FC34BF62983 /* intro.cpp */; settings = {ATTRIBUTES = (); }; };
|
||||
E97B3CFAB59B49BACFFC5F7C /* moc_title.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 1080B6D395843B8F76A2E45E /* moc_title.cpp */; settings = {ATTRIBUTES = (); }; };
|
||||
E9F1CE7F9B18C7C85A50E62D /* style_auto.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 99B8D38F7F5858601230911E /* style_auto.cpp */; settings = {ATTRIBUTES = (); }; };
|
||||
EBA5E17368D2BBC6014E92B9 /* qcorewlanbearer in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = EE03BC5CA4628A6D6BEB0122 /* qcorewlanbearer */; };
|
||||
EBE29731916DB43BF49FE7A4 /* aboutbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = C194EDD00F76216057D48A5C /* aboutbox.cpp */; settings = {ATTRIBUTES = (); }; };
|
||||
ED2557A57C6782721DC494AF /* moc_connectionbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = FEC58F9D8A0963E5A9D4BE6F /* moc_connectionbox.cpp */; settings = {ATTRIBUTES = (); }; };
|
||||
F26454630C80841CBDCFE1CA /* Foundation.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = FCC237CA5AD60B9BA4447615 /* Foundation.framework */; };
|
||||
@@ -718,7 +717,6 @@
|
||||
CCA737EE379CDB10CC9A0F23 /* AVFoundation.framework in Link Binary With Libraries */,
|
||||
B78304F135DEF1F7A68393A6 /* CoreMedia.framework in Link Binary With Libraries */,
|
||||
F2A75ACAC9DF6A3F4E5711E7 /* AppKit.framework in Link Binary With Libraries */,
|
||||
EBA5E17368D2BBC6014E92B9 /* qcorewlanbearer in Link Binary With Libraries */,
|
||||
D0EECF370C58DDCACBC71BAD /* CoreWLAN.framework in Link Binary With Libraries */,
|
||||
8883FF366F2623E89D90A9E6 /* qgenericbearer in Link Binary With Libraries */,
|
||||
5058CB9D7BFFCE9F404A3700 /* Qt5Network in Link Binary With Libraries */,
|
||||
@@ -1703,7 +1701,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 0.8.24;
|
||||
CURRENT_PROJECT_VERSION = 0.8.27;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
@@ -1721,7 +1719,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CURRENT_PROJECT_VERSION = 0.8.24;
|
||||
CURRENT_PROJECT_VERSION = 0.8.27;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = fast;
|
||||
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
|
||||
@@ -1747,10 +1745,10 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 0.8.24;
|
||||
CURRENT_PROJECT_VERSION = 0.8.27;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DYLIB_COMPATIBILITY_VERSION = 0.8;
|
||||
DYLIB_CURRENT_VERSION = 0.8.24;
|
||||
DYLIB_CURRENT_VERSION = 0.8.27;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
@@ -1890,10 +1888,10 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 0.8.24;
|
||||
CURRENT_PROJECT_VERSION = 0.8.27;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DYLIB_COMPATIBILITY_VERSION = 0.8;
|
||||
DYLIB_CURRENT_VERSION = 0.8.24;
|
||||
DYLIB_CURRENT_VERSION = 0.8.27;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
echo 8024 0.8.24 0
|
||||
echo 8027 0.8.27 1
|
||||
# AppVersion AppVersionStr DevChannel
|
||||
|
||||
Reference in New Issue
Block a user