diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index 0ccb9abc5..b5b72657c 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,11 +1,11 @@ @echo OFF set "AppVersionStrMajor=0.8" -set "AppVersion=8046" -set "AppVersionStrSmall=0.8.46" -set "AppVersionStr=0.8.46" -set "AppVersionStrFull=0.8.46.0" -set "DevChannel=0" +set "AppVersion=8047" +set "AppVersionStrSmall=0.8.47" +set "AppVersionStr=0.8.47" +set "AppVersionStrFull=0.8.47.0" +set "DevChannel=1" if %DevChannel% neq 0 goto preparedev diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 1eb67ca5f..b462bcd6d 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -156,6 +156,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_dlg_new_group_name" = "Group name"; "lng_dlg_create_group" = "Create"; "lng_no_contacts" = "You have no contacts"; +"lng_no_chats" = "Your chats will be here"; "lng_contacts_loading" = "Loading.."; "lng_contacts_not_found" = "No contacts found"; "lng_dlg_search_chat" = "Search in this chat"; @@ -385,6 +386,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_create_group_title" = "New Group"; "lng_failed_add_participant" = "Could not add user. Try again later."; +"lng_failed_add_not_mutual" = "Sorry, if a person left a group, only a\nmutual contact can bring them back\n(they need to have your phone\nnumber, and you need theirs)."; "lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?"; "lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone."; @@ -469,7 +471,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_message_ph" = "Write a message.."; "lng_record_cancel" = "Release outside this field to cancel"; "lng_empty_history" = ""; -"lng_willbe_history" = "Please select chat to start messaging"; +"lng_willbe_history" = "Please select a chat to start messaging"; "lng_message_with_from" = "[c]{from}:[/c] {message}"; "lng_from_you" = "You"; "lng_bot_description" = "What can this bot do?"; @@ -627,7 +629,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}"; "lng_new_version_minor" = "— Bug fixes and other minor improvements"; -"lng_new_version_text" = "— Improved in-app media playback\n— Bug fixes and other minor improvements"; +"lng_new_version_text" = "— Search for messages in conversation\n— Clear messages history in groups\n— Contacts without messages are hidden from the conversations list"; "lng_menu_insert_unicode" = "Insert Unicode control character"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index c93bce807..75d8187c7 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -225,6 +225,23 @@ void ApiWrap::requestPeer(PeerData *peer) { _peerRequests.insert(peer, req); } +void ApiWrap::requestPeers(const QList &peers) { + QVector chats; + QVector users; + chats.reserve(peers.size()); + users.reserve(peers.size()); + for (QList::const_iterator i = peers.cbegin(), e = peers.cend(); i != e; ++i) { + if (!*i || _fullPeerRequests.contains(*i) || _peerRequests.contains(*i)) continue; + if ((*i)->chat) { + chats.push_back(MTP_int(App::chatFromPeer((*i)->id))); + } else { + users.push_back((*i)->asUser()->inputUser); + } + } + if (!chats.isEmpty()) MTP::send(MTPmessages_GetChats(MTP_vector(chats)), rpcDone(&ApiWrap::gotChats)); + if (!users.isEmpty()) MTP::send(MTPusers_GetUsers(MTP_vector(users)), rpcDone(&ApiWrap::gotUsers)); +} + void ApiWrap::gotChat(PeerData *peer, const MTPmessages_Chats &result) { _peerRequests.remove(peer); @@ -249,6 +266,14 @@ void ApiWrap::gotUser(PeerData *peer, const MTPVector &result) { } } +void ApiWrap::gotChats(const MTPmessages_Chats &result) { + App::feedChats(result.c_messages_chats().vchats); +} + +void ApiWrap::gotUsers(const MTPVector &result) { + App::feedUsers(result); +} + bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) { if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index f73561c4c..a57eb6bd9 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -32,6 +32,7 @@ public: void requestFullPeer(PeerData *peer); void requestPeer(PeerData *peer); + void requestPeers(const QList &peers); void requestWebPageDelayed(WebPageData *page); void clearWebPageRequest(WebPageData *page); @@ -72,6 +73,8 @@ private: void gotChat(PeerData *peer, const MTPmessages_Chats &result); void gotUser(PeerData *peer, const MTPVector &result); + void gotChats(const MTPmessages_Chats &result); + void gotUsers(const MTPVector &result); bool gotPeerFailed(PeerData *peer, const RPCError &err); PeerRequests _peerRequests; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 120fae069..ac3773b9e 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -416,6 +416,8 @@ namespace App { bool showPhone = !isServiceUser(data->id) && !(flags & (MTPDuser_flag_self | MTPDuser_flag_contact | MTPDuser_flag_mutual_contact)); bool showPhoneChanged = !isServiceUser(data->id) && !(flags & (MTPDuser_flag_self)) && ((showPhone && data->contact) || (!showPhone && !data->contact)); + // see also Local::readPeer + QString pname = (showPhoneChanged || phoneChanged || nameChanged) ? ((showPhone && !phone.isEmpty()) ? formatPhone(phone) : QString()) : data->nameOrPhone; data->setName(fname, lname, pname, uname); @@ -500,7 +502,6 @@ namespace App { data->count = d.vparticipants_count.v; data->left = d.vleft.v; data->forbidden = false; - data->access = 0; if (data->version < d.vversion.v) { data->version = d.vversion.v; data->participants = ChatData::Participants(); @@ -519,7 +520,6 @@ namespace App { data->count = -1; data->left = false; data->forbidden = true; - data->access = 0; } break; case mtpc_geoChat: { const MTPDgeoChat &d(chat.c_geoChat()); @@ -760,27 +760,23 @@ namespace App { return ImagePtr(); } + StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc) { + if (loc.type() == mtpc_fileLocation) { + const MTPDfileLocation &l(loc.c_fileLocation()); + return StorageImageLocation(w, h, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v); + } + return StorageImageLocation(w, h, 0, 0, 0, 0); + } + StorageImageLocation imageLocation(const MTPPhotoSize &size) { switch (size.type()) { case mtpc_photoSize: { const MTPDphotoSize &d(size.c_photoSize()); - if (d.vlocation.type() == mtpc_fileLocation) { - const MTPDfileLocation &l(d.vlocation.c_fileLocation()); - return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v); - } + return imageLocation(d.vw.v, d.vh.v, d.vlocation); } break; case mtpc_photoCachedSize: { const MTPDphotoCachedSize &d(size.c_photoCachedSize()); - if (d.vlocation.type() == mtpc_fileLocation) { - const MTPDfileLocation &l(d.vlocation.c_fileLocation()); - const string &s(d.vbytes.c_string().v); - QByteArray bytes(s.data(), s.size()); - return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v); - } else if (d.vlocation.type() == mtpc_fileLocationUnavailable) { - const string &s(d.vbytes.c_string().v); - QByteArray bytes(s.data(), s.size()); - return StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0); - } + return imageLocation(d.vw.v, d.vh.v, d.vlocation); } break; } return StorageImageLocation(); @@ -1697,6 +1693,8 @@ namespace App { randomData.clear(); mutedPeers.clear(); updatedPeers.clear(); + cSetSavedPeers(SavedPeers()); + cSetSavedPeersByTime(SavedPeersByTime()); for (PeersData::const_iterator i = peersData.cbegin(), e = peersData.cend(); i != e; ++i) { delete *i; } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index c1befed22..1033ef90d 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -127,6 +127,7 @@ namespace App { int32 maxMsgId(); ImagePtr image(const MTPPhotoSize &size); + StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc); StorageImageLocation imageLocation(const MTPPhotoSize &size); PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index d4c43461f..1d314fa9b 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -658,8 +658,8 @@ void Application::checkMapVersion() { psRegisterCustomScheme(); if (Local::oldMapVersion()) { QString versionFeatures; - if (cDevVersion() && Local::oldMapVersion() < 8044) { - versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sending media and recording audio status display");// .replace('@', qsl("@") + QChar(0x200D)); + if (cDevVersion() && Local::oldMapVersion() < 8047) { + versionFeatures = QString::fromUtf8("\xe2\x80\x94 Search for messages in conversation\n\xe2\x80\x94 Clear messages history in groups\n\xe2\x80\x94 Contacts without messages are hidden from the conversations list");// .replace('@', qsl("@") + QChar(0x200D)); } else if (!cDevVersion() && Local::oldMapVersion() < 8045) { versionFeatures = lang(lng_new_version_minor).trimmed(); } diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 311b26b6b..c5cec8baa 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -60,7 +60,7 @@ _byUsernameSel(-1), _addContactLnk(this, lang(lng_add_contact_button)) { DialogsIndexed &v(App::main()->dialogsList()); for (DialogRow *r = v.list.begin; r != v.list.end; r = r->next) { - if (r->history->peer->chat && !r->history->peer->asChat()->forbidden) { + if (r->history->peer->chat && !r->history->peer->asChat()->forbidden && !r->history->peer->asChat()->left) { _contacts->addToEnd(r->history); } } @@ -104,7 +104,7 @@ void ContactsInner::onAddBot() { void ContactsInner::peerUpdated(PeerData *peer) { if (_chat && (!peer || peer == _chat)) { - if (_chat->forbidden) { + if (_chat->forbidden || _chat->left) { App::wnd()->hideLayer(); } else if (!_chat->participants.isEmpty() || _chat->count <= 0) { for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) { @@ -180,11 +180,11 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) { if (i == _contactsData.cend()) { _contactsData.insert(peer, data = new ContactData()); data->inchat = (_chat && !peer->chat) ? _chat->participants.contains(peer->asUser()) : false; - data->check = false; + data->check = _checkedContacts.contains(peer); data->name.setText(st::profileListNameFont, peer->name, _textNameOptions); if (peer->chat) { ChatData *chat = peer->asChat(); - if (chat->forbidden) { + if (chat->forbidden || chat->left) { data->online = lang(lng_chat_status_unaccessible); } else { data->online = lng_chat_status_members(lt_count, chat->count); @@ -401,7 +401,7 @@ void ContactsInner::chooseParticipant() { if (_filter.isEmpty()) { if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) { if (d_byUsername[_byUsernameSel]->inchat) return; - changeCheckState(d_byUsername[_byUsernameSel]); + changeCheckState(d_byUsername[_byUsernameSel], _byUsername[_byUsernameSel]); } else { if (!_sel || contactData(_sel)->inchat) return; changeCheckState(_sel); @@ -409,7 +409,7 @@ void ContactsInner::chooseParticipant() { } else { if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) { if (d_byUsernameFiltered[_byUsernameSel]->inchat) return; - changeCheckState(d_byUsernameFiltered[_byUsernameSel]); + changeCheckState(d_byUsernameFiltered[_byUsernameSel], _byUsernameFiltered[_byUsernameSel]); ContactData *moving = d_byUsernameFiltered[_byUsernameSel]; int32 i = 0, l = d_byUsername.size(); @@ -470,15 +470,17 @@ void ContactsInner::chooseParticipant() { } void ContactsInner::changeCheckState(DialogRow *row) { - changeCheckState(contactData(row)); + changeCheckState(contactData(row), row->history->peer); } -void ContactsInner::changeCheckState(ContactData *data) { +void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) { if (data->check) { data->check = false; + _checkedContacts.remove(peer); --_selCount; } else if (_selCount + (_chat ? _chat->count : 0) < cMaxGroupCount()) { data->check = true; + _checkedContacts.insert(peer, true); ++_selCount; } } @@ -693,7 +695,7 @@ void ContactsInner::peopleReceived(const QString &query, const QVectorinchat = _chat ? _chat->participants.contains(u) : false; - d->check = false; + d->check = _checkedContacts.contains(u); d->name.setText(st::profileListNameFont, u->name, _textNameOptions); d->online = '@' + u->username; @@ -880,6 +882,11 @@ void ContactsInner::selectSkipPage(int32 h, int32 dir) { QVector ContactsInner::selected() { QVector result; + for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { + if (_checkedContacts.contains(row->history->peer)) { + contactData(row); // fill _contactsData + } + } result.reserve(_contactsData.size()); for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { if (i.value()->check && !i.key()->chat) { @@ -896,10 +903,15 @@ QVector ContactsInner::selected() { QVector ContactsInner::selectedInputs() { QVector result; + for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { + if (_checkedContacts.contains(row->history->peer)) { + contactData(row); // fill _contactsData + } + } result.reserve(_contactsData.size()); for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { if (i.value()->check && !i.key()->chat) { - result.push_back(i.key()->inputUser); + result.push_back(i.key()->asUser()->inputUser); } } for (int32 i = 0, l = _byUsername.size(); i < l; ++i) { @@ -911,6 +923,11 @@ QVector ContactsInner::selectedInputs() { } PeerData *ContactsInner::selectedUser() { + for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { + if (_checkedContacts.contains(row->history->peer)) { + contactData(row); // fill _contactsData + } + } for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { if (i.value()->check) { return i.key(); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 7fd8fc834..5b59f8a63 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -53,7 +53,7 @@ public: void loadProfilePhotos(int32 yFrom); void chooseParticipant(); void changeCheckState(DialogRow *row); - void changeCheckState(ContactData *data); + void changeCheckState(ContactData *data, PeerData *peer); void peopleReceived(const QString &query, const QVector &people); @@ -109,6 +109,8 @@ private: }; typedef QMap ContactsData; ContactsData _contactsData; + typedef QMap CheckedContacts; + CheckedContacts _checkedContacts; ContactData *contactData(DialogRow *row); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 377fea046..3a7421a80 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 8046; -static const wchar_t *AppVersionStr = L"0.8.46"; -static const bool DevVersion = false; +static const int32 AppVersion = 8047; +static const wchar_t *AppVersionStr = L"0.8.47"; +static const bool DevVersion = true; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 427f87d3f..771efe276 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -86,12 +86,12 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) { if (otherStart) { dialogs.list.paint(p, width(), r.top(), r.bottom(), active, selected); } - if (contactsNoDialogs.list.count) { + if (contactsNoDialogs.list.count && false) { contactsNoDialogs.list.paint(p, width(), r.top() - otherStart, r.bottom() - otherStart, active, selected); } else if (!otherStart) { p.setFont(st::noContactsFont->f); p.setPen(st::noContactsColor->p); - p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center); + p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_chats : lng_contacts_loading), style::al_center); } } else if (_state == FilteredState || _state == SearchedState) { if (!hashtagResults.isEmpty()) { @@ -317,7 +317,7 @@ void DialogsListWidget::onUpdateSelected(bool force) { if (newSel) { contactSel = false; } else { - newSel = contactsNoDialogs.list.rowAtY(mouseY - otherStart, st::dlgHeight); + newSel = 0;// contactsNoDialogs.list.rowAtY(mouseY - otherStart, st::dlgHeight); contactSel = true; } if (newSel != sel) { @@ -442,6 +442,8 @@ void DialogsListWidget::removePeer(PeerData *peer) { } } + Local::removeSavedPeer(peer); + emit App::main()->dialogsUpdated(); refresh(); @@ -481,7 +483,7 @@ void DialogsListWidget::dlgUpdated(History *history) { if (i != dialogs.list.rowByPeer.cend()) { update(0, i.value()->pos * st::dlgHeight, width(), st::dlgHeight); } else { - i = contactsNoDialogs.list.rowByPeer.find(history->peer->id); + i = contactsNoDialogs.list.rowByPeer.end();// find(history->peer->id); if (i != contactsNoDialogs.list.rowByPeer.cend()) { update(0, (dialogs.list.count + i.value()->pos) * st::dlgHeight, width(), st::dlgHeight); } @@ -771,6 +773,16 @@ void DialogsListWidget::dialogsReceived(const QVector &added) { refresh(); } +void DialogsListWidget::addAllSavedPeers() { + SavedPeersByTime &saved(cRefSavedPeersByTime()); + while (!saved.isEmpty()) { + History *history = App::history(saved.last()->id); + history->dialogs = dialogs.addToEnd(history); + contactsNoDialogs.del(history->peer); + saved.remove(saved.lastKey(), saved.last()); + } +} + void DialogsListWidget::searchReceived(const QVector &messages, bool fromStart, int32 fullCount) { if (fromStart) { clearSearchResults(false); @@ -810,7 +822,7 @@ void DialogsListWidget::contactsReceived(const QVector &contacts) { App::self()->contact = 1; } } - if (!sel && contactsNoDialogs.list.count) { + if (!sel && contactsNoDialogs.list.count && false) { sel = contactsNoDialogs.list.begin; contactSel = true; } @@ -827,11 +839,11 @@ int32 DialogsListWidget::addNewContact(int32 uid, bool select) { if (i == dialogs.list.rowByPeer.cend()) { DialogRow *added = contactsNoDialogs.addByName(history); if (!added) return -1; - if (select) { + if (select && false) { sel = added; contactSel = true; } - if (contactsNoDialogs.list.count == 1 && !dialogs.list.count) refresh(); +// if (contactsNoDialogs.list.count == 1 && !dialogs.list.count) refresh(); return added ? ((dialogs.list.count + added->pos) * st::dlgHeight) : -1; } if (select) { @@ -844,7 +856,7 @@ int32 DialogsListWidget::addNewContact(int32 uid, bool select) { void DialogsListWidget::refresh(bool toTop) { int32 h = 0; if (_state == DefaultState) { - h = (dialogs.list.count + contactsNoDialogs.list.count) * st::dlgHeight; + h = (dialogs.list.count/* + contactsNoDialogs.list.count*/) * st::dlgHeight; if (h) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); } else { @@ -936,6 +948,15 @@ void DialogsListWidget::clearFilter() { void DialogsListWidget::addDialog(const MTPDdialog &dialog) { History *history = App::history(App::peerFromMTP(dialog.vpeer), dialog.vunread_count.v, dialog.vread_inbox_max_id.v); + if (history->lastMsg) { + SavedPeersByTime &saved(cRefSavedPeersByTime()); + while (!saved.isEmpty() && history->lastMsg->date < saved.lastKey()) { + History *history = App::history(saved.last()->id); + history->dialogs = dialogs.addToEnd(history); + contactsNoDialogs.del(history->peer); + saved.remove(saved.lastKey(), saved.last()); + } + } History::DialogLinks links = dialogs.addToEnd(history); history->dialogs = links; contactsNoDialogs.del(history->peer); @@ -1714,6 +1735,7 @@ void DialogsWidget::onSearchMore(MsgId minMsgId) { void DialogsWidget::loadDialogs() { if (dlgPreloading) return; if (dlgCount >= 0 && dlgOffset >= dlgCount) { + list.addAllSavedPeers(); cSetDialogsReceived(true); return; } diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index ec7c1fa13..036a7695a 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -27,6 +27,7 @@ public: DialogsListWidget(QWidget *parent, MainWidget *main); void dialogsReceived(const QVector &dialogs); + void addAllSavedPeers(); void searchReceived(const QVector &messages, bool fromStart, int32 fullCount); void peopleReceived(const QString &query, const QVector &people); void showMore(int32 pixels); diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index c0d9ecd88..8ebec1dc2 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -29,6 +29,9 @@ struct StorageImageLocation { } StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : width(width), height(height), dc(location.vdc_id.v), volume(location.vvolume_id.v), local(location.vlocal_id.v), secret(location.vsecret.v) { } + bool isNull() const { + return !dc; + } int32 width, height; int32 dc; uint64 volume; @@ -36,6 +39,13 @@ struct StorageImageLocation { uint64 secret; }; +inline bool operator==(const StorageImageLocation &a, const StorageImageLocation &b) { + return !memcmp(&a, &b, sizeof(StorageImageLocation)); +} +inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation &b) { + return !(a == b); +} + class Image { public: diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 2716d1c4d..a486510a7 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -613,7 +613,7 @@ HistoryItem *Histories::addToBack(const MTPmessage &msg, int msgState) { if (!h.value()->loadedAtBottom()) { HistoryItem *item = h.value()->addToHistory(msg); if (item) { - h.value()->lastMsg = item; + h.value()->setLastMessage(item); if (msgState > 0) { h.value()->newItemAdded(item); } @@ -908,7 +908,8 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem * } } to->push_back(adding); - lastMsg = adding; + setLastMessage(adding); + adding->y = to->height; if (width) { int32 dh = adding->resize(width); @@ -919,6 +920,7 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem * if (newMsg) { newItemAdded(adding); } + HistoryMedia *media = adding->getMedia(true); if (media) { HistoryMediaType mt = media->type(); @@ -1414,14 +1416,24 @@ void History::getReadyFor(MsgId msgId) { } } +void History::setLastMessage(HistoryItem *msg) { + if (msg) { + if (!lastMsg) Local::removeSavedPeer(peer); + lastMsg = msg; + lastMsgDate = msg->date; + } else { + lastMsg = 0; + } +} + void History::fixLastMessage(bool wasAtBottom) { if (wasAtBottom && isEmpty()) { wasAtBottom = false; } if (wasAtBottom) { - lastMsg = back()->back(); + setLastMessage(back()->back()); } else { - lastMsg = 0; + setLastMessage(0); if (App::main()) { App::main()->checkPeerHistory(peer); } @@ -1482,7 +1494,7 @@ void History::clear(bool leaveItems) { showFrom = 0; } if (!leaveItems) { - lastMsg = 0; + setLastMessage(0); } for (int32 i = 0; i < OverviewCount; ++i) { if (!_overview[i].isEmpty() || !_overviewIds[i].isEmpty()) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 9e4a0810f..1379928b5 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -204,6 +204,7 @@ struct History : public QList { bool isReadyFor(MsgId msgId, bool check = false) const; // has messages for showing history at msgId void getReadyFor(MsgId msgId); + void setLastMessage(HistoryItem *msg); void fixLastMessage(bool wasAtBottom); MsgId minMsgId() const; @@ -218,6 +219,7 @@ struct History : public QList { PeerData *peer; bool oldLoaded, newLoaded; HistoryItem *lastMsg; + QDateTime lastMsgDate; typedef QList NotifyQueue; NotifyQueue notifies; @@ -446,11 +448,11 @@ struct DialogsList { DialogRow *row = addToEnd(history), *change = row; const QString &peerName(history->peer->name); - while (change->prev && change->prev->history->peer->name > peerName) { + while (change->prev && change->prev->history->peer->name.compare(peerName, Qt::CaseInsensitive) > 0) { change = change->prev; } if (!insertBefore(row, change)) { - while (change->next != end && change->next->history->peer->name < peerName) { + while (change->next != end && change->next->history->peer->name.compare(peerName, Qt::CaseInsensitive) < 0) { change = change->next; } insertAfter(row, change); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index aaf668f36..6e506cc1a 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2724,7 +2724,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { update(); return; } - if (_history->mySendActions.contains(SendActionTyping)) updateSendAction(_history, SendActionTyping, false); + if (_history->mySendActions.contains(SendActionTyping)) updateSendAction(_history, SendActionTyping, -1); } stopGif(); @@ -2912,7 +2912,7 @@ void HistoryWidget::updateControlsVisibility() { } else { _scroll.show(); } - if (!_peer->chat || !_peer->asChat()->forbidden) { + if ((_peer->chat && !_peer->asChat()->forbidden && !_peer->asChat()->left) || (!_peer->chat && _peer->asUser()->access != UserNoAccess)) { checkMentionDropdown(); if (isBotStart()) { if (_botStart.isHidden()) { @@ -4002,7 +4002,7 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) { int32 t = unixtime(); if (_peer->chat) { ChatData *chat = _peer->asChat(); - if (chat->forbidden) { + if (chat->forbidden || chat->left) { text = lang(lng_chat_status_unaccessible); } else if (chat->participants.isEmpty()) { text = _titlePeerText.isEmpty() ? lng_chat_status_members(lt_count, chat->count < 0 ? 0 : chat->count) : _titlePeerText; @@ -4391,7 +4391,7 @@ void HistoryWidget::onAudioProgress(MsgId newId) { HistoryItem *item = App::histItemById(newId); if (item) { AudioData *audio = (item->getMedia() && item->getMedia()->type() == MediaTypeAudio) ? static_cast(item->getMedia())->audio() : 0; - updateSendAction(item->history(), SendActionUploadAudio, audio->uploadOffset); + updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0); msgUpdated(item->history()->peer->id, item); } } @@ -4540,7 +4540,7 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, if (isBotStart()) { newScrollHeight -= _botStart.height(); } else { - if (!_peer->chat || !_peer->asChat()->forbidden) { + if ((_peer->chat && !_peer->asChat()->forbidden && !_peer->asChat()->left) || (!_peer->chat && _peer->asUser()->access != UserNoAccess)) { newScrollHeight -= (_field.height() + 2 * st::sendPadding); } if (replyToId() || App::main()->hasForwardingItems() || (_previewData && _previewData->pendingTill >= 0)) { diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index bf141a300..b3036fce4 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -18,6 +18,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "localstorage.h" +#include "mainwidget.h" #include "lang.h" namespace { @@ -505,6 +506,7 @@ namespace { lskUserSettings = 0x09, // no data lskRecentHashtags = 0x0a, // no data lskStickers = 0x0b, // no data + lskSavedPeers = 0x0c, // no data }; typedef QMap DraftsMap; @@ -530,6 +532,8 @@ namespace { FileKey _recentHashtagsKey = 0; bool _recentHashtagsWereRead = false; + FileKey _savedPeersKey = 0; + typedef QPair FileDesc; // file, size typedef QMap StorageMap; StorageMap _imagesMap, _stickerImagesMap, _audiosMap; @@ -1432,7 +1436,7 @@ namespace { DraftsNotReadMap draftsNotReadMap; StorageMap imagesMap, stickerImagesMap, audiosMap; qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0; - quint64 locationsKey = 0, recentStickersKeyOld = 0, stickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0; + quint64 locationsKey = 0, recentStickersKeyOld = 0, stickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0, savedPeersKey = 0; while (!map.stream.atEnd()) { quint32 keyType; map.stream >> keyType; @@ -1512,6 +1516,9 @@ namespace { case lskStickers: { map.stream >> stickersKey; } break; + case lskSavedPeers: { + map.stream >> savedPeersKey; + } break; default: LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType)); return Local::ReadMapFailed; @@ -1535,6 +1542,7 @@ namespace { _locationsKey = locationsKey; _recentStickersKeyOld = recentStickersKeyOld; _stickersKey = stickersKey; + _savedPeersKey = savedPeersKey; _backgroundKey = backgroundKey; _userSettingsKey = userSettingsKey; _recentHashtagsKey = recentHashtagsKey; @@ -1599,6 +1607,7 @@ namespace { if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64); if (_stickersKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64); @@ -1642,6 +1651,9 @@ namespace { if (_stickersKey) { mapData.stream << quint32(lskStickers) << quint64(_stickersKey); } + if (_savedPeersKey) { + mapData.stream << quint32(lskSavedPeers) << quint64(_savedPeersKey); + } if (_backgroundKey) { mapData.stream << quint32(lskBackground) << quint64(_backgroundKey); } @@ -1900,7 +1912,7 @@ namespace Local { _draftsNotReadMap.clear(); _stickerImagesMap.clear(); _audiosMap.clear(); - _locationsKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0; + _locationsKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; _mapChanged = true; _writeMap(WriteMapNow); @@ -2293,6 +2305,23 @@ namespace Local { return _storageAudiosSize; } + void _writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) { + stream << qint32(loc.width) << qint32(loc.height); + stream << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret); + } + + uint32 _storageImageLocationSize() { + // width + height + dc + volume + local + secret + return sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64); + } + + StorageImageLocation _readStorageImageLocation(FileReadDescriptor &from) { + qint32 thumbWidth, thumbHeight, thumbDc, thumbLocal; + quint64 thumbVolume, thumbSecret; + from.stream >> thumbWidth >> thumbHeight >> thumbDc >> thumbVolume >> thumbLocal >> thumbSecret; + return StorageImageLocation(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret); + } + void _writeStickerSet(QDataStream &stream, uint64 setId) { StickerSets::const_iterator it = cStickerSets().constFind(setId); if (it == cStickerSets().cend()) return; @@ -2321,8 +2350,7 @@ namespace Local { stream << qint32(StickerSetTypeEmpty); } break; } - const StorageImageLocation &loc(doc->sticker()->loc); - stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret); + _writeStorageImageLocation(stream, doc->sticker()->loc); } } @@ -2358,8 +2386,8 @@ namespace Local { // id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker()->alt) + sizeof(qint32); - // thumb-width + thumb-height + thumb-dc + thumb-volume + thumb-local + thumb-secret - size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64); + // loc + size += _storageImageLocationSize(); } ++setsCount; } @@ -2519,9 +2547,7 @@ namespace Local { qint32 date, dc, size, width, height, type, typeOfSet; stickers.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> alt >> typeOfSet; - qint32 thumbWidth, thumbHeight, thumbDc, thumbLocal; - quint64 thumbVolume, thumbSecret; - stickers.stream >> thumbWidth >> thumbHeight >> thumbDc >> thumbVolume >> thumbLocal >> thumbSecret; + StorageImageLocation thumb(_readStorageImageLocation(stickers)); if (read.contains(id)) continue; read.insert(id, true); @@ -2552,7 +2578,6 @@ namespace Local { attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); } - StorageImageLocation thumb(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret); DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.dc ? ImagePtr(thumb) : ImagePtr(), dc, size, thumb); if (!doc->sticker()) continue; @@ -2705,6 +2730,193 @@ namespace Local { cSetRecentSearchHashtags(search); } + uint32 _peerSize(PeerData *peer) { + uint32 result = sizeof(quint64) + sizeof(quint64) + _storageImageLocationSize(); + if (peer->chat) { + ChatData *chat = peer->asChat(); + + // name + count + date + version + admin + forbidden + left + invitationUrl + result += _stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(chat->invitationUrl); + } else { + UserData *user = peer->asUser(); + + // first + last + phone + username + access + onlineTill + contact + botInfoVersion + result += _stringSize(user->firstName) + _stringSize(user->lastName) + _stringSize(user->phone) + _stringSize(user->username) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32); + } + return result; + } + + void _writePeer(QDataStream &stream, PeerData *peer) { + stream << quint64(peer->id) << quint64(peer->photoId); + _writeStorageImageLocation(stream, peer->photoLoc); + if (peer->chat) { + ChatData *chat = peer->asChat(); + + stream << chat->name << qint32(chat->count) << qint32(chat->date) << qint32(chat->version) << qint32(chat->admin); + stream << qint32(chat->forbidden ? 1 : 0) << qint32(chat->left ? 1 : 0) << chat->invitationUrl; + } else { + UserData *user = peer->asUser(); + + stream << user->firstName << user->lastName << user->phone << user->username << quint64(user->access) << qint32(user->onlineTill) << qint32(user->contact) << qint32(user->botInfo ? user->botInfo->version : -1); + } + } + + PeerData *_readPeer(FileReadDescriptor &from) { + PeerData *result = 0; + quint64 peerId = 0, photoId = 0; + from.stream >> peerId >> photoId; + + StorageImageLocation photoLoc(_readStorageImageLocation(from)); + + result = App::peer(peerId); + result->loaded = true; + if (result->chat) { + ChatData *chat = result->asChat(); + + QString name, invitationUrl; + qint32 count, date, version, admin, forbidden, left; + from.stream >> name >> count >> date >> version >> admin >> forbidden >> left >> invitationUrl; + + chat->updateName(name, QString(), QString()); + chat->count = count; + chat->date = date; + chat->version = version; + chat->admin = admin; + chat->forbidden = (forbidden == 1); + chat->left = (left == 1); + chat->invitationUrl = invitationUrl; + + chat->input = MTP_inputPeerChat(MTP_int(App::chatFromPeer(chat->id))); + + chat->photo = photoLoc.isNull() ? ImagePtr(chatDefPhoto(chat->colorIndex)) : ImagePtr(photoLoc); + } else { + UserData *user = result->asUser(); + + QString first, last, phone, username; + quint64 access; + qint32 onlineTill, contact, botInfoVersion; + from.stream >> first >> last >> phone >> username >> access >> onlineTill >> contact >> botInfoVersion; + + bool showPhone = !isServiceUser(user->id) && (user->id != MTP::authedId()) && (contact <= 0); + QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString(); + + user->setName(first, last, pname, username); + + user->access = access; + user->onlineTill = onlineTill; + user->contact = contact; + user->setBotInfoVersion(botInfoVersion); + + if (user->id == MTP::authedId()) { + user->input = MTP_inputPeerSelf(); + user->inputUser = MTP_inputUserSelf(); + } else if (user->contact > 0 || !user->access) { + user->input = MTP_inputPeerContact(MTP_int(App::userFromPeer(user->id))); + user->inputUser = MTP_inputUserContact(MTP_int(App::userFromPeer(user->id))); + } else { + user->input = MTP_inputPeerForeign(MTP_int(App::userFromPeer(user->id)), MTP_long(user->access)); + user->inputUser = MTP_inputUserForeign(MTP_int(App::userFromPeer(user->id)), MTP_long(user->access)); + } + + user->photo = photoLoc.isNull() ? ImagePtr(userDefPhoto(user->colorIndex)) : ImagePtr(photoLoc); + } + App::markPeerUpdated(result); + emit App::main()->peerPhotoChanged(result); + return result; + } + + void writeSavedPeers() { + if (!_working()) return; + + const SavedPeers &saved(cSavedPeers()); + if (saved.isEmpty()) { + if (_savedPeersKey) { + clearKey(_savedPeersKey); + _savedPeersKey = 0; + _mapChanged = true; + } + _writeMap(); + } else { + if (!_savedPeersKey) { + _savedPeersKey = genKey(); + _mapChanged = true; + _writeMap(WriteMapFast); + } + quint32 size = sizeof(quint32); + for (SavedPeers::const_iterator i = saved.cbegin(); i != saved.cend(); ++i) { + size += _peerSize(i.key()) + _dateTimeSize(); + } + + EncryptedDescriptor data(size); + data.stream << quint32(saved.size()); + for (SavedPeers::const_iterator i = saved.cbegin(); i != saved.cend(); ++i) { + _writePeer(data.stream, i.key()); + data.stream << i.value(); + } + + FileWriteDescriptor file(_savedPeersKey); + file.writeEncrypted(data); + } + } + + void readSavedPeers() { + if (!_savedPeersKey) return; + + FileReadDescriptor saved; + if (!readEncryptedFile(saved, _savedPeersKey)) { + clearKey(_savedPeersKey); + _savedPeersKey = 0; + _writeMap(); + return; + } + + quint32 count = 0; + saved.stream >> count; + cRefSavedPeers().clear(); + cRefSavedPeersByTime().clear(); + QList peers; + peers.reserve(count); + for (uint32 i = 0; i < count; ++i) { + PeerData *peer = _readPeer(saved); + if (!peer) break; + + QDateTime t; + saved.stream >> t; + + cRefSavedPeers().insert(peer, t); + cRefSavedPeersByTime().insert(t, peer); + peers.push_back(peer); + } + App::emitPeerUpdated(); + App::api()->requestPeers(peers); + } + + void addSavedPeer(PeerData *peer, const QDateTime &position) { + SavedPeers &savedPeers(cRefSavedPeers()); + SavedPeers::iterator i = savedPeers.find(peer); + if (i == savedPeers.cend()) { + savedPeers.insert(peer, position); + } else if (i.value() != position) { + cRefSavedPeersByTime().remove(i.value(), peer); + i.value() = position; + cRefSavedPeersByTime().insert(i.value(), peer); + } + writeSavedPeers(); + } + + void removeSavedPeer(PeerData *peer) { + SavedPeers &savedPeers(cRefSavedPeers()); + if (savedPeers.isEmpty()) return; + + SavedPeers::iterator i = savedPeers.find(peer); + if (i != savedPeers.cend()) { + cRefSavedPeersByTime().remove(i.value(), peer); + savedPeers.erase(i); + + writeSavedPeers(); + } + } + struct ClearManagerData { QThread *thread; StorageMap images, stickers, audios; @@ -2764,6 +2976,10 @@ namespace Local { _recentHashtagsKey = 0; _mapChanged = true; } + if (_savedPeersKey) { + _savedPeersKey = 0; + _mapChanged = true; + } _writeMap(); } else { if (task & ClearManagerStorage) { diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index d3df02d3b..65fe3c7cf 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -141,4 +141,8 @@ namespace Local { void writeRecentHashtags(); void readRecentHashtags(); + void addSavedPeer(PeerData *peer, const QDateTime &position); + void removeSavedPeer(PeerData *peer); + void readSavedPeers(); + }; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 47922db5b..0013557e3 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -788,7 +788,7 @@ void MainWidget::deleteConversation(PeerData *peer) { void MainWidget::clearHistory(PeerData *peer) { History *h = App::history(peer->id); if (h->lastMsg) { -// Local::savePeerPosition(h->peer, h->lastMsg->date); + Local::addSavedPeer(h->peer, h->lastMsg->date); } h->clear(); h->newLoaded = h->oldLoaded = true; @@ -813,6 +813,8 @@ bool MainWidget::addParticipantFail(UserData *user, const RPCError &error) { QString text = lang(lng_failed_add_participant); if (error.type() == "USER_LEFT_CHAT") { // trying to return banned user to his group + } else if (error.type() == "USER_NOT_MUTUAL_CONTACT") { // trying to return user who does not have me in contacts + text = lang(lng_failed_add_not_mutual); } else if (error.type() == "USER_ALREADY_PARTICIPANT" && user->botInfo) { text = lang(lng_bot_already_in_group); } @@ -856,7 +858,12 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu if ((profile && profile->peer() == peer) || (overview && overview->peer() == peer) || _stack.contains(peer) || history.peer() == peer) { showDialogs(); } - dialogs.removePeer(peer); + if (peer->chat && peer->asChat()->left) { + dialogs.removePeer(peer); + } else { + History *h = App::historyLoaded(peer->id); + if (h) Local::addSavedPeer(peer, h->lastMsgDate); + } } else { History *h = App::historyLoaded(peer->id); if (!h->lastMsg) { @@ -2757,6 +2764,8 @@ void MainWidget::start(const MTPUser &user) { Local::writeMtpData(); } + Local::readSavedPeers(); + cSetOtherOnline(0); App::feedUsers(MTP_vector(1, user)); App::app()->startUpdateCheck(); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index b8eb89701..a9a61afba 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -159,6 +159,9 @@ int gOtherOnline = 0; float64 gSongVolume = 0.9; +SavedPeers gSavedPeers; +SavedPeersByTime gSavedPeersByTime; + void settingsParseArgs(int argc, char *argv[]) { gCustomNotifies = true; #ifdef Q_OS_MAC diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 7443c09e0..f5b577fbf 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -308,4 +308,10 @@ DeclareSetting(int, OtherOnline); DeclareSetting(float64, SongVolume); +struct PeerData; +typedef QMap SavedPeers; +typedef QMultiMap SavedPeersByTime; +DeclareRefSetting(SavedPeers, SavedPeers); +DeclareRefSetting(SavedPeersByTime, SavedPeersByTime); + void settingsParseArgs(int argc, char *argv[]); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index d60be556e..080b346af 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -90,10 +90,10 @@ NotifySettingsPtr globalNotifyAllPtr = UnknownNotifySettings, globalNotifyUsersP 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)) +, photoId(UnknownPeerPhotoId) , nameVersion(0) , notify(UnknownNotifySettings) { @@ -132,14 +132,16 @@ void PeerData::updateName(const QString &newName, const QString &newNameOrPhone, } } -void UserData::setPhoto(const MTPUserProfilePhoto &p) { +void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer as well PhotoId newPhotoId = photoId; ImagePtr newPhoto = photo; + StorageImageLocation newPhotoLoc = photoLoc; switch (p.type()) { case mtpc_userProfilePhoto: { const MTPDuserProfilePhoto d(p.c_userProfilePhoto()); newPhotoId = d.vphoto_id.v; - newPhoto = ImagePtr(160, 160, d.vphoto_small, userDefPhoto(colorIndex)); + newPhotoLoc = App::imageLocation(160, 160, d.vphoto_small); + newPhoto = newPhotoLoc.isNull() ? userDefPhoto(colorIndex) : ImagePtr(newPhotoLoc); //App::feedPhoto(App::photoFromUserPhoto(MTP_int(id & 0xFFFFFFFF), MTP_int(unixtime()), p)); } break; default: { @@ -151,11 +153,13 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { } else { newPhoto = userDefPhoto(colorIndex); } + newPhotoLoc = StorageImageLocation(); } break; } - if (newPhotoId != photoId || newPhoto.v() != photo.v()) { + if (newPhotoId != photoId || newPhoto.v() != photo.v() || newPhotoLoc != photoLoc) { photoId = newPhotoId; photo = newPhoto; + photoLoc = newPhotoLoc; emit App::main()->peerPhotoChanged(this); } } @@ -226,6 +230,7 @@ void UserData::setBotInfoVersion(int32 version) { botInfo->inited = false; } } + void UserData::setBotInfo(const MTPBotInfo &info) { switch (info.type()) { case mtpc_botInfoEmpty: @@ -305,23 +310,33 @@ void UserData::madeAction() { } } -void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { +void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see Local::readPeer as well + PhotoId newPhotoId = photoId; + ImagePtr newPhoto = photo; + StorageImageLocation newPhotoLoc = photoLoc; 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 != UnknownPeerPhotoId) { - photoId = phId; + newPhotoId = phId; } + newPhotoLoc = App::imageLocation(160, 160, d.vphoto_small); + newPhoto = newPhotoLoc.isNull() ? chatDefPhoto(colorIndex) : ImagePtr(newPhotoLoc); +// photoFull = ImagePtr(640, 640, d.vphoto_big, chatDefPhoto(colorIndex)); } break; default: { - photo = chatDefPhoto(colorIndex); - photoFull = ImagePtr(); - photoId = 0; + newPhotoId = 0; + newPhotoLoc = StorageImageLocation(); + newPhoto = chatDefPhoto(colorIndex); +// photoFull = ImagePtr(); } break; } - emit App::main()->peerPhotoChanged(this); + if (newPhotoId != photoId || newPhoto.v() != photo.v() || newPhotoLoc != photoLoc) { + photoId = newPhotoId; + photo = newPhoto; + photoLoc = newPhotoLoc; + emit App::main()->peerPhotoChanged(this); + } } void PhotoLink::onClick(Qt::MouseButton button) const { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 152d47b1c..6bcdd6c5c 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -59,8 +59,10 @@ style::color peerColor(int32 index); ImagePtr userDefPhoto(int32 index); ImagePtr chatDefPhoto(int32 index); -struct ChatData; +static const PhotoId UnknownPeerPhotoId = 0xFFFFFFFFFFFFFFFFULL; + struct UserData; +struct ChatData; struct PeerData { PeerData(const PeerId &id); virtual ~PeerData() { @@ -94,13 +96,13 @@ struct PeerData { bool loaded; bool chat; - uint64 access; MTPinputPeer input; - MTPinputUser inputUser; int32 colorIndex; style::color color; ImagePtr photo; + PhotoId photoId; + StorageImageLocation photoLoc; int32 nameVersion; @@ -165,11 +167,9 @@ struct BotInfo { QString startToken, startGroupToken; }; -static const PhotoId UnknownPeerPhotoId = 0xFFFFFFFFFFFFFFFFULL; - struct PhotoData; struct UserData : public PeerData { - UserData(const PeerId &id) : PeerData(id), photoId(UnknownPeerPhotoId), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1), botInfo(0) { + UserData(const PeerId &id) : PeerData(id), access(0), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1), botInfo(0) { } void setPhoto(const MTPUserProfilePhoto &photo); void setName(const QString &first, const QString &last, const QString &phoneName, const QString &username); @@ -180,12 +180,15 @@ struct UserData : public PeerData { void madeAction(); // pseudo-online + uint64 access; + + MTPinputUser inputUser; + 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 @@ -198,7 +201,7 @@ struct UserData : public PeerData { }; struct ChatData : public PeerData { - ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), botStatus(0), photoId(UnknownPeerPhotoId) { + ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), botStatus(0) { } void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId); int32 count; @@ -216,8 +219,7 @@ struct ChatData : public PeerData { typedef QMap MarkupSenders; MarkupSenders markupSenders; int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other - ImagePtr photoFull; - PhotoId photoId; +// ImagePtr photoFull; QString invitationUrl; // geo }; diff --git a/Telegram/SourceFiles/telegram.qrc b/Telegram/SourceFiles/telegram.qrc index 3418022e0..6a0af5648 100644 --- a/Telegram/SourceFiles/telegram.qrc +++ b/Telegram/SourceFiles/telegram.qrc @@ -1,42 +1,42 @@ - - art/fonts/OpenSans-Regular.ttf - art/fonts/OpenSans-Bold.ttf - art/fonts/OpenSans-Semibold.ttf - art/newmsg.wav - art/bg.jpg - art/bg0.png - art/sprite.png - art/sprite_125x.png - art/sprite_150x.png - art/sprite_200x.png - art/blank.gif - art/icon256.png - art/iconbig256.png - - - art/chatcolor1.png - art/chatcolor2.png - art/chatcolor3.png - art/chatcolor4.png - art/usercolor1.png - art/usercolor2.png - art/usercolor3.png - art/usercolor4.png - art/usercolor5.png - art/usercolor6.png - art/usercolor7.png - art/usercolor8.png - - - qmime/freedesktop.org.xml - - - langs/lang_it.strings - langs/lang_es.strings - langs/lang_de.strings - langs/lang_nl.strings - langs/lang_pt_BR.strings - langs/lang_ko.strings - + + art/fonts/OpenSans-Regular.ttf + art/fonts/OpenSans-Bold.ttf + art/fonts/OpenSans-Semibold.ttf + art/newmsg.wav + art/bg.jpg + art/bg0.png + art/sprite.png + art/sprite_125x.png + art/sprite_150x.png + art/sprite_200x.png + art/blank.gif + art/icon256.png + art/iconbig256.png + + + art/chatcolor1.png + art/chatcolor2.png + art/chatcolor3.png + art/chatcolor4.png + art/usercolor1.png + art/usercolor2.png + art/usercolor3.png + art/usercolor4.png + art/usercolor5.png + art/usercolor6.png + art/usercolor7.png + art/usercolor8.png + + + qmime/freedesktop.org.xml + + + langs/lang_it.strings + langs/lang_es.strings + langs/lang_de.strings + langs/lang_nl.strings + langs/lang_pt_BR.strings + langs/lang_ko.strings + diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index 745d81e8e..b066496f6 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.8.46 + 0.8.47 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) CFBundleSignature diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index dee5d4b5f..14b5bc3c7 100644 Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 2797647c0..80b18b34f 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -130,6 +130,7 @@ AnySuitable true Speed + /Zm110 %(AdditionalOptions) Windows diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 175b9d219..d0f9fc4ba 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1707,7 +1707,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.8.46; + CURRENT_PROJECT_VERSION = 0.8.47; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1725,7 +1725,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.8.46; + CURRENT_PROJECT_VERSION = 0.8.47; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1751,10 +1751,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.8.46; + CURRENT_PROJECT_VERSION = 0.8.47; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 0.8; - DYLIB_CURRENT_VERSION = 0.8.46; + DYLIB_CURRENT_VERSION = 0.8.47; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1885,10 +1885,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.8.46; + CURRENT_PROJECT_VERSION = 0.8.47; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.8; - DYLIB_CURRENT_VERSION = 0.8.46; + DYLIB_CURRENT_VERSION = 0.8.47; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; diff --git a/Telegram/Version.sh b/Telegram/Version.sh index 8f625316a..8761287d7 100755 --- a/Telegram/Version.sh +++ b/Telegram/Version.sh @@ -1,2 +1,2 @@ -echo 0.8 8046 0.8.46 0 +echo 0.8 8047 0.8.47 1 # AppVersionStrMajor AppVersion AppVersionStr DevChannel