mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-24 20:33:19 +00:00
links preview done
This commit is contained in:
parent
868d5f60f3
commit
f3bb155b0a
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 |
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -210,6 +210,7 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) {
|
||||
if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter, _replyTo);
|
||||
}
|
||||
}
|
||||
emit confirmed();
|
||||
emit closed();
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,10 @@ public:
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
~PhotoSendBox();
|
||||
|
||||
signals:
|
||||
|
||||
void confirmed();
|
||||
|
||||
public slots:
|
||||
|
||||
void onSend(bool ctrlShiftEnter = false);
|
||||
|
@ -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);
|
||||
|
@ -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") {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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
@ -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 ¤tText);
|
||||
void initMediaFromText(QString ¤tText);
|
||||
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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -52,6 +52,7 @@ public:
|
||||
bool animStep(float64 ms);
|
||||
|
||||
void activate();
|
||||
void prepareShow();
|
||||
void deactivate();
|
||||
void onNext();
|
||||
void onBack();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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: {
|
||||
|
@ -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);
|
||||
|
@ -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()) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -57,6 +57,5 @@ private:
|
||||
FlatButton _submit;
|
||||
LinkButton _logout;
|
||||
QString _error;
|
||||
QTimer _errorTimer;
|
||||
|
||||
};
|
||||
|
@ -95,6 +95,8 @@ RecentStickerPack gRecentStickers;
|
||||
RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags;
|
||||
|
||||
bool gPasswordRecovered = false;
|
||||
int32 gPasscodeBadTries = 0;
|
||||
uint64 gPasscodeLastTry = 0;
|
||||
|
||||
int32 gLang = -2; // auto
|
||||
QString gLangFile;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
633
Telegram/SourceFiles/structs.cpp
Normal file
633
Telegram/SourceFiles/structs.cpp
Normal 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;
|
||||
}
|
546
Telegram/SourceFiles/structs.h
Normal file
546
Telegram/SourceFiles/structs.h
Normal 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;
|
||||
};
|
@ -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>
|
||||
|
@ -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">
|
||||
|
Loading…
Reference in New Issue
Block a user