diff --git a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py index a6a9b1afdedcc3..988adeca93b205 100644 --- a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py +++ b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py @@ -767,19 +767,20 @@ def addTextSerializeInit(lst, dct): else: typesText += ' = default;\n'; - if (withData): - typesText += getters; - if (withType): - typesText += '\n'; - typesText += '\ttemplate \n'; - typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n'; - visitorMethods += 'template \n'; - visitorMethods += 'decltype(auto) MTP' + restype + '::match(Method &&method, Methods &&...methods) const {\n'; - visitorMethods += '\tswitch (_type) {\n'; - visitorMethods += visitor; - visitorMethods += '\t}\n'; - visitorMethods += '\tUnexpected("Type in MTP' + restype + '::match.");\n'; - visitorMethods += '}\n\n'; + typesText += getters; + typesText += '\n'; + typesText += '\ttemplate \n'; + typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n'; + visitorMethods += 'template \n'; + visitorMethods += 'decltype(auto) MTP' + restype + '::match(Method &&method, Methods &&...methods) const {\n'; + if (withType): + visitorMethods += '\tswitch (_type) {\n'; + visitorMethods += visitor; + visitorMethods += '\t}\n'; + visitorMethods += '\tUnexpected("Type in MTP' + restype + '::match.");\n'; + else: + visitorMethods += '\treturn base::match_method(c_' + v[0][0] + '(), std::forward(method), std::forward(methods)...);\n'; + visitorMethods += '}\n\n'; typesText += '\n\tuint32 innerLength() const;\n'; # size method methods += '\nuint32 MTP' + restype + '::innerLength() const {\n'; @@ -843,7 +844,7 @@ def addTextSerializeInit(lst, dct): if (withData): typesText += constructsText; - methods += constructsBodies; + methods += constructsBodies; if (friendDecl): typesText += '\n' + friendDecl; diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 966cb47431bbbc..a5ce12a8810de1 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -7,14 +7,19 @@ For license and copyright information please follow this link: */ #pragma once -#include -#include #include "logs.h" #include "core/basic_types.h" #include "base/flags.h" #include "base/algorithm.h" #include "base/assertion.h" +#include +#include +#include + +#include +#include + // Define specializations for QByteArray for Qt 5.3.2, because // QByteArray in Qt 5.3.2 doesn't declare "pointer" subtype. #ifdef OS_MAC_OLD diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 90fda141c3dc08..13c14f646078e5 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -15,6 +15,7 @@ For license and copyright information please follow this link: namespace App { // Hackish.. QString formatPhone(QString phone); } // namespace App +QString FillAmountAndCurrency(uint64 amount, const QString ¤cy); namespace Export { namespace Data { @@ -272,21 +273,46 @@ Document ParseDocument( return result; } -Utf8String FormatDateTime( - TimeId date, - QChar dateSeparator, - QChar timeSeparator, - QChar separator) { - const auto value = QDateTime::fromTime_t(date); - return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3" - + separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6" - ).arg(value.date().year() - ).arg(value.date().month(), 2, 10, QChar('0') - ).arg(value.date().day(), 2, 10, QChar('0') - ).arg(value.time().hour(), 2, 10, QChar('0') - ).arg(value.time().minute(), 2, 10, QChar('0') - ).arg(value.time().second(), 2, 10, QChar('0') - ).toUtf8(); +GeoPoint ParseGeoPoint(const MTPGeoPoint &data) { + auto result = GeoPoint(); + data.match([&](const MTPDgeoPoint &data) { + result.latitude = data.vlat.v; + result.longitude = data.vlong.v; + result.valid = true; + }, [](const MTPDgeoPointEmpty &data) {}); + return result; +} + +Venue ParseVenue(const MTPDmessageMediaVenue &data) { + auto result = Venue(); + result.point = ParseGeoPoint(data.vgeo); + result.title = ParseString(data.vtitle); + result.address = ParseString(data.vaddress); + return result; +} + +Game ParseGame(const MTPGame &data, int32 botId) { + return data.match([&](const MTPDgame &data) { + auto result = Game(); + result.id = data.vid.v; + result.title = ParseString(data.vtitle); + result.description = ParseString(data.vdescription); + result.shortName = ParseString(data.vshort_name); + result.botId = botId; + return result; + }); +} + +Invoice ParseInvoice(const MTPDmessageMediaInvoice &data) { + auto result = Invoice(); + result.title = ParseString(data.vtitle); + result.description = ParseString(data.vdescription); + result.currency = ParseString(data.vcurrency); + result.amount = data.vtotal_amount.v; + if (data.has_receipt_msg_id()) { + result.receiptMsgId = data.vreceipt_msg_id.v; + } + return result; } UserpicsSlice ParseUserpicsSlice(const MTPVector &data) { @@ -303,10 +329,10 @@ UserpicsSlice ParseUserpicsSlice(const MTPVector &data) { return result; } -User ParseUser(const MTPUser &data) { - auto result = User(); +ContactInfo ParseContactInfo(const MTPUser &data) { + auto result = ContactInfo(); data.match([&](const MTPDuser &data) { - result.id = data.vid.v; + result.userId = data.vid.v; if (data.has_first_name()) { result.firstName = ParseString(data.vfirst_name); } @@ -316,15 +342,36 @@ User ParseUser(const MTPUser &data) { if (data.has_phone()) { result.phoneNumber = ParseString(data.vphone); } + }, [&](const MTPDuserEmpty &data) { + result.userId = data.vid.v; + }); + return result; +} + +ContactInfo ParseContactInfo(const MTPDmessageMediaContact &data) { + auto result = ContactInfo(); + result.userId = data.vuser_id.v; + result.firstName = ParseString(data.vfirst_name); + result.lastName = ParseString(data.vlast_name); + result.phoneNumber = ParseString(data.vphone_number); + return result; +} + +User ParseUser(const MTPUser &data) { + auto result = User(); + result.info = ParseContactInfo(data); + data.match([&](const MTPDuser &data) { if (data.has_username()) { result.username = ParseString(data.vusername); } + if (data.has_bot_info_version()) { + result.isBot = true; + } const auto access_hash = data.has_access_hash() ? data.vaccess_hash : MTP_long(0); result.input = MTP_inputUser(data.vid, access_hash); }, [&](const MTPDuserEmpty &data) { - result.id = data.vid.v; result.input = MTP_inputUser(data.vid, MTP_long(0)); }); return result; @@ -334,7 +381,7 @@ std::map ParseUsersList(const MTPVector &data) { auto result = std::map(); for (const auto &user : data.v) { auto parsed = ParseUser(user); - result.emplace(parsed.id, std::move(parsed)); + result.emplace(parsed.info.userId, std::move(parsed)); } return result; } @@ -382,6 +429,21 @@ std::map ParseChatsList(const MTPVector &data) { return result; } +Utf8String ContactInfo::name() const { + return firstName.isEmpty() + ? (lastName.isEmpty() + ? Utf8String() + : lastName) + : (lastName.isEmpty() + ? firstName + : firstName + ' ' + lastName); + +} + +Utf8String User::name() const { + return info.name(); +} + const User *Peer::user() const { return base::get_if(&data); } @@ -391,7 +453,7 @@ const Chat *Peer::chat() const { PeerId Peer::id() const { if (const auto user = this->user()) { - return UserPeerId(user->id); + return UserPeerId(user->info.userId); } else if (const auto chat = this->chat()) { return ChatPeerId(chat->id); } @@ -400,13 +462,7 @@ PeerId Peer::id() const { Utf8String Peer::name() const { if (const auto user = this->user()) { - return user->firstName.isEmpty() - ? (user->lastName.isEmpty() - ? Utf8String() - : user->lastName) - : (user->lastName.isEmpty() - ? user->firstName - : user->firstName + ' ' + user->lastName); + return user->name(); } else if (const auto chat = this->chat()) { return chat->title; } @@ -432,7 +488,9 @@ std::map ParsePeersLists( auto result = std::map(); for (const auto &user : users.v) { auto parsed = ParseUser(user); - result.emplace(UserPeerId(parsed.id), Peer{ std::move(parsed) }); + result.emplace( + UserPeerId(parsed.info.userId), + Peer{ std::move(parsed) }); } for (const auto &chat : chats.v) { auto parsed = ParseChat(chat); @@ -446,7 +504,7 @@ File &Media::file() { return data.image.file; }, [](Document &data) -> File& { return data.file; - }, [](base::none_type &) -> File& { + }, [](auto&) -> File& { static File result; return result; }); @@ -457,7 +515,7 @@ const File &Media::file() const { return data.image.file; }, [](const Document &data) -> const File& { return data.file; - }, [](const base::none_type &) -> const File& { + }, [](const auto &) -> const File& { static const File result; return result; }); @@ -466,7 +524,8 @@ const File &Media::file() const { Media ParseMedia( const MTPMessageMedia &data, const QString &folder, - TimeId date) { + TimeId date, + int32 botId) { Expects(folder.isEmpty() || folder.endsWith(QChar('/'))); auto result = Media(); @@ -480,8 +539,11 @@ Media ParseMedia( result.ttl = data.vttl_seconds.v; } }, [&](const MTPDmessageMediaGeo &data) { + result.content = ParseGeoPoint(data.vgeo); }, [&](const MTPDmessageMediaContact &data) { + result.content = ParseContactInfo(data); }, [&](const MTPDmessageMediaUnsupported &data) { + result.content = UnsupportedMedia(); }, [&](const MTPDmessageMediaDocument &data) { result.content = data.has_document() ? ParseDocument( @@ -489,12 +551,20 @@ Media ParseMedia( folder + "Files/", date) : Document(); + if (data.has_ttl_seconds()) { + result.ttl = data.vttl_seconds.v; + } }, [&](const MTPDmessageMediaWebPage &data) { // Ignore web pages. }, [&](const MTPDmessageMediaVenue &data) { + result.content = ParseVenue(data); }, [&](const MTPDmessageMediaGame &data) { + result.content = ParseGame(data.vgame, botId); }, [&](const MTPDmessageMediaInvoice &data) { + result.content = ParseInvoice(data); }, [&](const MTPDmessageMediaGeoLive &data) { + result.content = ParseGeoPoint(data.vgeo); + result.ttl = data.vperiod.v; }, [](const MTPDmessageMediaEmpty &data) {}); return result; } @@ -505,30 +575,155 @@ ServiceAction ParseServiceAction( TimeId date) { auto result = ServiceAction(); data.match([&](const MTPDmessageActionChatCreate &data) { + auto content = ActionChatCreate(); + content.title = ParseString(data.vtitle); + content.userIds.reserve(data.vusers.v.size()); + for (const auto &userId : data.vusers.v) { + content.userIds.push_back(userId.v); + } + result.content = content; }, [&](const MTPDmessageActionChatEditTitle &data) { + auto content = ActionChatEditTitle(); + content.title = ParseString(data.vtitle); + result.content = content; }, [&](const MTPDmessageActionChatEditPhoto &data) { + auto content = ActionChatEditPhoto(); + content.photo = ParsePhoto( + data.vphoto, + mediaFolder + "Photos/" + PreparePhotoFileName(date)); + result.content = content; }, [&](const MTPDmessageActionChatDeletePhoto &data) { + result.content = ActionChatDeletePhoto(); }, [&](const MTPDmessageActionChatAddUser &data) { + auto content = ActionChatAddUser(); + content.userIds.reserve(data.vusers.v.size()); + for (const auto &user : data.vusers.v) { + content.userIds.push_back(user.v); + } + result.content = content; }, [&](const MTPDmessageActionChatDeleteUser &data) { + auto content = ActionChatDeleteUser(); + content.userId = data.vuser_id.v; + result.content = content; }, [&](const MTPDmessageActionChatJoinedByLink &data) { + auto content = ActionChatJoinedByLink(); + content.inviterId = data.vinviter_id.v; + result.content = content; }, [&](const MTPDmessageActionChannelCreate &data) { + auto content = ActionChannelCreate(); + content.title = ParseString(data.vtitle); + result.content = content; }, [&](const MTPDmessageActionChatMigrateTo &data) { + auto content = ActionChatMigrateTo(); + content.channelId = data.vchannel_id.v; + result.content = content; }, [&](const MTPDmessageActionChannelMigrateFrom &data) { + auto content = ActionChannelMigrateFrom(); + content.title = ParseString(data.vtitle); + content.chatId = data.vchat_id.v; + result.content = content; }, [&](const MTPDmessageActionPinMessage &data) { + result.content = ActionPinMessage(); }, [&](const MTPDmessageActionHistoryClear &data) { + result.content = ActionHistoryClear(); }, [&](const MTPDmessageActionGameScore &data) { + auto content = ActionGameScore(); + content.gameId = data.vgame_id.v; + content.score = data.vscore.v; + result.content = content; }, [&](const MTPDmessageActionPaymentSentMe &data) { + // Should not be in user inbox. }, [&](const MTPDmessageActionPaymentSent &data) { + auto content = ActionPaymentSent(); + content.currency = ParseString(data.vcurrency); + content.amount = data.vtotal_amount.v; + result.content = content; }, [&](const MTPDmessageActionPhoneCall &data) { + auto content = ActionPhoneCall(); + if (data.has_duration()) { + content.duration = data.vduration.v; + } + if (data.has_reason()) { + using Reason = ActionPhoneCall::DiscardReason; + content.discardReason = data.vreason.match( + [](const MTPDphoneCallDiscardReasonMissed &data) { + return Reason::Missed; + }, [](const MTPDphoneCallDiscardReasonDisconnect &data) { + return Reason::Disconnect; + }, [](const MTPDphoneCallDiscardReasonHangup &data) { + return Reason::Hangup; + }, [](const MTPDphoneCallDiscardReasonBusy &data) { + return Reason::Busy; + }); + } + result.content = content; }, [&](const MTPDmessageActionScreenshotTaken &data) { + result.content = ActionScreenshotTaken(); }, [&](const MTPDmessageActionCustomAction &data) { + auto content = ActionCustomAction(); + content.message = ParseString(data.vmessage); + result.content = content; }, [&](const MTPDmessageActionBotAllowed &data) { + auto content = ActionBotAllowed(); + content.domain = ParseString(data.vdomain); + result.content = content; }, [&](const MTPDmessageActionSecureValuesSentMe &data) { + // Should not be in user inbox. }, [&](const MTPDmessageActionSecureValuesSent &data) { + auto content = ActionSecureValuesSent(); + content.types.reserve(data.vtypes.v.size()); + using Type = ActionSecureValuesSent::Type; + for (const auto &type : data.vtypes.v) { + content.types.push_back(type.match( + [](const MTPDsecureValueTypePersonalDetails &data) { + return Type::PersonalDetails; + }, [](const MTPDsecureValueTypePassport &data) { + return Type::Passport; + }, [](const MTPDsecureValueTypeDriverLicense &data) { + return Type::DriverLicense; + }, [](const MTPDsecureValueTypeIdentityCard &data) { + return Type::IdentityCard; + }, [](const MTPDsecureValueTypeInternalPassport &data) { + return Type::InternalPassport; + }, [](const MTPDsecureValueTypeAddress &data) { + return Type::Address; + }, [](const MTPDsecureValueTypeUtilityBill &data) { + return Type::UtilityBill; + }, [](const MTPDsecureValueTypeBankStatement &data) { + return Type::BankStatement; + }, [](const MTPDsecureValueTypeRentalAgreement &data) { + return Type::RentalAgreement; + }, [](const MTPDsecureValueTypePassportRegistration &data) { + return Type::PassportRegistration; + }, [](const MTPDsecureValueTypeTemporaryRegistration &data) { + return Type::TemporaryRegistration; + }, [](const MTPDsecureValueTypePhone &data) { + return Type::Phone; + }, [](const MTPDsecureValueTypeEmail &data) { + return Type::Email; + })); + } + result.content = content; }, [](const MTPDmessageActionEmpty &data) {}); return result; } +File &Message::file() { + const auto service = &action.content; + if (const auto photo = base::get_if(service)) { + return photo->photo.image.file; + } + return media.file(); +} + +const File &Message::file() const { + const auto service = &action.content; + if (const auto photo = base::get_if(service)) { + return photo->photo.image.file; + } + return media.file(); +} + Message ParseMessage(const MTPMessage &data, const QString &mediaFolder) { auto result = Message(); data.match([&](const MTPDmessage &data) { @@ -540,6 +735,22 @@ Message ParseMessage(const MTPMessage &data, const QString &mediaFolder) { if (data.has_from_id()) { result.fromId = data.vfrom_id.v; } + if (data.has_fwd_from()) { + result.forwardedFromId = data.vfwd_from.match( + [](const MTPDmessageFwdHeader &data) { + if (data.has_channel_id()) { + return ChatPeerId(data.vchannel_id.v); + } else if (data.has_saved_from_peer()) { + return ParsePeerId(data.vsaved_from_peer); + } else if (data.has_from_id()) { + return UserPeerId(data.vfrom_id.v); + } + return PeerId(0); + }); + } + if (data.has_post_author()) { + result.signature = ParseString(data.vpost_author); + } if (data.has_reply_to_msg_id()) { result.replyToMsgId = data.vreply_to_msg_id.v; } @@ -547,7 +758,15 @@ Message ParseMessage(const MTPMessage &data, const QString &mediaFolder) { result.viaBotId = data.vvia_bot_id.v; } if (data.has_media()) { - result.media = ParseMedia(data.vmedia, mediaFolder, date); + result.media = ParseMedia( + data.vmedia, + mediaFolder, + date, + (result.viaBotId + ? result.viaBotId + : result.forwardedFromId + ? result.forwardedFromId + : result.fromId)); } result.text = ParseString(data.vmessage); }, [&](const MTPDmessageService &data) { @@ -599,9 +818,9 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) { for (const auto &contact : contacts.vcontacts.v) { const auto userId = contact.c_contact().vuser_id.v; if (const auto i = map.find(userId); i != end(map)) { - result.list.push_back(i->second); + result.list.push_back(i->second.info); } else { - result.list.push_back(User()); + result.list.push_back(ContactInfo()); } } return result; @@ -610,10 +829,10 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) { std::vector SortedContactsIndices(const ContactsList &data) { const auto names = ranges::view::all( data.list - ) | ranges::view::transform([](const Data::User &user) { - return (QString::fromUtf8(user.firstName) + ) | ranges::view::transform([](const Data::ContactInfo &info) { + return (QString::fromUtf8(info.firstName) + ' ' - + QString::fromUtf8(user.lastName)).toLower(); + + QString::fromUtf8(info.lastName)).toLower(); }) | ranges::to_vector; auto indices = ranges::view::ints(0, int(data.list.size())) @@ -716,5 +935,28 @@ Utf8String FormatPhoneNumber(const Utf8String &phoneNumber) { : App::formatPhone(QString::fromUtf8(phoneNumber)).toUtf8(); } +Utf8String FormatDateTime( + TimeId date, + QChar dateSeparator, + QChar timeSeparator, + QChar separator) { + const auto value = QDateTime::fromTime_t(date); + return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3" + + separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6" + ).arg(value.date().year() + ).arg(value.date().month(), 2, 10, QChar('0') + ).arg(value.date().day(), 2, 10, QChar('0') + ).arg(value.time().hour(), 2, 10, QChar('0') + ).arg(value.time().minute(), 2, 10, QChar('0') + ).arg(value.time().second(), 2, 10, QChar('0') + ).toUtf8(); +} + +Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy) { + return FillAmountAndCurrency( + amount, + QString::fromUtf8(currency)).toUtf8(); +} + } // namespace Data } // namespace Export diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 4d0c59c431abf4..49c13892467259 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -13,6 +13,7 @@ For license and copyright information please follow this link: #include #include + #include namespace Export { @@ -95,20 +96,61 @@ struct Document { bool isAudioFile = false; }; +struct GeoPoint { + float64 latitude = 0.; + float64 longitude = 0.; + bool valid = false; +}; + +struct Venue { + GeoPoint point; + Utf8String title; + Utf8String address; +}; + +struct Game { + uint64 id = 0; + Utf8String shortName; + Utf8String title; + Utf8String description; + + int32 botId = 0; +}; + +struct Invoice { + Utf8String title; + Utf8String description; + Utf8String currency; + uint64 amount = 0; + int32 receiptMsgId = 0; +}; + struct UserpicsSlice { std::vector list; }; UserpicsSlice ParseUserpicsSlice(const MTPVector &data); -struct User { - int32 id = 0; +struct ContactInfo { + int32 userId = 0; Utf8String firstName; Utf8String lastName; Utf8String phoneNumber; + + Utf8String name() const; +}; + +ContactInfo ParseContactInfo(const MTPUser &data); +ContactInfo ParseContactInfo(const MTPDmessageMediaContact &data); + +struct User { + ContactInfo info; Utf8String username; + bool isBot = false; - MTPInputUser input; + MTPInputUser input = MTP_inputUserEmpty(); + + Utf8String name() const; }; User ParseUser(const MTPUser &data); @@ -120,7 +162,7 @@ struct Chat { Utf8String username; bool broadcast = false; - MTPInputPeer input; + MTPInputPeer input = MTP_inputPeerEmpty(); }; Chat ParseChat(const MTPChat &data); @@ -150,7 +192,7 @@ struct PersonalInfo { PersonalInfo ParsePersonalInfo(const MTPUserFull &data); struct ContactsList { - std::vector list; + std::vector list; }; ContactsList ParseContactsList(const MTPcontacts_Contacts &data); @@ -175,8 +217,19 @@ struct SessionsList { SessionsList ParseSessionsList(const MTPaccount_Authorizations &data); +struct UnsupportedMedia { +}; + struct Media { - base::optional_variant content; + base::optional_variant< + Photo, + Document, + ContactInfo, + GeoPoint, + Venue, + Game, + Invoice, + UnsupportedMedia> content; TimeId ttl = 0; File &file(); @@ -188,8 +241,126 @@ Media ParseMedia( const QString &folder, TimeId date); +struct ActionChatCreate { + Utf8String title; + std::vector userIds; +}; + +struct ActionChatEditTitle { + Utf8String title; +}; + +struct ActionChatEditPhoto { + Photo photo; +}; + +struct ActionChatDeletePhoto { +}; + +struct ActionChatAddUser { + std::vector userIds; +}; + +struct ActionChatDeleteUser { + int32 userId = 0; +}; + +struct ActionChatJoinedByLink { + int32 inviterId = 0; +}; + +struct ActionChannelCreate { + Utf8String title; +}; + +struct ActionChatMigrateTo { + int32 channelId = 0; +}; + +struct ActionChannelMigrateFrom { + Utf8String title; + int32 chatId = 0; +}; + +struct ActionPinMessage { +}; + +struct ActionHistoryClear { +}; + +struct ActionGameScore { + uint64 gameId = 0; + int score = 0; +}; + +struct ActionPaymentSent { + Utf8String currency; + uint64 amount = 0; +}; + +struct ActionPhoneCall { + enum class DiscardReason { + Unknown, + Missed, + Disconnect, + Hangup, + Busy, + }; + DiscardReason discardReason = DiscardReason::Unknown; + int duration = 0; +}; + +struct ActionScreenshotTaken { +}; + +struct ActionCustomAction { + Utf8String message; +}; + +struct ActionBotAllowed { + Utf8String domain; +}; + +struct ActionSecureValuesSent { + enum class Type { + PersonalDetails, + Passport, + DriverLicense, + IdentityCard, + InternalPassport, + Address, + UtilityBill, + BankStatement, + RentalAgreement, + PassportRegistration, + TemporaryRegistration, + Phone, + Email, + }; + std::vector types; +}; + struct ServiceAction { - base::optional_variant<> data; + base::optional_variant< + ActionChatCreate, + ActionChatEditTitle, + ActionChatEditPhoto, + ActionChatDeletePhoto, + ActionChatAddUser, + ActionChatDeleteUser, + ActionChatJoinedByLink, + ActionChannelCreate, + ActionChatMigrateTo, + ActionChannelMigrateFrom, + ActionPinMessage, + ActionHistoryClear, + ActionGameScore, + ActionPaymentSent, + ActionPhoneCall, + ActionScreenshotTaken, + ActionCustomAction, + ActionBotAllowed, + ActionSecureValuesSent> content; }; ServiceAction ParseServiceAction( @@ -202,12 +373,16 @@ struct Message { TimeId date = 0; TimeId edited = 0; int32 fromId = 0; + PeerId forwardedFromId = 0; + Utf8String signature; int32 viaBotId = 0; int32 replyToMsgId = 0; Utf8String text; Media media; ServiceAction action; + File &file(); + const File &file() const; }; Message ParseMessage(const MTPMessage &data, const QString &mediaFolder); @@ -226,7 +401,7 @@ struct DialogInfo { Type type = Type::Unknown; Utf8String name; - MTPInputPeer input; + MTPInputPeer input = MTP_inputPeerEmpty(); int32 topMessageId = 0; TimeId topMessageDate = 0; @@ -259,5 +434,7 @@ Utf8String FormatDateTime( QChar timeSeparator = QChar(':'), QChar separator = QChar(' ')); +Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy); + } // namespace Data } // namespace Export diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index 1c873754da8f01..951b7cebd63b87 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -470,7 +470,7 @@ void ApiWrap::loadNextMessageFile() { if (index >= list.size()) { break; } - const auto &file = list[index].media.file(); + const auto &file = list[index].file(); if (WillLoadFile(file)) { loadFile( file, @@ -479,16 +479,16 @@ void ApiWrap::loadNextMessageFile() { } } + if (!list.empty()) { + process->offsetId = list.back().id + 1; + } _dialogsProcess->sliceOne(*base::take(process->slice)); if (process->lastSlice) { finishMessages(); - return; + } else { + requestMessagesSlice(); } - - Assert(!list.empty()); - process->offsetId = list.back().id + 1; - requestMessagesSlice(); } void ApiWrap::loadMessageFileDone(const QString &relativePath) { @@ -501,7 +501,7 @@ void ApiWrap::loadMessageFileDone(const QString &relativePath) { const auto process = _dialogsProcess->single.get(); const auto index = process->fileIndex; - process->slice->list[index].media.file().relativePath = relativePath; + process->slice->list[index].file().relativePath = relativePath; loadNextMessageFile(); } diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp index 1821b6803aa497..4caa064b1dbb70 100644 --- a/Telegram/SourceFiles/export/export_controller.cpp +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -217,7 +217,7 @@ void Controller::fillExportSteps() { void Controller::exportNext() { if (!++_stepIndex) { - _writer->start(_settings.path); + _writer->start(_settings); } if (_stepIndex >= _steps.size()) { _writer->finish(); diff --git a/Telegram/SourceFiles/export/export_pch.h b/Telegram/SourceFiles/export/export_pch.h index 3f86482e01afcc..f5a4057528abf9 100644 --- a/Telegram/SourceFiles/export/export_pch.h +++ b/Telegram/SourceFiles/export/export_pch.h @@ -11,8 +11,14 @@ For license and copyright information please follow this link: #include #include #include +#include +#include +#include +#include + #include #include + #include #include #include diff --git a/Telegram/SourceFiles/export/export_settings.h b/Telegram/SourceFiles/export/export_settings.h index 0913821b43857d..ab9bf16187209f 100644 --- a/Telegram/SourceFiles/export/export_settings.h +++ b/Telegram/SourceFiles/export/export_settings.h @@ -50,6 +50,7 @@ struct Settings { friend inline constexpr auto is_flag_type(Type) { return true; }; QString path; + QString internalLinksDomain; Output::Format format = Output::Format(); Types types = DefaultTypes(); @@ -62,7 +63,8 @@ struct Settings { | Type::Userpics | Type::Contacts | Type::Sessions - | Type::PersonalChats; + | Type::PersonalChats + | Type::PrivateGroups; } static inline Types DefaultFullChats() { diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.h b/Telegram/SourceFiles/export/output/export_output_abstract.h index 9788081d618326..3d0bfd65b46c49 100644 --- a/Telegram/SourceFiles/export/output/export_output_abstract.h +++ b/Telegram/SourceFiles/export/output/export_output_abstract.h @@ -21,6 +21,8 @@ struct DialogInfo; struct MessagesSlice; } // namespace Data +struct Settings; + namespace Output { enum class Format { @@ -31,7 +33,7 @@ enum class Format { class AbstractWriter { public: - virtual bool start(const QString &folder) = 0; + virtual bool start(const Settings &settings) = 0; virtual bool writePersonal(const Data::PersonalInfo &data) = 0; diff --git a/Telegram/SourceFiles/export/output/export_output_file.cpp b/Telegram/SourceFiles/export/output/export_output_file.cpp index bd68b611f9a263..539c8ab275929b 100644 --- a/Telegram/SourceFiles/export/output/export_output_file.cpp +++ b/Telegram/SourceFiles/export/output/export_output_file.cpp @@ -9,6 +9,7 @@ For license and copyright information please follow this link: #include #include + #include namespace Export { diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index 03d883389590fb..c86c7701398240 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -8,6 +8,7 @@ For license and copyright information please follow this link: #include "export/output/export_output_text.h" #include "export/data/export_data_types.h" +#include "core/utils.h" #include @@ -83,12 +84,312 @@ Data::Utf8String FormatUsername(const Data::Utf8String &username) { return username.isEmpty() ? username : ('@' + username); } +QByteArray FormatFilePath(const Data::File &file) { + return file.relativePath.toUtf8(); +} + +QByteArray SerializeMessage( + const Data::Message &message, + const std::map &peers, + const QString &internalLinksDomain) { + using namespace Data; + + if (message.media.content.is()) { + return "Error! This message is not supported " + "by this version of Telegram Desktop. " + "Please update the application."; + } + + const auto peer = [&](PeerId peerId) -> const Peer& { + if (const auto i = peers.find(peerId); i != end(peers)) { + return i->second; + } + static auto empty = Peer{ User() }; + return empty; + }; + const auto user = [&](int32 userId) -> const User& { + if (const auto result = peer(UserPeerId(userId)).user()) { + return *result; + } + static auto empty = User(); + return empty; + }; + const auto chat = [&](int32 chatId) -> const Chat& { + if (const auto result = peer(ChatPeerId(chatId)).chat()) { + return *result; + } + static auto empty = Chat(); + return empty; + }; + + auto values = std::vector>{ + { "ID", NumberToString(message.id) }, + { "Date", FormatDateTime(message.date) }, + { "Edited", FormatDateTime(message.edited) }, + }; + const auto push = [&](const QByteArray &key, const QByteArray &value) { + if (!value.isEmpty()) { + values.emplace_back(key, value); + } + }; + const auto wrapPeerName = [&](PeerId peerId) { + const auto result = peer(peerId).name(); + return result.isEmpty() ? QByteArray("(unknown peer)") : result; + }; + const auto wrapUserName = [&](int32 userId) { + const auto result = user(userId).name(); + return result.isEmpty() ? QByteArray("(unknown user)") : result; + }; + const auto pushFrom = [&](const QByteArray &label = "From") { + if (message.fromId) { + push(label, wrapUserName(message.fromId)); + } + }; + const auto pushReplyToMsgId = [&]( + const QByteArray &label = "Reply to message") { + if (message.replyToMsgId) { + push(label, "ID-" + NumberToString(message.replyToMsgId)); + } + }; + const auto pushUserNames = [&]( + const std::vector &data, + const QByteArray &labelOne = "Member", + const QByteArray &labelMany = "Members") { + auto list = std::vector(); + for (const auto userId : data) { + list.push_back(wrapUserName(userId)); + } + if (list.size() == 1) { + push(labelOne, list[0]); + } else if (!list.empty()) { + push(labelMany, JoinList(", ", list)); + } + }; + const auto pushActor = [&] { + pushFrom("Actor"); + }; + const auto pushAction = [&](const QByteArray &action) { + push("Action", action); + }; + message.action.content.match([&](const ActionChatCreate &data) { + pushActor(); + pushAction("Create group"); + push("Title", data.title); + pushUserNames(data.userIds); + }, [&](const ActionChatEditTitle &data) { + pushActor(); + pushAction("Edit group title"); + push("New title", data.title); + }, [&](const ActionChatEditPhoto &data) { + pushActor(); + pushAction("Edit group photo"); + push("Photo", FormatFilePath(data.photo.image.file)); + }, [&](const ActionChatDeletePhoto &data) { + pushActor(); + pushAction("Delete group photo"); + }, [&](const ActionChatAddUser &data) { + pushActor(); + pushAction("Invite members"); + pushUserNames(data.userIds); + }, [&](const ActionChatDeleteUser &data) { + pushActor(); + pushAction("Remove members"); + push("Member", wrapUserName(data.userId)); + }, [&](const ActionChatJoinedByLink &data) { + pushActor(); + pushAction("Join group by link"); + push("Inviter", wrapUserName(data.inviterId)); + }, [&](const ActionChannelCreate &data) { + pushActor(); + pushAction("Create channel"); + push("Title", data.title); + }, [&](const ActionChatMigrateTo &data) { + pushActor(); + pushAction("Migrate this group to supergroup"); + }, [&](const ActionChannelMigrateFrom &data) { + pushActor(); + pushAction("Migrate this supergroup from group"); + push("Title", data.title); + }, [&](const ActionPinMessage &data) { + pushActor(); + pushAction("Pin message"); + pushReplyToMsgId("Message"); + }, [&](const ActionHistoryClear &data) { + pushActor(); + pushAction("Clear history"); + }, [&](const ActionGameScore &data) { + pushActor(); + pushAction("Score in a game"); + pushReplyToMsgId("Game message"); + push("Score", NumberToString(data.score)); + }, [&](const ActionPaymentSent &data) { + pushAction("Send payment"); + push( + "Amount", + Data::FormatMoneyAmount(data.amount, data.currency)); + pushReplyToMsgId("Invoice message"); + }, [&](const ActionPhoneCall &data) { + pushActor(); + pushAction("Phone call"); + if (data.duration) { + push("Duration", NumberToString(data.duration) + " sec."); + } + using Reason = ActionPhoneCall::DiscardReason; + push("Discard reason", [&] { + switch (data.discardReason) { + case Reason::Busy: return "Busy"; + case Reason::Disconnect: return "Disconnect"; + case Reason::Hangup: return "Hangup"; + case Reason::Missed: return "Missed"; + } + return ""; + }()); + }, [&](const ActionScreenshotTaken &data) { + pushActor(); + pushAction("Take screenshot"); + }, [&](const ActionCustomAction &data) { + pushActor(); + push("Information", data.message); + }, [&](const ActionBotAllowed &data) { + pushAction("Allow sending messages"); + push("Reason", "Login on \"" + data.domain + "\""); + }, [&](const ActionSecureValuesSent &data) { + pushAction("Send Telegram Passport values"); + auto list = std::vector(); + for (const auto type : data.types) { + list.push_back([&] { + using Type = ActionSecureValuesSent::Type; + switch (type) { + case Type::PersonalDetails: return "Personal details"; + case Type::Passport: return "Passport"; + case Type::DriverLicense: return "Driver license"; + case Type::IdentityCard: return "Identity card"; + case Type::InternalPassport: return "Internal passport"; + case Type::Address: return "Address information"; + case Type::UtilityBill: return "Utility bill"; + case Type::BankStatement: return "Bank statement"; + case Type::RentalAgreement: return "Rental agreement"; + case Type::PassportRegistration: + return "Passport registration"; + case Type::TemporaryRegistration: + return "Temporary registration"; + case Type::Phone: return "Phone number"; + case Type::Email: return "Email"; + } + return ""; + }()); + } + if (list.size() == 1) { + push("Value", list[0]); + } else if (!list.empty()) { + push("Values", JoinList(", ", list)); + } + }, [](const base::none_type &) {}); + + if (!message.action.content) { + pushFrom(); + push("Author", message.signature); + if (message.forwardedFromId) { + push("Forwarded from", wrapPeerName(message.forwardedFromId)); + } + pushReplyToMsgId(); + if (message.viaBotId) { + push("Via", user(message.viaBotId).username); + } + } + + message.media.content.match([&](const Photo &photo) { + }, [&](const Document &data) { + const auto pushPath = [&](const QByteArray &label) { + push(label, FormatFilePath(data.file)); + }; + if (!data.stickerEmoji.isEmpty()) { + pushPath("Sticker"); + push("Emoji", data.stickerEmoji); + } else if (data.isVideoMessage) { + pushPath("Video message"); + } else if (data.isVoiceMessage) { + pushPath("Voice message"); + } else if (data.isAnimated) { + pushPath("Animation"); + } else if (data.isVideoFile) { + pushPath("Video file"); + } else if (data.isAudioFile) { + pushPath("Audio file"); + push("Performer", data.songPerformer); + push("Title", data.songTitle); + } else { + pushPath("File"); + } + if (data.stickerEmoji.isEmpty()) { + push("Mime type", data.mime); + } + if (data.duration) { + push("Duration", NumberToString(data.duration) + " sec."); + } + if (data.width && data.height) { + push("Width", NumberToString(data.width)); + push("Height", NumberToString(data.height)); + } + }, [&](const ContactInfo &data) { + push("Contact information", SerializeKeyValue({ + { "First name", data.firstName }, + { "Last name", data.lastName }, + { "Phone number", FormatPhoneNumber(data.phoneNumber) }, + })); + }, [&](const GeoPoint &data) { + push("Location", data.valid ? SerializeKeyValue({ + { "Latitude", NumberToString(data.latitude) }, + { "Longitude", NumberToString(data.longitude) }, + }) : QByteArray("(empty value)")); + }, [&](const Venue &data) { + push("Place name", data.title); + push("Address", data.address); + if (data.point.valid) { + push("Location", SerializeKeyValue({ + { "Latitude", NumberToString(data.point.latitude) }, + { "Longitude", NumberToString(data.point.longitude) }, + })); + } + }, [&](const Game &data) { + push("Game", data.title); + push("Description", data.description); + if (data.botId != 0 && !data.shortName.isEmpty()) { + const auto bot = user(data.botId); + if (bot.isBot && !bot.username.isEmpty()) { + push("Link", internalLinksDomain.toUtf8() + + bot.username + + "?game=" + + data.shortName); + } + } + }, [&](const Invoice &data) { + push("Invoice", SerializeKeyValue({ + { "Title", data.title }, + { "Description", data.description }, + { + "Amount", + Data::FormatMoneyAmount(data.amount, data.currency) + }, + { "Receipt message", (data.receiptMsgId + ? "ID-" + NumberToString(data.receiptMsgId) + : QByteArray()) } + })); + }, [](const UnsupportedMedia &data) { + Unexpected("Unsupported message."); + }, [](const base::none_type &) {}); + + push("Text", message.text); + + return SerializeKeyValue(std::move(values)); +} + } // namespace -bool TextWriter::start(const QString &folder) { - Expects(folder.endsWith('/')); +bool TextWriter::start(const Settings &settings) { + Expects(settings.path.endsWith('/')); - _folder = folder; + _settings = base::duplicate(settings); _result = fileWithRelativePath(mainFileRelativePath()); return true; } @@ -96,13 +397,14 @@ bool TextWriter::start(const QString &folder) { bool TextWriter::writePersonal(const Data::PersonalInfo &data) { Expects(_result != nullptr); + const auto &info = data.user.info; const auto serialized = "Personal information" + kLineBreak + kLineBreak + SerializeKeyValue({ - { "First name", data.user.firstName }, - { "Last name", data.user.lastName }, - { "Phone number", Data::FormatPhoneNumber(data.user.phoneNumber) }, + { "First name", info.firstName }, + { "Last name", info.lastName }, + { "Phone number", Data::FormatPhoneNumber(info.phoneNumber) }, { "Username", FormatUsername(data.user.username) }, { "Bio", data.bio }, }) @@ -158,7 +460,7 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) { list.reserve(data.list.size()); for (const auto &index : Data::SortedContactsIndices(data)) { const auto &contact = data.list[index]; - if (!contact.id) { + if (!contact.userId) { list.push_back("(user unavailable)" + kLineBreak); } else if (contact.firstName.isEmpty() && contact.lastName.isEmpty() @@ -300,11 +602,10 @@ bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) { list.reserve(data.list.size()); auto index = 0; for (const auto &message : data.list) { - list.push_back(SerializeKeyValue({ - { "ID", Data::NumberToString(message.id) }, - { "Date", Data::FormatDateTime(message.date) }, - { "Text", message.text } - })); + list.push_back(SerializeMessage( + message, + data.peers, + _settings.internalLinksDomain)); } const auto full = _dialog->empty() ? JoinList(kLineBreak, list) @@ -336,12 +637,12 @@ QString TextWriter::mainFileRelativePath() const { } QString TextWriter::pathWithRelativePath(const QString &path) const { - return _folder + path; + return _settings.path + path; } std::unique_ptr TextWriter::fileWithRelativePath( const QString &path) const { - return std::make_unique(_folder + path); + return std::make_unique(pathWithRelativePath(path)); } } // namespace Output diff --git a/Telegram/SourceFiles/export/output/export_output_text.h b/Telegram/SourceFiles/export/output/export_output_text.h index 16170d559ae1c3..714684386c8b25 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.h +++ b/Telegram/SourceFiles/export/output/export_output_text.h @@ -9,13 +9,14 @@ For license and copyright information please follow this link: #include "export/output/export_output_abstract.h" #include "export/output/export_output_file.h" +#include "export/export_settings.h" namespace Export { namespace Output { class TextWriter : public AbstractWriter { public: - bool start(const QString &folder) override; + bool start(const Settings &settings) override; bool writePersonal(const Data::PersonalInfo &data) override; @@ -42,7 +43,7 @@ class TextWriter : public AbstractWriter { QString pathWithRelativePath(const QString &path) const; std::unique_ptr fileWithRelativePath(const QString &path) const; - QString _folder; + Settings _settings; std::unique_ptr _result; int _userpicsCount = 0; diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index 1c8b9d87af897f..b43b677bfde8ab 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -29,6 +29,8 @@ SettingsWidget::SettingsWidget(QWidget *parent) } else { _data.path = Global::DownloadPath(); } + _data.internalLinksDomain = Global::InternalLinksDomain(); + setupContent(); } diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index c21dadcfde0f8a..942fcf9b35cfa7 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -97,6 +97,61 @@ std::unique_ptr CreateAttach( } // namespace +QString FillAmountAndCurrency(uint64 amount, const QString ¤cy) { + static const auto ShortCurrencyNames = QMap { + { qsl("USD"), QString::fromUtf8("\x24") }, + { qsl("GBP"), QString::fromUtf8("\xC2\xA3") }, + { qsl("EUR"), QString::fromUtf8("\xE2\x82\xAC") }, + { qsl("JPY"), QString::fromUtf8("\xC2\xA5") }, + }; + static const auto Denominators = QMap { + { qsl("CLF"), 10000 }, + { qsl("BHD"), 1000 }, + { qsl("IQD"), 1000 }, + { qsl("JOD"), 1000 }, + { qsl("KWD"), 1000 }, + { qsl("LYD"), 1000 }, + { qsl("OMR"), 1000 }, + { qsl("TND"), 1000 }, + { qsl("BIF"), 1 }, + { qsl("BYR"), 1 }, + { qsl("CLP"), 1 }, + { qsl("CVE"), 1 }, + { qsl("DJF"), 1 }, + { qsl("GNF"), 1 }, + { qsl("ISK"), 1 }, + { qsl("JPY"), 1 }, + { qsl("KMF"), 1 }, + { qsl("KRW"), 1 }, + { qsl("MGA"), 1 }, + { qsl("PYG"), 1 }, + { qsl("RWF"), 1 }, + { qsl("UGX"), 1 }, + { qsl("UYI"), 1 }, + { qsl("VND"), 1 }, + { qsl("VUV"), 1 }, + { qsl("XAF"), 1 }, + { qsl("XOF"), 1 }, + { qsl("XPF"), 1 }, + { qsl("MRO"), 10 }, + }; + const auto currencyText = ShortCurrencyNames.value(currency, currency); + const auto denominator = Denominators.value(currency, 100); + const auto currencyValue = amount / float64(denominator); + const auto digits = [&] { + auto result = 0; + for (auto test = 1; test < denominator; test *= 10) { + ++result; + } + return result; + }(); + return QLocale::system().toCurrencyString(currencyValue, currencyText); + //auto amountBucks = amount / 100; + //auto amountCents = amount % 100; + //auto amountText = qsl("%1,%2").arg(amountBucks).arg(amountCents, 2, 10, QChar('0')); + //return currencyText + amountText; +} + void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { if (p == _savel || p == _cancell) { if (active && !dataLoaded()) { @@ -4259,63 +4314,6 @@ HistoryInvoice::HistoryInvoice( fillFromData(invoice); } -QString HistoryInvoice::fillAmountAndCurrency( - uint64 amount, - const QString ¤cy) { - static const auto ShortCurrencyNames = QMap { - { qsl("USD"), QString::fromUtf8("\x24") }, - { qsl("GBP"), QString::fromUtf8("\xC2\xA3") }, - { qsl("EUR"), QString::fromUtf8("\xE2\x82\xAC") }, - { qsl("JPY"), QString::fromUtf8("\xC2\xA5") }, - }; - static const auto Denominators = QMap { - { qsl("CLF"), 10000 }, - { qsl("BHD"), 1000 }, - { qsl("IQD"), 1000 }, - { qsl("JOD"), 1000 }, - { qsl("KWD"), 1000 }, - { qsl("LYD"), 1000 }, - { qsl("OMR"), 1000 }, - { qsl("TND"), 1000 }, - { qsl("BIF"), 1 }, - { qsl("BYR"), 1 }, - { qsl("CLP"), 1 }, - { qsl("CVE"), 1 }, - { qsl("DJF"), 1 }, - { qsl("GNF"), 1 }, - { qsl("ISK"), 1 }, - { qsl("JPY"), 1 }, - { qsl("KMF"), 1 }, - { qsl("KRW"), 1 }, - { qsl("MGA"), 1 }, - { qsl("PYG"), 1 }, - { qsl("RWF"), 1 }, - { qsl("UGX"), 1 }, - { qsl("UYI"), 1 }, - { qsl("VND"), 1 }, - { qsl("VUV"), 1 }, - { qsl("XAF"), 1 }, - { qsl("XOF"), 1 }, - { qsl("XPF"), 1 }, - { qsl("MRO"), 10 }, - }; - const auto currencyText = ShortCurrencyNames.value(currency, currency); - const auto denominator = Denominators.value(currency, 100); - const auto currencyValue = amount / float64(denominator); - const auto digits = [&] { - auto result = 0; - for (auto test = 1; test < denominator; test *= 10) { - ++result; - } - return result; - }(); - return QLocale::system().toCurrencyString(currencyValue, currencyText); - //auto amountBucks = amount / 100; - //auto amountCents = amount % 100; - //auto amountText = qsl("%1,%2").arg(amountBucks).arg(amountCents, 2, 10, QChar('0')); - //return currencyText + amountText; -} - void HistoryInvoice::fillFromData(not_null invoice) { // init attach auto labelText = [&] { @@ -4330,7 +4328,7 @@ void HistoryInvoice::fillFromData(not_null invoice) { return lang(lng_payments_invoice_label); }; auto statusText = TextWithEntities { - fillAmountAndCurrency(invoice->amount, invoice->currency), + FillAmountAndCurrency(invoice->amount, invoice->currency), EntitiesInText() }; statusText.entities.push_back(EntityInText(EntityInTextBold, 0, statusText.text.size())); diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 6d8b7f7e27b3c0..e7dbda14ebec0e 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -41,6 +41,8 @@ namespace Ui { class EmptyUserpic; } // namespace Ui +QString FillAmountAndCurrency(uint64 amount, const QString ¤cy); + class HistoryFileMedia : public HistoryMedia { public: using HistoryMedia::HistoryMedia; @@ -850,7 +852,6 @@ class HistoryInvoice : public HistoryMedia { QString getTitle() const { return _title.originalText(); } - static QString fillAmountAndCurrency(uint64 amount, const QString ¤cy); bool hideMessageText() const override { return false; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 475f7bebcd319a..6918757237312e 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -608,7 +608,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { UpdateComponents(HistoryServicePayment::Bit()); auto amount = message.vaction.c_messageActionPaymentSent().vtotal_amount.v; auto currency = qs(message.vaction.c_messageActionPaymentSent().vcurrency); - Get()->amount = HistoryInvoice::fillAmountAndCurrency(amount, currency); + Get()->amount = FillAmountAndCurrency(amount, currency); } if (message.has_reply_to_msg_id()) { if (message.vaction.type() == mtpc_messageActionPinMessage) {