links preview done

This commit is contained in:
John Preston 2015-04-04 23:01:34 +03:00
parent 868d5f60f3
commit f3bb155b0a
67 changed files with 2712 additions and 1416 deletions

View File

@ -89,7 +89,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_chat_status_members_online" = "{count:_not_used_|# member|# members}, {count_online:_not_used_|# online|# online}";
"lng_server_error" = "Internal server error.";
"lng_flood_error" = "Too much tries. Please try again later.";
"lng_flood_error" = "Too many tries. Please try again later.";
"lng_deleted" = "Unknown";
"lng_deleted_message" = "Deleted message";
@ -127,16 +127,21 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_signin_title" = "Cloud password check";
"lng_signin_desc" = "Please enter your cloud password.";
"lng_signin_recover_desc" = "Please enter code from recovery email.";
"lng_signin_recover_desc" = "Please enter the code from the e-mail.";
"lng_signin_password" = "Your cloud password";
"lng_signin_code" = "Code from email";
"lng_signin_recover" = "Recover by email";
"lng_signin_code" = "Code from e-mail";
"lng_signin_recover" = "Forgot password?";
"lng_signin_hint" = "Hint: {password_hint}";
"lng_signin_recover_hint" = "Code was sent to {recover_email}.";
"lng_signin_recover_hint" = "Code was sent to {recover_email}";
"lng_signin_bad_password" = "You have entered a wrong password.";
"lng_signin_wrong_code" = "You have entered an invalid code. Please try again.";
"lng_signin_try_password" = "I remember the password";
"lng_signin_password_removed" = "Your cloud password was removed.\nYou can set new one in Settings.";
"lng_signin_try_password" = "Having trouble accessing your e-mail?";
"lng_signin_password_removed" = "Your cloud password was disabled.\nYou can set a new one in Settings.";
"lng_signin_no_email_forgot" = "Since you haven't provided a recovery\ne-mail when setting up your password, your remaining options are either to remember your password or to reset your account.";
"lng_signin_cant_email_forgot" = "If you can't restore access to the e-mail, your remaining options are either to remember your password or to reset your account.";
"lng_signin_reset_account" = "Reset your account";
"lng_sigin_sure_reset" = "Warning!\n\nYou will lose all your chats and messages,\nalong with any media and files you shared!\n\nDo you want to reset your account?";
"lng_sigin_reset" = "Reset";
"lng_signup_title" = "Information and photo";
"lng_signup_desc" = "Please enter your name and\nupload a photo.";
@ -266,27 +271,28 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_submit" = "Submit";
"lng_passcode_logout" = "Log out";
"lng_cloud_password_waiting" = "Confirmation sent to {email}..";
"lng_cloud_password_waiting" = "Confirmation link sent to {email}..";
"lng_cloud_password_change" = "Change cloud password";
"lng_cloud_password_create" = "Create cloud password";
"lng_cloud_password_remove" = "Remove cloud password";
"lng_cloud_password_set" = "Turn on cloud password";
"lng_cloud_password_set" = "Enable two-step verification";
"lng_cloud_password_edit" = "Change cloud password";
"lng_cloud_password_enter_old" = "Enter old password";
"lng_cloud_password_enter_new" = "Enter new password";
"lng_cloud_password_confirm_new" = "Re-enter new password";
"lng_cloud_password_hint" = "Enter new password hint";
"lng_cloud_password_bad" = "Password and hint should differ.";
"lng_cloud_password_email" = "Enter recover email";
"lng_cloud_password_bad_email" = "Incorrect email, please try other.";
"lng_cloud_password_about" = "Each new device authorization will require entering cloud password or recover by email.";
"lng_cloud_password_about_recover" = "Warning! Are you sure you don't want to\nenter a recover email address?\n\nIf you forget your cloud password\nyou will loose all your account data.";
"lng_cloud_password_almost" = "A confirmation link was sent\nto the email you provided.\n\nYour cloud password will be enabled\nonly after you follow that link.";
"lng_cloud_password_was_set" = "Your new password is enabled.";
"lng_cloud_password_removed" = "Your cloud password was removed.";
"lng_cloud_password_differ" = "Passwords are different";
"lng_cloud_password_hint" = "Enter password hint";
"lng_cloud_password_bad" = "Password and hint cannot be the same.";
"lng_cloud_password_email" = "Enter recovery e-mail";
"lng_cloud_password_bad_email" = "Incorrect e-mail, please try other.";
"lng_cloud_password_about" = "This password will be required when you log in on a new device in addition to the pin code.";
"lng_cloud_password_about_recover" = "Warning! Are you sure you don't want to\nadd a password recovery e-mail?\n\nIf you forget your password, you will\nlose access to your Telegram account.";
"lng_cloud_password_almost" = "A confirmation link was sent\nto the e-mail you provided.\n\nTwo-step verification will be enabled\nas soon as you follow that link.";
"lng_cloud_password_was_set" = "Two-step verification enabled.";
"lng_cloud_password_updated" = "Your cloud password was updated.";
"lng_cloud_password_removed" = "Two-step verification was disabled.";
"lng_cloud_password_differ" = "Passwords do not match";
"lng_cloud_password_wrong" = "Wrong cloud password";
"lng_cloud_password_is_same" = "Cloud password was not changed";
"lng_cloud_password_is_same" = "Password was not changed";
"lng_connection_type" = "Connection type:";
"lng_connection_auto_connecting" = "Default (connecting..)";
@ -531,7 +537,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "Image was saved to your [c]Downloads[/c] folder";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate the new session there.\n\nThanks,\nThe Telegram Team";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team";
"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";

View File

@ -458,6 +458,16 @@ btnSelectCancel: flatButton(btnSelectDone) {
downColor: btnNoHover;
}
btnSelectSep: #e0e0e0;
btnRedLink: linkButton(btnDefLink) {
color: #d15948;
overColor: #d15948;
downColor: #db6352;
}
btnRedDone: flatButton(btnSelectDone) {
color: #d15948;
overColor: #d15948;
downColor: #db6352;
}
countryList: countryList {
notFoundColor: #aaa;//rgb(20, 136, 210);
@ -825,6 +835,7 @@ msgBG: ':/gui/art/bg.png' / 2:':/gui/art/bg_125x.png' / 3:':/gui/art/bg_150x.png
msgSendingRect: sprite(260px, 20px, 20px, 20px);
msgCheckRect: sprite(320px, 0px, 20px, 20px);
msgCheckPos: point(5px, 1px);
msgDblCheckRect: sprite(300px, 0px, 20px, 20px);
msgSelectCheckRect: sprite(160px, 0px, 20px, 20px);
msgSelectDblCheckRect: sprite(140px, 0px, 20px, 20px);
@ -1379,15 +1390,15 @@ dropdownMediaPhotos: iconedButton(dropdownAttachPhoto) {
width: 200px;
}
dropdownMediaVideos: iconedButton(dropdownMediaPhotos) {
icon: sprite(79px, 348px, 24px, 24px);
downIcon: sprite(79px, 348px, 24px, 24px);
icon: sprite(92px, 348px, 24px, 24px);
downIcon: sprite(92px, 348px, 24px, 24px);
}
dropdownMediaDocuments: iconedButton(dropdownAttachDocument) {
width: 200px;
}
dropdownMediaAudios: iconedButton(dropdownMediaDocuments) {
icon: sprite(49px, 348px, 24px, 24px);
downIcon: sprite(49px, 348px, 24px, 24px);
icon: sprite(62px, 348px, 24px, 24px);
downIcon: sprite(62px, 348px, 24px, 24px);
}
dragFont: font(28px semibold);
@ -1721,6 +1732,7 @@ usernameCancel: flatButton(btnSelectCancel) {
youtubeIcon: sprite(336px, 221px, 60px, 60px);
vimeoIcon: sprite(336px, 283px, 60px, 60px);
videoIcon: sprite(0px, 340px, 60px, 60px);
locationSize: size(320, 240);
langsWidth: 220px;
@ -1779,3 +1791,11 @@ sessionTerminate: iconedButton(notifyClose) {
width: 16px;
height: 16px;
}
webPageLeft: 10px;
webPageBar: 2px;
webPageTitleFont: font(fsize semibold);
webPageDescriptionFont: font(fsize);
webPagePhotoSkip: 5px;
webPagePhotoSize: 100px;
webPagePhotoDelta: 8px;

View File

@ -30,6 +30,7 @@ ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
App::initBackground();
connect(&_replyToTimer, SIGNAL(timeout()), this, SLOT(resolveReplyTo()));
connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages()));
}
void ApiWrap::init() {
@ -94,6 +95,25 @@ void ApiWrap::requestFullPeer(PeerData *peer) {
_fullRequests.insert(peer, req);
}
void ApiWrap::requestWebPageDelayed(WebPageData *page) {
if (page->pendingTill <= 0) return;
_webPagesPending.insert(page, 0);
int32 left = (page->pendingTill - unixtime()) * 1000;
if (!_webPagesTimer.isActive() || left <= _webPagesTimer.remainingTime()) {
_webPagesTimer.start((left < 0 ? 0 : left) + 1);
}
}
void ApiWrap::clearWebPageRequest(WebPageData *page) {
_webPagesPending.remove(page);
if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) _webPagesTimer.stop();
}
void ApiWrap::clearWebPageRequests() {
_webPagesPending.clear();
_webPagesTimer.stop();
}
void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
const MTPDmessages_chatFull &d(result.c_messages_chatFull());
App::feedUsers(d.vusers);
@ -123,7 +143,9 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
emit fullPeerLoaded(peer);
}
bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &err) {
bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_fullRequests.remove(peer);
return true;
}
@ -146,6 +168,34 @@ void ApiWrap::resolveReplyTo() {
}
}
void ApiWrap::resolveWebPages() {
QVector<MTPint> ids;
const WebPageItems &items(App::webPageItems());
ids.reserve(_webPagesPending.size());
int32 t = unixtime(), m = INT_MAX;
for (WebPagesPending::const_iterator i = _webPagesPending.cbegin(), e = _webPagesPending.cend(); i != e; ++i) {
if (i.value()) continue;
if (i.key()->pendingTill <= t) {
WebPageItems::const_iterator j = items.constFind(i.key());
if (j != items.cend() && !j.value().isEmpty()) {
ids.push_back(MTP_int(j.value().begin().key()->id));
}
} else {
m = qMin(m, i.key()->pendingTill - t);
}
}
if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotWebPages));
for (WebPagesPending::iterator i = _webPagesPending.begin(); i != _webPagesPending.cend(); ++i) {
if (i.value()) continue;
if (i.key()->pendingTill <= t) {
i.value() = req;
}
}
}
if (m < INT_MAX) _webPagesTimer.start(m * 1000);
}
void ApiWrap::gotReplyTo(const MTPmessages_Messages &msgs, mtpRequestId req) {
switch (msgs.type()) {
case mtpc_messages_messages:
@ -176,6 +226,61 @@ void ApiWrap::gotReplyTo(const MTPmessages_Messages &msgs, mtpRequestId req) {
}
}
void ApiWrap::gotWebPages(const MTPmessages_Messages &msgs, mtpRequestId req) {
const QVector<MTPMessage> *v = 0;
switch (msgs.type()) {
case mtpc_messages_messages:
App::feedUsers(msgs.c_messages_messages().vusers);
App::feedChats(msgs.c_messages_messages().vchats);
v = &msgs.c_messages_messages().vmessages.c_vector().v;
break;
case mtpc_messages_messagesSlice:
App::feedUsers(msgs.c_messages_messagesSlice().vusers);
App::feedChats(msgs.c_messages_messagesSlice().vchats);
v = &msgs.c_messages_messagesSlice().vmessages.c_vector().v;
break;
}
QMap<int32, int32> msgsIds; // copied from feedMsgs
for (int32 i = 0, l = v->size(); i < l; ++i) {
const MTPMessage &msg(v->at(i));
switch (msg.type()) {
case mtpc_message: msgsIds.insert(msg.c_message().vid.v, i); break;
case mtpc_messageEmpty: msgsIds.insert(msg.c_messageEmpty().vid.v, i); break;
case mtpc_messageService: msgsIds.insert(msg.c_messageService().vid.v, i); break;
}
}
MainWidget *m = App::main();
for (QMap<int32, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
HistoryItem *item = App::histories().addToBack(v->at(*i), -1);
if (item) {
item->initDimensions();
if (m) m->itemResized(item);
}
}
const WebPageItems &items(App::webPageItems());
for (WebPagesPending::iterator i = _webPagesPending.begin(); i != _webPagesPending.cend(); ++i) {
if (i.value() == req) {
if (i.key()->pendingTill > 0) {
i.key()->pendingTill = -1;
WebPageItems::const_iterator j = items.constFind(i.key());
if (j != items.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
if (m) m->itemResized(k.key());
}
}
}
i = _webPagesPending.erase(i);
} else {
++i;
}
}
}
ApiWrap::~ApiWrap() {
App::deinitMedia(false);
}

View File

@ -32,6 +32,10 @@ public:
void requestFullPeer(PeerData *peer);
void requestWebPageDelayed(WebPageData *page);
void clearWebPageRequest(WebPageData *page);
void clearWebPageRequests();
~ApiWrap();
signals:
@ -41,6 +45,7 @@ signals:
public slots:
void resolveReplyTo();
void resolveWebPages();
private:
@ -61,4 +66,9 @@ private:
typedef QMap<PeerData*, mtpRequestId> FullRequests;
FullRequests _fullRequests;
void gotWebPages(const MTPmessages_Messages &result, mtpRequestId req);
typedef QMap<WebPageData*, mtpRequestId> WebPagesPending;
WebPagesPending _webPagesPending;
SingleTimer _webPagesTimer;
};

View File

@ -49,9 +49,13 @@ namespace {
typedef QHash<DocumentId, DocumentData*> DocumentsData;
DocumentsData documentsData;
typedef QHash<WebPageId, WebPageData*> WebPagesData;
WebPagesData webPagesData;
VideoItems videoItems;
AudioItems audioItems;
DocumentItems documentItems;
WebPageItems webPageItems;
typedef QMap<HistoryItem*, QMap<HistoryReply*, bool> > RepliesTo;
RepliesTo repliesTo;
@ -321,7 +325,7 @@ namespace App {
data->inputUser = MTP_inputUserContact(d.vid);
data->setName(lang(lng_deleted), QString(), QString(), QString());
data->setPhoto(MTP_userProfilePhotoEmpty());
data->access = 0;
data->access = UserNoAccess;
wasContact = (data->contact > 0);
status = &emptyStatus;
data->contact = -1;
@ -333,9 +337,10 @@ namespace App {
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->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 = 0;
data->access = UserNoAccess;
wasContact = (data->contact > 0);
status = &emptyStatus;
data->contact = -1;
@ -376,7 +381,7 @@ namespace App {
} 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);
@ -601,7 +606,7 @@ namespace App {
const QVector<MTPMessage> &v(msgs.c_vector().v);
QMap<int32, int32> msgsIds;
for (int32 i = 0, l = v.size(); i < l; ++i) {
const MTPMessage &msg(v[i]);
const MTPMessage &msg(v.at(i));
switch (msg.type()) {
case mtpc_message: msgsIds.insert(msg.c_message().vid.v, i); break;
case mtpc_messageEmpty: msgsIds.insert(msg.c_messageEmpty().vid.v, i); break;
@ -609,7 +614,7 @@ namespace App {
}
}
for (QMap<int32, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
histories().addToBack(v[*i], msgsState);
histories().addToBack(v.at(*i), msgsState);
}
}
@ -726,7 +731,7 @@ namespace App {
if (user->inputUser.type() != mtpc_inputUserSelf) user->inputUser = MTP_inputUserContact(userId);
}
} else {
if (user->access) {
if (user->access && user->access != UserNoAccess) {
if (user->input.type() != mtpc_inputPeerSelf) user->input = MTP_inputPeerForeign(userId, MTP_long(user->access));
if (user->inputUser.type() != mtpc_inputUserSelf) user->inputUser = MTP_inputUserForeign(userId, MTP_long(user->access));
}
@ -900,6 +905,23 @@ namespace App {
return App::document(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v);
}
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
return App::webPage(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), webpage.has_site_name() ? qs(webpage.vsite_name) : QString(), webpage.has_title() ? qs(webpage.vtitle) : QString(), webpage.has_description() ? qs(webpage.vdescription) : QString(), webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : 0, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString());
}
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert) {
return App::webPage(webpage.vid.v, convert, QString(), QString(), QString(), QString(), QString(), QString(), 0, 0, QString(), webpage.vdate.v);
}
WebPageData *feedWebPage(const MTPWebPage &webpage) {
switch (webpage.type()) {
case mtpc_webPage: return App::feedWebPage(webpage.c_webPage());
case mtpc_webPageEmpty: return App::webPage(webpage.c_webPageEmpty().vid.v);
case mtpc_webPagePending: return App::feedWebPage(webpage.c_webPagePending());
}
return 0;
}
UserData *userLoaded(const PeerId &user) {
PeerData *peer = peerLoaded(user);
return (peer && peer->loaded) ? peer->asUser() : 0;
@ -1180,6 +1202,80 @@ namespace App {
return result;
}
WebPageData *webPage(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, int32 duration, const QString &author, int32 pendingTill) {
if (convert) {
if (convert->id != webPage) {
WebPagesData::iterator i = webPagesData.find(convert->id);
if (i != webPagesData.cend() && i.value() == convert) {
webPagesData.erase(i);
}
convert->id = webPage;
}
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill)) {
convert->type = toWebPageType(type);
convert->url = url;
convert->displayUrl = displayUrl;
convert->siteName = siteName;
convert->title = title;
convert->description = description;
convert->photo = photo;
convert->duration = duration;
convert->author = author;
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
convert->pendingTill = pendingTill;
MainWidget *m = App::main();
WebPageItems::const_iterator j = ::webPageItems.constFind(convert);
if (j != ::webPageItems.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
if (m) m->itemResized(k.key());
}
}
}
}
WebPagesData::const_iterator i = webPagesData.constFind(webPage);
WebPageData *result;
if (i == webPagesData.cend()) {
if (convert) {
result = convert;
} else {
result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, photo, duration, author, pendingTill);
if (pendingTill > 0 && api()) {
api()->requestWebPageDelayed(result);
}
}
webPagesData.insert(webPage, result);
} else {
result = i.value();
if (result != convert) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill)) {
result->type = toWebPageType(type);
result->url = url;
result->displayUrl = displayUrl;
result->siteName = siteName;
result->title = title;
result->description = description;
result->photo = photo;
result->duration = duration;
result->author = author;
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
result->pendingTill = pendingTill;
MainWidget *m = App::main();
WebPageItems::const_iterator j = ::webPageItems.constFind(result);
if (j != ::webPageItems.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
if (m) m->itemResized(k.key());
}
}
}
}
}
return result;
}
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type, const QString &url) {
ImageLinksData::const_iterator i = imageLinksData.constFind(imageLink);
ImageLinkData *result;
@ -1384,6 +1480,11 @@ namespace App {
delete *i;
}
documentsData.clear();
for (WebPagesData::const_iterator i = webPagesData.cbegin(), e = webPagesData.cend(); i != e; ++i) {
delete *i;
}
webPagesData.clear();
if (api()) api()->clearWebPageRequests();
cSetRecentStickers(RecentStickerPack());
cSetStickersHash(QByteArray());
cSetStickers(AllStickers());
@ -1391,6 +1492,7 @@ namespace App {
::videoItems.clear();
::audioItems.clear();
::documentItems.clear();
::webPageItems.clear();
::repliesTo.clear();
lastPhotos.clear();
lastPhotosMap.clear();
@ -1683,6 +1785,18 @@ namespace App {
return ::documentItems;
}
void regWebPageItem(WebPageData *data, HistoryItem *item) {
::webPageItems[data][item] = true;
}
void unregWebPageItem(WebPageData *data, HistoryItem *item) {
::webPageItems[data].remove(item);
}
const WebPageItems &webPageItems() {
return ::webPageItems;
}
void setProxySettings(QNetworkAccessManager &manager) {
if (cConnectionType() == dbictHttpProxy) {
const ConnectionProxy &p(cConnectionProxy());

View File

@ -34,6 +34,7 @@ typedef QMap<HistoryItem*, bool> HistoryItemsMap;
typedef QHash<VideoData*, HistoryItemsMap> VideoItems;
typedef QHash<AudioData*, HistoryItemsMap> AudioItems;
typedef QHash<DocumentData*, HistoryItemsMap> DocumentItems;
typedef QHash<WebPageData*, HistoryItemsMap> WebPageItems;
namespace App {
Application *app();
@ -96,6 +97,9 @@ namespace App {
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = 0);
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert = 0);
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert = 0);
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert = 0);
WebPageData *feedWebPage(const MTPWebPage &webpage);
UserData *userLoaded(const PeerId &user);
ChatData *chatLoaded(const PeerId &chat);
@ -115,6 +119,7 @@ namespace App {
VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
WebPageData *webPage(const WebPageId &webPage, WebPageData *convert = 0, const QString &type = QString(), const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = 0);
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
void forgetMedia();
@ -181,6 +186,10 @@ namespace App {
void unregDocumentItem(DocumentData *data, HistoryItem *item);
const DocumentItems &documentItems();
void regWebPageItem(WebPageData *data, HistoryItem *item);
void unregWebPageItem(WebPageData *data, HistoryItem *item);
const WebPageItems &webPageItems();
void setProxySettings(QNetworkAccessManager &manager);
void setProxySettings(QTcpSocket &socket);

View File

@ -317,8 +317,10 @@ void Application::chatPhotoDone(PeerId peer, const MTPUpdates &updates) {
emit peerPhotoDone(peer);
}
bool Application::peerPhotoFail(PeerId peer, const RPCError &e) {
LOG(("Application Error: update photo failed %1: %2").arg(e.type()).arg(e.description()));
bool Application::peerPhotoFail(PeerId peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("Application Error: update photo failed %1: %2").arg(error.type()).arg(error.description()));
cancelPhotoUpdate(peer);
emit peerPhotoFail(peer);
return true;
@ -652,8 +654,8 @@ void Application::checkMapVersion() {
psRegisterCustomScheme();
if (Local::oldMapVersion()) {
QString versionFeatures;
if (DevChannel && Local::oldMapVersion() < 7026) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Langs updated, some bugs fixed").replace('@', qsl("@") + QChar(0x200D));
if (DevChannel && Local::oldMapVersion() < 8001) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 View all your sessions and terminate any of them\n\xe2\x80\x94 Two-step verification by additional cloud password\n\xe2\x80\x94 Twitter, YouTube, Instagram links preview\n\xe2\x80\x94 Text is pasted from clipboard when clipboard has both text and image and image sending was cancelled").replace('@', qsl("@") + QChar(0x200D));
} else if (!DevChannel && Local::oldMapVersion() < 8000) {
versionFeatures = lang(lng_new_version7026).trimmed();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -62,7 +62,7 @@ bool AbstractBox::paint(QPainter &p) {
return result;
}
void AbstractBox::paintTitle(QPainter &p, const QString &title, bool withShadow) {
void AbstractBox::paintTitle(Painter &p, const QString &title, bool withShadow) {
if (withShadow) {
// paint shadow
p.fillRect(0, st::boxTitleHeight, width(), st::scrollDef.topsh, st::scrollDef.shColor->b);
@ -71,7 +71,7 @@ void AbstractBox::paintTitle(QPainter &p, const QString &title, bool withShadow)
// paint box title
p.setFont(st::boxTitleFont->f);
p.setPen(st::black->p);
p.drawText(st::boxTitlePos.x(), st::boxTitlePos.y() + st::boxTitleFont->ascent, title);
p.drawTextLeft(st::boxTitlePos.x(), st::boxTitlePos.y(), width() - 2 * st::boxTitlePos.x(), title);
}
void AbstractBox::paintGrayTitle(QPainter &p, const QString &title) {

View File

@ -39,7 +39,7 @@ protected:
void prepare();
bool paint(QPainter &p);
void paintTitle(QPainter &p, const QString &title, bool withShadow);
void paintTitle(Painter &p, const QString &title, bool withShadow);
void paintGrayTitle(QPainter &p, const QString &title);
void setMaxHeight(int32 maxHeight);
void resizeMaxHeight(int32 newWidth, int32 maxHeight);

View File

@ -155,7 +155,7 @@ void AddContactBox::keyPressEvent(QKeyEvent *e) {
}
void AddContactBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
if (_retryButton.isHidden()) {
@ -226,7 +226,8 @@ void AddContactBox::onSaveSelfDone(const MTPUser &user) {
}
bool AddContactBox::onSaveSelfFail(const RPCError &error) {
_addRequest = 0;
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
QString err(error.type());
QString firstName = textOneLine(_firstInput.text()), lastName = textOneLine(_lastInput.text());
if (err == "NAME_NOT_MODIFIED") {
@ -247,6 +248,8 @@ bool AddContactBox::onSaveSelfFail(const RPCError &error) {
}
bool AddContactBox::onSaveFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_addRequest = 0;
QString err(error.type());
QString firstName = _firstInput.text().trimmed(), lastName = _lastInput.text().trimmed();

View File

@ -65,7 +65,7 @@ void AutoLockBox::showAll() {
}
void AutoLockBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_passcode_autolock), true);

View File

@ -29,9 +29,9 @@ TextParseOptions _confirmBoxTextOptions = {
Qt::LayoutDirectionAuto, // dir
};
ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const QString &cancelText) : _infoMsg(false),
_confirm(this, doneText.isEmpty() ? lang(lng_continue) : doneText, st::btnSelectDone),
_cancel(this, cancelText.isEmpty() ? lang(lng_cancel) : cancelText, st::btnSelectCancel),
ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const QString &cancelText, const style::flatButton &doneStyle, const style::flatButton &cancelStyle) : _infoMsg(false),
_confirm(this, doneText.isEmpty() ? lang(lng_continue) : doneText, doneStyle),
_cancel(this, cancelText.isEmpty() ? lang(lng_cancel) : cancelText, cancelStyle),
_close(this, QString(), st::btnInfoClose),
_text(100) {
init(text);

View File

@ -24,7 +24,7 @@ class ConfirmBox : public AbstractBox, public RPCSender {
public:
ConfirmBox(const QString &text, const QString &doneText = QString(), const QString &cancelText = QString());
ConfirmBox(const QString &text, const QString &doneText = QString(), const QString &cancelText = QString(), const style::flatButton &doneStyle = st::btnSelectDone, const style::flatButton &cancelStyle = st::btnSelectCancel);
ConfirmBox(const QString &text, bool noDone, const QString &cancelText = QString());
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);

View File

@ -94,7 +94,7 @@ void ConnectionBox::showDone() {
}
void ConnectionBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_connection_header), true);

View File

@ -942,6 +942,8 @@ void ContactsBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId r
}
bool ContactsBox::peopleFailed(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (_peopleRequest == req) {
_peopleRequest = 0;
_peopleFull = true;
@ -1000,7 +1002,7 @@ void ContactsBox::keyPressEvent(QKeyEvent *e) {
}
void ContactsBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
if (_inner.chat() || _inner.creatingChat()) {
@ -1104,7 +1106,7 @@ void CreateGroupBox::keyPressEvent(QKeyEvent *e) {
}
void CreateGroupBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_create_group_title), true);
@ -1161,12 +1163,14 @@ void CreateGroupBox::created(const MTPUpdates &updates) {
}
}
bool CreateGroupBox::failed(const RPCError &e) {
bool CreateGroupBox::failed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_createRequestId = 0;
if (e.type() == "NO_CHAT_TITLE") {
if (error.type() == "NO_CHAT_TITLE") {
_name.setFocus();
return true;
} else if (e.type() == "USERS_TOO_FEW") {
} else if (error.type() == "USERS_TOO_FEW") {
emit closed();
return true;
}

View File

@ -78,7 +78,7 @@ void DownloadPathBox::showAll() {
}
void DownloadPathBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_download_path_header), true);

View File

@ -94,7 +94,7 @@ void LanguageBox::mousePressEvent(QMouseEvent *e) {
}
void LanguageBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_languages), true);

View File

@ -24,7 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "localstorage.h"
PasscodeBox::PasscodeBox(bool turningOff) : _turningOff(turningOff), _cloudPwd(false),
PasscodeBox::PasscodeBox(bool turningOff) : _replacedBy(0), _turningOff(turningOff), _cloudPwd(false),
_setRequest(0), _hasRecovery(false), _aboutHeight(0),
_about(st::boxWidth - st::addContactPadding.left() - st::addContactPadding.right()),
_saveButton(this, lang(_turningOff ? lng_passcode_remove_button : lng_settings_save), st::btnSelectDone),
@ -39,7 +39,7 @@ _recover(this, lang(lng_signin_recover)) {
prepare();
}
PasscodeBox::PasscodeBox(const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff) : _turningOff(turningOff), _cloudPwd(true),
PasscodeBox::PasscodeBox(const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff) : _replacedBy(0), _turningOff(turningOff), _cloudPwd(true),
_setRequest(0), _newSalt(newSalt), _curSalt(curSalt), _hasRecovery(hasRecovery), _hint(hint), _aboutHeight(0),
_about(st::boxWidth - st::addContactPadding.left() - st::addContactPadding.right()),
_saveButton(this, lang(_turningOff ? lng_passcode_remove_button : lng_settings_save), st::btnSelectDone),
@ -81,13 +81,12 @@ void PasscodeBox::init() {
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
_badOldTimer.setSingleShot(true);
connect(&_badOldTimer, SIGNAL(timeout()), this, SLOT(onBadOldPasscode()));
connect(&_oldPasscode, SIGNAL(changed()), this, SLOT(onOldChanged()));
connect(&_newPasscode, SIGNAL(changed()), this, SLOT(onNewChanged()));
connect(&_reenterPasscode, SIGNAL(changed()), this, SLOT(onNewChanged()));
connect(&_recoverEmail, SIGNAL(changed()), this, SLOT(onEmailChanged()));
connect(&_recover, SIGNAL(clicked()), this, SLOT(onRecoverByEmail()));
}
void PasscodeBox::hideAll() {
@ -182,7 +181,7 @@ void PasscodeBox::keyPressEvent(QKeyEvent *e) {
}
void PasscodeBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, _boxTitle, true);
@ -252,11 +251,12 @@ void PasscodeBox::showDone() {
void PasscodeBox::setPasswordDone(const MTPBool &result) {
_setRequest = 0;
emit reloadPassword();
ConfirmBox *box = new ConfirmBox(lang(_reenterPasscode.isHidden() ? lng_cloud_password_removed : lng_cloud_password_was_set), true, lang(lng_about_done));
ConfirmBox *box = new ConfirmBox(lang(_reenterPasscode.isHidden() ? lng_cloud_password_removed : (_oldPasscode.isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated)), true, lang(lng_about_done));
App::wnd()->showLayer(box, true);
}
bool PasscodeBox::setPasswordFail(const RPCError &error) {
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
_setRequest = 0;
QString err = error.type();
if (err == "PASSWORD_HASH_INVALID") {
@ -283,6 +283,13 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
ConfirmBox *box = new ConfirmBox(lang(lng_cloud_password_almost), true, lang(lng_about_done));
App::wnd()->showLayer(box, true);
emit reloadPassword();
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
if (_oldPasscode.isHidden()) return false;
_oldPasscode.selectAll();
_oldPasscode.setFocus();
_oldPasscode.notaBene();
_oldError = lang(lng_flood_error);
}
return true;
}
@ -293,22 +300,28 @@ void PasscodeBox::onSave(bool force) {
QString old = _oldPasscode.text(), pwd = _newPasscode.text(), conf = _reenterPasscode.text();
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : cHasPasscode();
if (!_cloudPwd && (_turningOff || has)) {
if (!passcodeCanTry()) {
_oldError = lang(lng_flood_error);
_oldPasscode.setFocus();
_oldPasscode.notaBene();
update();
return;
}
if (Local::checkPasscode(old.toUtf8())) {
cSetPasscodeBadTries(0);
if (_turningOff) pwd = conf = QString();
} else {
_oldPasscode.setDisabled(true);
_newPasscode.setDisabled(true);
_reenterPasscode.setDisabled(true);
_saveButton.setDisabled(true);
_oldError = QString();
update();
_badOldTimer.start(WrongPasscodeTimeout);
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
cSetPasscodeLastTry(getms(true));
onBadOldPasscode();
return;
}
}
if (!_turningOff && pwd.isEmpty()) {
_newPasscode.setFocus();
_newPasscode.notaBene();
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
return;
}
if (pwd != conf) {
@ -318,18 +331,28 @@ void PasscodeBox::onSave(bool force) {
_newError = lang(_cloudPwd ? lng_cloud_password_differ : lng_passcode_differ);
update();
}
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
} else if (!_turningOff && has && old == pwd) {
_newPasscode.setFocus();
_newPasscode.notaBene();
_newError = lang(_cloudPwd ? lng_cloud_password_differ : lng_passcode_is_same);
_newError = lang(_cloudPwd ? lng_cloud_password_is_same : lng_passcode_is_same);
update();
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
} else if (_cloudPwd) {
QString hint = _passwordHint.text(), email = _recoverEmail.text().trimmed();
if (_cloudPwd && pwd == hint && !_passwordHint.isHidden() && !_newPasscode.isHidden()) {
_newPasscode.setFocus();
_newPasscode.notaBene();
_newError = lang(lng_cloud_password_bad);
update();
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
return;
}
if (!_recoverEmail.isHidden() && email.isEmpty() && !force) {
ConfirmBox *box = new ConfirmBox(lang(lng_cloud_password_about_recover));
connect(box, SIGNAL(confirmed()), this, SLOT(onForceNoMail()));
connect(box, SIGNAL(confirmed()), box, SLOT(onClose()));
App::wnd()->replaceLayer(box);
_replacedBy = new ConfirmBox(lang(lng_cloud_password_about_recover));
connect(_replacedBy, SIGNAL(confirmed()), this, SLOT(onForceNoMail()));
connect(_replacedBy, SIGNAL(destroyed(QObject*)), this, SLOT(onBoxDestroyed(QObject*)));
App::wnd()->replaceLayer(_replacedBy);
} else {
QByteArray newPasswordData = pwd.isEmpty() ? QByteArray() : (_newSalt + pwd.toUtf8() + _newSalt);
QByteArray newPasswordHash = pwd.isEmpty() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
@ -352,6 +375,7 @@ void PasscodeBox::onSave(bool force) {
_setRequest = MTP::send(MTPaccount_UpdatePasswordSettings(MTP_string(oldPasswordHash), settings), rpcDone(&PasscodeBox::setPasswordDone), rpcFail(&PasscodeBox::setPasswordFail));
}
} else {
cSetPasscodeBadTries(0);
Local::setPasscode(pwd.toUtf8());
App::wnd()->checkAutoLock();
App::wnd()->getTitle()->showUpdateBtn();
@ -360,10 +384,6 @@ void PasscodeBox::onSave(bool force) {
}
void PasscodeBox::onBadOldPasscode() {
_oldPasscode.setDisabled(false);
_newPasscode.setDisabled(false);
_reenterPasscode.setDisabled(false);
_saveButton.setDisabled(false);
_oldPasscode.selectAll();
_oldPasscode.setFocus();
_oldPasscode.notaBene();
@ -396,6 +416,12 @@ void PasscodeBox::onForceNoMail() {
onSave(true);
}
void PasscodeBox::onBoxDestroyed(QObject *obj) {
if (obj == _replacedBy) {
_replacedBy = 0;
}
}
void PasscodeBox::onRecoverByEmail() {
if (_pattern.isEmpty()) {
_pattern = "-";
@ -412,10 +438,11 @@ void PasscodeBox::onRecoverExpired() {
void PasscodeBox::recover() {
if (_pattern == "-") return;
RecoverBox *box = new RecoverBox(_pattern);
connect(box, SIGNAL(reloadPassword()), this, SIGNAL(reloadPassword()));
connect(box, SIGNAL(recoveryExpired()), this, SLOT(onRecoverExpired()));
App::wnd()->replaceLayer(box);
_replacedBy = new RecoverBox(_pattern);
connect(_replacedBy, SIGNAL(reloadPassword()), this, SIGNAL(reloadPassword()));
connect(_replacedBy, SIGNAL(recoveryExpired()), this, SLOT(onRecoverExpired()));
connect(_replacedBy, SIGNAL(destroyed(QObject*)), this, SLOT(onBoxDestroyed(QObject*)));
App::wnd()->replaceLayer(_replacedBy);
}
void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
@ -424,6 +451,8 @@ void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
}
bool PasscodeBox::recoverStartFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_pattern = QString();
onClose();
return true;
@ -470,7 +499,7 @@ void RecoverBox::keyPressEvent(QKeyEvent *e) {
}
void RecoverBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_signin_recover), true);
@ -478,8 +507,9 @@ void RecoverBox::paintEvent(QPaintEvent *e) {
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
p.setFont(st::usernameFont->f);
int32 w = width() - st::addContactPadding.left() - st::addContactPadding.right();
p.drawText(QRect(st::addContactPadding.left(), _recoverCode.y() - st::usernameSkip, w, st::usernameSkip), st::usernameFont->m.elidedText(_pattern, Qt::ElideRight, w), style::al_center);
p.drawText(QRect(st::addContactPadding.left(), _recoverCode.y() - st::usernameSkip - st::addContactPadding.top(), w, st::addContactPadding.top() + st::usernameSkip), st::usernameFont->m.elidedText(_pattern, Qt::ElideRight, w), style::al_center);
if (!_error.isEmpty()) {
p.setPen(st::setErrColor->p);
@ -548,8 +578,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
update();
_recoverCode.notaBene();
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
_error = lang(lng_flood_error);
update();
_recoverCode.notaBene();

View File

@ -39,6 +39,7 @@ public slots:
void onNewChanged();
void onEmailChanged();
void onForceNoMail();
void onBoxDestroyed(QObject *obj);
void onRecoverByEmail();
void onRecoverExpired();
@ -63,6 +64,7 @@ private:
void recover();
QString _pattern;
AbstractBox *_replacedBy;
bool _turningOff, _cloudPwd;
mtpRequestId _setRequest;
@ -79,7 +81,6 @@ private:
FlatInput _oldPasscode, _newPasscode, _reenterPasscode, _passwordHint, _recoverEmail;
LinkButton _recover;
QTimer _badOldTimer;
QString _oldError, _newError, _emailError;
};

View File

@ -210,6 +210,7 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) {
if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter, _replyTo);
}
}
emit confirmed();
emit closed();
}

View File

@ -32,6 +32,10 @@ public:
void resizeEvent(QResizeEvent *e);
~PhotoSendBox();
signals:
void confirmed();
public slots:
void onSend(bool ctrlShiftEnter = false);

View File

@ -106,6 +106,8 @@ void SessionsInner::terminateDone(uint64 hash, const MTPBool &result) {
}
bool SessionsInner::terminateFail(uint64 hash, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
TerminateButtons::iterator i = _terminateButtons.find(hash);
if (i != _terminateButtons.end()) {
i.value()->show();
@ -257,18 +259,18 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
SessionData data;
data.hash = d.vhash.v;
QString appName, systemVer = qs(d.vsystem_version);
if (d.vapi_id.v == 2040 || d.vapi_id.v == 17349) {
appName = (d.vapi_id.v == 2040) ? qsl("Telegram Desktop") : qsl("Telegram Desktop (GitHub)");
QString appName, systemVer = qs(d.vsystem_version), deviceModel = qs(d.vdevice_model);
if (d.vapi_id.v == 17349) {
appName = qs(d.vapp_name);// (d.vapi_id.v == 2040) ? qsl("Telegram Desktop") : qsl("Telegram Desktop (GitHub)");
if (systemVer == QLatin1String("windows")) {
systemVer = qsl("Windows");
deviceModel = qsl("Windows");
} else if (systemVer == QLatin1String("os x")) {
systemVer = qsl("Mac OS X");
deviceModel = qsl("Mac OS X");
} else if (systemVer == QLatin1String("linux")) {
systemVer = qsl("Linux");
deviceModel = qsl("Linux");
}
} else {
appName = qs(d.vapp_name);
appName = qs(d.vapp_name);// +qsl(" for ") + qs(d.vplatform);
}
data.name = appName;
data.nameWidth = st::sessionNameFont->m.width(data.name);
@ -277,7 +279,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
CountriesByISO2::const_iterator j = countries.constFind(country);
if (j != countries.cend()) country = QString::fromUtf8(j.value()->name);
data.info = country + QLatin1String(" (") + qs(d.vip) + QLatin1String("), ") + systemVer;
data.info = country + QLatin1String(" (") + qs(d.vip) + QLatin1String("), ") + deviceModel;
if (!data.hash || (d.vflags.v & 1)) {
data.active = QString();
data.activeWidth = 0;
@ -292,7 +294,16 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
}
_current = data;
} else {
data.active = date(d.vdate_active.v ? d.vdate_active : d.vdate_created).toString(qsl("hh:mm"));
QDateTime now(QDateTime::currentDateTime()), lastTime(date(d.vdate_active.v ? d.vdate_active : d.vdate_created));
QDate nowDate(now.date()), lastDate(lastTime.date());
QString dt;
if (lastDate == nowDate) {
data.active = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) {
data.active = langDayOfWeek(lastDate);
} else {
data.active = lastDate.toString(qsl("d.MM.yy"));
}
data.activeWidth = st::sessionActiveFont->m.width(data.active);
int32 availForName = availOther - st::sessionPadding.right() - data.activeWidth;
if (data.nameWidth > availForName) {
@ -381,6 +392,8 @@ void SessionsBox::terminateAllDone(const MTPBool &result) {
}
bool SessionsBox::terminateAllFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
if (_shortPollRequest) {
MTP::cancel(_shortPollRequest);

View File

@ -99,7 +99,7 @@ void UsernameBox::keyPressEvent(QKeyEvent *e) {
}
void UsernameBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_username_title), true);
@ -194,6 +194,8 @@ void UsernameBox::onUpdateDone(const MTPUser &user) {
}
bool UsernameBox::onUpdateFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_saveRequest = 0;
QString err(error.type()), name = getName();
if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == App::self()->username) {
@ -227,6 +229,8 @@ void UsernameBox::onCheckDone(const MTPBool &result) {
}
bool UsernameBox::onCheckFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_checkRequest = 0;
QString err(error.type());
if (err == "USERNAME_INVALID") {

View File

@ -121,6 +121,7 @@ enum {
HiddenIsOnlineAfterMessage = 30, // user with hidden last seen stays online for such amount of seconds in the interface
ServiceUserId = 777000,
WebPageUserId = 701000,
CacheBackgroundTimeout = 3000, // cache background scaled image after 3s
BackgroundsInRow = 3,

View File

@ -464,6 +464,7 @@ void DialogsListWidget::enterEvent(QEvent *e) {
void DialogsListWidget::leaveEvent(QEvent *e) {
setMouseTracking(false);
selByMouse = false;
if (sel || filteredSel >= 0 || hashtagSel >= 0 || searchedSel >= 0 || peopleSel >= 0) {
sel = 0;
filteredSel = searchedSel = peopleSel = hashtagSel = -1;
@ -745,6 +746,7 @@ int32 DialogsListWidget::addNewContact(int32 uid, bool select) {
sel = added;
contactSel = true;
}
if (contactsNoDialogs.list.count == 1 && !dialogs.list.count) refresh();
return added ? ((dialogs.list.count + added->pos) * st::dlgHeight) : -1;
}
if (select) {
@ -1091,7 +1093,7 @@ bool DialogsListWidget::choosePeer() {
if (msgId) {
saveRecentHashtags(filter);
}
bool chosen = (!App::main()->selectingPeer() && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
bool chosen = (!App::main()->selectingPeer(true) && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
App::main()->showPeer(history->peer->id, msgId);
if (chosen) {
emit searchResultChosen();
@ -1519,8 +1521,10 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs) {
}
}
bool DialogsWidget::dialogsFailed(const RPCError &e) {
LOG(("RPC Error: %1 %2: %3").arg(e.code()).arg(e.type()).arg(e.description()));
bool DialogsWidget::dialogsFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("RPC Error: %1 %2: %3").arg(error.code()).arg(error.type()).arg(error.description()));
dlgPreloading = 0;
return true;
}
@ -1614,7 +1618,9 @@ void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &contacts) {
}
}
bool DialogsWidget::contactsFailed() {
bool DialogsWidget::contactsFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
return true;
}
@ -1680,6 +1686,8 @@ void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId
}
bool DialogsWidget::searchFailed(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (_searchRequest == req) {
_searchRequest = 0;
_searchFull = true;
@ -1688,6 +1696,8 @@ bool DialogsWidget::searchFailed(const RPCError &error, mtpRequestId req) {
}
bool DialogsWidget::peopleFailed(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (_peopleRequest == req) {
_peopleRequest = 0;
_peopleFull = true;

View File

@ -232,8 +232,8 @@ private:
bool _drawShadow;
void unreadCountsReceived(const QVector<MTPDialog> &dialogs);
bool dialogsFailed(const RPCError &e);
bool contactsFailed();
bool dialogsFailed(const RPCError &error);
bool contactsFailed(const RPCError &error);
bool searchFailed(const RPCError &error, mtpRequestId req);
bool peopleFailed(const RPCError &error, mtpRequestId req);

View File

@ -256,7 +256,9 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
sendNext();
}
bool FileUploader::partFailed(const RPCError &err, mtpRequestId requestId) {
bool FileUploader::partFailed(const RPCError &error, mtpRequestId requestId) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (requestsSent.constFind(requestId) != requestsSent.cend() || docRequestsSent.constFind(requestId) != docRequestsSent.cend()) { // failed to upload current file
currentFailed();
}

View File

@ -68,6 +68,10 @@ void FlatTextarea::onTouchTimer() {
_touchRightButton = true;
}
void FlatTextarea::insertFromMimeData(const QMimeData *source) {
QTextEdit::insertFromMimeData(source);
}
bool FlatTextarea::viewportEvent(QEvent *e) {
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
QTouchEvent *ev = static_cast<QTouchEvent*>(e);

View File

@ -29,6 +29,8 @@ public:
FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &ph = QString(), const QString &val = QString());
QString val() const;
void insertFromMimeData(const QMimeData *source);
bool viewportEvent(QEvent *e);
void touchEvent(QTouchEvent *e);
void paintEvent(QPaintEvent *e);

View File

@ -54,7 +54,7 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st)
}
void ScrollBar::recountSize() {
setGeometry(_vertical ? QRect(_area->width() - _st->width, 0, _st->width, _area->height()) : QRect(0, _area->height() - _st->width, _area->width(), _st->width));
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), 0, _st->width, _area->height()) : QRect(0, _area->height() - _st->width, _area->width(), _st->width));
}
void ScrollBar::updateBar(bool force) {

View File

@ -854,7 +854,7 @@ void TextLink::onClick(Qt::MouseButton button) const {
} else if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) {
App::openLocalUrl(url);
} else {
QDesktopServices::openUrl(TextLink::encoded());
QDesktopServices::openUrl(url);
}
}
}
@ -881,7 +881,7 @@ public:
return _blockEnd(t, i, e) - (*i)->from();
}
TextPainter(QPainter *p, const Text *t) : _p(p), _t(t), _elideLast(false), _str(0), _elideSavedBlock(0), _lnkResult(0), _inTextFlag(0), _getSymbol(0), _getSymbolAfter(0), _getSymbolUpon(0) {
TextPainter(QPainter *p, const Text *t) : _p(p), _t(t), _elideLast(false), _elideRemoveFromEnd(0), _str(0), _elideSavedBlock(0), _lnkResult(0), _inTextFlag(0), _getSymbol(0), _getSymbolAfter(0), _getSymbolUpon(0) {
}
void initNextParagraph(Text::TextBlocks::const_iterator i) {
@ -1010,6 +1010,9 @@ public:
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
_wLeft = _w - (b->f_width() - last_rBearing);
if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
_wLeft -= _elideRemoveFromEnd;
}
_parDirection = static_cast<NewlineBlock*>(b)->nextDirection();
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir();
@ -1079,6 +1082,9 @@ public:
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
_wLeft = _w - (j_width - last_rBearing);
if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
_wLeft -= _elideRemoveFromEnd;
}
longWordLine = true;
f = j + 1;
@ -1102,6 +1108,9 @@ public:
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
_wLeft = _w - (b->f_width() - last_rBearing);
if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
_wLeft -= _elideRemoveFromEnd;
}
longWordLine = true;
continue;
@ -1116,12 +1125,13 @@ public:
}
}
void drawElided(int32 left, int32 top, int32 w, style::align align, int32 lines, int32 yFrom, int32 yTo) {
void drawElided(int32 left, int32 top, int32 w, style::align align, int32 lines, int32 yFrom, int32 yTo, int32 removeFromEnd) {
if (lines <= 0) return;
if (yTo < 0 || (lines - 1) * _t->_font->height < yTo) {
yTo = lines * _t->_font->height;
_elideLast = true;
_elideRemoveFromEnd = removeFromEnd;
}
draw(left, top, w, align, yFrom, yTo);
}
@ -1575,7 +1585,7 @@ public:
eShapeLine(line);
int32 elideWidth = _f->m.width(_Elide);
_wLeft = _w - elideWidth;
_wLeft = _w - elideWidth - _elideRemoveFromEnd;
int firstItem = engine.findItem(line.from), lastItem = engine.findItem(line.from + line.length - 1);
int nItems = (firstItem >= 0 && lastItem >= firstItem) ? (lastItem - firstItem + 1) : 0, i;
@ -2264,6 +2274,7 @@ private:
QPainter *_p;
const Text *_t;
bool _elideLast;
int32 _elideRemoveFromEnd;
style::align _align;
QPen _originalPen;
int32 _yFrom, _yTo;
@ -2326,6 +2337,20 @@ Text::Text(style::font font, const QString &text, const TextParseOptions &option
}
}
Text::Text(const Text &other) :
_minResizeWidth(other._minResizeWidth), _maxWidth(other._maxWidth),
_minHeight(other._minHeight),
_text(other._text),
_font(other._font),
_blocks(other._blocks.size()),
_links(other._links),
_startDir(other._startDir)
{
for (int32 i = 0, l = _blocks.size(); i < l; ++i) {
_blocks[i] = other._blocks.at(i)->clone();
}
}
void Text::setText(style::font font, const QString &text, const TextParseOptions &options) {
if (!_textStyle) _initDefault();
_font = font;
@ -2597,10 +2622,10 @@ void Text::draw(QPainter &painter, int32 left, int32 top, int32 w, style::align
p.draw(left, top, w, align, yFrom, yTo, selectedFrom, selectedTo);
}
void Text::drawElided(QPainter &painter, int32 left, int32 top, int32 w, int32 lines, style::align align, int32 yFrom, int32 yTo) const {
void Text::drawElided(QPainter &painter, int32 left, int32 top, int32 w, int32 lines, style::align align, int32 yFrom, int32 yTo, int32 removeFromEnd) const {
// painter.fillRect(QRect(left, top, w, countHeight(w)), QColor(0, 0, 0, 32)); // debug
TextPainter p(&painter, this);
p.drawElided(left, top, w, align, lines, yFrom, yTo);
p.drawElided(left, top, w, align, lines, yFrom, yTo, removeFromEnd);
}
const TextLinkPtr &Text::link(int32 x, int32 y, int32 width, style::align align) const {

View File

@ -105,6 +105,7 @@ public:
return tmp;//_color;
}
virtual ITextBlock *clone() const = 0;
virtual ~ITextBlock() {
}
@ -125,6 +126,10 @@ public:
return _nextDir;
}
ITextBlock *clone() const {
return new NewlineBlock(*this);
}
private:
NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, st::transparent, 0), _nextDir(Qt::LayoutDirectionAuto) {
@ -160,6 +165,10 @@ public:
return _words.isEmpty() ? 0 : _words.back().f_rbearing();
}
ITextBlock *clone() const {
return new TextBlock(*this);
}
private:
TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex);
@ -177,6 +186,10 @@ private:
class EmojiBlock : public ITextBlock {
public:
ITextBlock *clone() const {
return new EmojiBlock(*this);
}
private:
EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex, const EmojiData *emoji);
@ -196,6 +209,10 @@ public:
return _height;
}
ITextBlock *clone() const {
return new SkipBlock(*this);
}
private:
SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex);
@ -399,6 +416,7 @@ public:
Text(int32 minResizeWidth = QFIXED_MAX);
Text(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false);
Text(const Text &other);
int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
@ -417,13 +435,16 @@ public:
void replaceFont(style::font f); // does not recount anything, use at your own risk!
void draw(QPainter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, uint16 selectedFrom = 0, uint16 selectedTo = 0) const;
void drawElided(QPainter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1) const;
void drawElided(QPainter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0) const;
const TextLinkPtr &link(int32 x, int32 y, int32 width, style::align align = style::al_left) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, int32 width, style::align align = style::al_left) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y, int32 width, style::align align = style::al_left) const;
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType selectType) const;
bool isEmpty() const {
return _text.isEmpty();
}
QString original(uint16 selectedFrom = 0, uint16 selectedTo = 0xFFFF, bool expandLinks = true) const;
bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation

File diff suppressed because it is too large Load Diff

View File

@ -17,13 +17,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
typedef uint64 PeerId;
typedef uint64 PhotoId;
typedef uint64 VideoId;
typedef uint64 AudioId;
typedef uint64 DocumentId;
typedef int32 MsgId;
void historyInit();
class HistoryItem;
@ -39,467 +32,8 @@ typedef QMap<int32, HistoryItem*> SelectedItemSet;
extern TextParseOptions _textNameOptions, _textDlgOptions;
struct NotifySettings {
NotifySettings() : mute(0), sound("default"), previews(true), events(1) {
}
int32 mute;
string sound;
bool previews;
int32 events;
};
typedef NotifySettings *NotifySettingsPtr;
#include "structs.h"
static const NotifySettingsPtr UnknownNotifySettings = NotifySettingsPtr(0);
static const NotifySettingsPtr EmptyNotifySettings = NotifySettingsPtr(1);
extern NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats;
extern NotifySettingsPtr globalNotifyAllPtr, globalNotifyUsersPtr, globalNotifyChatsPtr;
inline bool isNotifyMuted(NotifySettingsPtr settings) {
if (settings == UnknownNotifySettings || settings == EmptyNotifySettings) {
return false;
}
return (settings->mute > unixtime());
}
style::color peerColor(int32 index);
ImagePtr userDefPhoto(int32 index);
ImagePtr chatDefPhoto(int32 index);
struct ChatData;
struct UserData;
struct PeerData {
PeerData(const PeerId &id);
virtual ~PeerData() {
if (notify != UnknownNotifySettings && notify != EmptyNotifySettings) {
delete notify;
notify = UnknownNotifySettings;
}
}
UserData *asUser();
const UserData *asUser() const;
ChatData *asChat();
const ChatData *asChat() const;
void updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername);
void fillNames();
virtual void nameUpdated() {
}
PeerId id;
QString name;
QString nameOrPhone;
typedef QSet<QString> Names;
Names names; // for filtering
typedef QSet<QChar> NameFirstChars;
NameFirstChars chars;
bool loaded;
bool chat;
uint64 access;
MTPinputPeer input;
MTPinputUser inputUser;
int32 colorIndex;
style::color color;
ImagePtr photo;
int32 nameVersion;
NotifySettingsPtr notify;
};
class PeerLink : public ITextLink {
public:
PeerLink(PeerData *peer) : _peer(peer) {
}
void onClick(Qt::MouseButton button) const;
PeerData *peer() const {
return _peer;
}
private:
PeerData *_peer;
};
struct PhotoData;
struct UserData : public PeerData {
UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1) {
}
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 nameUpdated();
QString firstName;
QString lastName;
QString username;
QString phone;
Text nameText;
PhotoId photoId;
TextLinkPtr lnk;
int32 onlineTill;
int32 contact; // -1 - not contact, cant add (self, empty, deleted, foreign), 0 - not contact, can add (request), 1 - contact
typedef QList<PhotoData*> Photos;
Photos photos;
int32 photosCount; // -1 not loaded, 0 all loaded
};
struct ChatData : public PeerData {
ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), photoId(0) {
}
void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = 0);
int32 count;
int32 date;
int32 version;
int32 admin;
bool left;
bool forbidden;
typedef QMap<UserData*, int32> Participants;
Participants participants;
typedef QMap<UserData*, bool> CanKick;
CanKick cankick;
typedef QList<UserData*> LastAuthors;
LastAuthors lastAuthors;
ImagePtr photoFull;
PhotoId photoId;
// geo
};
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()) :
id(id), access(access), user(user), date(date), thumb(thumb), medium(medium), full(full), chat(0) {
}
void forget() {
thumb->forget();
replyPreview->forget();
medium->forget();
full->forget();
}
PhotoId id;
uint64 access;
int32 user;
int32 date;
ImagePtr thumb, replyPreview;
ImagePtr medium;
ImagePtr full;
ChatData *chat; // for chat photos connection
// geo, caption
int32 cachew;
QPixmap cache;
};
class PhotoLink : public ITextLink {
public:
PhotoLink(PhotoData *photo) : _photo(photo), _peer(0) {
}
PhotoLink(PhotoData *photo, PeerData *peer) : _photo(photo), _peer(peer) {
}
void onClick(Qt::MouseButton button) const;
PhotoData *photo() const {
return _photo;
}
PeerData *peer() const {
return _peer;
}
private:
PhotoData *_photo;
PeerData *_peer;
};
enum FileStatus {
FileFailed = -1,
FileUploading = 0,
FileReady = 1,
};
struct VideoData {
VideoData(const VideoId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void forget() {
thumb->forget();
replyPreview->forget();
}
void save(const QString &toFile);
void cancel(bool beforeDownload = false) {
mtpFileLoader *l = loader;
loader = 0;
if (l) {
l->cancel();
l->deleteLater();
l->rpcInvalidate();
}
location = FileLocation();
if (!beforeDownload) {
openOnSave = openOnSaveMsgId = 0;
}
}
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
}
loader->deleteLater();
loader->rpcInvalidate();
loader = 0;
}
QString already(bool check = false);
VideoId id;
uint64 access;
int32 user;
int32 date;
int32 duration;
int32 w, h;
ImagePtr thumb, replyPreview;
int32 dc, size;
// geo, caption
FileStatus status;
int32 uploadOffset;
mtpTypeId fileType;
int32 openOnSave, openOnSaveMsgId;
mtpFileLoader *loader;
FileLocation location;
};
class VideoLink : public ITextLink {
public:
VideoLink(VideoData *video) : _video(video) {
}
VideoData *video() const {
return _video;
}
private:
VideoData *_video;
};
class VideoSaveLink : public VideoLink {
public:
VideoSaveLink(VideoData *video) : VideoLink(video) {
}
void doSave(bool forceSavingAs = false) const;
void onClick(Qt::MouseButton button) const;
};
class VideoOpenLink : public VideoLink {
public:
VideoOpenLink(VideoData *video) : VideoLink(video) {
}
void onClick(Qt::MouseButton button) const;
};
class VideoCancelLink : public VideoLink {
public:
VideoCancelLink(VideoData *video) : VideoLink(video) {
}
void onClick(Qt::MouseButton button) const;
};
struct AudioData {
AudioData(const AudioId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
void forget() {
}
void save(const QString &toFile);
void cancel(bool beforeDownload = false) {
mtpFileLoader *l = loader;
loader = 0;
if (l) {
l->cancel();
l->deleteLater();
l->rpcInvalidate();
}
location = FileLocation();
if (!beforeDownload) {
openOnSave = openOnSaveMsgId = 0;
}
}
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
data = loader->bytes();
}
loader->deleteLater();
loader->rpcInvalidate();
loader = 0;
}
QString already(bool check = false);
AudioId id;
uint64 access;
int32 user;
int32 date;
QString mime;
int32 duration;
int32 dc;
int32 size;
FileStatus status;
int32 uploadOffset;
int32 openOnSave, openOnSaveMsgId;
mtpFileLoader *loader;
FileLocation location;
QByteArray data;
int32 md5[8];
};
class AudioLink : public ITextLink {
public:
AudioLink(AudioData *audio) : _audio(audio) {
}
AudioData *audio() const {
return _audio;
}
private:
AudioData *_audio;
};
class AudioSaveLink : public AudioLink {
public:
AudioSaveLink(AudioData *audio) : AudioLink(audio) {
}
void doSave(bool forceSavingAs = false) const;
void onClick(Qt::MouseButton button) const;
};
class AudioOpenLink : public AudioLink {
public:
AudioOpenLink(AudioData *audio) : AudioLink(audio) {
}
void onClick(Qt::MouseButton button) const;
};
class AudioCancelLink : public AudioLink {
public:
AudioCancelLink(AudioData *audio) : AudioLink(audio) {
}
void onClick(Qt::MouseButton button) const;
};
enum DocumentType {
FileDocument,
VideoDocument,
AudioDocument,
StickerDocument,
AnimatedDocument
};
struct DocumentData {
DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void setattributes(const QVector<MTPDocumentAttribute> &attributes);
void forget() {
thumb->forget();
sticker->forget();
replyPreview->forget();
}
void save(const QString &toFile);
void cancel(bool beforeDownload = false) {
mtpFileLoader *l = loader;
loader = 0;
if (l) {
l->cancel();
l->deleteLater();
l->rpcInvalidate();
}
location = FileLocation();
if (!beforeDownload) {
openOnSave = openOnSaveMsgId = 0;
}
}
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
data = loader->bytes();
}
loader->deleteLater();
loader->rpcInvalidate();
loader = 0;
}
QString already(bool check = false);
DocumentId id;
DocumentType type;
QSize dimensions;
int32 duration;
uint64 access;
int32 date;
QString name, mime, alt; // alt - for stickers
ImagePtr thumb, replyPreview;
int32 dc;
int32 size;
FileStatus status;
int32 uploadOffset;
int32 openOnSave, openOnSaveMsgId;
mtpFileLoader *loader;
FileLocation location;
QByteArray data;
ImagePtr sticker;
int32 md5[8];
};
class DocumentLink : public ITextLink {
public:
DocumentLink(DocumentData *document) : _document(document) {
}
DocumentData *document() const {
return _document;
}
private:
DocumentData *_document;
};
class DocumentSaveLink : public DocumentLink {
public:
DocumentSaveLink(DocumentData *document) : DocumentLink(document) {
}
void doSave(bool forceSavingAs = false) const;
void onClick(Qt::MouseButton button) const;
};
class DocumentOpenLink : public DocumentLink {
public:
DocumentOpenLink(DocumentData *document) : DocumentLink(document) {
}
void onClick(Qt::MouseButton button) const;
};
class DocumentCancelLink : public DocumentLink {
public:
DocumentCancelLink(DocumentData *document) : DocumentLink(document) {
}
void onClick(Qt::MouseButton button) const;
};
MsgId clientMsgId();
struct History;
struct Histories : public QHash<PeerId, History*>, public Animated {
@ -521,7 +55,7 @@ struct Histories : public QHash<PeerId, History*>, public Animated {
}
HistoryItem *addToBack(const MTPmessage &msg, int msgState = 1); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message
// HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true);
// HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true);
typedef QMap<History*, uint64> TypingHistories; // when typing in this history started
TypingHistories typing;
@ -563,6 +97,7 @@ enum HistoryMediaType {
MediaTypeDocument,
MediaTypeSticker,
MediaTypeImageLink,
MediaTypeWebPage,
MediaTypeCount
};
@ -581,7 +116,7 @@ inline MediaOverviewType mediaToOverviewType(HistoryMediaType t) {
case MediaTypePhoto: return OverviewPhotos;
case MediaTypeVideo: return OverviewVideos;
case MediaTypeDocument: return OverviewDocuments;
// case MediaTypeSticker: return OverviewDocuments;
// case MediaTypeSticker: return OverviewDocuments;
case MediaTypeAudio: return OverviewAudios;
}
return OverviewCount;
@ -598,34 +133,6 @@ inline MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) {
return MTPMessagesFilter();
}
struct MessageCursor {
MessageCursor() : position(0), anchor(0), scroll(QFIXED_MAX) {
}
MessageCursor(int position, int anchor, int scroll) : position(position), anchor(anchor), scroll(scroll) {
}
MessageCursor(const QTextEdit &edit) {
fillFrom(edit);
}
void fillFrom(const QTextEdit &edit) {
QTextCursor c = edit.textCursor();
position = c.position();
anchor = c.anchor();
QScrollBar *s = edit.verticalScrollBar();
scroll = s ? s->value() : QFIXED_MAX;
}
void applyTo(QTextEdit &edit, bool *lock = 0) {
if (lock) *lock = true;
QTextCursor c = edit.textCursor();
c.setPosition(anchor, QTextCursor::MoveAnchor);
c.setPosition(position, QTextCursor::KeepAnchor);
edit.setTextCursor(c);
QScrollBar *s = edit.verticalScrollBar();
if (s) s->setValue(scroll);
if (lock) *lock = false;
}
int position, anchor, scroll;
};
class HistoryMedia;
class HistoryMessage;
class HistoryUnreadBar;
@ -1084,6 +591,9 @@ public:
int32 maxWidth() const {
return _maxw;
}
int32 minHeight() const {
return _minh;
}
virtual ~HistoryElem() {
}
@ -1206,6 +716,8 @@ public:
virtual HistoryMedia *getMedia(bool inOverview = false) const {
return 0;
}
virtual void setMedia(const MTPmessageMedia &media) {
}
virtual QString time() const {
return QString();
}
@ -1259,6 +771,8 @@ public:
HistoryMedia(int32 width = 0) : w(width) {
}
HistoryMedia(const HistoryMedia &other) : w(0) {
}
virtual HistoryMediaType type() const = 0;
virtual const QString inDialogsText() const = 0;
@ -1315,7 +829,7 @@ protected:
class HistoryPhoto : public HistoryMedia {
public:
HistoryPhoto(const MTPDphoto &photo, int32 width = 0);
HistoryPhoto(const MTPDphoto &photo);
HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
void init();
@ -1364,7 +878,7 @@ QString formatSizeText(qint64 size);
class HistoryVideo : public HistoryMedia {
public:
HistoryVideo(const MTPDvideo &video, int32 width = 0);
HistoryVideo(const MTPDvideo &video);
void initDimensions(const HistoryItem *parent);
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
@ -1402,7 +916,7 @@ private:
class HistoryAudio : public HistoryMedia {
public:
HistoryAudio(const MTPDaudio &audio, int32 width = 0);
HistoryAudio(const MTPDaudio &audio);
void initDimensions(const HistoryItem *parent);
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
@ -1434,7 +948,7 @@ private:
class HistoryDocument : public HistoryMedia {
public:
HistoryDocument(DocumentData *document, int32 width = 0);
HistoryDocument(DocumentData *document);
void initDimensions(const HistoryItem *parent);
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
@ -1482,7 +996,7 @@ private:
class HistorySticker : public HistoryMedia {
public:
HistorySticker(DocumentData *document, int32 width = 0);
HistorySticker(DocumentData *document);
void initDimensions(const HistoryItem *parent);
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
@ -1542,6 +1056,45 @@ private:
UserData *contact;
};
class HistoryWebPage : public HistoryMedia {
public:
HistoryWebPage(WebPageData *data);
void initDimensions(const HistoryItem *parent);
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
HistoryMediaType type() const {
return MediaTypeWebPage;
}
const QString inDialogsText() const;
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
HistoryMedia *clone() const;
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
bool hasReplyPreview() const {
return data->photo && !data->photo->thumb->isNull();
}
ImagePtr replyPreview();
private:
WebPageData *data;
TextLinkPtr _openl, _photol;
bool _asArticle;
Text _title, _description;
int32 _siteNameWidth;
QString _duration;
int32 _durationWidth;
int16 _pixw, _pixh;
};
void initImageLinkManager();
void reinitImageLinkManager();
void deinitImageLinkManager();
@ -1597,7 +1150,7 @@ private:
class HistoryImageLink : public HistoryMedia {
public:
HistoryImageLink(const QString &url, int32 width = 0);
HistoryImageLink(const QString &url);
int32 fullWidth() const;
int32 fullHeight() const;
void initDimensions(const HistoryItem *parent);
@ -1628,11 +1181,16 @@ public:
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc);
void initMedia(const MTPMessageMedia &media, QString &currentText);
void initMediaFromText(QString &currentText);
void initMediaFromDocument(DocumentData *doc);
void initDimensions(const HistoryItem *parent = 0);
void initDimensions(const QString &text);
void fromNameUpdated() const;
bool justMedia() const {
return _media && _text.isEmpty();
}
bool uploading() const;
void draw(QPainter &p, uint32 selection) const;
@ -1658,6 +1216,7 @@ public:
QString selectedText(uint32 selection) const;
QString inDialogsText() const;
HistoryMedia *getMedia(bool inOverview = false) const;
void setMedia(const MTPmessageMedia &media);
QString time() const {
return _time;
@ -1731,6 +1290,7 @@ public:
HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
void initDimensions(const HistoryItem *parent = 0);
bool updateReplyTo(bool force = false);
void replyToNameUpdated() const;
int32 replyToWidth() const;

View File

@ -1292,7 +1292,7 @@ void MessageField::insertFromMimeData(const QMimeData *source) {
if (source->hasImage()) {
QImage img = qvariant_cast<QImage>(source->imageData());
if (!img.isNull()) {
history->uploadImage(img);
history->uploadImage(img, false, source->text());
return;
}
}
@ -1385,6 +1385,10 @@ bool HistoryHider::animStep(float64 ms) {
return res;
}
bool HistoryHider::withConfirm() const {
return _sharedContact || _sendPath;
}
void HistoryHider::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (!hiding || !cacheForAnim.isNull() || !offered) {
@ -1470,6 +1474,7 @@ void HistoryHider::forward() {
parent()->onForward(offered->id, _forwardSelected);
}
}
emit forwarded();
}
void HistoryHider::forwardDone() {
@ -1903,6 +1908,8 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
}
bool HistoryWidget::stickersFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_lastStickersUpdate = getms(true);
_stickersUpdateRequest = 0;
return true;
@ -2258,8 +2265,10 @@ void HistoryWidget::historyWasRead(bool force) {
App::main()->readServerHistory(hist, force);
}
bool HistoryWidget::messagesFailed(const RPCError &e, mtpRequestId requestId) {
LOG(("RPC Error: %1 %2: %3").arg(e.code()).arg(e.type()).arg(e.description()));
bool HistoryWidget::messagesFailed(const RPCError &error, mtpRequestId requestId) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("RPC Error: %1 %2: %3").arg(error.code()).arg(error.type()).arg(error.description()));
if (histPreloading == requestId) {
histPreloading = 0;
} else if (histPreloadingDown == requestId) {
@ -2587,11 +2596,6 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const
h->sendRequestId = MTP::send(MTPmessages_SendMedia(p->input, MTP_int(replyTo), MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
App::historyRegRandom(randomId, newId);
if (hist && histPeer && peer == histPeer->id) {
App::main()->historyToDown(hist);
}
App::main()->dialogsToUp();
peerMessagesUpdated(peer);
App::main()->finishForwarding(h);
cancelReply();
@ -2613,7 +2617,7 @@ PeerData *HistoryWidget::activePeer() const {
}
MsgId HistoryWidget::activeMsgId() const {
return hist ? hist->activeMsgId : (_activeHist ? _activeHist->activeMsgId : 0);
return (_loadingAroundId >= 0) ? _loadingAroundId : (hist ? hist->activeMsgId : (_activeHist ? _activeHist->activeMsgId : 0));
}
int32 HistoryWidget::lastWidth() const {
@ -3046,12 +3050,13 @@ void HistoryWidget::onFieldCursorChanged() {
onDraftSaveDelayed();
}
void HistoryWidget::uploadImage(const QImage &img, bool withText) {
void HistoryWidget::uploadImage(const QImage &img, bool withText, const QString &source) {
if (!hist || confirmImageId) return;
App::wnd()->activateWindow();
confirmImage = img;
confirmWithText = withText;
confirmSource = source;
confirmImageId = imageLoader.append(img, histPeer->id, _replyToId, ToPreparePhoto);
}
@ -3109,7 +3114,10 @@ void HistoryWidget::onPhotoReady() {
for (ReadyLocalMedias::const_iterator i = list.cbegin(), e = list.cend(); i != e; ++i) {
if (i->id == confirmImageId) {
App::wnd()->showLayer(new PhotoSendBox(*i));
PhotoSendBox *box = new PhotoSendBox(*i);
connect(box, SIGNAL(confirmed()), this, SLOT(onSendConfirmed()));
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onSendCancelled()));
App::wnd()->showLayer(box);
} else {
confirmSendImage(*i);
}
@ -3117,6 +3125,17 @@ void HistoryWidget::onPhotoReady() {
list.clear();
}
void HistoryWidget::onSendConfirmed() {
if (!confirmSource.isEmpty()) confirmSource = QString();
}
void HistoryWidget::onSendCancelled() {
if (!confirmSource.isEmpty()) {
_field.textCursor().insertText(confirmSource);
confirmSource = QString();
}
}
void HistoryWidget::onPhotoFailed(quint64 id) {
}
@ -3341,8 +3360,8 @@ void HistoryWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (_replyReturn == oldItem) _replyReturn = newItem;
}
void HistoryWidget::itemResized(HistoryItem *row) {
updateListSize(0, false, false, row);
void HistoryWidget::itemResized(HistoryItem *row, bool scrollToIt) {
updateListSize(0, false, false, row, scrollToIt);
}
void HistoryWidget::updateScrollColors() {
@ -3354,7 +3373,7 @@ MsgId HistoryWidget::replyToId() const {
return _replyToId;
}
void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, HistoryItem *resizedItem) {
void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, HistoryItem *resizedItem, bool scrollToIt) {
if (!hist || (!_histInited && !initial)) return;
if (!isVisible()) {
@ -3382,7 +3401,7 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown,
_scroll.show();
}
_list->updateSize();
if (resizedItem && !resizedItem->detached()) {
if (resizedItem && !resizedItem->detached() && scrollToIt) {
int32 firstItemY = _list->height() - hist->height - st::historyPadding;
if (newSt + _scroll.height() < firstItemY + resizedItem->block()->y + resizedItem->y + resizedItem->height()) {
newSt = firstItemY + resizedItem->block()->y + resizedItem->y + resizedItem->height() - _scroll.height();

View File

@ -197,6 +197,7 @@ public:
HistoryHider(MainWidget *parent); // send path from command line argument
bool animStep(float64 ms);
bool withConfirm() const;
void paintEvent(QPaintEvent *e);
void keyPressEvent(QKeyEvent *e);
@ -217,6 +218,10 @@ public slots:
void startHide();
void forward();
signals:
void forwarded();
private:
void init();
@ -296,7 +301,7 @@ public:
void typingDone(const MTPBool &result, mtpRequestId req);
void destroyData();
void uploadImage(const QImage &img, bool withText = false);
void uploadImage(const QImage &img, bool withText = false, const QString &source = QString());
void uploadFile(const QString &file, bool withText = false); // with confirmation
void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText = false);
void uploadConfirmImageUncompressed(bool ctrlShiftEnter, MsgId replyTo);
@ -342,7 +347,7 @@ public:
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void itemResized(HistoryItem *item);
void itemResized(HistoryItem *item, bool scrollToIt);
void updateScrollColors();
@ -394,6 +399,8 @@ public slots:
void onDocumentDrop(QDropEvent *e);
void onPhotoReady();
void onSendConfirmed();
void onSendCancelled();
void onPhotoFailed(quint64 id);
void showPeer(const PeerId &peer, MsgId msgId = 0, bool force = false, bool leaveActive = false);
void clearLoadingAround();
@ -440,7 +447,7 @@ private:
QList<MsgId> _replyReturns;
bool messagesFailed(const RPCError &error, mtpRequestId requestId);
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0);
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0, bool scrollToIt = false);
void addMessagesToFront(const QVector<MTPMessage> &messages);
void addMessagesToBack(const QVector<MTPMessage> &messages);
@ -497,6 +504,7 @@ private:
QImage confirmImage;
PhotoId confirmImageId;
bool confirmWithText;
QString confirmSource;
QString titlePeerText;
int32 titlePeerTextWidth;

View File

@ -107,7 +107,7 @@ void IntroWidget::onParentResize(const QSize &newSize) {
void IntroWidget::onIntroBack() {
if (!current) return;
moving = -1;
moving = (current == 4) ? -2 : -1;
prepareMove();
}
@ -125,11 +125,13 @@ bool IntroWidget::createNext() {
case 1: stages[current + 1] = code = new IntroCode(this); break;
case 2:
if (_pwdSalt.isEmpty()) {
if (signup) delete signup;
stages[current + 1] = signup = new IntroSignup(this);
} else {
stages[current + 1] = pwdcheck = new IntroPwdCheck(this);
}
break;
case 3: stages[current + 1] = signup = new IntroSignup(this); break;
}
}
_back.raise();
@ -138,11 +140,14 @@ bool IntroWidget::createNext() {
void IntroWidget::prepareMove() {
if (cacheForHide.isNull() || cacheForHideInd != current) makeHideCache();
stages[current + moving]->prepareShow();
if (cacheForShow.isNull() || cacheForShowInd != current + moving) makeShowCache();
xCoordHide = anim::ivalue(0, -moving * st::introSlideShift);
int32 m = (moving > 0) ? 1 : -1;
xCoordHide = anim::ivalue(0, -m * st::introSlideShift);
cAlphaHide = anim::fvalue(1, 0);
xCoordShow = anim::ivalue(moving * st::introSlideShift, 0);
xCoordShow = anim::ivalue(m * st::introSlideShift, 0);
cAlphaShow = anim::fvalue(0, 1);
anim::start(this);
@ -315,7 +320,7 @@ void IntroWidget::setPwdSalt(const QByteArray &salt) {
_pwdSalt = salt;
delete signup;
delete pwdcheck;
stages[3] = 0;
stages[3] = stages[4] = 0;
signup = 0;
pwdcheck = 0;
}

View File

@ -104,7 +104,7 @@ private:
IntroCode *code;
IntroSignup *signup;
IntroPwdCheck *pwdcheck;
IntroStage *stages[4];
IntroStage *stages[5];
int current, moving, visibilityChanging;
QString _phone, _phone_hash;
@ -131,6 +131,8 @@ public:
}
virtual void activate() = 0; // show and activate
virtual void prepareShow() {
}
virtual void deactivate() = 0; // deactivate and hide
virtual void onNext() = 0;
virtual void onBack() = 0;

View File

@ -158,6 +158,14 @@ void IntroCode::activate() {
code.setFocus();
}
void IntroCode::prepareShow() {
code.setText(QString());
if (sentRequest) {
MTP::cancel(sentRequest);
sentRequest = 0;
}
}
void IntroCode::deactivate() {
callTimer.stop();
hide();
@ -222,8 +230,7 @@ bool IntroCode::codeSubmitFail(const RPCError &error) {
checkRequest.start(1000);
sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&IntroCode::gotPassword), rpcFail(&IntroCode::codeSubmitFail));
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
code.setFocus();
return true;

View File

@ -52,6 +52,7 @@ public:
bool animStep(float64 ms);
void activate();
void prepareShow();
void deactivate();
void onNext();
void onBack();

View File

@ -266,8 +266,7 @@ bool IntroPhone::phoneSubmitFail(const RPCError &error) {
showError(lang(lng_bad_phone));
enableAll(true);
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
enableAll(true);
return true;

View File

@ -36,7 +36,9 @@ _hint(parent->getPwdHint()),
_pwdField(this, st::inpIntroPassword, lang(lng_signin_password)),
_codeField(this, st::inpIntroPassword, lang(lng_signin_code)),
_toRecover(this, lang(lng_signin_recover)),
_toPassword(this, lang(lng_signin_try_password)) {
_toPassword(this, lang(lng_signin_try_password)),
_reset(this, lang(lng_signin_reset_account), st::btnRedLink),
sentRequest(0) {
setVisible(false);
setGeometry(parent->innerRect());
@ -46,6 +48,7 @@ _toPassword(this, lang(lng_signin_try_password)) {
connect(&_toPassword, SIGNAL(clicked()), this, SLOT(onToPassword()));
connect(&_pwdField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(&_codeField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(&_reset, SIGNAL(clicked()), this, SLOT(onReset()));
_pwdField.setEchoMode(QLineEdit::Password);
@ -54,7 +57,8 @@ _toPassword(this, lang(lng_signin_try_password)) {
}
_codeField.hide();
_toPassword.hide();
_toRecover.setVisible(_hasRecovery);
_toRecover.show();
_reset.hide();
setMouseTracking(true);
}
@ -98,6 +102,7 @@ void IntroPwdCheck::resizeEvent(QResizeEvent *e) {
_codeField.move((width() - _codeField.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_toRecover.move(_next.x() + (_pwdField.width() - _toRecover.width()) / 2, _next.y() + _next.height() + st::introFinishSkip);
_toPassword.move(_next.x() + (_pwdField.width() - _toPassword.width()) / 2, _next.y() + _next.height() + st::introFinishSkip);
_reset.move((width() - _reset.width()) / 2, _toRecover.y() + _toRecover.height() + st::introFinishSkip);
}
textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
}
@ -168,6 +173,7 @@ void IntroPwdCheck::onCheckRequest() {
}
void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
sentRequest = 0;
stopCheck();
if (recover) {
cSetPasswordRecovered(true);
@ -183,6 +189,7 @@ void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &res
}
bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
sentRequest = 0;
stopCheck();
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
@ -193,10 +200,9 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
return true;
} else if (err == "PASSWORD_EMPTY") {
intro()->onIntroBack();
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (err.startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
_pwdField.setFocus();
_pwdField.notaBene();
return true;
}
if (cDebug()) { // internal server error
@ -209,6 +215,7 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
}
bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
sentRequest = 0;
stopCheck();
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
@ -225,10 +232,9 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
return true;
} else if (err == "CODE_INVALID") {
showError(lang(lng_signin_wrong_code));
_pwdField.notaBene();
_codeField.notaBene();
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (err.startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
_codeField.notaBene();
return true;
@ -260,34 +266,80 @@ bool IntroPwdCheck::recoverStartFail(const RPCError &error) {
}
void IntroPwdCheck::onToRecover() {
showError("");
_toRecover.hide();
_toPassword.show();
_pwdField.hide();
_pwdField.setText(QString());
_codeField.show();
_codeField.setFocus();
if (_emailPattern.isEmpty()) {
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&IntroPwdCheck::recoverStarted), rpcFail(&IntroPwdCheck::recoverStartFail));
if (_hasRecovery) {
if (sentRequest) {
MTP::cancel(sentRequest);
sentRequest = 0;
}
showError("");
_toRecover.hide();
_toPassword.show();
_pwdField.hide();
_pwdField.setText(QString());
_codeField.show();
_codeField.setFocus();
if (_emailPattern.isEmpty()) {
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&IntroPwdCheck::recoverStarted), rpcFail(&IntroPwdCheck::recoverStartFail));
}
update();
} else {
ConfirmBox *box = new ConfirmBox(lang(lng_signin_no_email_forgot), true);
App::wnd()->showLayer(box);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset()));
}
update();
}
void IntroPwdCheck::onToPassword() {
ConfirmBox *box = new ConfirmBox(lang(lng_signin_no_email_forgot), true);
App::wnd()->showLayer(box);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset()));
}
void IntroPwdCheck::onToReset() {
if (sentRequest) {
MTP::cancel(sentRequest);
sentRequest = 0;
}
_toRecover.show();
_toPassword.hide();
_pwdField.show();
_codeField.hide();
_codeField.setText(QString());
_pwdField.setFocus();
_reset.show();
update();
}
void IntroPwdCheck::onReset() {
if (sentRequest) return;
ConfirmBox *box = new ConfirmBox(lang(lng_sigin_sure_reset), lang(lng_sigin_reset), QString(), st::btnRedDone);
connect(box, SIGNAL(confirmed()), this, SLOT(onResetSure()));
App::wnd()->showLayer(box);
}
void IntroPwdCheck::onResetSure() {
if (sentRequest) return;
sentRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&IntroPwdCheck::deleteDone), rpcFail(&IntroPwdCheck::deleteFail));
}
bool IntroPwdCheck::deleteFail(const RPCError &error) {
if (mtpIsFlood(error)) return false;
sentRequest = 0;
showError(lang(lng_server_error));
return true;
}
void IntroPwdCheck::deleteDone(const MTPBool &v) {
App::wnd()->hideLayer();
intro()->onIntroNext();
}
void IntroPwdCheck::onInputChange() {
showError("");
}
void IntroPwdCheck::onSubmitPwd(bool force) {
if (sentRequest) return;
if (_pwdField.isHidden()) {
if (!force && !_codeField.isEnabled()) return;
QString code = _codeField.text().trimmed();

View File

@ -53,12 +53,18 @@ public slots:
void onToPassword();
void onInputChange();
void onCheckRequest();
void onToReset();
void onReset();
void onResetSure();
private:
void showError(const QString &err);
void stopCheck();
void deleteDone(const MTPBool &result);
bool deleteFail(const RPCError &error);
QString error;
anim::fvalue errorAlpha;
@ -71,7 +77,7 @@ private:
QString _hint, _emailPattern;
FlatInput _pwdField, _codeField;
LinkButton _toRecover, _toPassword;
LinkButton _toRecover, _toPassword, _reset;
mtpRequestId sentRequest;
Text _hintText;

View File

@ -239,8 +239,7 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) {
showError(lang(lng_bad_name));
last.setFocus();
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
last.setFocus();
return true;

View File

@ -483,31 +483,37 @@ void MainWidget::cancelForwarding() {
}
void MainWidget::finishForwarding(History *hist) {
if (_toForward.isEmpty() || !hist) return;
App::main()->readServerHistory(hist, false);
if (_toForward.size() < 2) {
uint64 randomId = MTP::nonce<uint64>();
MsgId newId = clientMsgId();
hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(_toForward.cbegin().value()));
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);
} else {
QVector<MTPint> ids;
QVector<MTPlong> randomIds;
ids.reserve(_toForward.size());
randomIds.reserve(_toForward.size());
for (SelectedItemSet::const_iterator i = _toForward.cbegin(), e = _toForward.cend(); i != e; ++i) {
if (!hist) return;
if (!_toForward.isEmpty()) {
App::main()->readServerHistory(hist, false);
if (_toForward.size() < 2) {
uint64 randomId = MTP::nonce<uint64>();
//MsgId newId = clientMsgId();
//hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(i.value()));
//App::historyRegRandom(randomId, newId);
ids.push_back(MTP_int(i.key()));
randomIds.push_back(MTP_long(randomId));
MsgId newId = clientMsgId();
hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(_toForward.cbegin().value()));
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);
} else {
QVector<MTPint> ids;
QVector<MTPlong> randomIds;
ids.reserve(_toForward.size());
randomIds.reserve(_toForward.size());
for (SelectedItemSet::const_iterator i = _toForward.cbegin(), e = _toForward.cend(); i != e; ++i) {
uint64 randomId = MTP::nonce<uint64>();
//MsgId newId = clientMsgId();
//hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(i.value()));
//App::historyRegRandom(randomId, newId);
ids.push_back(MTP_int(i.key()));
randomIds.push_back(MTP_long(randomId));
}
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(hist->peer->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds)), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
}
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(hist->peer->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds)), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
if (history.peer() == hist->peer) history.peerMessagesUpdated();
cancelForwarding();
}
if (history.peer() == hist->peer) history.peerMessagesUpdated();
cancelForwarding();
historyToDown(hist);
dialogsToUp();
history.peerMessagesUpdated(hist->peer->id);
}
void MainWidget::onShareContact(const PeerId &peer, UserData *contact) {
@ -556,9 +562,14 @@ void MainWidget::noHider(HistoryHider *destroyed) {
}
void MainWidget::hiderLayer(HistoryHider *h) {
if (App::passcoded()) return;
if (App::passcoded()) {
delete h;
return;
}
hider = h;
connect(hider, SIGNAL(forwarded()), &dialogs, SLOT(onCancelSearch()));
dialogsToUp();
if (cWideMode()) {
hider->show();
resizeEvent(0);
@ -604,8 +615,8 @@ void MainWidget::shareContactLayer(UserData *contact) {
hiderLayer(new HistoryHider(this, contact));
}
bool MainWidget::selectingPeer() {
return !!hider;
bool MainWidget::selectingPeer(bool withConfirm) {
return hider ? (withConfirm ? hider->withConfirm() : true) : false;
}
void MainWidget::offerPeer(PeerId peer) {
@ -636,8 +647,10 @@ void MainWidget::dialogsActivate() {
dialogs.activate();
}
bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &e) {
if (e.type() == "CHAT_ID_INVALID") { // left this chat already
bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (error.type() == "CHAT_ID_INVALID") { // left this chat already
if ((profile && profile->peer() == peer) || (overview && overview->peer() == peer) || _stack.contains(peer) || history.peer() == peer) {
showPeer(0, 0, false, true);
}
@ -695,7 +708,7 @@ void MainWidget::clearHistory(PeerData *peer) {
if (!peer->chat && peer->asUser()->contact <= 0) {
dialogs.removePeer(peer->asUser());
}
dialogs.dialogsToUp();
dialogsToUp();
dialogs.update();
App::history(peer->id)->clear();
MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer));
@ -713,10 +726,12 @@ void MainWidget::addParticipants(ChatData *chat, const QVector<UserData*> &users
showPeer(chat->id, 0, false);
}
bool MainWidget::addParticipantFail(ChatData *chat, const RPCError &e) {
bool MainWidget::addParticipantFail(ChatData *chat, 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);
if (e.type() == "USER_LEFT_CHAT") { // trying to return banned user to his group
if (error.type() == "USER_LEFT_CHAT") { // trying to return banned user to his group
}
return false;
}
@ -727,8 +742,10 @@ void MainWidget::kickParticipant(ChatData *chat, UserData *user) {
showPeer(chat->id, 0, false);
}
bool MainWidget::kickParticipantFail(ChatData *chat, const RPCError &e) {
e.type();
bool MainWidget::kickParticipantFail(ChatData *chat, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
error.type();
return false;
}
@ -764,13 +781,15 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
}
}
bool MainWidget::sendPhotoFailed(uint64 randomId, const RPCError &e) {
if (e.type() == qsl("PHOTO_INVALID_DIMENSIONS")) {
bool MainWidget::sendPhotoFailed(uint64 randomId, const RPCError &error) {
if (mtpIsFlood(error)) return false;
if (error.type() == qsl("PHOTO_INVALID_DIMENSIONS")) {
if (_resendImgRandomIds.isEmpty()) {
ConfirmBox *box = new ConfirmBox(lang(lng_bad_image_for_photo));
connect(box, SIGNAL(confirmed()), this, SLOT(onResendAsDocument()));
connect(box, SIGNAL(cancelled()), this, SLOT(onCancelResend()));
connect(box, SIGNAL(destroyed()), this, SLOT(onCancelResend()));
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onCancelResend()));
App::wnd()->showLayer(box);
}
_resendImgRandomIds.push_back(randomId);
@ -886,11 +905,6 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
}
finishForwarding(hist);
historyToDown(hist);
if (history.peer() == hist->peer) {
history.peerMessagesUpdated();
}
}
void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo) {
@ -1088,16 +1102,18 @@ void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
}
}
void MainWidget::itemResized(HistoryItem *row) {
void MainWidget::itemResized(HistoryItem *row, bool scrollToIt) {
if (!row || (history.peer() == row->history()->peer && !row->detached())) {
history.itemResized(row);
history.itemResized(row, scrollToIt);
}
if (overview) {
overview->itemResized(row);
overview->itemResized(row, scrollToIt);
}
}
bool MainWidget::overviewFailed(PeerData *peer, const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
MediaOverviewType type = OverviewCount;
for (int32 i = 0; i < OverviewCount; ++i) {
OverviewsPreload::iterator j = _overviewPreload[i].find(peer);
@ -1463,6 +1479,8 @@ void MainWidget::serviceHistoryDone(const MTPmessages_Messages &msgs) {
}
bool MainWidget::serviceHistoryFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
App::wnd()->showDelayedServiceMsgs();
return false;
}
@ -1860,6 +1878,13 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
return;
}
}
if (d.vmedia.type() != mtpc_messageMediaEmpty) {
HistoryItem *item = App::histItemById(d.vid.v);
if (item) {
item->setMedia(d.vmedia);
}
}
} break;
case mtpc_messages_sentMessageLink: {
@ -1874,6 +1899,13 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
}
}
App::feedUserLinks(d.vlinks);
if (d.vmedia.type() != mtpc_messageMediaEmpty) {
HistoryItem *item = App::histItemById(d.vid.v);
if (item) {
item->setMedia(d.vmedia);
}
}
} break;
};
}
@ -2293,8 +2325,10 @@ void MainWidget::feedDifference(const MTPVector<MTPUser> &users, const MTPVector
history.peerMessagesUpdated();
}
bool MainWidget::failDifference(const RPCError &e) {
LOG(("RPC Error: %1 %2: %3").arg(e.code()).arg(e.type()).arg(e.description()));
bool MainWidget::failDifference(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("RPC Error: %1 %2: %3").arg(error.code()).arg(error.type()).arg(error.description()));
_failDifferenceTimer.start(_failDifferenceTimeout * 1000);
if (_failDifferenceTimeout < 64) _failDifferenceTimeout *= 2;
return true;
@ -2385,6 +2419,8 @@ void MainWidget::usernameResolveDone(bool toProfile, const MTPUser &user) {
}
bool MainWidget::usernameResolveFail(QString name, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (error.code() == 400) {
App::wnd()->showLayer(new ConfirmBox(lng_username_not_found(lt_user, name), true));
}
@ -2479,7 +2515,9 @@ void MainWidget::gotNotifySetting(MTPInputNotifyPeer peer, const MTPPeerNotifySe
App::wnd()->notifySettingGot();
}
bool MainWidget::failNotifySetting(MTPInputNotifyPeer peer) {
bool MainWidget::failNotifySetting(MTPInputNotifyPeer peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
gotNotifySetting(peer, MTP_peerNotifySettingsEmpty());
return true;
}
@ -2852,7 +2890,15 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updateWebPage: {
const MTPDupdateWebPage &d(update.c_updateWebPage());
//
WebPageData *page = App::feedWebPage(d.vwebpage);
const WebPageItems &items(App::webPageItems());
WebPageItems::const_iterator i = items.constFind(page);
if (i != items.cend()) {
for (HistoryItemsMap::const_iterator j = i.value().cbegin(), e = i.value().cend(); j != e; ++j) {
j.key()->initDimensions();
itemResized(j.key());
}
}
} break;
case mtpc_updateDeleteMessages: {

View File

@ -191,7 +191,7 @@ public:
bool started();
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
void gotNotifySetting(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings);
bool failNotifySetting(MTPInputNotifyPeer peer);
bool failNotifySetting(MTPInputNotifyPeer peer, const RPCError &error);
void updateNotifySetting(PeerData *peer, bool enabled);
@ -251,7 +251,7 @@ public:
void onForward(const PeerId &peer, bool forwardSelected);
void onShareContact(const PeerId &peer, UserData *contact);
void onSendPaths(const PeerId &peer);
bool selectingPeer();
bool selectingPeer(bool withConfirm = false);
void offerPeer(PeerId peer);
void focusPeerSelect();
void dialogsActivate();
@ -297,7 +297,7 @@ public:
void changingMsgId(HistoryItem *row, MsgId newId);
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void itemResized(HistoryItem *row);
void itemResized(HistoryItem *row, bool scrollToIt = false);
void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
void peerUsernameChanged(PeerData *peer);

View File

@ -517,7 +517,19 @@ void MediaView::showPhoto(PhotoData *photo) {
_x = (_avail.width() - _w) / 2;
_y = st::medviewPolaroid.top() + (_avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar - _h) / 2;
_width = _w;
_from = App::user(_photo->user);
if (_photo->user == WebPageUserId && _msgid) {
if (HistoryItem *item = App::histItemById(_msgid)) {
if (dynamic_cast<HistoryForwarded*>(item)) {
_from = static_cast<HistoryForwarded*>(item)->fromForwarded();
} else {
_from = item->from();
}
} else {
_from = App::user(_photo->user);
}
} else {
_from = App::user(_photo->user);
}
updateControls();
_photo->full->load();
if (isHidden()) {

View File

@ -112,6 +112,8 @@ namespace {
}
bool importFail(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth import failed
return true;
}
@ -131,6 +133,8 @@ namespace {
}
bool exportFail(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
if (i != authExportRequests.cend()) {
authWaiters[i.value()].clear();
@ -535,6 +539,10 @@ namespace _mtp_internal {
}
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data
if (err.type().startsWith(qsl("FLOOD_WAIT_"))) {
if (onFail && (*onFail)(requestId, err)) return true;
}
if (onErrorDefault(requestId, err)) {
return false;
}
@ -633,8 +641,8 @@ namespace MTP {
void setdc(int32 dc, bool fromZeroOnly) {
if (!dc || !_started) return;
mtpSetDC(dc, fromZeroOnly);
if (dc != mainSession->getDC()) {
mainSession = _mtp_internal::getSession(dc);
if (maindc() != mainSession->getDC()) {
mainSession = _mtp_internal::getSession(maindc());
}
Local::writeMtpData();
}

View File

@ -142,7 +142,9 @@ namespace {
mtpConfigLoader()->done();
}
bool configFailed(const RPCError &err) {
bool configFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
loadingConfig = false;
LOG(("MTP Error: failed to get config!"));
return false;

View File

@ -272,6 +272,8 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
}
bool mtpFileLoader::partFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
finishFail();
return true;
}

View File

@ -61,6 +61,10 @@ private:
QString _type, _description;
};
inline bool mtpIsFlood(const RPCError &error) {
return error.type().startsWith(qsl("FLOOD_WAIT_"));
}
class RPCAbstractDoneHandler { // abstract done
public:
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const = 0;

View File

@ -4013,11 +4013,21 @@ 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_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" display_url: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" title: "); ++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(" photo: "); ++stages.back(); types.push_back(0); 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_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" url: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" display_url: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" type: "); ++stages.back(); if (flag & MTPDwebPage::flag_type) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 5: to.add(" site_name: "); ++stages.back(); if (flag & MTPDwebPage::flag_site_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 6: to.add(" title: "); ++stages.back(); if (flag & MTPDwebPage::flag_title) { 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 7: to.add(" description: "); ++stages.back(); if (flag & MTPDwebPage::flag_description) { 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 8: to.add(" photo: "); ++stages.back(); if (flag & MTPDwebPage::flag_photo) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 4 IN FIELD flags ]"); } break;
case 9: to.add(" embed_url: "); ++stages.back(); if (flag & MTPDwebPage::flag_embed_url) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break;
case 10: to.add(" embed_type: "); ++stages.back(); if (flag & MTPDwebPage::flag_embed_type) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break;
case 11: to.add(" embed_width: "); ++stages.back(); if (flag & MTPDwebPage::flag_embed_width) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
case 12: to.add(" embed_height: "); ++stages.back(); if (flag & MTPDwebPage::flag_embed_height) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
case 13: to.add(" duration: "); ++stages.back(); if (flag & MTPDwebPage::flag_duration) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 7 IN FIELD flags ]"); } break;
case 14: to.add(" author: "); ++stages.back(); if (flag & MTPDwebPage::flag_author) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 8 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;

View File

@ -361,7 +361,7 @@ enum {
mtpc_updateWebPage = 0x2cc36971,
mtpc_webPageEmpty = 0xeb1477e8,
mtpc_webPagePending = 0xc586da1c,
mtpc_webPage = 0x39c1cef9,
mtpc_webPage = 0xa31ea0b5,
mtpc_messageMediaWebPage = 0xa32dd600,
mtpc_authorization = 0x7bf2e6f6,
mtpc_account_authorizations = 0x1250abde,
@ -7290,7 +7290,7 @@ private:
friend MTPwebPage MTP_webPageEmpty(const MTPlong &_id);
friend MTPwebPage MTP_webPagePending(const MTPlong &_id, MTPint _date);
friend MTPwebPage MTP_webPage(const MTPlong &_id, const MTPstring &_display_url, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo);
friend MTPwebPage MTP_webPage(MTPint _flags, const MTPlong &_id, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_site_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPstring &_embed_url, const MTPstring &_embed_type, MTPint _embed_width, MTPint _embed_height, MTPint _duration, const MTPstring &_author);
mtpTypeId _type;
};
@ -8511,13 +8511,13 @@ public:
MTPMessageMedia vmedia;
enum {
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_from_id = (1 << 2),
};
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
};
@ -9597,13 +9597,13 @@ public:
MTPint vreply_to_msg_id;
enum {
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_from_id = (1 << 2),
};
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
};
@ -9627,13 +9627,13 @@ public:
MTPint vreply_to_msg_id;
enum {
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_from_id = (1 << 2),
};
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
};
@ -10419,14 +10419,50 @@ class MTPDwebPage : public mtpDataImpl<MTPDwebPage> {
public:
MTPDwebPage() {
}
MTPDwebPage(const MTPlong &_id, const MTPstring &_display_url, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo) : vid(_id), vdisplay_url(_display_url), vtitle(_title), vdescription(_description), vphoto(_photo) {
MTPDwebPage(MTPint _flags, const MTPlong &_id, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_site_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPstring &_embed_url, const MTPstring &_embed_type, MTPint _embed_width, MTPint _embed_height, MTPint _duration, const MTPstring &_author) : vflags(_flags), vid(_id), vurl(_url), vdisplay_url(_display_url), vtype(_type), vsite_name(_site_name), vtitle(_title), vdescription(_description), vphoto(_photo), vembed_url(_embed_url), vembed_type(_embed_type), vembed_width(_embed_width), vembed_height(_embed_height), vduration(_duration), vauthor(_author) {
}
MTPint vflags;
MTPlong vid;
MTPstring vurl;
MTPstring vdisplay_url;
MTPstring vtype;
MTPstring vsite_name;
MTPstring vtitle;
MTPstring vdescription;
MTPPhoto vphoto;
MTPstring vembed_url;
MTPstring vembed_type;
MTPint vembed_width;
MTPint vembed_height;
MTPint vduration;
MTPstring vauthor;
enum {
flag_embed_height = (1 << 6),
flag_embed_type = (1 << 5),
flag_duration = (1 << 7),
flag_photo = (1 << 4),
flag_embed_url = (1 << 5),
flag_author = (1 << 8),
flag_description = (1 << 3),
flag_type = (1 << 0),
flag_title = (1 << 2),
flag_embed_width = (1 << 6),
flag_site_name = (1 << 1),
};
bool has_embed_height() const { return vflags.v & flag_embed_height; }
bool has_embed_type() const { return vflags.v & flag_embed_type; }
bool has_duration() const { return vflags.v & flag_duration; }
bool has_photo() const { return vflags.v & flag_photo; }
bool has_embed_url() const { return vflags.v & flag_embed_url; }
bool has_author() const { return vflags.v & flag_author; }
bool has_description() const { return vflags.v & flag_description; }
bool has_type() const { return vflags.v & flag_type; }
bool has_title() const { return vflags.v & flag_title; }
bool has_embed_width() const { return vflags.v & flag_embed_width; }
bool has_site_name() const { return vflags.v & flag_site_name; }
};
class MTPDauthorization : public mtpDataImpl<MTPDauthorization> {
@ -10512,14 +10548,14 @@ public:
enum {
flag_new_salt = (1 << 0),
flag_new_password_hash = (1 << 0),
flag_email = (1 << 1),
flag_hint = (1 << 0),
flag_email = (1 << 1),
};
bool has_new_salt() const { return vflags.v & flag_new_salt; }
bool has_new_password_hash() const { return vflags.v & flag_new_password_hash; }
bool has_email() const { return vflags.v & flag_email; }
bool has_hint() const { return vflags.v & flag_hint; }
bool has_email() const { return vflags.v & flag_email; }
};
class MTPDauth_passwordRecovery : public mtpDataImpl<MTPDauth_passwordRecovery> {
@ -23956,7 +23992,7 @@ inline uint32 MTPwebPage::innerLength() const {
}
case mtpc_webPage: {
const MTPDwebPage &v(c_webPage());
return v.vid.innerLength() + v.vdisplay_url.innerLength() + v.vtitle.innerLength() + v.vdescription.innerLength() + v.vphoto.innerLength();
return v.vflags.innerLength() + v.vid.innerLength() + v.vurl.innerLength() + v.vdisplay_url.innerLength() + (v.has_type() ? v.vtype.innerLength() : 0) + (v.has_site_name() ? v.vsite_name.innerLength() : 0) + (v.has_title() ? v.vtitle.innerLength() : 0) + (v.has_description() ? v.vdescription.innerLength() : 0) + (v.has_photo() ? v.vphoto.innerLength() : 0) + (v.has_embed_url() ? v.vembed_url.innerLength() : 0) + (v.has_embed_type() ? v.vembed_type.innerLength() : 0) + (v.has_embed_width() ? v.vembed_width.innerLength() : 0) + (v.has_embed_height() ? v.vembed_height.innerLength() : 0) + (v.has_duration() ? v.vduration.innerLength() : 0) + (v.has_author() ? v.vauthor.innerLength() : 0);
}
}
return 0;
@ -23982,11 +24018,21 @@ inline void MTPwebPage::read(const mtpPrime *&from, const mtpPrime *end, mtpType
case mtpc_webPage: _type = cons; {
if (!data) setData(new MTPDwebPage());
MTPDwebPage &v(_webPage());
v.vflags.read(from, end);
v.vid.read(from, end);
v.vurl.read(from, end);
v.vdisplay_url.read(from, end);
v.vtitle.read(from, end);
v.vdescription.read(from, end);
v.vphoto.read(from, end);
if (v.has_type()) { v.vtype.read(from, end); } else { v.vtype = MTPstring(); }
if (v.has_site_name()) { v.vsite_name.read(from, end); } else { v.vsite_name = MTPstring(); }
if (v.has_title()) { v.vtitle.read(from, end); } else { v.vtitle = MTPstring(); }
if (v.has_description()) { v.vdescription.read(from, end); } else { v.vdescription = MTPstring(); }
if (v.has_photo()) { v.vphoto.read(from, end); } else { v.vphoto = MTPPhoto(); }
if (v.has_embed_url()) { v.vembed_url.read(from, end); } else { v.vembed_url = MTPstring(); }
if (v.has_embed_type()) { v.vembed_type.read(from, end); } else { v.vembed_type = MTPstring(); }
if (v.has_embed_width()) { v.vembed_width.read(from, end); } else { v.vembed_width = MTPint(); }
if (v.has_embed_height()) { v.vembed_height.read(from, end); } else { v.vembed_height = MTPint(); }
if (v.has_duration()) { v.vduration.read(from, end); } else { v.vduration = MTPint(); }
if (v.has_author()) { v.vauthor.read(from, end); } else { v.vauthor = MTPstring(); }
} break;
default: throw mtpErrorUnexpected(cons, "MTPwebPage");
}
@ -24004,11 +24050,21 @@ inline void MTPwebPage::write(mtpBuffer &to) const {
} break;
case mtpc_webPage: {
const MTPDwebPage &v(c_webPage());
v.vflags.write(to);
v.vid.write(to);
v.vurl.write(to);
v.vdisplay_url.write(to);
v.vtitle.write(to);
v.vdescription.write(to);
v.vphoto.write(to);
if (v.has_type()) v.vtype.write(to);
if (v.has_site_name()) v.vsite_name.write(to);
if (v.has_title()) v.vtitle.write(to);
if (v.has_description()) v.vdescription.write(to);
if (v.has_photo()) v.vphoto.write(to);
if (v.has_embed_url()) v.vembed_url.write(to);
if (v.has_embed_type()) v.vembed_type.write(to);
if (v.has_embed_width()) v.vembed_width.write(to);
if (v.has_embed_height()) v.vembed_height.write(to);
if (v.has_duration()) v.vduration.write(to);
if (v.has_author()) v.vauthor.write(to);
} break;
}
}
@ -24032,8 +24088,8 @@ inline MTPwebPage MTP_webPageEmpty(const MTPlong &_id) {
inline MTPwebPage MTP_webPagePending(const MTPlong &_id, MTPint _date) {
return MTPwebPage(new MTPDwebPagePending(_id, _date));
}
inline MTPwebPage MTP_webPage(const MTPlong &_id, const MTPstring &_display_url, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo) {
return MTPwebPage(new MTPDwebPage(_id, _display_url, _title, _description, _photo));
inline MTPwebPage MTP_webPage(MTPint _flags, const MTPlong &_id, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_site_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPstring &_embed_url, const MTPstring &_embed_type, MTPint _embed_width, MTPint _embed_height, MTPint _duration, const MTPstring &_author) {
return MTPwebPage(new MTPDwebPage(_flags, _id, _url, _display_url, _type, _site_name, _title, _description, _photo, _embed_url, _embed_type, _embed_width, _embed_height, _duration, _author));
}
inline MTPauthorization::MTPauthorization() : mtpDataOwner(new MTPDauthorization()) {

View File

@ -556,7 +556,7 @@ updateWebPage#2cc36971 webpage:WebPage = Update;
webPageEmpty#eb1477e8 id:long = WebPage;
webPagePending#c586da1c id:long date:int = WebPage;
webPage#39c1cef9 id:long display_url:string title:string description:string photo:Photo = WebPage;
webPage#a31ea0b5 flags:# id:long url:string display_url:string type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string = WebPage;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;

View File

@ -1441,7 +1441,7 @@ void OverviewInner::itemRemoved(HistoryItem *item) {
parentWidget()->update();
}
void OverviewInner::itemResized(HistoryItem *item) {
void OverviewInner::itemResized(HistoryItem *item, bool scrollToIt) {
if (_type != OverviewPhotos) {
HistoryMedia *media = item ? item->getMedia(true) : 0;
if (!media) return;
@ -1462,11 +1462,13 @@ void OverviewInner::itemResized(HistoryItem *item) {
_height = _items[l - 1].y;
_addToY = (_height < _minHeight) ? (_minHeight - _height) : 0;
resize(width(), _minHeight > _height ? _minHeight : _height);
if (_addToY + _height - from > _scroll->scrollTop() + _scroll->height()) {
_scroll->scrollToY(_addToY + _height - from - _scroll->height());
}
if (_addToY + _height - _items[i].y < _scroll->scrollTop()) {
_scroll->scrollToY(_addToY + _height - _items[i].y);
if (scrollToIt) {
if (_addToY + _height - from > _scroll->scrollTop() + _scroll->height()) {
_scroll->scrollToY(_addToY + _height - from - _scroll->height());
}
if (_addToY + _height - _items[i].y < _scroll->scrollTop()) {
_scroll->scrollToY(_addToY + _height - _items[i].y);
}
}
parentWidget()->update();
}
@ -1780,9 +1782,9 @@ void OverviewWidget::itemRemoved(HistoryItem *row) {
}
}
void OverviewWidget::itemResized(HistoryItem *row) {
void OverviewWidget::itemResized(HistoryItem *row, bool scrollToIt) {
if (!row || row->history()->peer == peer()) {
_inner.itemResized(row);
_inner.itemResized(row, scrollToIt);
}
}

View File

@ -61,7 +61,7 @@ public:
void changingMsgId(HistoryItem *row, MsgId newId);
void msgUpdated(const HistoryItem *msg);
void itemRemoved(HistoryItem *item);
void itemResized(HistoryItem *item);
void itemResized(HistoryItem *item, bool scrollToIt);
void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const;
void clearSelectedItems(bool onlyTextSelection = false);
@ -214,7 +214,7 @@ public:
void changingMsgId(HistoryItem *row, MsgId newId);
void msgUpdated(PeerId peer, const HistoryItem *msg);
void itemRemoved(HistoryItem *item);
void itemResized(HistoryItem *row);
void itemResized(HistoryItem *row, bool scrollToIt);
QPoint clampMousePosition(QPoint point);

View File

@ -36,9 +36,6 @@ _logout(this, lang(lng_passcode_logout)) {
_passcode.setEchoMode(QLineEdit::Password);
connect(&_submit, SIGNAL(clicked()), this, SLOT(onSubmit()));
_errorTimer.setSingleShot(true);
connect(&_errorTimer, SIGNAL(timeout()), this, SLOT(onError()));
connect(&_passcode, SIGNAL(changed()), this, SLOT(onChanged()));
connect(&_passcode, SIGNAL(accepted()), this, SLOT(onSubmit()));
@ -58,18 +55,27 @@ void PasscodeWidget::onSubmit() {
_passcode.notaBene();
return;
}
if (!passcodeCanTry()) {
_error = lang(lng_flood_error);
_passcode.setFocus();
_passcode.notaBene();
update();
return;
}
if (App::main()) {
if (Local::checkPasscode(_passcode.text().toUtf8())) {
cSetPasscodeBadTries(0);
App::wnd()->clearPasscode();
} else {
_error = QString();
_passcode.setDisabled(true);
_errorTimer.start(WrongPasscodeTimeout);
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
cSetPasscodeLastTry(getms(true));
onError();
return;
}
} else {
if (Local::readMap(_passcode.text().toUtf8()) != Local::ReadMapPassNeeded) {
cSetPasscodeBadTries(0);
App::app()->checkMapVersion();
MTP::start();
@ -79,10 +85,9 @@ void PasscodeWidget::onSubmit() {
App::wnd()->setupIntro(true);
}
} else {
_error = QString();
_passcode.setDisabled(true);
_errorTimer.start(WrongPasscodeTimeout);
update();
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
cSetPasscodeLastTry(getms(true));
onError();
return;
}
}
@ -90,7 +95,6 @@ void PasscodeWidget::onSubmit() {
void PasscodeWidget::onError() {
_error = lang(lng_passcode_wrong);
_passcode.setDisabled(false);
_passcode.selectAll();
_passcode.setFocus();
_passcode.notaBene();

View File

@ -57,6 +57,5 @@ private:
FlatButton _submit;
LinkButton _logout;
QString _error;
QTimer _errorTimer;
};

View File

@ -95,6 +95,8 @@ RecentStickerPack gRecentStickers;
RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags;
bool gPasswordRecovered = false;
int32 gPasscodeBadTries = 0;
uint64 gPasscodeLastTry = 0;
int32 gLang = -2; // auto
QString gLangFile;

View File

@ -183,6 +183,22 @@ DeclareSetting(RecentHashtagPack, RecentSearchHashtags);
DeclareSetting(bool, PasswordRecovered);
DeclareSetting(int32, PasscodeBadTries);
DeclareSetting(uint64, PasscodeLastTry);
inline bool passcodeCanTry() {
if (cPasscodeBadTries() < 3) return true;
uint64 dt = getms(true) - cPasscodeLastTry();
switch (cPasscodeBadTries()) {
case 3: return dt >= 5000;
case 4: return dt >= 10000;
case 5: return dt >= 15000;
case 6: return dt >= 20000;
case 7: return dt >= 25000;
}
return dt >= 30000;
}
inline void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag) {
RecentHashtagPack::iterator i = recent.begin(), e = recent.end();
for (; i != e; ++i) {

View File

@ -894,6 +894,8 @@ void SettingsInner::offPasswordDone(const MTPBool &result) {
}
bool SettingsInner::offPasswordFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
onReloadPassword();
return true;
}

View File

@ -0,0 +1,633 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "style.h"
#include "lang.h"
#include "history.h"
#include "mainwidget.h"
#include "application.h"
#include "fileuploader.h"
#include "window.h"
#include "gui/filedialog.h"
#include "audio.h"
#include "localstorage.h"
namespace {
int32 peerColorIndex(const PeerId &peer) {
int32 myId(MTP::authedId()), peerId(peer & 0xFFFFFFFFL);
bool chat = (peer & 0x100000000L);
if (chat) {
int ch = 0;
}
QByteArray both(qsl("%1%2").arg(peerId).arg(myId).toUtf8());
if (both.size() > 15) {
both = both.mid(0, 15);
}
uchar md5[16];
hashMd5(both.constData(), both.size(), md5);
return (md5[peerId & 0x0F] & (chat ? 0x03 : 0x07));
}
}
style::color peerColor(int32 index) {
static const style::color peerColors[8] = {
style::color(st::color1),
style::color(st::color2),
style::color(st::color3),
style::color(st::color4),
style::color(st::color5),
style::color(st::color6),
style::color(st::color7),
style::color(st::color8)
};
return peerColors[index];
}
ImagePtr userDefPhoto(int32 index) {
static const ImagePtr userDefPhotos[8] = {
ImagePtr(qsl(":/ava/art/usercolor1.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor2.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor3.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor4.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor5.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor6.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor7.png"), "PNG"),
ImagePtr(qsl(":/ava/art/usercolor8.png"), "PNG"),
};
return userDefPhotos[index];
}
ImagePtr chatDefPhoto(int32 index) {
static const ImagePtr chatDefPhotos[4] = {
ImagePtr(qsl(":/ava/art/chatcolor1.png"), "PNG"),
ImagePtr(qsl(":/ava/art/chatcolor2.png"), "PNG"),
ImagePtr(qsl(":/ava/art/chatcolor3.png"), "PNG"),
ImagePtr(qsl(":/ava/art/chatcolor4.png"), "PNG"),
};
return chatDefPhotos[index];
}
NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats;
NotifySettingsPtr globalNotifyAllPtr = UnknownNotifySettings, globalNotifyUsersPtr = UnknownNotifySettings, globalNotifyChatsPtr = UnknownNotifySettings;
PeerData::PeerData(const PeerId &id) : id(id)
, loaded(false)
, chat(App::isChat(id))
, access(0)
, colorIndex(peerColorIndex(id))
, color(peerColor(colorIndex))
, photo(chat ? chatDefPhoto(colorIndex) : userDefPhoto(colorIndex))
, nameVersion(0)
, notify(UnknownNotifySettings)
{
}
UserData *PeerData::asUser() {
return chat ? App::user(id & 0xFFFFFFFFL) : static_cast<UserData *>(this);
}
const UserData *PeerData::asUser() const {
return chat ? App::user(id & 0xFFFFFFFFL) : static_cast<const UserData *>(this);
}
ChatData *PeerData::asChat() {
return chat ? static_cast<ChatData *>(this) : App::chat(id | 0x100000000L);
}
const ChatData *PeerData::asChat() const {
return chat ? static_cast<const ChatData *>(this) : App::chat(id | 0x100000000L);
}
void PeerData::updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername) {
if (name == newName && nameOrPhone == newNameOrPhone && (chat || asUser()->username == newUsername) && nameVersion > 0) return;
++nameVersion;
name = newName;
nameOrPhone = newNameOrPhone;
if (!chat) asUser()->username = newUsername;
Names oldNames = names;
NameFirstChars oldChars = chars;
fillNames();
App::history(id)->updateNameText();
if (App::main()) {
emit App::main()->peerNameChanged(this, oldNames, oldChars);
}
nameUpdated();
}
void UserData::setPhoto(const MTPUserProfilePhoto &p) {
switch (p.type()) {
case mtpc_userProfilePhoto: {
const MTPDuserProfilePhoto d(p.c_userProfilePhoto());
photoId = d.vphoto_id.v;
photo = ImagePtr(160, 160, d.vphoto_small, userDefPhoto(colorIndex));
// App::feedPhoto(App::photoFromUserPhoto(MTP_int(id & 0xFFFFFFFF), MTP_int(unixtime()), p));
} break;
default: {
photoId = 0;
if (id == ServiceUserId) {
photo = ImagePtr(QPixmap::fromImage(App::wnd()->iconLarge().scaledToWidth(160, Qt::SmoothTransformation), Qt::ColorOnly), "PNG");
} else {
photo = userDefPhoto(colorIndex);
}
} break;
}
emit App::main()->peerPhotoChanged(this);
}
void PeerData::fillNames() {
names.clear();
chars.clear();
QString toIndex = textAccentFold(name);
if (nameOrPhone != name) {
toIndex += ' ' + textAccentFold(nameOrPhone);
}
if (!chat) {
toIndex += ' ' + textAccentFold(asUser()->username);
}
if (cRussianLetters().match(toIndex).hasMatch()) {
toIndex += ' ' + translitRusEng(toIndex);
}
toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
QStringList namesList = toIndex.toLower().split(cWordSplit(), QString::SkipEmptyParts);
for (QStringList::const_iterator i = namesList.cbegin(), e = namesList.cend(); i != e; ++i) {
names.insert(*i);
chars.insert(i->at(0));
}
}
void UserData::setName(const QString &first, const QString &last, const QString &phoneName, const QString &usern) {
bool updName = !first.isEmpty() || !last.isEmpty(), updUsername = (username != usern);
if (updName && first.trimmed().isEmpty()) {
firstName = last;
lastName = QString();
updateName(firstName, phoneName, usern);
} else {
if (updName) {
firstName = first;
lastName = last;
}
updateName(lastName.isEmpty() ? firstName : (firstName + ' ' + lastName), phoneName, usern);
}
if (updUsername) {
if (App::main()) {
App::main()->peerUsernameChanged(this);
}
}
}
void UserData::setPhone(const QString &newPhone) {
phone = newPhone;
++nameVersion;
}
void UserData::nameUpdated() {
nameText.setText(st::msgNameFont, name, _textNameOptions);
}
void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) {
switch (p.type()) {
case mtpc_chatPhoto: {
const MTPDchatPhoto d(p.c_chatPhoto());
photo = ImagePtr(160, 160, d.vphoto_small, chatDefPhoto(colorIndex));
photoFull = ImagePtr(640, 640, d.vphoto_big, chatDefPhoto(colorIndex));
if (phId) {
photoId = phId;
}
} break;
default: {
photo = chatDefPhoto(colorIndex);
photoFull = ImagePtr();
photoId = 0;
} break;
}
emit App::main()->peerPhotoChanged(this);
}
void PhotoLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton) {
App::wnd()->showPhoto(this, App::hoveredLinkItem());
}
}
QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir()) {
#ifdef Q_OS_WIN
name = name.replace(QRegularExpression(qsl("[\\\\\\/\\:\\*\\?\\\"\\<\\>\\|]")), qsl("_"));
#elif defined Q_OS_MAC
name = name.replace(QRegularExpression(qsl("[\\:]")), qsl("_"));
#elif defined Q_OS_LINUX
name = name.replace(QRegularExpression(qsl("[\\/]")), qsl("_"));
#endif
if (cAskDownloadPath() || savingAs) {
if (!name.isEmpty() && name.at(0) == QChar::fromLatin1('.')) {
name = filedialogDefaultName(prefix, name);
} else if (dir.path() != qsl(".")) {
QString path = dir.absolutePath();
if (path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeUserSettings();
}
}
return filedialogGetSaveFile(name, title, filter, name) ? name : QString();
}
QString path;
if (cDownloadPath().isEmpty()) {
path = psDownloadPath();
} else if (cDownloadPath() == qsl("tmp")) {
path = cTempDir();
} else {
path = cDownloadPath();
}
if (name.isEmpty()) name = qsl(".unknown");
if (name.at(0) == QChar::fromLatin1('.')) {
if (!QDir().exists(path)) QDir().mkpath(path);
return filedialogDefaultName(prefix, name, path);
}
if (dir.path() != qsl(".")) {
path = dir.absolutePath() + '/';
}
QString nameStart, extension;
int32 extPos = name.lastIndexOf('.');
if (extPos >= 0) {
nameStart = name.mid(0, extPos);
extension = name.mid(extPos);
} else {
nameStart = name;
}
QString nameBase = path + nameStart;
name = nameBase + extension;
for (int i = 0; QFileInfo(name).exists(); ++i) {
name = nameBase + QString(" (%1)").arg(i + 2) + extension;
}
if (!QDir().exists(path)) QDir().mkpath(path);
return name;
}
void VideoOpenLink::onClick(Qt::MouseButton button) const {
VideoData *data = video();
if ((!data->user && !data->date) || button != Qt::LeftButton) return;
QString already = data->already(true);
if (!already.isEmpty()) {
psOpenFile(already);
return;
}
if (data->status != FileReady) return;
QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), qsl(".mov"), false);
if (!filename.isEmpty()) {
data->openOnSave = 1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->save(filename);
}
}
void VideoSaveLink::doSave(bool forceSavingAs) const {
VideoData *data = video();
if (!data->user && !data->date) return;
QString already = data->already(true);
if (!already.isEmpty() && !forceSavingAs) {
QPoint pos(QCursor::pos());
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
}
} else {
QFileInfo alreadyInfo(already);
QDir alreadyDir(already.isEmpty() ? QDir() : alreadyInfo.dir());
QString name = already.isEmpty() ? QString(".mov") : alreadyInfo.fileName();
QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), name, forceSavingAs, alreadyDir);
if (!filename.isEmpty()) {
if (forceSavingAs) {
data->cancel();
} else if (!already.isEmpty()) {
data->openOnSave = -1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
}
data->save(filename);
}
}
}
void VideoSaveLink::onClick(Qt::MouseButton button) const {
if (button != Qt::LeftButton) return;
doSave();
}
void VideoCancelLink::onClick(Qt::MouseButton button) const {
VideoData *data = video();
if ((!data->user && !data->date) || button != Qt::LeftButton) return;
data->cancel();
}
VideoData::VideoData(const VideoId &id, const uint64 &access, int32 user, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) :
id(id), access(access), user(user), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
location = Local::readFileLocation(mediaKey(mtpc_inputVideoFileLocation, dc, id));
}
void VideoData::save(const QString &toFile) {
cancel(true);
loader = new mtpFileLoader(dc, id, access, mtpc_inputVideoFileLocation, toFile, size);
loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(videoLoadProgress(mtpFileLoader*)));
loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(videoLoadFailed(mtpFileLoader*, bool)));
loader->start();
}
QString VideoData::already(bool check) {
if (!check) return location.name;
if (!location.check()) location = Local::readFileLocation(mediaKey(mtpc_inputVideoFileLocation, dc, id));
return location.name;
}
void AudioOpenLink::onClick(Qt::MouseButton button) const {
AudioData *data = audio();
if ((!data->user && !data->date) || button != Qt::LeftButton) return;
bool mp3 = (data->mime == QLatin1String("audio/mp3"));
QString already = data->already(true);
bool play = !mp3 && audioVoice();
if (!already.isEmpty() || (!data->data.isEmpty() && play)) {
if (play) {
AudioData *playing = 0;
VoiceMessageState playingState = VoiceMessageStopped;
audioVoice()->currentState(&playing, &playingState);
if (playing == data && playingState != VoiceMessageStopped) {
audioVoice()->pauseresume();
} else {
audioVoice()->play(data);
}
} else {
psOpenFile(already);
}
return;
}
if (data->status != FileReady) return;
QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false);
if (!filename.isEmpty()) {
data->openOnSave = 1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->save(filename);
}
}
void AudioSaveLink::doSave(bool forceSavingAs) const {
AudioData *data = audio();
if (!data->user && !data->date) return;
QString already = data->already(true);
if (!already.isEmpty() && !forceSavingAs) {
QPoint pos(QCursor::pos());
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
}
} else {
QFileInfo alreadyInfo(already);
QDir alreadyDir(already.isEmpty() ? QDir() : alreadyInfo.dir());
bool mp3 = (data->mime == QLatin1String("audio/mp3"));
QString name = already.isEmpty() ? (mp3 ? qsl(".mp3") : qsl(".ogg")) : alreadyInfo.fileName();
QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), name, forceSavingAs, alreadyDir);
if (!filename.isEmpty()) {
if (forceSavingAs) {
data->cancel();
} else if (!already.isEmpty()) {
data->openOnSave = -1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
}
data->save(filename);
}
}
}
void AudioSaveLink::onClick(Qt::MouseButton button) const {
if (button != Qt::LeftButton) return;
doSave();
}
void AudioCancelLink::onClick(Qt::MouseButton button) const {
AudioData *data = audio();
if ((!data->user && !data->date) || button != Qt::LeftButton) return;
data->cancel();
}
AudioData::AudioData(const AudioId &id, const uint64 &access, int32 user, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) :
id(id), access(access), user(user), date(date), mime(mime), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
location = Local::readFileLocation(mediaKey(mtpc_inputAudioFileLocation, dc, id));
}
void AudioData::save(const QString &toFile) {
cancel(true);
loader = new mtpFileLoader(dc, id, access, mtpc_inputAudioFileLocation, toFile, size, (size < AudioVoiceMsgInMemory));
loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(audioLoadProgress(mtpFileLoader*)));
loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(audioLoadFailed(mtpFileLoader*, bool)));
loader->start();
}
QString AudioData::already(bool check) {
if (!check) return location.name;
if (!location.check()) location = Local::readFileLocation(mediaKey(mtpc_inputAudioFileLocation, dc, id));
return location.name;
}
void DocumentOpenLink::onClick(Qt::MouseButton button) const {
DocumentData *data = document();
if (!data->date || button != Qt::LeftButton) return;
QString already = data->already(true);
if (!already.isEmpty()) {
if (data->size < MediaViewImageSizeLimit) {
QImageReader reader(already);
if (reader.canRead()) {
if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) {
startGif(App::hoveredLinkItem(), already);
} else {
App::wnd()->showDocument(data, QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly), App::hoveredLinkItem());
}
} else {
psOpenFile(already);
}
} else {
psOpenFile(already);
}
return;
}
if (data->status != FileReady) return;
QString name = data->name, filter;
MimeType mimeType = mimeTypeForName(data->mime);
QStringList p = mimeType.globPatterns();
QString pattern = p.isEmpty() ? QString() : p.front();
if (name.isEmpty()) {
name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString());
}
if (pattern.isEmpty()) {
filter = qsl("All files (*.*)");
} else {
filter = mimeType.filterString() + qsl(";;All files (*.*)");
}
QString filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, false);
if (!filename.isEmpty()) {
data->openOnSave = 1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->save(filename);
}
}
void DocumentSaveLink::doSave(bool forceSavingAs) const {
DocumentData *data = document();
if (!data->date) return;
QString already = data->already(true);
if (!already.isEmpty() && !forceSavingAs) {
QPoint pos(QCursor::pos());
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
}
} else {
QFileInfo alreadyInfo(already);
QDir alreadyDir(already.isEmpty() ? QDir() : alreadyInfo.dir());
QString name = already.isEmpty() ? data->name : alreadyInfo.fileName(), filter;
MimeType mimeType = mimeTypeForName(data->mime);
QStringList p = mimeType.globPatterns();
QString pattern = p.isEmpty() ? QString() : p.front();
if (name.isEmpty()) {
name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString());
}
if (pattern.isEmpty()) {
filter = qsl("All files (*.*)");
} else {
filter = mimeType.filterString() + qsl(";;All files (*.*)");
}
QString filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, forceSavingAs, alreadyDir);
if (!filename.isEmpty()) {
if (forceSavingAs) {
data->cancel();
} else if (!already.isEmpty()) {
data->openOnSave = -1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
}
data->save(filename);
}
}
}
void DocumentSaveLink::onClick(Qt::MouseButton button) const {
if (button != Qt::LeftButton) return;
doSave();
}
void DocumentCancelLink::onClick(Qt::MouseButton button) const {
DocumentData *data = document();
if (!data->date || button != Qt::LeftButton) return;
data->cancel();
}
DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) :
id(id), type(FileDocument), duration(0), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
setattributes(attributes);
location = Local::readFileLocation(mediaKey(mtpc_inputDocumentFileLocation, dc, id));
}
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
for (int32 i = 0, l = attributes.size(); i < l; ++i) {
switch (attributes[i].type()) {
case mtpc_documentAttributeImageSize: {
const MTPDdocumentAttributeImageSize &d(attributes[i].c_documentAttributeImageSize());
dimensions = QSize(d.vw.v, d.vh.v);
} break;
case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument) type = AnimatedDocument; break;
case mtpc_documentAttributeSticker: {
const MTPDdocumentAttributeSticker &d(attributes[i].c_documentAttributeSticker());
if (type == FileDocument) type = StickerDocument;
alt = qs(d.valt);
} break;
case mtpc_documentAttributeVideo: {
const MTPDdocumentAttributeVideo &d(attributes[i].c_documentAttributeVideo());
type = VideoDocument;
duration = d.vduration.v;
dimensions = QSize(d.vw.v, d.vh.v);
} break;
case mtpc_documentAttributeAudio: {
const MTPDdocumentAttributeAudio &d(attributes[i].c_documentAttributeAudio());
type = AudioDocument;
duration = d.vduration.v;
} break;
case mtpc_documentAttributeFilename: name = qs(attributes[i].c_documentAttributeFilename().vfile_name); break;
}
}
}
void DocumentData::save(const QString &toFile) {
cancel(true);
bool isSticker = (type == StickerDocument) && (dimensions.width() > 0) && (dimensions.height() > 0) && (size < StickerInMemory);
loader = new mtpFileLoader(dc, id, access, mtpc_inputDocumentFileLocation, toFile, size, isSticker);
loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(documentLoadProgress(mtpFileLoader*)));
loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(documentLoadFailed(mtpFileLoader*, bool)));
loader->start();
}
QString DocumentData::already(bool check) {
if (!check) return location.name;
if (!location.check()) location = Local::readFileLocation(mediaKey(mtpc_inputDocumentFileLocation, dc, id));
return location.name;
}
WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, int32 duration, const QString &author, int32 pendingTill) :
id(id), type(type), url(url), displayUrl(displayUrl), siteName(siteName), title(title), description(description), photo(photo), duration(duration), author(author), pendingTill(pendingTill) {
}
void PeerLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton && App::main()) {
App::main()->showPeerProfile(peer());
}
}
void MessageLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton && App::main()) {
HistoryItem *current = App::mousedItem();
if (current && current->history()->peer->id == peer()) {
App::main()->pushReplyReturn(current);
}
App::main()->showPeer(peer(), msgid());
}
}
MsgId clientMsgId() {
static MsgId current = -2000000000;
return ++current;
}

View File

@ -0,0 +1,546 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
typedef uint64 PeerId;
typedef uint64 PhotoId;
typedef uint64 VideoId;
typedef uint64 AudioId;
typedef uint64 DocumentId;
typedef uint64 WebPageId;
typedef int32 MsgId;
struct NotifySettings {
NotifySettings() : mute(0), sound("default"), previews(true), events(1) {
}
int32 mute;
string sound;
bool previews;
int32 events;
};
typedef NotifySettings *NotifySettingsPtr;
static const NotifySettingsPtr UnknownNotifySettings = NotifySettingsPtr(0);
static const NotifySettingsPtr EmptyNotifySettings = NotifySettingsPtr(1);
extern NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats;
extern NotifySettingsPtr globalNotifyAllPtr, globalNotifyUsersPtr, globalNotifyChatsPtr;
inline bool isNotifyMuted(NotifySettingsPtr settings) {
if (settings == UnknownNotifySettings || settings == EmptyNotifySettings) {
return false;
}
return (settings->mute > unixtime());
}
style::color peerColor(int32 index);
ImagePtr userDefPhoto(int32 index);
ImagePtr chatDefPhoto(int32 index);
struct ChatData;
struct UserData;
struct PeerData {
PeerData(const PeerId &id);
virtual ~PeerData() {
if (notify != UnknownNotifySettings && notify != EmptyNotifySettings) {
delete notify;
notify = UnknownNotifySettings;
}
}
UserData *asUser();
const UserData *asUser() const;
ChatData *asChat();
const ChatData *asChat() const;
void updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername);
void fillNames();
virtual void nameUpdated() {
}
PeerId id;
QString name;
QString nameOrPhone;
typedef QSet<QString> Names;
Names names; // for filtering
typedef QSet<QChar> NameFirstChars;
NameFirstChars chars;
bool loaded;
bool chat;
uint64 access;
MTPinputPeer input;
MTPinputUser inputUser;
int32 colorIndex;
style::color color;
ImagePtr photo;
int32 nameVersion;
NotifySettingsPtr notify;
};
static const uint64 UserNoAccess = 0xFFFFFFFFFFFFFFFFULL;
class PeerLink : public ITextLink {
public:
PeerLink(PeerData *peer) : _peer(peer) {
}
void onClick(Qt::MouseButton button) const;
PeerData *peer() const {
return _peer;
}
private:
PeerData *_peer;
};
struct PhotoData;
struct UserData : public PeerData {
UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1) {
}
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 nameUpdated();
QString firstName;
QString lastName;
QString username;
QString phone;
Text nameText;
PhotoId photoId;
TextLinkPtr lnk;
int32 onlineTill;
int32 contact; // -1 - not contact, cant add (self, empty, deleted, foreign), 0 - not contact, can add (request), 1 - contact
typedef QList<PhotoData*> Photos;
Photos photos;
int32 photosCount; // -1 not loaded, 0 all loaded
};
struct ChatData : public PeerData {
ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), photoId(0) {
}
void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = 0);
int32 count;
int32 date;
int32 version;
int32 admin;
bool left;
bool forbidden;
typedef QMap<UserData*, int32> Participants;
Participants participants;
typedef QMap<UserData*, bool> CanKick;
CanKick cankick;
typedef QList<UserData*> LastAuthors;
LastAuthors lastAuthors;
ImagePtr photoFull;
PhotoId photoId;
// geo
};
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()) :
id(id), access(access), user(user), date(date), thumb(thumb), medium(medium), full(full), chat(0) {
}
void forget() {
thumb->forget();
replyPreview->forget();
medium->forget();
full->forget();
}
PhotoId id;
uint64 access;
int32 user;
int32 date;
ImagePtr thumb, replyPreview;
ImagePtr medium;
ImagePtr full;
ChatData *chat; // for chat photos connection
// geo, caption
int32 cachew;
QPixmap cache;
};
class PhotoLink : public ITextLink {
public:
PhotoLink(PhotoData *photo) : _photo(photo), _peer(0) {
}
PhotoLink(PhotoData *photo, PeerData *peer) : _photo(photo), _peer(peer) {
}
void onClick(Qt::MouseButton button) const;
PhotoData *photo() const {
return _photo;
}
PeerData *peer() const {
return _peer;
}
private:
PhotoData *_photo;
PeerData *_peer;
};
enum FileStatus {
FileFailed = -1,
FileUploading = 0,
FileReady = 1,
};
struct VideoData {
VideoData(const VideoId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void forget() {
thumb->forget();
replyPreview->forget();
}
void save(const QString &toFile);
void cancel(bool beforeDownload = false) {
mtpFileLoader *l = loader;
loader = 0;
if (l) {
l->cancel();
l->deleteLater();
l->rpcInvalidate();
}
location = FileLocation();
if (!beforeDownload) {
openOnSave = openOnSaveMsgId = 0;
}
}
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
}
loader->deleteLater();
loader->rpcInvalidate();
loader = 0;
}
QString already(bool check = false);
VideoId id;
uint64 access;
int32 user;
int32 date;
int32 duration;
int32 w, h;
ImagePtr thumb, replyPreview;
int32 dc, size;
// geo, caption
FileStatus status;
int32 uploadOffset;
mtpTypeId fileType;
int32 openOnSave, openOnSaveMsgId;
mtpFileLoader *loader;
FileLocation location;
};
class VideoLink : public ITextLink {
public:
VideoLink(VideoData *video) : _video(video) {
}
VideoData *video() const {
return _video;
}
private:
VideoData *_video;
};
class VideoSaveLink : public VideoLink {
public:
VideoSaveLink(VideoData *video) : VideoLink(video) {
}
void doSave(bool forceSavingAs = false) const;
void onClick(Qt::MouseButton button) const;
};
class VideoOpenLink : public VideoLink {
public:
VideoOpenLink(VideoData *video) : VideoLink(video) {
}
void onClick(Qt::MouseButton button) const;
};
class VideoCancelLink : public VideoLink {
public:
VideoCancelLink(VideoData *video) : VideoLink(video) {
}
void onClick(Qt::MouseButton button) const;
};
struct AudioData {
AudioData(const AudioId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
void forget() {
}
void save(const QString &toFile);
void cancel(bool beforeDownload = false) {
mtpFileLoader *l = loader;
loader = 0;
if (l) {
l->cancel();
l->deleteLater();
l->rpcInvalidate();
}
location = FileLocation();
if (!beforeDownload) {
openOnSave = openOnSaveMsgId = 0;
}
}
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
data = loader->bytes();
}
loader->deleteLater();
loader->rpcInvalidate();
loader = 0;
}
QString already(bool check = false);
AudioId id;
uint64 access;
int32 user;
int32 date;
QString mime;
int32 duration;
int32 dc;
int32 size;
FileStatus status;
int32 uploadOffset;
int32 openOnSave, openOnSaveMsgId;
mtpFileLoader *loader;
FileLocation location;
QByteArray data;
int32 md5[8];
};
class AudioLink : public ITextLink {
public:
AudioLink(AudioData *audio) : _audio(audio) {
}
AudioData *audio() const {
return _audio;
}
private:
AudioData *_audio;
};
class AudioSaveLink : public AudioLink {
public:
AudioSaveLink(AudioData *audio) : AudioLink(audio) {
}
void doSave(bool forceSavingAs = false) const;
void onClick(Qt::MouseButton button) const;
};
class AudioOpenLink : public AudioLink {
public:
AudioOpenLink(AudioData *audio) : AudioLink(audio) {
}
void onClick(Qt::MouseButton button) const;
};
class AudioCancelLink : public AudioLink {
public:
AudioCancelLink(AudioData *audio) : AudioLink(audio) {
}
void onClick(Qt::MouseButton button) const;
};
enum DocumentType {
FileDocument,
VideoDocument,
AudioDocument,
StickerDocument,
AnimatedDocument
};
struct DocumentData {
DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void setattributes(const QVector<MTPDocumentAttribute> &attributes);
void forget() {
thumb->forget();
sticker->forget();
replyPreview->forget();
}
void save(const QString &toFile);
void cancel(bool beforeDownload = false) {
mtpFileLoader *l = loader;
loader = 0;
if (l) {
l->cancel();
l->deleteLater();
l->rpcInvalidate();
}
location = FileLocation();
if (!beforeDownload) {
openOnSave = openOnSaveMsgId = 0;
}
}
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
data = loader->bytes();
}
loader->deleteLater();
loader->rpcInvalidate();
loader = 0;
}
QString already(bool check = false);
DocumentId id;
DocumentType type;
QSize dimensions;
int32 duration;
uint64 access;
int32 date;
QString name, mime, alt; // alt - for stickers
ImagePtr thumb, replyPreview;
int32 dc;
int32 size;
FileStatus status;
int32 uploadOffset;
int32 openOnSave, openOnSaveMsgId;
mtpFileLoader *loader;
FileLocation location;
QByteArray data;
ImagePtr sticker;
int32 md5[8];
};
class DocumentLink : public ITextLink {
public:
DocumentLink(DocumentData *document) : _document(document) {
}
DocumentData *document() const {
return _document;
}
private:
DocumentData *_document;
};
class DocumentSaveLink : public DocumentLink {
public:
DocumentSaveLink(DocumentData *document) : DocumentLink(document) {
}
void doSave(bool forceSavingAs = false) const;
void onClick(Qt::MouseButton button) const;
};
class DocumentOpenLink : public DocumentLink {
public:
DocumentOpenLink(DocumentData *document) : DocumentLink(document) {
}
void onClick(Qt::MouseButton button) const;
};
class DocumentCancelLink : public DocumentLink {
public:
DocumentCancelLink(DocumentData *document) : DocumentLink(document) {
}
void onClick(Qt::MouseButton button) const;
};
enum WebPageType {
WebPagePhoto,
WebPageVideo,
WebPageProfile,
WebPageArticle
};
inline WebPageType toWebPageType(const QString &type) {
if (type == QLatin1String("photo")) return WebPagePhoto;
if (type == QLatin1String("video")) return WebPageVideo;
if (type == QLatin1String("profile")) return WebPageProfile;
return WebPageArticle;
}
struct WebPageData {
WebPageData(const WebPageId &id, WebPageType type = WebPageArticle, const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = 0);
void forget() {
if (photo) photo->forget();
}
WebPageId id;
WebPageType type;
QString url, displayUrl, siteName, title, description;
int32 duration;
QString author;
PhotoData *photo;
int32 pendingTill;
};
MsgId clientMsgId();
struct MessageCursor {
MessageCursor() : position(0), anchor(0), scroll(QFIXED_MAX) {
}
MessageCursor(int position, int anchor, int scroll) : position(position), anchor(anchor), scroll(scroll) {
}
MessageCursor(const QTextEdit &edit) {
fillFrom(edit);
}
void fillFrom(const QTextEdit &edit) {
QTextCursor c = edit.textCursor();
position = c.position();
anchor = c.anchor();
QScrollBar *s = edit.verticalScrollBar();
scroll = s ? s->value() : QFIXED_MAX;
}
void applyTo(QTextEdit &edit, bool *lock = 0) {
if (lock) *lock = true;
QTextCursor c = edit.textCursor();
c.setPosition(anchor, QTextCursor::MoveAnchor);
c.setPosition(position, QTextCursor::KeepAnchor);
edit.setTextCursor(c);
QScrollBar *s = edit.verticalScrollBar();
if (s) s->setValue(scroll);
if (lock) *lock = false;
}
int position, anchor, scroll;
};

View File

@ -1019,6 +1019,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SourceFiles\structs.cpp" />
<ClCompile Include="SourceFiles\sysbuttons.cpp" />
<ClCompile Include="SourceFiles\title.cpp" />
<ClCompile Include="SourceFiles\types.cpp" />
@ -1961,6 +1962,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="SourceFiles\settings.h" />
<ClInclude Include="SourceFiles\structs.h" />
<ClInclude Include="SourceFiles\style.h" />
<CustomBuild Include="SourceFiles\sysbuttons.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing sysbuttons.h...</Message>

View File

@ -870,6 +870,9 @@
<ClCompile Include="GeneratedFiles\Release\moc_intropwdcheck.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\structs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h">
@ -953,6 +956,9 @@
<ClInclude Include="SourceFiles\pspecific_mac_p.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\structs.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="SourceFiles\mtproto\mtpConnection.h">