Show toast on successfull payment.

This commit is contained in:
John Preston 2021-04-12 12:50:31 +04:00
parent a2d2c8a208
commit 35ff621b5b
6 changed files with 127 additions and 82 deletions

View File

@ -1878,6 +1878,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_invoice_label" = "Invoice";
"lng_payments_invoice_label_test" = "Test invoice";
"lng_payments_receipt_button" = "Receipt";
"lng_payments_success" = "You paid {amount} for {title}.";
"lng_payments_checkout_title" = "Checkout";
"lng_payments_receipt_title" = "Receipt";

View File

@ -42,6 +42,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "support/support_helper.h"
#include "ui/image/image.h"
#include "ui/text/text_options.h"
#include "ui/toasts/common_toasts.h"
#include "ui/text/text_utilities.h"
#include "payments/payments_checkout_process.h"
#include "core/crash_reports.h"
#include "core/application.h"
#include "base/unixtime.h"
@ -852,14 +855,19 @@ void History::applyMessageChanges(
void History::applyServiceChanges(
not_null<HistoryItem*> item,
const MTPDmessageService &data) {
auto &action = data.vaction();
switch (action.type()) {
case mtpc_messageActionChatAddUser: {
auto &d = action.c_messageActionChatAddUser();
const auto replyTo = data.vreply_to();
const auto setGroupCallFrom = [&](const auto &data) {
if (const auto channel = peer->asChannel()) {
channel->setGroupCall(data.vcall());
} else if (const auto chat = peer->asChat()) {
chat->setGroupCall(data.vcall());
}
};
data.vaction().match([&](const MTPDmessageActionChatAddUser &data) {
if (const auto megagroup = peer->asMegagroup()) {
const auto mgInfo = megagroup->mgInfo.get();
Assert(mgInfo != nullptr);
for (const auto &userId : d.vusers().v) {
for (const auto &userId : data.vusers().v) {
if (const auto user = owner().userLoaded(userId.v)) {
if (!base::contains(mgInfo->lastParticipants, user)) {
mgInfo->lastParticipants.push_front(user);
@ -870,21 +878,19 @@ void History::applyServiceChanges(
}
if (user->isBot()) {
peer->asChannel()->mgInfo->bots.insert(user);
if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) {
if (peer->asChannel()->mgInfo->botStatus != 0
&& peer->asChannel()->mgInfo->botStatus < 2) {
peer->asChannel()->mgInfo->botStatus = 2;
}
}
}
}
}
} break;
case mtpc_messageActionChatJoinedByLink: {
auto &d = action.c_messageActionChatJoinedByLink();
if (auto megagroup = peer->asMegagroup()) {
auto mgInfo = megagroup->mgInfo.get();
}, [&](const MTPDmessageActionChatJoinedByLink &data) {
if (const auto megagroup = peer->asMegagroup()) {
const auto mgInfo = megagroup->mgInfo.get();
Assert(mgInfo != nullptr);
if (auto user = item->from()->asUser()) {
if (const auto user = item->from()->asUser()) {
if (!base::contains(mgInfo->lastParticipants, user)) {
mgInfo->lastParticipants.push_front(user);
session().changes().peerUpdated(
@ -900,25 +906,20 @@ void History::applyServiceChanges(
}
}
}
} break;
case mtpc_messageActionChatDeletePhoto: {
}, [&](const MTPDmessageActionChatDeletePhoto &data) {
if (const auto chat = peer->asChat()) {
chat->setPhoto(MTP_chatPhotoEmpty());
}
} break;
case mtpc_messageActionChatDeleteUser: {
auto &d = action.c_messageActionChatDeleteUser();
auto uid = d.vuser_id().v;
}, [&](const MTPDmessageActionChatDeleteUser &data) {
const auto uid = data.vuser_id().v;
if (lastKeyboardFrom == peerFromUser(uid)) {
clearLastKeyboard();
}
if (auto megagroup = peer->asMegagroup()) {
if (auto user = owner().userLoaded(uid)) {
auto mgInfo = megagroup->mgInfo.get();
if (const auto megagroup = peer->asMegagroup()) {
if (const auto user = owner().userLoaded(uid)) {
const auto mgInfo = megagroup->mgInfo.get();
Assert(mgInfo != nullptr);
auto i = ranges::find(
const auto i = ranges::find(
mgInfo->lastParticipants,
user,
[](not_null<UserData*> user) { return user.get(); });
@ -930,15 +931,18 @@ void History::applyServiceChanges(
}
owner().removeMegagroupParticipant(megagroup, user);
if (megagroup->membersCount() > 1) {
megagroup->setMembersCount(megagroup->membersCount() - 1);
megagroup->setMembersCount(
megagroup->membersCount() - 1);
} else {
mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
mgInfo->lastParticipantsStatus
|= MegagroupInfo::LastParticipantsCountOutdated;
mgInfo->lastParticipantsCount = 0;
}
if (mgInfo->lastAdmins.contains(user)) {
mgInfo->lastAdmins.remove(user);
if (megagroup->adminsCount() > 1) {
megagroup->setAdminsCount(megagroup->adminsCount() - 1);
megagroup->setAdminsCount(
megagroup->adminsCount() - 1);
}
session().changes().peerUpdated(
peer,
@ -951,11 +955,8 @@ void History::applyServiceChanges(
}
Data::ChannelAdminChanges(megagroup).remove(uid);
}
} break;
case mtpc_messageActionChatEditPhoto: {
const auto &d = action.c_messageActionChatEditPhoto();
d.vphoto().match([&](const MTPDphoto &data) {
}, [&](const MTPDmessageActionChatEditPhoto &data) {
data.vphoto().match([&](const MTPDphoto &data) {
using Flag = MTPDchatPhoto::Flag;
const auto photo = owner().processPhoto(data);
photo->peer = peer;
@ -980,37 +981,27 @@ void History::applyServiceChanges(
channel->setPhoto(MTP_chatPhotoEmpty());
}
});
} break;
case mtpc_messageActionChatEditTitle: {
auto &d = action.c_messageActionChatEditTitle();
if (auto chat = peer->asChat()) {
chat->setName(qs(d.vtitle()));
}, [&](const MTPDmessageActionChatEditTitle &data) {
if (const auto chat = peer->asChat()) {
chat->setName(qs(data.vtitle()));
}
} break;
case mtpc_messageActionChatMigrateTo: {
}, [&](const MTPDmessageActionChatMigrateTo &data) {
if (const auto chat = peer->asChat()) {
chat->addFlags(MTPDchat::Flag::f_deactivated);
const auto &d = action.c_messageActionChatMigrateTo();
if (const auto channel = owner().channelLoaded(d.vchannel_id().v)) {
if (const auto channel = owner().channelLoaded(
data.vchannel_id().v)) {
Data::ApplyMigration(chat, channel);
}
}
} break;
case mtpc_messageActionChannelMigrateFrom: {
}, [&](const MTPDmessageActionChannelMigrateFrom &data) {
if (const auto channel = peer->asChannel()) {
channel->addFlags(MTPDchannel::Flag::f_megagroup);
const auto &d = action.c_messageActionChannelMigrateFrom();
if (const auto chat = owner().chatLoaded(d.vchat_id().v)) {
if (const auto chat = owner().chatLoaded(data.vchat_id().v)) {
Data::ApplyMigration(chat, channel);
}
}
} break;
case mtpc_messageActionPinMessage: {
if (const auto replyTo = data.vreply_to()) {
}, [&](const MTPDmessageActionPinMessage &data) {
if (replyTo) {
replyTo->match([&](const MTPDmessageReplyHeader &data) {
const auto id = data.vreply_to_msg_id().v;
if (item) {
@ -1023,20 +1014,34 @@ void History::applyServiceChanges(
}
});
}
} break;
case mtpc_messageActionGroupCall:
case mtpc_messageActionGroupCallScheduled: {
const auto &call = (action.type() == mtpc_messageActionGroupCall)
? action.c_messageActionGroupCall().vcall()
: action.c_messageActionGroupCallScheduled().vcall();
if (const auto channel = peer->asChannel()) {
channel->setGroupCall(call);
} else if (const auto chat = peer->asChat()) {
chat->setGroupCall(call);
}, [&](const MTPDmessageActionGroupCall &data) {
setGroupCallFrom(data);
}, [&](const MTPDmessageActionGroupCallScheduled &data) {
setGroupCallFrom(data);
}, [&](const MTPDmessageActionPaymentSent &data) {
if (const auto payment = item->Get<HistoryServicePayment>()) {
if (const auto message = payment->msg) {
if (const auto media = message->media()) {
if (const auto invoice = media->invoice()) {
using Payments::CheckoutProcess;
if (CheckoutProcess::TakePaymentStarted(message)) {
// Toast on a current active window.
Ui::ShowMultilineToast({
.text = tr::lng_payments_success(
tr::now,
lt_amount,
Ui::Text::Bold(payment->amount),
lt_title,
Ui::Text::Bold(invoice->title),
Ui::Text::WithEntities),
});
}
}
}
}
}
} break;
}
}, [](const auto &) {
});
}
void History::mainViewRemoved(

View File

@ -33,6 +33,7 @@ namespace {
struct SessionProcesses {
base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> map;
base::flat_set<FullMsgId> paymentStarted;
rpl::lifetime lifetime;
};
@ -93,6 +94,47 @@ void CheckoutProcess::Start(
j->second->requestActivate();
}
bool CheckoutProcess::TakePaymentStarted(
not_null<const HistoryItem*> item) {
const auto session = &item->history()->session();
const auto itemId = item->fullId();
const auto i = Processes.find(session);
if (i == end(Processes) || !i->second.paymentStarted.contains(itemId)) {
return false;
}
i->second.paymentStarted.erase(itemId);
const auto j = i->second.map.find(itemId);
if (j != end(i->second.map)) {
j->second->closeAndReactivate();
} else if (i->second.paymentStarted.empty() && i->second.map.empty()) {
Processes.erase(i);
}
return true;
}
void CheckoutProcess::RegisterPaymentStart(
not_null<CheckoutProcess*> process) {
const auto i = Processes.find(process->_session);
Assert(i != end(Processes));
for (const auto &[itemId, itemProcess] : i->second.map) {
if (itemProcess.get() == process) {
i->second.paymentStarted.emplace(itemId);
}
}
}
void CheckoutProcess::UnregisterPaymentStart(
not_null<CheckoutProcess*> process) {
const auto i = Processes.find(process->_session);
if (i != end(Processes)) {
for (const auto &[itemId, itemProcess] : i->second.map) {
if (itemProcess.get() == process) {
i->second.paymentStarted.emplace(itemId);
}
}
}
}
CheckoutProcess::CheckoutProcess(
not_null<PeerData*> peer,
MsgId itemId,
@ -164,9 +206,11 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
requestSetPassword();
}
}, [&](const TmpPasswordRequired &) {
UnregisterPaymentStart(this);
_submitState = SubmitState::Validated;
requestPassword();
}, [&](const BotTrustRequired &data) {
UnregisterPaymentStart(this);
_submitState = SubmitState::Validated;
_panel->showWarning(data.bot->name, data.provider->name);
if (const auto box = _enterPasswordBox.data()) {
@ -276,20 +320,7 @@ void CheckoutProcess::handleError(const Error &error) {
}
} break;
case Error::Type::SmartGlocal: {
//using Field = Ui::CardField;
//if (id == u"InvalidNumber"_q || id == u"IncorrectNumber"_q) {
// showCardError(Field::Number);
//} else if (id == u"InvalidCVC"_q || id == u"IncorrectCVC"_q) {
// showCardError(Field::Cvc);
//} else if (id == u"InvalidExpiryMonth"_q
// || id == u"InvalidExpiryYear"_q
// || id == u"ExpiredCard"_q) {
// showCardError(Field::ExpireDate);
//} else if (id == u"CardDeclined"_q) {
// showToast({ tr::lng_payments_card_declined(tr::now) });
//} else {
showToast({ "SmartGlocal Error: " + id });
//}
showToast({ "SmartGlocal Error: " + id });
} break;
case Error::Type::TmpPassword:
if (const auto box = _enterPasswordBox.data()) {
@ -303,6 +334,7 @@ void CheckoutProcess::handleError(const Error &error) {
box->closeBox();
}
if (_submitState == SubmitState::Finishing) {
UnregisterPaymentStart(this);
_submitState = SubmitState::Validated;
}
if (id == u"INVOICE_ALREADY_PAID"_q) {
@ -359,7 +391,7 @@ void CheckoutProcess::close() {
return;
}
i->second.map.erase(j);
if (i->second.map.empty()) {
if (i->second.map.empty() && i->second.paymentStarted.empty()) {
Processes.erase(i);
}
}
@ -388,6 +420,7 @@ void CheckoutProcess::panelSubmit() {
} else if (!method.newCredentials && !method.savedCredentials) {
editPaymentMethod();
} else {
RegisterPaymentStart(this);
_submitState = SubmitState::Finishing;
_form->submit();
}

View File

@ -52,6 +52,8 @@ public:
not_null<const HistoryItem*> item,
Mode mode,
Fn<void()> reactivate);
[[nodiscard]] static bool TakePaymentStarted(
not_null<const HistoryItem*> item);
CheckoutProcess(
not_null<PeerData*> peer,
@ -70,6 +72,9 @@ private:
};
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
static void RegisterPaymentStart(not_null<CheckoutProcess*> process);
static void UnregisterPaymentStart(not_null<CheckoutProcess*> process);
void setReactivateCallback(Fn<void()> reactivate);
void requestActivate();
void closeAndReactivate();

View File

@ -323,7 +323,7 @@ not_null<RpWidget*> EditCard::setupContent() {
if (_native.needCardholderName) {
_name = add({
.type = FieldType::CardNumber,
.type = FieldType::Text,
.placeholder = tr::lng_payments_card_holder(),
.validator = CardHolderNameValidator(),
});

View File

@ -278,6 +278,7 @@ bool Panel::showWebview(
if (!_webview && !createWebview()) {
return false;
}
_widget->destroyLayer();
_webview->navigate(url);
_widget->setBackAllowed(allowBack);
if (bottomText) {