Added country-based poll restrictions with selectable allowed countries

This commit is contained in:
23rd
2026-04-09 10:18:29 +03:00
committed by John Preston
parent acd10b1e1d
commit 3ce201593c
7 changed files with 133 additions and 2 deletions
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg">
<title>Filled / filled_poll_country</title>
<defs>
<linearGradient id="filled_poll_country_gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#0BC4BA"/>
<stop offset="100%" stop-color="#1196E4"/>
</linearGradient>
</defs>
<g id="Filled-/-filled_poll_country" stroke="none" fill="none" fill-rule="evenodd">
<path d="M36,9.33333333 C24.8,9.33333333 14.6666667,17.92 14.6666667,31.2 C14.6666667,39.68 21.2,51.7866667 34.24,63.28 C35.2533333,64.16 36.7733333,64.16 37.7866667,63.28 C50.8,51.7866667 57.3333333,39.68 57.3333333,31.2 C57.3333333,17.92 47.2,9.33333333 36,9.33333333 Z M36,37 C32.15,37 29,33.85 29,30 C29,26.15 32.15,23 36,23 C39.85,23 43,26.15 43,30 C43,33.85 39.85,37 36,37 Z" id="Shape" fill="url(#filled_poll_country_gradient)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 988 B

+6
View File
@@ -6997,6 +6997,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_polls_create_hide_results_about" = "If you switch this on, results will appear only after the poll closes.";
"lng_polls_create_restrict_to_subscribers" = "Restrict to Subscribers";
"lng_polls_create_restrict_to_subscribers_about" = "Only subscribers who joined 24+ hours ago can vote.";
"lng_polls_create_limit_by_country" = "Limit by Country";
"lng_polls_create_limit_by_country_about" = "Only users from selected countries can vote.";
"lng_polls_create_allowed_countries" = "Allowed Countries";
"lng_polls_create_countries_count#one" = "{count} country";
"lng_polls_create_countries_count#other" = "{count} countries";
"lng_polls_create_choose_country" = "Please choose at least one country.";
"lng_polls_create_duration_custom" = "Custom";
"lng_polls_create_deadline_title" = "Deadline";
"lng_polls_create_deadline_button" = "Set Deadline";
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/event_filter.h"
#include "base/random.h"
#include "base/unique_qptr.h"
#include "countries/countries_instance.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h"
@@ -38,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/stickers/data_custom_emoji.h"
#include "history/view/media/menu/history_view_poll_menu.h"
#include "history/view/history_view_schedule_box.h"
#include "info/channel_statistics/boosts/giveaway/select_countries_box.h"
#include "lang/lang_keys.h"
#include "layout/layout_document_generic_preview.h"
#include "main/main_app_config.h"
@@ -1458,6 +1460,7 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
rpl::event_stream<bool> showWhoVotedForceOn;
rpl::variable<int> closePeriod = 0;
rpl::variable<TimeId> closeDate = TimeId(0);
rpl::variable<std::vector<QString>> countriesValue;
std::shared_ptr<PollMediaState> descriptionMedia
= std::make_shared<PollMediaState>();
std::shared_ptr<PollMediaState> solutionMedia
@@ -2745,6 +2748,68 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
rpl::single(!!(_chosen & PollData::Flag::SubscribersOnly)),
st::detailedSettingsButtonStyle).get()
: nullptr;
const auto limitByCountry = isBroadcastChannel
? AddPollToggleButton(
container,
tr::lng_polls_create_limit_by_country(),
tr::lng_polls_create_limit_by_country_about(),
{
.icon = &st::pollBoxFilledPollCountryIcon,
.background = &st::settingsIconBg6,
},
rpl::single(false),
st::detailedSettingsButtonStyle).get()
: nullptr;
const auto countriesWrap = limitByCountry
? container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)))
: nullptr;
const auto countriesButton = [=] {
if (!countriesWrap) {
return (Ui::SettingsButton*)(nullptr);
}
const auto inner = countriesWrap->entity();
return AddButtonWithLabel(
inner,
tr::lng_polls_create_allowed_countries(),
state->countriesValue.value(
) | rpl::map([=](const std::vector<QString> &countries) {
if (countries.empty()) {
return QString();
}
if (countries.size() == 1) {
return Countries::Instance().countryNameByISO2(
countries.front());
}
return tr::lng_polls_create_countries_count(
tr::now,
lt_count,
countries.size());
}),
st::settingsButtonNoIcon).get();
}();
if (countriesWrap) {
countriesWrap->toggleOn(
rpl::single(limitByCountry->toggled())
| rpl::then(limitByCountry->toggledChanges()));
}
if (countriesButton) {
countriesButton->setClickedCallback([=] {
const auto done = [=](std::vector<QString> countries) {
state->countriesValue = std::move(countries);
};
const auto checkError = [](int) {
return false;
};
show->show(Box(
Ui::SelectCountriesBox,
state->countriesValue.current(),
done,
checkError));
});
}
const auto solution = setupSolution(
container,
@@ -2879,6 +2944,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
&& restrictToSubscribers->toggled());
const auto hideResultsEnabled = duration->toggled()
&& hideResults->toggled();
result.countries = (limitByCountry
&& limitByCountry->toggled())
? state->countriesValue.current()
: std::vector<QString>();
result.setFlags(Flag(0)
| (publicVotes ? Flag::PublicVotes : Flag(0))
| (multiChoice ? Flag::MultiChoice : Flag(0))
@@ -2950,6 +3019,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
} else {
state->error &= ~Error::Deadline;
}
if (limitByCountry
&& limitByCountry->toggled()
&& state->countriesValue.current().empty()) {
state->error |= Error::Country;
} else {
state->error &= ~Error::Country;
}
};
const auto showError = [show = uiShow()](
tr::phrase<> text) {
@@ -3009,6 +3085,11 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
ShowMediaUploadingToast();
} else if (state->error & Error::Deadline) {
showError(tr::lng_polls_create_deadline_expired);
} else if (state->error & Error::Country) {
showError(tr::lng_polls_create_choose_country);
if (countriesButton) {
scrollToWidget(countriesButton);
}
} else if (!state->error) {
auto result = collectResult();
result.options = sendOptions;
@@ -3068,6 +3149,12 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
if (restrictToSubscribers) {
restrictToSubscribers->finishAnimating();
}
if (limitByCountry) {
limitByCountry->finishAnimating();
}
if (countriesWrap) {
countriesWrap->finishAnimating();
}
return result;
}
@@ -68,6 +68,7 @@ private:
Solution = 0x10,
Media = 0x20,
Deadline = 0x40,
Country = 0x80,
};
friend constexpr inline bool is_flag_type(Error) { return true; }
using Errors = base::flags<Error>;
+1
View File
@@ -137,6 +137,7 @@ pollBoxFilledPollRevoteIcon: icon{{ "poll/filled/filled_poll_revote", activeButt
pollBoxFilledPollShuffleIcon: icon{{ "poll/filled/filled_poll_shuffle", activeButtonFg }};
pollBoxFilledPollMultipleIcon: icon{{ "poll/filled/filled_poll_multiple", activeButtonFg }};
pollBoxFilledPollSubscribersIcon: icon{{ "poll/filled/filled_poll_subscribers-20x20", activeButtonFg }};
pollBoxFilledPollCountryIcon: icon{{ "poll/filled/filled_poll_country-20x20", activeButtonFg }};
pollAttachTextSkip: 28px;
pollAttachProgressMargin: 4px;
+24 -2
View File
@@ -109,6 +109,13 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
| (poll.is_subscribers_only() ? Flag::SubscribersOnly : Flag(0));
const auto newCloseDate = poll.vclose_date().value_or_empty();
const auto newClosePeriod = poll.vclose_period().value_or_empty();
auto newCountries = std::vector<QString>();
if (const auto countries = poll.vcountries_iso2()) {
newCountries.reserve(countries->v.size());
for (const auto &country : countries->v) {
newCountries.push_back(qs(country));
}
}
auto newAnswers = ranges::views::all(
poll.vanswers().v
) | ranges::views::transform([&](const MTPPollAnswer &data) {
@@ -146,6 +153,7 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
const auto changed1 = (question != newQuestion)
|| (closeDate != newCloseDate)
|| (closePeriod != newClosePeriod)
|| (countries != newCountries)
|| (_flags != newFlags);
const auto changed2 = (answers != newAnswers);
if (!changed1 && !changed2) {
@@ -155,6 +163,7 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
question = newQuestion;
closeDate = newCloseDate;
closePeriod = newClosePeriod;
countries = std::move(newCountries);
_flags = newFlags;
}
if (changed2) {
@@ -410,6 +419,13 @@ QString PollData::debugString() const {
if (!solution.text.isEmpty()) {
result += u"Solution: "_q + solution.text + u'\n';
}
if (!countries.empty()) {
result += u"Countries: "_q + countries.front();
for (auto i = 1, count = int(countries.size()); i != count; ++i) {
result += u", "_q + countries[i];
}
result += u'\n';
}
return result;
}
@@ -528,6 +544,11 @@ MTPPoll PollDataToMTP(not_null<const PollData*> poll, bool close) {
poll->answers,
ranges::back_inserter(answers),
convert);
auto countries = QVector<MTPstring>();
countries.reserve(poll->countries.size());
for (const auto &country : poll->countries) {
countries.push_back(MTP_string(country));
}
using Flag = MTPDpoll::Flag;
const auto flags = ((poll->closed() || close) ? Flag::f_closed : Flag(0))
| (poll->multiChoice() ? Flag::f_multiple_choice : Flag(0))
@@ -541,7 +562,8 @@ MTPPoll PollDataToMTP(not_null<const PollData*> poll, bool close) {
: Flag(0))
| (poll->subscribersOnly() ? Flag::f_subscribers_only : Flag(0))
| (poll->closePeriod > 0 ? Flag::f_close_period : Flag(0))
| (poll->closeDate > 0 ? Flag::f_close_date : Flag(0));
| (poll->closeDate > 0 ? Flag::f_close_date : Flag(0))
| (countries.isEmpty() ? Flag(0) : Flag::f_countries_iso2);
return MTP_poll(
MTP_long(poll->id),
MTP_flags(flags),
@@ -551,7 +573,7 @@ MTPPoll PollDataToMTP(not_null<const PollData*> poll, bool close) {
MTP_vector<MTPPollAnswer>(answers),
MTP_int(poll->closePeriod),
MTP_int(poll->closeDate),
MTP_vector<MTPstring>(), // countries_iso2
MTP_vector<MTPstring>(std::move(countries)), // countries_iso2
MTP_long(0));
}
+1
View File
@@ -114,6 +114,7 @@ struct PollData {
TextWithEntities solution;
PollMedia attachedMedia;
PollMedia solutionMedia;
std::vector<QString> countries;
TimeId closePeriod = 0;
TimeId closeDate = 0;
int totalVoters = 0;