mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Added ability to create invite links with subscription.
This commit is contained in:
parent
44c1109798
commit
8158c52e82
10 changed files with 306 additions and 23 deletions
|
@ -1474,6 +1474,8 @@ PRIVATE
|
|||
support/support_preload.h
|
||||
support/support_templates.cpp
|
||||
support/support_templates.h
|
||||
ui/boxes/edit_invite_link_session.cpp
|
||||
ui/boxes/edit_invite_link_session.h
|
||||
ui/chat/attach/attach_item_single_file_preview.cpp
|
||||
ui/chat/attach/attach_item_single_file_preview.h
|
||||
ui/chat/attach/attach_item_single_media_preview.cpp
|
||||
|
|
|
@ -2021,6 +2021,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_group_invite_about_no_approve" = "New users will be able to join the group without being approved by the admins.";
|
||||
"lng_group_invite_about_approve_channel" = "New users will be able to join the channel only after having been approved by the admins.";
|
||||
"lng_group_invite_about_no_approve_channel" = "New users will be able to join the channel without being approved by the admins.";
|
||||
"lng_group_invite_subscription" = "Require Monthly Fee";
|
||||
"lng_group_invite_subscription_ph" = "Stars Amount per month";
|
||||
"lng_group_invite_subscription_price" = "~{cost} / month";
|
||||
"lng_group_invite_subscription_toast" = "Sorry, you cannot change the number of Stars for an already created invite link.";
|
||||
"lng_group_invite_subscription_about" = "Charge a subscription fee from people joining your channel via this link. {link}";
|
||||
"lng_group_invite_subscription_about_link" = "Learn more {emoji}";
|
||||
"lng_group_invite_subscription_about_url" = "https://telegram.org/tos/stars";
|
||||
|
||||
"lng_group_request_to_join" = "Request to Join";
|
||||
"lng_group_request_about" = "This group accepts new members only after they are approved by its admins.";
|
||||
|
|
|
@ -737,6 +737,12 @@ auto InviteLinks::parse(
|
|||
return std::optional<Link>(Link{
|
||||
.link = qs(data.vlink()),
|
||||
.label = qs(data.vtitle().value_or_empty()),
|
||||
.subscription = data.vsubscription_pricing()
|
||||
? Data::PeerSubscription{
|
||||
data.vsubscription_pricing()->data().vamount().v,
|
||||
data.vsubscription_pricing()->data().vperiod().v,
|
||||
}
|
||||
: Data::PeerSubscription(),
|
||||
.admin = peer->session().data().user(data.vadmin_id()),
|
||||
.date = data.vdate().v,
|
||||
.startDate = data.vstart_date().value_or_empty(),
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Api {
|
|||
struct InviteLink {
|
||||
QString link;
|
||||
QString label;
|
||||
Data::PeerSubscription subscription;
|
||||
not_null<UserData*> admin;
|
||||
TimeId date = 0;
|
||||
TimeId startDate = 0;
|
||||
|
|
|
@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/boxes/edit_invite_link.h"
|
||||
#include "ui/boxes/edit_invite_link_session.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "boxes/share_box.h"
|
||||
|
@ -1251,6 +1252,8 @@ object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
|||
object_ptr<Ui::BoxContent> EditLinkBox(
|
||||
not_null<PeerData*> peer,
|
||||
const Api::InviteLink &data) {
|
||||
constexpr auto kPeriod = 3600 * 24 * 30;
|
||||
constexpr auto kTestModePeriod = 300;
|
||||
const auto creating = data.link.isEmpty();
|
||||
const auto box = std::make_shared<QPointer<Ui::GenericBox>>();
|
||||
using Fields = Ui::InviteLinkFields;
|
||||
|
@ -1266,6 +1269,9 @@ object_ptr<Ui::BoxContent> EditLinkBox(
|
|||
};
|
||||
if (creating) {
|
||||
Assert(data.admin->isSelf());
|
||||
const auto period = peer->session().isTestMode()
|
||||
? kTestModePeriod
|
||||
: kPeriod;
|
||||
peer->session().api().inviteLinks().create({
|
||||
peer,
|
||||
finish,
|
||||
|
@ -1273,6 +1279,7 @@ object_ptr<Ui::BoxContent> EditLinkBox(
|
|||
result.expireDate,
|
||||
result.usageLimit,
|
||||
result.requestApproval,
|
||||
{ uint64(result.subscriptionCredits), period },
|
||||
});
|
||||
} else {
|
||||
peer->session().api().inviteLinks().edit(
|
||||
|
@ -1288,26 +1295,31 @@ object_ptr<Ui::BoxContent> EditLinkBox(
|
|||
};
|
||||
const auto isGroup = !peer->isBroadcast();
|
||||
const auto isPublic = peer->isChannel() && peer->asChannel()->isPublic();
|
||||
if (creating) {
|
||||
auto object = Box(Ui::CreateInviteLinkBox, isGroup, isPublic, done);
|
||||
*box = Ui::MakeWeak(object.data());
|
||||
return object;
|
||||
} else {
|
||||
auto object = Box(
|
||||
Ui::EditInviteLinkBox,
|
||||
Fields{
|
||||
.link = data.link,
|
||||
.label = data.label,
|
||||
.expireDate = data.expireDate,
|
||||
.usageLimit = data.usageLimit,
|
||||
.requestApproval = data.requestApproval,
|
||||
.isGroup = isGroup,
|
||||
.isPublic = isPublic,
|
||||
},
|
||||
done);
|
||||
*box = Ui::MakeWeak(object.data());
|
||||
return object;
|
||||
}
|
||||
auto object = Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto fill = [=] {
|
||||
return Ui::FillCreateInviteLinkSubscriptionToggle(box, peer);
|
||||
};
|
||||
if (creating) {
|
||||
Ui::CreateInviteLinkBox(box, fill, isGroup, isPublic, done);
|
||||
} else {
|
||||
Ui::EditInviteLinkBox(
|
||||
box,
|
||||
fill,
|
||||
Fields{
|
||||
.link = data.link,
|
||||
.label = data.label,
|
||||
.expireDate = data.expireDate,
|
||||
.usageLimit = data.usageLimit,
|
||||
.subscriptionCredits = data.subscription.period,
|
||||
.requestApproval = data.requestApproval,
|
||||
.isGroup = isGroup,
|
||||
.isPublic = isPublic,
|
||||
},
|
||||
done);
|
||||
}
|
||||
});
|
||||
*box = Ui::MakeWeak(object.data());
|
||||
return object;
|
||||
}
|
||||
|
||||
object_ptr<Ui::BoxContent> RevokeLinkBox(
|
||||
|
|
|
@ -992,6 +992,24 @@ inviteLinkQrSkip: 24px;
|
|||
inviteLinkQrMargin: margins(0px, 0px, 0px, 13px);
|
||||
inviteLinkQrValuePadding: margins(22px, 0px, 22px, 12px);
|
||||
|
||||
inviteLinkCreditsField: InputField(defaultInputField) {
|
||||
textMargins: margins(23px, 10px, 0px, 0px);
|
||||
textAlign: align(left);
|
||||
textFg: historyComposeAreaFg;
|
||||
textBg: historyComposeAreaBg;
|
||||
heightMin: 36px;
|
||||
heightMax: 36px;
|
||||
placeholderFg: placeholderFg;
|
||||
placeholderFgActive: placeholderFgActive;
|
||||
placeholderFgError: placeholderFgActive;
|
||||
placeholderMargins: margins(0px, 0px, 2px, 0px);
|
||||
placeholderAlign: align(left);
|
||||
placeholderScale: 0.;
|
||||
placeholderFont: normalFont;
|
||||
placeholderShift: -50px;
|
||||
duration: 100;
|
||||
}
|
||||
|
||||
usernamesReorderIcon: icon {{ "stickers_reorder", dialogsMenuIconFg }};
|
||||
|
||||
infoAboutGigagroup: FlatLabel(defaultFlatLabel) {
|
||||
|
|
|
@ -11,9 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/choose_date_time.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/fields/number_input.h"
|
||||
#include "ui/effects/credits_graphics.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
@ -44,6 +47,7 @@ constexpr auto kMaxLabelLength = 32;
|
|||
|
||||
void EditInviteLinkBox(
|
||||
not_null<GenericBox*> box,
|
||||
Fn<InviteLinkSubscriptionToggle()> fillSubscription,
|
||||
const InviteLinkFields &data,
|
||||
Fn<void(InviteLinkFields)> done) {
|
||||
using namespace rpl::mappers;
|
||||
|
@ -95,14 +99,17 @@ void EditInviteLinkBox(
|
|||
int expireValue = 0;
|
||||
int usageValue = 0;
|
||||
rpl::variable<bool> requestApproval = false;
|
||||
rpl::variable<bool> subscription = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>(State{
|
||||
.expireValue = expire,
|
||||
.usageValue = usage,
|
||||
.requestApproval = (data.requestApproval && !isPublic),
|
||||
.subscription = false,
|
||||
});
|
||||
|
||||
const auto requestApproval = isPublic
|
||||
const auto subscriptionLocked = data.subscriptionCredits > 0;
|
||||
const auto requestApproval = (isPublic || subscriptionLocked)
|
||||
? nullptr
|
||||
: container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
|
@ -111,8 +118,11 @@ void EditInviteLinkBox(
|
|||
st::settingsButtonNoIcon),
|
||||
style::margins{ 0, 0, 0, st::defaultVerticalListSkip });
|
||||
if (requestApproval) {
|
||||
requestApproval->toggleOn(state->requestApproval.value());
|
||||
state->requestApproval = requestApproval->toggledValue();
|
||||
requestApproval->toggleOn(state->requestApproval.value(), true);
|
||||
requestApproval->setClickedCallback([=] {
|
||||
state->requestApproval.force_assign(!requestApproval->toggled());
|
||||
state->subscription.force_assign(false);
|
||||
});
|
||||
addDivider(container, rpl::conditional(
|
||||
state->requestApproval.value(),
|
||||
(isGroup
|
||||
|
@ -122,6 +132,30 @@ void EditInviteLinkBox(
|
|||
? tr::lng_group_invite_about_no_approve()
|
||||
: tr::lng_group_invite_about_no_approve_channel())));
|
||||
}
|
||||
auto credits = (Ui::NumberInput*)(nullptr);
|
||||
if (!isPublic && fillSubscription) {
|
||||
Ui::AddSkip(container);
|
||||
const auto &[subscription, input] = fillSubscription();
|
||||
credits = input.get();
|
||||
subscription->toggleOn(state->subscription.value(), true);
|
||||
if (subscriptionLocked) {
|
||||
input->setText(QString::number(data.subscriptionCredits));
|
||||
input->setReadOnly(true);
|
||||
state->subscription.force_assign(true);
|
||||
state->requestApproval.force_assign(false);
|
||||
subscription->setToggleLocked(true);
|
||||
subscription->finishAnimating();
|
||||
}
|
||||
subscription->setClickedCallback([=, show = box->uiShow()] {
|
||||
if (subscriptionLocked) {
|
||||
show->showToast(
|
||||
tr::lng_group_invite_subscription_toast(tr::now));
|
||||
return;
|
||||
}
|
||||
state->subscription.force_assign(!subscription->toggled());
|
||||
state->requestApproval.force_assign(false);
|
||||
});
|
||||
}
|
||||
|
||||
const auto labelField = container->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
|
@ -327,6 +361,9 @@ void EditInviteLinkBox(
|
|||
.label = label,
|
||||
.expireDate = expireDate,
|
||||
.usageLimit = usageLimit,
|
||||
.subscriptionCredits = credits
|
||||
? credits->getLastText().toInt()
|
||||
: 0,
|
||||
.requestApproval = state->requestApproval.current(),
|
||||
.isGroup = isGroup,
|
||||
.isPublic = isPublic,
|
||||
|
@ -337,11 +374,13 @@ void EditInviteLinkBox(
|
|||
|
||||
void CreateInviteLinkBox(
|
||||
not_null<GenericBox*> box,
|
||||
Fn<InviteLinkSubscriptionToggle()> fillSubscription,
|
||||
bool isGroup,
|
||||
bool isPublic,
|
||||
Fn<void(InviteLinkFields)> done) {
|
||||
EditInviteLinkBox(
|
||||
box,
|
||||
std::move(fillSubscription),
|
||||
InviteLinkFields{ .isGroup = isGroup, .isPublic = isPublic },
|
||||
std::move(done));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Ui {
|
||||
|
||||
class GenericBox;
|
||||
class NumberInput;
|
||||
class SettingsButton;
|
||||
|
||||
struct InviteLinkFields {
|
||||
|
@ -17,18 +18,26 @@ struct InviteLinkFields {
|
|||
QString label;
|
||||
TimeId expireDate = 0;
|
||||
int usageLimit = 0;
|
||||
int subscriptionCredits = 0;
|
||||
bool requestApproval = false;
|
||||
bool isGroup = false;
|
||||
bool isPublic = false;
|
||||
};
|
||||
|
||||
struct InviteLinkSubscriptionToggle final {
|
||||
not_null<Ui::SettingsButton*> button;
|
||||
not_null<Ui::NumberInput*> amount;
|
||||
};
|
||||
|
||||
void EditInviteLinkBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Fn<InviteLinkSubscriptionToggle()> fillSubscription,
|
||||
const InviteLinkFields &data,
|
||||
Fn<void(InviteLinkFields)> done);
|
||||
|
||||
void CreateInviteLinkBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Fn<InviteLinkSubscriptionToggle()> fillSubscription,
|
||||
bool isGroup,
|
||||
bool isPublic,
|
||||
Fn<void(InviteLinkFields)> done);
|
||||
|
|
166
Telegram/SourceFiles/ui/boxes/edit_invite_link_session.cpp
Normal file
166
Telegram/SourceFiles/ui/boxes/edit_invite_link_session.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "ui/boxes/edit_invite_link_session.h"
|
||||
|
||||
#include "api/api_credits.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/edit_invite_link.h" // InviteLinkSubscriptionToggle
|
||||
#include "ui/effects/credits_graphics.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/fields/number_input.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
InviteLinkSubscriptionToggle FillCreateInviteLinkSubscriptionToggle(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer) {
|
||||
struct State final {
|
||||
float64 usdRate = 0;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
const auto currency = u"USD"_q;
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
const auto toggle = container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
container,
|
||||
tr::lng_group_invite_subscription(),
|
||||
st::settingsButtonNoIconLocked),
|
||||
style::margins{ 0, 0, 0, st::defaultVerticalListSkip });
|
||||
|
||||
const auto maxCredits = peer->session().appConfig().get<int>(
|
||||
u"stars_subscription_amount_max"_q,
|
||||
2500);
|
||||
|
||||
const auto &st = st::inviteLinkCreditsField;
|
||||
const auto skip = st.textMargins.top() / 2;
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
box->setShowFinishedCallback([=] {
|
||||
wrap->toggleOn(toggle->toggledValue());
|
||||
wrap->finishAnimating();
|
||||
});
|
||||
const auto inputContainer = wrap->entity()->add(
|
||||
CreateSkipWidget(container, st.heightMin - skip));
|
||||
const auto input = CreateChild<NumberInput>(
|
||||
inputContainer,
|
||||
st,
|
||||
tr::lng_group_invite_subscription_ph(),
|
||||
QString(),
|
||||
maxCredits);
|
||||
wrap->toggledValue() | rpl::start_with_next([=](bool shown) {
|
||||
if (shown) {
|
||||
input->setFocus();
|
||||
}
|
||||
}, input->lifetime());
|
||||
const auto icon = CreateSingleStarWidget(
|
||||
inputContainer,
|
||||
st.style.font->height);
|
||||
const auto priceOverlay = Ui::CreateChild<Ui::RpWidget>(inputContainer);
|
||||
priceOverlay->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
inputContainer->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &size) {
|
||||
input->resize(
|
||||
size.width() - rect::m::sum::h(st::boxRowPadding),
|
||||
st.heightMin);
|
||||
input->moveToLeft(st::boxRowPadding.left(), -skip);
|
||||
icon->moveToLeft(
|
||||
st::boxRowPadding.left(),
|
||||
input->pos().y() + st.textMargins.top());
|
||||
priceOverlay->resize(size);
|
||||
}, input->lifetime());
|
||||
ToggleChildrenVisibility(inputContainer, true);
|
||||
QObject::connect(input, &Ui::MaskedInputField::changed, [=] {
|
||||
priceOverlay->update();
|
||||
});
|
||||
priceOverlay->paintRequest(
|
||||
) | rpl::start_with_next([=, right = st::boxRowPadding.right()] {
|
||||
if (state->usdRate <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto amount = input->getLastText().toDouble();
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto text = tr::lng_group_invite_subscription_price(
|
||||
tr::now,
|
||||
lt_cost,
|
||||
Ui::FillAmountAndCurrency(amount * state->usdRate, currency));
|
||||
auto p = QPainter(priceOverlay);
|
||||
p.setFont(st.placeholderFont);
|
||||
p.setPen(st.placeholderFg);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
const auto textWidth = st.placeholderFont->width(text);
|
||||
const auto m = QMargins(0, skip, right, 0);
|
||||
p.drawText(priceOverlay->rect() - m, text, style::al_right);
|
||||
}, priceOverlay->lifetime());
|
||||
|
||||
{
|
||||
auto &lifetime = priceOverlay->lifetime();
|
||||
const auto api = lifetime.make_state<Api::CreditsEarnStatistics>(
|
||||
peer);
|
||||
api->request(
|
||||
) | rpl::start_with_done([=] {
|
||||
state->usdRate = api->data().usdRate;
|
||||
priceOverlay->update();
|
||||
}, priceOverlay->lifetime());
|
||||
}
|
||||
|
||||
const auto arrow = Ui::Text::SingleCustomEmoji(
|
||||
peer->owner().customEmojiManager().registerInternalEmoji(
|
||||
st::topicButtonArrow,
|
||||
st::channelEarnLearnArrowMargins,
|
||||
false));
|
||||
auto about = Ui::CreateLabelWithCustomEmoji(
|
||||
container,
|
||||
tr::lng_group_invite_subscription_about(
|
||||
lt_link,
|
||||
tr::lng_group_invite_subscription_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(arrow),
|
||||
Ui::Text::RichLangValue
|
||||
) | rpl::map([](TextWithEntities text) {
|
||||
return Ui::Text::Link(
|
||||
std::move(text),
|
||||
tr::lng_group_invite_subscription_about_url(tr::now));
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
{ .session = &peer->session() },
|
||||
st::boxDividerLabel);
|
||||
Ui::AddSkip(wrap->entity());
|
||||
Ui::AddSkip(wrap->entity());
|
||||
container->add(object_ptr<Ui::DividerLabel>(
|
||||
container,
|
||||
std::move(about),
|
||||
st::defaultBoxDividerLabelPadding,
|
||||
RectPart::Top | RectPart::Bottom));
|
||||
return { toggle, input };
|
||||
}
|
||||
|
||||
} // namespace Ui
|
23
Telegram/SourceFiles/ui/boxes/edit_invite_link_session.h
Normal file
23
Telegram/SourceFiles/ui/boxes/edit_invite_link_session.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class GenericBox;
|
||||
class SettingsButton;
|
||||
|
||||
struct InviteLinkSubscriptionToggle;
|
||||
|
||||
InviteLinkSubscriptionToggle FillCreateInviteLinkSubscriptionToggle(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
} // namespace Ui
|
Loading…
Add table
Reference in a new issue