mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 15:43:55 +02:00
Add a box to create / edit invite links.
This commit is contained in:
parent
01ecf0ca93
commit
1cce383d15
9 changed files with 392 additions and 86 deletions
|
@ -1166,9 +1166,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_channel_invite_private" = "This channel is private.\nPlease join it to continue viewing its content.";
|
"lng_channel_invite_private" = "This channel is private.\nPlease join it to continue viewing its content.";
|
||||||
|
|
||||||
"lng_group_invite_create" = "Create an invite link"; // #TODO links legacy
|
"lng_group_invite_create" = "Create an invite link"; // #TODO links legacy
|
||||||
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; // #TODO links legacy
|
|
||||||
"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link."; // #TODO links legacy
|
|
||||||
"lng_group_invite_create_new" = "Revoke invite link"; // #TODO links legacy
|
|
||||||
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
|
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
|
||||||
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
||||||
"lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already.";
|
"lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already.";
|
||||||
|
@ -1186,12 +1183,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_invite_manage_about" = "You can create additional invite links that have limited time or number of usages.";
|
"lng_group_invite_manage_about" = "You can create additional invite links that have limited time or number of usages.";
|
||||||
"lng_group_invite_title" = "Invite links";
|
"lng_group_invite_title" = "Invite links";
|
||||||
"lng_group_invite_add" = "Create a New Link";
|
"lng_group_invite_add" = "Create a New Link";
|
||||||
|
"lng_group_invite_add_about" = "You can generate invite links that will expire after they've been used.";
|
||||||
"lng_group_invite_expires_at" = "This link expires {when}.";
|
"lng_group_invite_expires_at" = "This link expires {when}.";
|
||||||
"lng_group_invite_created_by" = "Link created by";
|
"lng_group_invite_created_by" = "Link created by";
|
||||||
"lng_group_invite_context_copy" = "Copy";
|
"lng_group_invite_context_copy" = "Copy";
|
||||||
"lng_group_invite_context_share" = "Share";
|
"lng_group_invite_context_share" = "Share";
|
||||||
"lng_group_invite_context_edit" = "Edit";
|
"lng_group_invite_context_edit" = "Edit";
|
||||||
"lng_group_invite_context_revoke" = "Revoke";
|
"lng_group_invite_context_revoke" = "Revoke";
|
||||||
|
"lng_group_invite_context_delete" = "Delete";
|
||||||
|
"lng_group_invite_context_delete_all" = "Delete all";
|
||||||
|
"lng_group_invite_revoked_title" = "Revoked links";
|
||||||
"lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?";
|
"lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?";
|
||||||
"lng_group_invite_link_expired" = "Expired";
|
"lng_group_invite_link_expired" = "Expired";
|
||||||
"lng_group_invite_link_revoked" = "Revoked";
|
"lng_group_invite_link_revoked" = "Revoked";
|
||||||
|
@ -1199,10 +1200,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_invite_new_title" = "New Link";
|
"lng_group_invite_new_title" = "New Link";
|
||||||
"lng_group_invite_expire_title" = "Limit by time period";
|
"lng_group_invite_expire_title" = "Limit by time period";
|
||||||
"lng_group_invite_expire_about" = "You can make the link expire after a certain time.";
|
"lng_group_invite_expire_about" = "You can make the link expire after a certain time.";
|
||||||
"lng_group_invite_expire_custom" = "Set custom duration";
|
"lng_group_invite_expire_never" = "No limit";
|
||||||
"lng_group_invite_usage_title" = "Limit number of uses.";
|
"lng_group_invite_expire_custom" = "Custom";
|
||||||
|
"lng_group_invite_usage_title" = "Limit number of uses";
|
||||||
"lng_group_invite_usage_about" = "You can make the link expire after it has been used for a certain number of times.";
|
"lng_group_invite_usage_about" = "You can make the link expire after it has been used for a certain number of times.";
|
||||||
"lng_group_invite_usage_custom" = "Enter custom limit";
|
"lng_group_invite_expire_after" = "Expire after";
|
||||||
|
"lng_group_invite_custom_limit" = "Enter custom limit";
|
||||||
|
"lng_group_invite_usage_any" = "No limit";
|
||||||
|
"lng_group_invite_usage_custom" = "Custom";
|
||||||
|
|
||||||
"lng_channel_public_link_copied" = "Link copied to clipboard.";
|
"lng_channel_public_link_copied" = "Link copied to clipboard.";
|
||||||
"lng_context_about_private_link" = "This link will only work for members of this chat.";
|
"lng_context_about_private_link" = "This link will only work for members of this chat.";
|
||||||
|
|
|
@ -252,7 +252,7 @@ private:
|
||||||
|
|
||||||
QPointer<Ui::SlideWrap<>> _aboutSponsored;
|
QPointer<Ui::SlideWrap<>> _aboutSponsored;
|
||||||
QPointer<HostInput> _host;
|
QPointer<HostInput> _host;
|
||||||
QPointer<Ui::PortInput> _port;
|
QPointer<Ui::NumberInput> _port;
|
||||||
QPointer<Ui::InputField> _user;
|
QPointer<Ui::InputField> _user;
|
||||||
QPointer<Ui::PasswordInput> _password;
|
QPointer<Ui::PasswordInput> _password;
|
||||||
QPointer<Base64UrlInput> _secret;
|
QPointer<Base64UrlInput> _secret;
|
||||||
|
@ -928,11 +928,12 @@ void ProxyBox::setupSocketAddress(const ProxyData &data) {
|
||||||
st::connectionHostInputField,
|
st::connectionHostInputField,
|
||||||
tr::lng_connection_host_ph(),
|
tr::lng_connection_host_ph(),
|
||||||
data.host);
|
data.host);
|
||||||
_port = Ui::CreateChild<Ui::PortInput>(
|
_port = Ui::CreateChild<Ui::NumberInput>(
|
||||||
address,
|
address,
|
||||||
st::connectionPortInputField,
|
st::connectionPortInputField,
|
||||||
tr::lng_connection_port_ph(),
|
tr::lng_connection_port_ph(),
|
||||||
data.port ? QString::number(data.port) : QString());
|
data.port ? QString::number(data.port) : QString(),
|
||||||
|
65535);
|
||||||
address->widthValue(
|
address->widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
_port->moveToRight(0, 0);
|
_port->moveToRight(0, 0);
|
||||||
|
|
|
@ -1004,7 +1004,10 @@ void Controller::fillManageSection() {
|
||||||
});
|
});
|
||||||
}) | rpl::flatten_latest(
|
}) | rpl::flatten_latest(
|
||||||
) | ToPositiveNumberString(),
|
) | ToPositiveNumberString(),
|
||||||
[=] { Ui::show(Box(ManageInviteLinksBox, _peer)); },
|
[=] { Ui::show(
|
||||||
|
Box(ManageInviteLinksBox, _peer),
|
||||||
|
Ui::LayerOption::KeepOther);
|
||||||
|
},
|
||||||
st::infoIconInviteLinks);
|
st::infoIconInviteLinks);
|
||||||
}
|
}
|
||||||
if (canViewAdmins) {
|
if (canViewAdmins) {
|
||||||
|
|
|
@ -20,11 +20,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/abstract_button.h"
|
#include "ui/abstract_button.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/widgets/checkbox.h"
|
||||||
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/controls/invite_link_label.h"
|
#include "ui/controls/invite_link_label.h"
|
||||||
#include "ui/controls/invite_link_buttons.h"
|
#include "ui/controls/invite_link_buttons.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "history/view/history_view_group_call_tracker.h" // GenerateUs...
|
#include "history/view/history_view_group_call_tracker.h" // GenerateUs...
|
||||||
|
#include "history/view/history_view_schedule_box.h" // ChooseDateTimeBox.
|
||||||
#include "history/history_message.h" // GetErrorTextForSending.
|
#include "history/history_message.h" // GetErrorTextForSending.
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -40,6 +43,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "api/api_common.h"
|
#include "api/api_common.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
#include "styles/style_layers.h" // st::boxDividerLabel
|
||||||
|
#include "styles/style_settings.h" // st::settingsDividerLabelPadding
|
||||||
|
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
|
|
||||||
|
@ -48,8 +53,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kPreloadPages = 2;
|
constexpr auto kPreloadPages = 2;
|
||||||
constexpr auto kExpireSoonNominator = 3;
|
constexpr auto kMaxLimit = std::numeric_limits<int>::max();
|
||||||
constexpr auto kExpireSoonDenominator = 4;
|
constexpr auto kHour = 3600;
|
||||||
|
constexpr auto kDay = 86400;
|
||||||
|
|
||||||
enum class Color {
|
enum class Color {
|
||||||
Permanent,
|
Permanent,
|
||||||
|
@ -124,6 +130,27 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QString FormatExpireDate(TimeId date) {
|
||||||
|
if (date > 0) {
|
||||||
|
return langDateTime(base::unixtime::parse(date));
|
||||||
|
} else if (-date < kDay) {
|
||||||
|
return tr::lng_group_call_duration_hours(
|
||||||
|
tr::now,
|
||||||
|
lt_count,
|
||||||
|
(-date / kHour));
|
||||||
|
} else if (-date < 7 * kDay) {
|
||||||
|
return tr::lng_group_call_duration_days(
|
||||||
|
tr::now,
|
||||||
|
lt_count,
|
||||||
|
(-date / kDay));
|
||||||
|
} else {
|
||||||
|
return tr::lng_local_storage_limit_weeks(
|
||||||
|
tr::now,
|
||||||
|
lt_count,
|
||||||
|
(-date / (7 * kDay)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] uint64 ComputeRowId(const QString &link) {
|
[[nodiscard]] uint64 ComputeRowId(const QString &link) {
|
||||||
return XXH64(link.data(), link.size() * sizeof(ushort), 0);
|
return XXH64(link.data(), link.size() * sizeof(ushort), 0);
|
||||||
}
|
}
|
||||||
|
@ -291,9 +318,226 @@ void EditLinkBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const InviteLinkData &data) {
|
const InviteLinkData &data) {
|
||||||
box->setTitle(data.link.isEmpty()
|
const auto link = data.link;
|
||||||
|
box->setTitle(link.isEmpty()
|
||||||
? tr::lng_group_invite_new_title()
|
? tr::lng_group_invite_new_title()
|
||||||
: tr::lng_group_invite_edit_title());
|
: tr::lng_group_invite_edit_title());
|
||||||
|
|
||||||
|
using namespace Settings;
|
||||||
|
const auto container = box->verticalLayout();
|
||||||
|
|
||||||
|
AddSubsectionTitle(container, tr::lng_group_invite_expire_title());
|
||||||
|
const auto expiresWrap = container->add(object_ptr<Ui::VerticalLayout>(
|
||||||
|
container));
|
||||||
|
AddSkip(container);
|
||||||
|
|
||||||
|
AddDividerText(container, tr::lng_group_invite_expire_about());
|
||||||
|
AddSkip(container);
|
||||||
|
AddSubsectionTitle(container, tr::lng_group_invite_usage_title());
|
||||||
|
const auto usagesWrap = container->add(object_ptr<Ui::VerticalLayout>(
|
||||||
|
container));
|
||||||
|
AddSkip(container);
|
||||||
|
|
||||||
|
AddDividerText(container, tr::lng_group_invite_usage_about());
|
||||||
|
|
||||||
|
static const auto addButton = [](
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
const std::shared_ptr<Ui::RadiobuttonGroup> &group,
|
||||||
|
int value,
|
||||||
|
const QString &text) {
|
||||||
|
return container->add(
|
||||||
|
object_ptr<Ui::Radiobutton>(
|
||||||
|
container,
|
||||||
|
group,
|
||||||
|
value,
|
||||||
|
text),
|
||||||
|
st::inviteLinkLimitMargin);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
const auto expire = data.expireDate ? data.expireDate : kMaxLimit;
|
||||||
|
const auto expireGroup = std::make_shared<Ui::RadiobuttonGroup>(expire);
|
||||||
|
const auto usage = data.usageLimit ? data.usageLimit : kMaxLimit;
|
||||||
|
const auto usageGroup = std::make_shared<Ui::RadiobuttonGroup>(usage);
|
||||||
|
|
||||||
|
using Buttons = base::flat_map<int, base::unique_qptr<Ui::Radiobutton>>;
|
||||||
|
struct State {
|
||||||
|
Buttons expireButtons;
|
||||||
|
Buttons usageButtons;
|
||||||
|
int expireValue = 0;
|
||||||
|
int usageValue = 0;
|
||||||
|
};
|
||||||
|
const auto state = container->lifetime().make_state<State>(State{
|
||||||
|
.expireValue = expire,
|
||||||
|
.usageValue = usage
|
||||||
|
});
|
||||||
|
const auto regenerate = [=] {
|
||||||
|
expireGroup->setValue(state->expireValue);
|
||||||
|
usageGroup->setValue(state->usageValue);
|
||||||
|
|
||||||
|
auto expires = std::vector{ kMaxLimit, -kHour, -kDay, -kDay * 7, 0 };
|
||||||
|
auto usages = std::vector{ kMaxLimit, 1, 10, 100, 0 };
|
||||||
|
auto defaults = State();
|
||||||
|
for (auto i = begin(expires); i != end(expires); ++i) {
|
||||||
|
if (*i == state->expireValue) {
|
||||||
|
break;
|
||||||
|
} else if (*i == kMaxLimit) {
|
||||||
|
continue;
|
||||||
|
} else if (!*i || (now - *i >= state->expireValue)) {
|
||||||
|
expires.insert(i, state->expireValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto i = begin(usages); i != end(usages); ++i) {
|
||||||
|
if (*i == state->usageValue) {
|
||||||
|
break;
|
||||||
|
} else if (*i == kMaxLimit) {
|
||||||
|
continue;
|
||||||
|
} else if (!*i || *i > state->usageValue) {
|
||||||
|
usages.insert(i, state->usageValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->expireButtons.clear();
|
||||||
|
state->usageButtons.clear();
|
||||||
|
for (const auto limit : expires) {
|
||||||
|
const auto text = (limit == kMaxLimit)
|
||||||
|
? tr::lng_group_invite_expire_never(tr::now)
|
||||||
|
: !limit
|
||||||
|
? tr::lng_group_invite_expire_custom(tr::now)
|
||||||
|
: FormatExpireDate(limit);
|
||||||
|
state->expireButtons.emplace(
|
||||||
|
limit,
|
||||||
|
addButton(expiresWrap, expireGroup, limit, text));
|
||||||
|
}
|
||||||
|
for (const auto limit : usages) {
|
||||||
|
const auto text = (limit == kMaxLimit)
|
||||||
|
? tr::lng_group_invite_usage_any(tr::now)
|
||||||
|
: !limit
|
||||||
|
? tr::lng_group_invite_usage_custom(tr::now)
|
||||||
|
: QString("%L1").arg(limit);
|
||||||
|
state->usageButtons.emplace(
|
||||||
|
limit,
|
||||||
|
addButton(usagesWrap, usageGroup, limit, text));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto guard = Ui::MakeWeak(box);
|
||||||
|
expireGroup->setChangedCallback([=](int value) {
|
||||||
|
if (value) {
|
||||||
|
state->expireValue = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expireGroup->setValue(state->expireValue);
|
||||||
|
box->getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
const auto save = [=](TimeId result) {
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (guard) {
|
||||||
|
state->expireValue = result;
|
||||||
|
regenerate();
|
||||||
|
}
|
||||||
|
box->closeBox();
|
||||||
|
};
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
const auto time = (state->expireValue == kMaxLimit)
|
||||||
|
? (now + kDay)
|
||||||
|
: (state->expireValue > now)
|
||||||
|
? state->expireValue
|
||||||
|
: (state->expireValue < 0)
|
||||||
|
? (now - state->expireValue)
|
||||||
|
: (now + kDay);
|
||||||
|
HistoryView::ChooseDateTimeBox(
|
||||||
|
box,
|
||||||
|
tr::lng_group_invite_expire_after(),
|
||||||
|
tr::lng_settings_save(),
|
||||||
|
save,
|
||||||
|
time);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
usageGroup->setChangedCallback([=](int value) {
|
||||||
|
if (value) {
|
||||||
|
state->usageValue = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usageGroup->setValue(state->usageValue);
|
||||||
|
box->getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
const auto height = st::boxPadding.bottom()
|
||||||
|
+ st::defaultInputField.heightMin
|
||||||
|
+ st::boxPadding.bottom();
|
||||||
|
box->setTitle(tr::lng_group_invite_expire_after());
|
||||||
|
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
box,
|
||||||
|
height));
|
||||||
|
const auto input = Ui::CreateChild<Ui::NumberInput>(
|
||||||
|
wrap,
|
||||||
|
st::defaultInputField,
|
||||||
|
tr::lng_group_invite_custom_limit(),
|
||||||
|
(state->usageValue == kMaxLimit
|
||||||
|
? QString()
|
||||||
|
: QString::number(state->usageValue)),
|
||||||
|
200'000);
|
||||||
|
wrap->widthValue(
|
||||||
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
input->resize(width, input->height());
|
||||||
|
input->moveToLeft(0, st::boxPadding.bottom());
|
||||||
|
}, input->lifetime());
|
||||||
|
box->setFocusCallback([=] {
|
||||||
|
input->setFocusFast();
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto save = [=] {
|
||||||
|
const auto value = input->getLastText().toInt();
|
||||||
|
if (value <= 0) {
|
||||||
|
input->showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (guard) {
|
||||||
|
state->usageValue = value;
|
||||||
|
regenerate();
|
||||||
|
}
|
||||||
|
box->closeBox();
|
||||||
|
};
|
||||||
|
QObject::connect(input, &Ui::NumberInput::submitted, save);
|
||||||
|
box->addButton(tr::lng_settings_save(), save);
|
||||||
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
regenerate();
|
||||||
|
|
||||||
|
const auto &saveLabel = link.isEmpty()
|
||||||
|
? tr::lng_formatting_link_create
|
||||||
|
: tr::lng_settings_save;
|
||||||
|
box->addButton(saveLabel(), [=] {
|
||||||
|
const auto expireDate = (state->expireValue == kMaxLimit)
|
||||||
|
? 0
|
||||||
|
: (state->expireValue < 0)
|
||||||
|
? (base::unixtime::now() - state->expireValue)
|
||||||
|
: state->expireValue;
|
||||||
|
const auto usageLimit = (state->usageValue == kMaxLimit)
|
||||||
|
? 0
|
||||||
|
: state->usageValue;
|
||||||
|
const auto done = [=](const Api::InviteLink &result) {
|
||||||
|
box->closeBox();
|
||||||
|
};
|
||||||
|
if (link.isEmpty()) {
|
||||||
|
peer->session().api().inviteLinks().create(
|
||||||
|
peer,
|
||||||
|
done,
|
||||||
|
expireDate,
|
||||||
|
usageLimit);
|
||||||
|
} else {
|
||||||
|
peer->session().api().inviteLinks().edit(
|
||||||
|
peer,
|
||||||
|
link,
|
||||||
|
expireDate,
|
||||||
|
usageLimit,
|
||||||
|
done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateLinkBox(
|
void CreateLinkBox(
|
||||||
|
@ -380,7 +624,7 @@ class Controller final
|
||||||
, public RowDelegate
|
, public RowDelegate
|
||||||
, public base::has_weak_ptr {
|
, public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
explicit Controller(not_null<PeerData*> peer);
|
Controller(not_null<PeerData*> peer, bool revoked);
|
||||||
|
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
void rowClicked(not_null<PeerListRow*> row) override;
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
|
@ -406,6 +650,7 @@ private:
|
||||||
not_null<PeerListRow*> row);
|
not_null<PeerListRow*> row);
|
||||||
|
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
|
bool _revoked = false;
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
|
||||||
std::array<QImage, int(Color::Count)> _icons;
|
std::array<QImage, int(Color::Count)> _icons;
|
||||||
|
@ -413,8 +658,9 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Controller::Controller(not_null<PeerData*> peer)
|
Controller::Controller(not_null<PeerData*> peer, bool revoked)
|
||||||
: _peer(peer) {
|
: _peer(peer)
|
||||||
|
, _revoked(revoked) {
|
||||||
style::PaletteChanged(
|
style::PaletteChanged(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
for (auto &image : _icons) {
|
for (auto &image : _icons) {
|
||||||
|
@ -559,6 +805,7 @@ void Controller::rowPaintIcon(
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
auto pen = QPen((*bg)->c);
|
auto pen = QPen((*bg)->c);
|
||||||
pen.setWidth(stroke);
|
pen.setWidth(stroke);
|
||||||
|
pen.setCapStyle(Qt::RoundCap);
|
||||||
p.setPen(pen);
|
p.setPen(pen);
|
||||||
p.setBrush(Qt::NoBrush);
|
p.setBrush(Qt::NoBrush);
|
||||||
|
|
||||||
|
@ -568,7 +815,7 @@ void Controller::rowPaintIcon(
|
||||||
margins,
|
margins,
|
||||||
margins,
|
margins,
|
||||||
margins,
|
margins,
|
||||||
}), 0, kFullArcLength * progress);
|
}), (kFullArcLength / 4), kFullArcLength * (1. - progress));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,6 +993,26 @@ void AddPermanentLinkBlock(
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<Ui::RpWidget*> AddLinksList(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
bool revoked) {
|
||||||
|
const auto delegate = container->lifetime().make_state<
|
||||||
|
PeerListContentDelegateSimple
|
||||||
|
>();
|
||||||
|
const auto controller = container->lifetime().make_state<Controller>(
|
||||||
|
peer,
|
||||||
|
revoked);
|
||||||
|
controller->setStyleOverrides(&st::inviteLinkList);
|
||||||
|
const auto content = container->add(object_ptr<PeerListContent>(
|
||||||
|
container,
|
||||||
|
controller));
|
||||||
|
delegate->setContent(content);
|
||||||
|
controller->setDelegate(delegate);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
void ManageInviteLinksBox(
|
void ManageInviteLinksBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
not_null<PeerData*> peer) {
|
not_null<PeerData*> peer) {
|
||||||
|
@ -760,14 +1027,34 @@ void ManageInviteLinksBox(
|
||||||
box->getDelegate()->show(Box(CreateLinkBox, peer));
|
box->getDelegate()->show(Box(CreateLinkBox, peer));
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto delegate = box->lifetime().make_state<
|
const auto list = AddLinksList(container, peer, false);
|
||||||
PeerListContentDelegateSimple
|
const auto dividerAbout = container->add(object_ptr<Ui::SlideWrap<>>(
|
||||||
>();
|
|
||||||
const auto controller = box->lifetime().make_state<Controller>(peer);
|
|
||||||
controller->setStyleOverrides(&st::inviteLinkList);
|
|
||||||
const auto content = container->add(object_ptr<PeerListContent>(
|
|
||||||
container,
|
container,
|
||||||
controller));
|
object_ptr<Ui::DividerLabel>(
|
||||||
delegate->setContent(content);
|
container,
|
||||||
controller->setDelegate(delegate);
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
tr::lng_group_invite_add_about(),
|
||||||
|
st::boxDividerLabel),
|
||||||
|
st::settingsDividerLabelPadding)));
|
||||||
|
const auto divider = container->add(object_ptr<Ui::SlideWrap<>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::BoxContentDivider>(container)));
|
||||||
|
const auto header = container->add(object_ptr<Ui::SlideWrap<>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
tr::lng_group_invite_revoked_title(),
|
||||||
|
st::settingsSubsectionTitle),
|
||||||
|
st::inviteLinkRevokedTitlePadding));
|
||||||
|
const auto revoked = AddLinksList(container, peer, true);
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
list->heightValue(),
|
||||||
|
revoked->heightValue()
|
||||||
|
) | rpl::start_with_next([=](int list, int revoked) {
|
||||||
|
dividerAbout->toggle(!list, anim::type::instant);
|
||||||
|
divider->toggle(list > 0 && revoked > 0, anim::type::instant);
|
||||||
|
header->toggle(revoked > 0, anim::type::instant);
|
||||||
|
}, header->lifetime());
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,9 +117,6 @@ private:
|
||||||
rpl::producer<QString> &&text,
|
rpl::producer<QString> &&text,
|
||||||
not_null<const style::FlatLabel*> st);
|
not_null<const style::FlatLabel*> st);
|
||||||
|
|
||||||
void createInviteLink();
|
|
||||||
void revokeInviteLink(const QString &link);
|
|
||||||
|
|
||||||
void fillPrivaciesButtons(
|
void fillPrivaciesButtons(
|
||||||
not_null<Ui::VerticalLayout*> parent,
|
not_null<Ui::VerticalLayout*> parent,
|
||||||
std::optional<Privacy> savedValue = std::nullopt);
|
std::optional<Privacy> savedValue = std::nullopt);
|
||||||
|
@ -185,7 +182,10 @@ void Controller::createContent() {
|
||||||
_wrap.get(),
|
_wrap.get(),
|
||||||
tr::lng_group_invite_manage(),
|
tr::lng_group_invite_manage(),
|
||||||
rpl::single(QString()),
|
rpl::single(QString()),
|
||||||
[=] { Ui::show(Box(ManageInviteLinksBox, _peer)); },
|
[=] { Ui::show(
|
||||||
|
Box(ManageInviteLinksBox, _peer),
|
||||||
|
Ui::LayerOption::KeepOther);
|
||||||
|
},
|
||||||
st::manageGroupButton,
|
st::manageGroupButton,
|
||||||
&st::infoIconInviteLinks));
|
&st::infoIconInviteLinks));
|
||||||
AddSkip(_wrap.get());
|
AddSkip(_wrap.get());
|
||||||
|
@ -529,31 +529,6 @@ void Controller::showUsernameResult(
|
||||||
_usernameResultTexts.fire(std::move(text));
|
_usernameResultTexts.fire(std::move(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::createInviteLink() {
|
|
||||||
const auto callback = crl::guard(this, [=](Fn<void()> &&close) {
|
|
||||||
close();
|
|
||||||
_peer->session().api().inviteLinks().create(_peer->migrateToOrMe());
|
|
||||||
});
|
|
||||||
auto box = Box<ConfirmBox>(
|
|
||||||
(_isGroup
|
|
||||||
? tr::lng_group_invite_about
|
|
||||||
: tr::lng_group_invite_about_channel)(tr::now),
|
|
||||||
std::move(callback));
|
|
||||||
Ui::show(std::move(box), Ui::LayerOption::KeepOther);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::revokeInviteLink(const QString &link) {
|
|
||||||
const auto callback = crl::guard(this, [=](Fn<void()> &&close) {
|
|
||||||
close();
|
|
||||||
const auto peer = _peer->migrateToOrMe();
|
|
||||||
peer->session().api().inviteLinks().revoke(peer, link);
|
|
||||||
});
|
|
||||||
auto box = Box<ConfirmBox>(
|
|
||||||
tr::lng_group_invite_about_new(tr::now),
|
|
||||||
std::move(callback));
|
|
||||||
Ui::show(std::move(box), Ui::LayerOption::KeepOther);
|
|
||||||
}
|
|
||||||
|
|
||||||
object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() {
|
object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() {
|
||||||
Expects(_wrap != nullptr);
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
|
|
@ -596,19 +596,18 @@ TimeId DefaultScheduleTime() {
|
||||||
|
|
||||||
bool CanScheduleUntilOnline(not_null<PeerData*> peer) {
|
bool CanScheduleUntilOnline(not_null<PeerData*> peer) {
|
||||||
return !peer->isSelf()
|
return !peer->isSelf()
|
||||||
&& peer->isUser()
|
&& peer->isUser()
|
||||||
&& !peer->asUser()->isBot()
|
&& !peer->asUser()->isBot()
|
||||||
&& (peer->asUser()->onlineTill > 0);
|
&& (peer->asUser()->onlineTill > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduleBox(
|
ChooseDateTimeBoxDescriptor ChooseDateTimeBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
SendMenu::Type type,
|
rpl::producer<QString> title,
|
||||||
Fn<void(Api::SendOptions)> done,
|
rpl::producer<QString> submit,
|
||||||
|
Fn<void(TimeId)> done,
|
||||||
TimeId time) {
|
TimeId time) {
|
||||||
box->setTitle((type == SendMenu::Type::Reminder)
|
box->setTitle(std::move(title));
|
||||||
? tr::lng_remind_title()
|
|
||||||
: tr::lng_schedule_title());
|
|
||||||
box->setWidth(st::boxWideWidth);
|
box->setWidth(st::boxWideWidth);
|
||||||
|
|
||||||
const auto date = Ui::CreateChild<rpl::variable<QDate>>(
|
const auto date = Ui::CreateChild<rpl::variable<QDate>>(
|
||||||
|
@ -716,46 +715,67 @@ void ScheduleBox(
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
const auto save = [=](bool silent, bool untilOnline = false) {
|
const auto save = [=] {
|
||||||
|
if (const auto result = collect()) {
|
||||||
|
done(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
timeInput->submitRequests(
|
||||||
|
) | rpl::start_with_next(
|
||||||
|
save,
|
||||||
|
timeInput->lifetime());
|
||||||
|
|
||||||
|
auto result = ChooseDateTimeBoxDescriptor();
|
||||||
|
box->setFocusCallback([=] { timeInput->setFocusFast(); });
|
||||||
|
result.submit = box->addButton(std::move(submit), save);
|
||||||
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduleBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
SendMenu::Type type,
|
||||||
|
Fn<void(Api::SendOptions)> done,
|
||||||
|
TimeId time) {
|
||||||
|
const auto save = [=](bool silent, TimeId scheduleDate) {
|
||||||
|
if (!scheduleDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Pro tip: Hold Ctrl key to send a silent scheduled message!
|
// Pro tip: Hold Ctrl key to send a silent scheduled message!
|
||||||
auto ctrl =
|
auto ctrl =
|
||||||
(QGuiApplication::keyboardModifiers() == Qt::ControlModifier);
|
(QGuiApplication::keyboardModifiers() == Qt::ControlModifier);
|
||||||
auto result = Api::SendOptions();
|
auto result = Api::SendOptions();
|
||||||
result.silent = silent || ctrl;
|
result.silent = silent || ctrl;
|
||||||
result.scheduled = untilOnline
|
result.scheduled = scheduleDate;
|
||||||
? Data::ScheduledMessages::kScheduledUntilOnlineTimestamp
|
const auto copy = done;
|
||||||
: collect();
|
|
||||||
if (!result.scheduled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto copy = done;
|
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
copy(result);
|
copy(result);
|
||||||
};
|
};
|
||||||
timeInput->submitRequests(
|
auto descriptor = ChooseDateTimeBox(
|
||||||
) | rpl::start_with_next([=] {
|
box,
|
||||||
save(false);
|
(type == SendMenu::Type::Reminder
|
||||||
}, timeInput->lifetime());
|
? tr::lng_remind_title()
|
||||||
|
: tr::lng_schedule_title()),
|
||||||
|
tr::lng_schedule_button(),
|
||||||
|
[=](TimeId result) { save(false, result); },
|
||||||
|
time);
|
||||||
|
|
||||||
box->setFocusCallback([=] { timeInput->setFocusFast(); });
|
|
||||||
const auto submit = box->addButton(tr::lng_schedule_button(), [=] {
|
|
||||||
save(false);
|
|
||||||
});
|
|
||||||
SendMenu::SetupMenuAndShortcuts(
|
SendMenu::SetupMenuAndShortcuts(
|
||||||
submit.data(),
|
descriptor.submit.data(),
|
||||||
[=] { return SendMenu::Type::SilentOnly; },
|
[=] { return SendMenu::Type::SilentOnly; },
|
||||||
[=] { save(true); },
|
[=] { save(true, descriptor.collect()); },
|
||||||
nullptr);
|
nullptr);
|
||||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
|
|
||||||
if (type == SendMenu::Type::ScheduledToUser) {
|
if (type == SendMenu::Type::ScheduledToUser) {
|
||||||
const auto sendUntilOnline = box->addTopButton(st::infoTopBarMenu);
|
const auto sendUntilOnline = box->addTopButton(st::infoTopBarMenu);
|
||||||
|
const auto timestamp
|
||||||
|
= Data::ScheduledMessages::kScheduledUntilOnlineTimestamp;
|
||||||
FillSendUntilOnlineMenu(
|
FillSendUntilOnlineMenu(
|
||||||
sendUntilOnline.data(),
|
sendUntilOnline.data(),
|
||||||
[=] { save(false, true); });
|
[=] { save(false, timestamp); });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -21,6 +21,19 @@ namespace HistoryView {
|
||||||
|
|
||||||
[[nodiscard]] TimeId DefaultScheduleTime();
|
[[nodiscard]] TimeId DefaultScheduleTime();
|
||||||
[[nodiscard]] bool CanScheduleUntilOnline(not_null<PeerData*> peer);
|
[[nodiscard]] bool CanScheduleUntilOnline(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
struct ChooseDateTimeBoxDescriptor {
|
||||||
|
QPointer<Ui::RoundButton> submit;
|
||||||
|
Fn<TimeId()> collect;
|
||||||
|
};
|
||||||
|
|
||||||
|
ChooseDateTimeBoxDescriptor ChooseDateTimeBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
rpl::producer<QString> title,
|
||||||
|
rpl::producer<QString> submit,
|
||||||
|
Fn<void(TimeId)> done,
|
||||||
|
TimeId time);
|
||||||
|
|
||||||
void ScheduleBox(
|
void ScheduleBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
SendMenu::Type type,
|
SendMenu::Type type,
|
||||||
|
|
|
@ -917,3 +917,5 @@ inviteLinkIconSkip: 7px;
|
||||||
inviteLinkIconStroke: 2px;
|
inviteLinkIconStroke: 2px;
|
||||||
inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }};
|
inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }};
|
||||||
inviteLinkThreeDotsSkip: 8px;
|
inviteLinkThreeDotsSkip: 8px;
|
||||||
|
inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 9px);
|
||||||
|
inviteLinkLimitMargin: margins(22px, 8px, 22px, 8px);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 912f0b48a648e7f12e7bcba82a0f0ec326aab8dd
|
Subproject commit a5fb99372184d5f3c00e5e851aaa16d8d28d2ce1
|
Loading…
Add table
Reference in a new issue