| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732 |
- /*
- 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 "boxes/peers/edit_peer_info_box.h"
- #include "apiwrap.h"
- #include "api/api_credits.h"
- #include "api/api_peer_photo.h"
- #include "api/api_statistics.h"
- #include "api/api_user_names.h"
- #include "main/main_session.h"
- #include "ui/boxes/confirm_box.h"
- #include "base/event_filter.h"
- #include "boxes/peers/edit_participants_box.h"
- #include "boxes/peers/edit_peer_color_box.h"
- #include "boxes/peers/edit_peer_common.h"
- #include "boxes/peers/edit_peer_type_box.h"
- #include "boxes/peers/edit_peer_history_visibility_box.h"
- #include "boxes/peers/edit_peer_permissions_box.h"
- #include "boxes/peers/edit_peer_invite_links.h"
- #include "boxes/peers/edit_linked_chat_box.h"
- #include "boxes/peers/edit_peer_requests_box.h"
- #include "boxes/peers/edit_peer_reactions.h"
- #include "boxes/peers/replace_boost_box.h"
- #include "boxes/peers/verify_peers_box.h"
- #include "boxes/peer_list_controllers.h"
- #include "boxes/stickers_box.h"
- #include "boxes/username_box.h"
- #include "chat_helpers/emoji_suggestions_widget.h"
- #include "chat_helpers/tabbed_panel.h"
- #include "chat_helpers/tabbed_selector.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "data/components/credits.h"
- #include "data/data_channel.h"
- #include "data/data_chat.h"
- #include "data/data_peer.h"
- #include "data/data_session.h"
- #include "data/data_changes.h"
- #include "data/data_message_reactions.h"
- #include "data/data_peer_values.h"
- #include "data/data_premium_limits.h"
- #include "data/data_user.h"
- #include "history/admin_log/history_admin_log_section.h"
- #include "info/bot/earn/info_bot_earn_widget.h"
- #include "info/bot/starref/info_bot_starref_join_widget.h"
- #include "info/bot/starref/info_bot_starref_setup_widget.h"
- #include "info/channel_statistics/boosts/info_boosts_widget.h"
- #include "info/channel_statistics/earn/earn_format.h"
- #include "info/channel_statistics/earn/earn_icons.h"
- #include "info/channel_statistics/earn/info_channel_earn_widget.h"
- #include "info/profile/info_profile_values.h"
- #include "info/info_memento.h"
- #include "lang/lang_keys.h"
- #include "mtproto/sender.h"
- #include "main/main_app_config.h"
- #include "settings/settings_common.h"
- #include "ui/boxes/boost_box.h"
- #include "ui/controls/emoji_button.h"
- #include "ui/controls/userpic_button.h"
- #include "ui/effects/premium_graphics.h"
- #include "ui/new_badges.h"
- #include "ui/rect.h"
- #include "ui/rp_widget.h"
- #include "ui/vertical_list.h"
- #include "ui/toast/toast.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/fields/input_field.h"
- #include "ui/widgets/labels.h"
- #include "ui/wrap/padding_wrap.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/wrap/vertical_layout.h"
- #include "ui/ui_utility.h"
- #include "window/window_session_controller.h"
- #include "api/api_invite_links.h"
- #include "styles/style_chat_helpers.h"
- #include "styles/style_layers.h"
- #include "styles/style_menu_icons.h"
- #include "styles/style_settings.h"
- #include "styles/style_boxes.h"
- #include "styles/style_info.h"
- #include <QtSvg/QSvgRenderer>
- namespace {
- constexpr auto kBotManagerUsername = "BotFather"_cs;
- [[nodiscard]] auto ToPositiveNumberString() {
- return rpl::map([](int count) {
- return count ? QString::number(count) : QString();
- });
- }
- [[nodiscard]] int EnableForumMinMembers(not_null<PeerData*> peer) {
- return peer->session().appConfig().get<int>(
- u"forum_upgrade_participants_min"_q,
- 200);
- }
- void AddSkip(
- not_null<Ui::VerticalLayout*> container,
- int top = st::editPeerTopButtonsLayoutSkip,
- int bottom = st::editPeerTopButtonsLayoutSkipToBottom) {
- Ui::AddSkip(container, top);
- Ui::AddDivider(container);
- Ui::AddSkip(container, bottom);
- }
- void AddButtonWithCount(
- not_null<Ui::VerticalLayout*> parent,
- rpl::producer<QString> &&text,
- rpl::producer<QString> &&count,
- Fn<void()> callback,
- Settings::IconDescriptor &&descriptor) {
- parent->add(EditPeerInfoBox::CreateButton(
- parent,
- std::move(text),
- std::move(count),
- std::move(callback),
- st::manageGroupButton,
- std::move(descriptor)));
- }
- not_null<Ui::SettingsButton*> AddButtonWithText(
- not_null<Ui::VerticalLayout*> parent,
- rpl::producer<QString> &&text,
- rpl::producer<QString> &&label,
- Fn<void()> callback,
- Settings::IconDescriptor &&descriptor) {
- return parent->add(EditPeerInfoBox::CreateButton(
- parent,
- std::move(text),
- std::move(label),
- std::move(callback),
- st::manageGroupTopButtonWithText,
- std::move(descriptor)));
- }
- void AddButtonDelete(
- not_null<Ui::VerticalLayout*> parent,
- rpl::producer<QString> &&text,
- Fn<void()> callback) {
- parent->add(EditPeerInfoBox::CreateButton(
- parent,
- std::move(text),
- rpl::single(QString()),
- std::move(callback),
- st::manageDeleteGroupButton,
- {}));
- }
- void SaveDefaultRestrictions(
- not_null<PeerData*> peer,
- ChatRestrictions rights,
- Fn<void()> done) {
- const auto api = &peer->session().api();
- const auto key = Api::RequestKey("default_restrictions", peer->id);
- const auto requestId = api->request(
- MTPmessages_EditChatDefaultBannedRights(
- peer->input,
- MTP_chatBannedRights(
- MTP_flags(
- MTPDchatBannedRights::Flags::from_raw(uint32(rights))),
- MTP_int(0)))
- ).done([=](const MTPUpdates &result) {
- api->clearModifyRequest(key);
- api->applyUpdates(result);
- done();
- }).fail([=](const MTP::Error &error) {
- api->clearModifyRequest(key);
- if (error.type() != u"CHAT_NOT_MODIFIED"_q) {
- return;
- }
- if (const auto chat = peer->asChat()) {
- chat->setDefaultRestrictions(rights);
- } else if (const auto channel = peer->asChannel()) {
- channel->setDefaultRestrictions(rights);
- } else {
- Unexpected("Peer in ApiWrap::saveDefaultRestrictions.");
- }
- done();
- }).send();
- api->registerModifyRequest(key, requestId);
- }
- void SaveSlowmodeSeconds(
- not_null<ChannelData*> channel,
- int seconds,
- Fn<void()> done) {
- const auto api = &channel->session().api();
- const auto key = Api::RequestKey("slowmode_seconds", channel->id);
- const auto requestId = api->request(MTPchannels_ToggleSlowMode(
- channel->inputChannel,
- MTP_int(seconds)
- )).done([=](const MTPUpdates &result) {
- api->clearModifyRequest(key);
- api->applyUpdates(result);
- channel->setSlowmodeSeconds(seconds);
- done();
- }).fail([=](const MTP::Error &error) {
- api->clearModifyRequest(key);
- if (error.type() != u"CHAT_NOT_MODIFIED"_q) {
- return;
- }
- channel->setSlowmodeSeconds(seconds);
- done();
- }).send();
- api->registerModifyRequest(key, requestId);
- }
- void SaveStarsPerMessage(
- not_null<ChannelData*> channel,
- int starsPerMessage,
- Fn<void()> done) {
- const auto api = &channel->session().api();
- const auto key = Api::RequestKey("stars_per_message", channel->id);
- const auto requestId = api->request(MTPchannels_UpdatePaidMessagesPrice(
- channel->inputChannel,
- MTP_long(starsPerMessage)
- )).done([=](const MTPUpdates &result) {
- api->clearModifyRequest(key);
- api->applyUpdates(result);
- channel->setStarsPerMessage(starsPerMessage);
- done();
- }).fail([=](const MTP::Error &error) {
- api->clearModifyRequest(key);
- if (error.type() != u"CHAT_NOT_MODIFIED"_q) {
- return;
- }
- channel->setStarsPerMessage(starsPerMessage);
- done();
- }).send();
- api->registerModifyRequest(key, requestId);
- }
- void SaveBoostsUnrestrict(
- not_null<ChannelData*> channel,
- int boostsUnrestrict,
- Fn<void()> done) {
- const auto api = &channel->session().api();
- const auto key = Api::RequestKey("boosts_unrestrict", channel->id);
- const auto requestId = api->request(
- MTPchannels_SetBoostsToUnblockRestrictions(
- channel->inputChannel,
- MTP_int(boostsUnrestrict))
- ).done([=](const MTPUpdates &result) {
- api->clearModifyRequest(key);
- api->applyUpdates(result);
- channel->setBoostsUnrestrict(
- channel->boostsApplied(),
- boostsUnrestrict);
- done();
- }).fail([=](const MTP::Error &error) {
- api->clearModifyRequest(key);
- if (error.type() != u"CHAT_NOT_MODIFIED"_q) {
- return;
- }
- channel->setBoostsUnrestrict(
- channel->boostsApplied(),
- boostsUnrestrict);
- done();
- }).send();
- api->registerModifyRequest(key, requestId);
- }
- void ShowEditPermissions(
- not_null<Window::SessionNavigation*> navigation,
- not_null<PeerData*> peer) {
- auto createBox = [=](not_null<Ui::GenericBox*> box) {
- const auto saving = box->lifetime().make_state<int>(0);
- const auto save = [=](
- not_null<PeerData*> peer,
- EditPeerPermissionsBoxResult result) {
- Expects(result.slowmodeSeconds == 0 || peer->isChannel());
- const auto close = crl::guard(box, [=] { box->closeBox(); });
- SaveDefaultRestrictions(
- peer,
- result.rights,
- close);
- if (const auto channel = peer->asChannel()) {
- SaveSlowmodeSeconds(channel, result.slowmodeSeconds, close);
- SaveBoostsUnrestrict(
- channel,
- result.boostsUnrestrict,
- close);
- SaveStarsPerMessage(channel, result.starsPerMessage, close);
- }
- };
- auto done = [=](EditPeerPermissionsBoxResult result) {
- if (*saving) {
- return;
- }
- *saving = true;
- const auto saveFor = peer->migrateToOrMe();
- const auto chat = saveFor->asChat();
- if (!chat
- || (!result.slowmodeSeconds
- && !result.boostsUnrestrict
- && !result.starsPerMessage)) {
- save(saveFor, result);
- return;
- }
- const auto api = &peer->session().api();
- api->migrateChat(chat, [=](not_null<ChannelData*> channel) {
- save(channel, result);
- }, [=](const QString &) {
- *saving = false;
- });
- };
- ShowEditPeerPermissionsBox(box, navigation, peer, std::move(done));
- };
- navigation->parentController()->show(Box(std::move(createBox)));
- }
- class Controller : public base::has_weak_ptr {
- public:
- Controller(
- not_null<Window::SessionNavigation*> navigation,
- not_null<Ui::BoxContent*> box,
- not_null<PeerData*> peer);
- ~Controller();
- [[nodiscard]] object_ptr<Ui::VerticalLayout> createContent();
- void setFocus();
- private:
- struct Controls {
- Ui::InputField *title = nullptr;
- Ui::InputField *description = nullptr;
- Ui::UserpicButton *photo = nullptr;
- rpl::lifetime initialPhotoImageWaiting;
- Ui::VerticalLayout *buttonsLayout = nullptr;
- Ui::SettingsButton *forumToggle = nullptr;
- bool forumToggleLocked = false;
- bool levelRequested = false;
- Ui::SlideWrap<> *historyVisibilityWrap = nullptr;
- };
- struct Saving {
- std::optional<QString> username;
- std::optional<std::vector<QString>> usernamesOrder;
- std::optional<QString> title;
- std::optional<QString> description;
- std::optional<bool> hiddenPreHistory;
- std::optional<bool> forum;
- std::optional<bool> signatures;
- std::optional<bool> signatureProfiles;
- std::optional<bool> noForwards;
- std::optional<bool> joinToWrite;
- std::optional<bool> requestToJoin;
- std::optional<ChannelData*> linkedChat;
- };
- [[nodiscard]] object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
- [[nodiscard]] object_ptr<Ui::RpWidget> createTitleEdit();
- [[nodiscard]] object_ptr<Ui::RpWidget> createPhotoEdit();
- [[nodiscard]] object_ptr<Ui::RpWidget> createDescriptionEdit();
- [[nodiscard]] object_ptr<Ui::RpWidget> createManageGroupButtons();
- [[nodiscard]] object_ptr<Ui::RpWidget> createStickersEdit();
- [[nodiscard]] bool canEditInformation() const;
- [[nodiscard]] bool canEditReactions() const;
- void refreshHistoryVisibility();
- void refreshForumToggleLocked();
- void showEditPeerTypeBox(
- std::optional<rpl::producer<QString>> error = {});
- void showEditLinkedChatBox();
- void fillPrivacyTypeButton();
- void fillLinkedChatButton();
- //void fillInviteLinkButton();
- void fillForumButton();
- void fillColorIndexButton();
- void fillSignaturesButton();
- void fillHistoryVisibilityButton();
- void fillManageSection();
- void fillPendingRequestsButton();
- void fillBotUsernamesButton();
- void fillBotCurrencyButton();
- void fillBotCreditsButton();
- void fillBotAffiliateProgram();
- void fillBotEditIntroButton();
- void fillBotEditCommandsButton();
- void fillBotEditSettingsButton();
- void fillBotVerifyAccounts();
- void submitTitle();
- void submitDescription();
- void deleteWithConfirmation();
- void deleteChannel();
- void editReactions();
- [[nodiscard]] std::optional<Saving> validate() const;
- [[nodiscard]] bool validateUsernamesOrder(Saving &to) const;
- [[nodiscard]] bool validateUsername(Saving &to) const;
- [[nodiscard]] bool validateLinkedChat(Saving &to) const;
- [[nodiscard]] bool validateTitle(Saving &to) const;
- [[nodiscard]] bool validateDescription(Saving &to) const;
- [[nodiscard]] bool validateHistoryVisibility(Saving &to) const;
- [[nodiscard]] bool validateForum(Saving &to) const;
- [[nodiscard]] bool validateSignatures(Saving &to) const;
- [[nodiscard]] bool validateForwards(Saving &to) const;
- [[nodiscard]] bool validateJoinToWrite(Saving &to) const;
- [[nodiscard]] bool validateRequestToJoin(Saving &to) const;
- void save();
- void saveUsernamesOrder();
- void saveUsername();
- void saveLinkedChat();
- void saveTitle();
- void saveDescription();
- void saveHistoryVisibility();
- void saveForum();
- void saveSignatures();
- void saveForwards();
- void saveJoinToWrite();
- void saveRequestToJoin();
- void savePhoto();
- void pushSaveStage(FnMut<void()> &&lambda);
- void continueSave();
- void cancelSave();
- void toggleBotManager(const QString &command);
- void togglePreHistoryHidden(
- not_null<ChannelData*> channel,
- bool hidden,
- Fn<void()> done,
- Fn<void()> fail);
- void subscribeToMigration();
- void migrate(not_null<ChannelData*> channel);
- std::optional<ChannelData*> _linkedChatSavedValue;
- ChannelData *_linkedChatOriginalValue = nullptr;
- bool _channelHasLocationOriginalValue = false;
- std::optional<HistoryVisibility> _historyVisibilitySavedValue;
- std::optional<EditPeerTypeData> _typeDataSavedValue;
- std::optional<bool> _forumSavedValue;
- std::optional<bool> _signaturesSavedValue;
- std::optional<bool> _signatureProfilesSavedValue;
- const not_null<Window::SessionNavigation*> _navigation;
- const not_null<Ui::BoxContent*> _box;
- not_null<PeerData*> _peer;
- MTP::Sender _api;
- const bool _isGroup = false;
- const bool _isBot = false;
- base::unique_qptr<Ui::VerticalLayout> _wrap;
- Controls _controls;
- std::deque<FnMut<void()>> _saveStagesQueue;
- Saving _savingData;
- struct PrivacyAndForwards {
- Privacy privacy;
- bool noForwards = false;
- };
- const rpl::event_stream<PrivacyAndForwards> _privacyTypeUpdates;
- const rpl::event_stream<ChannelData*> _linkedChatUpdates;
- mtpRequestId _linkedChatsRequestId = 0;
- rpl::lifetime _lifetime;
- };
- Controller::Controller(
- not_null<Window::SessionNavigation*> navigation,
- not_null<Ui::BoxContent*> box,
- not_null<PeerData*> peer)
- : _navigation(navigation)
- , _box(box)
- , _peer(peer)
- , _api(&_peer->session().mtp())
- , _isGroup(_peer->isChat() || _peer->isMegagroup())
- , _isBot(_peer->isUser() && _peer->asUser()->botInfo) {
- _box->setTitle(_isBot
- ? tr::lng_edit_bot_title()
- : _isGroup
- ? tr::lng_edit_group()
- : tr::lng_edit_channel_title());
- _box->addButton(tr::lng_settings_save(), [=] {
- save();
- });
- _box->addButton(tr::lng_cancel(), [=] {
- _box->closeBox();
- });
- subscribeToMigration();
- _peer->updateFull();
- }
- Controller::~Controller() = default;
- void Controller::subscribeToMigration() {
- SubscribeToMigration(
- _peer,
- _lifetime,
- [=](not_null<ChannelData*> channel) { migrate(channel); });
- }
- void Controller::migrate(not_null<ChannelData*> channel) {
- _peer = channel;
- _peer->updateFull();
- }
- object_ptr<Ui::VerticalLayout> Controller::createContent() {
- auto result = object_ptr<Ui::VerticalLayout>(_box);
- _wrap.reset(result.data());
- _controls = Controls();
- _wrap->add(createPhotoAndTitleEdit());
- _wrap->add(createDescriptionEdit());
- _wrap->add(createManageGroupButtons());
- return result;
- }
- void Controller::setFocus() {
- if (_controls.title) {
- _controls.title->setFocusFast();
- }
- }
- object_ptr<Ui::RpWidget> Controller::createPhotoAndTitleEdit() {
- Expects(_wrap != nullptr);
- if (!canEditInformation()) {
- return nullptr;
- }
- auto result = object_ptr<Ui::RpWidget>(_wrap);
- const auto container = result.data();
- const auto photoWrap = Ui::AttachParentChild(
- container,
- createPhotoEdit());
- const auto titleEdit = Ui::AttachParentChild(
- container,
- createTitleEdit());
- photoWrap->heightValue(
- ) | rpl::start_with_next([container](int height) {
- container->resize(container->width(), height);
- }, photoWrap->lifetime());
- container->widthValue(
- ) | rpl::start_with_next([titleEdit](int width) {
- const auto left = st::editPeerPhotoMargins.left()
- + st::defaultUserpicButton.size.width();
- titleEdit->resizeToWidth(width - left);
- titleEdit->moveToLeft(left, 0, width);
- }, titleEdit->lifetime());
- return result;
- }
- object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
- Expects(_wrap != nullptr);
- using PhotoWrap = Ui::PaddingWrap<Ui::UserpicButton>;
- auto photoWrap = object_ptr<PhotoWrap>(
- _wrap,
- object_ptr<Ui::UserpicButton>(
- _wrap,
- _navigation->parentController(),
- _peer,
- Ui::UserpicButton::Role::ChangePhoto,
- Ui::UserpicButton::Source::PeerPhoto,
- st::defaultUserpicButton),
- st::editPeerPhotoMargins);
- _controls.photo = photoWrap->entity();
- _controls.photo->showCustomOnChosen();
- return photoWrap;
- }
- object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
- Expects(_wrap != nullptr);
- auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
- _wrap,
- object_ptr<Ui::InputField>(
- _wrap,
- st::editPeerTitleField,
- (_isBot
- ? tr::lng_dlg_new_bot_name
- : _isGroup
- ? tr::lng_dlg_new_group_name
- : tr::lng_dlg_new_channel_name)(),
- _peer->name()),
- st::editPeerTitleMargins);
- result->entity()->setMaxLength(Ui::EditPeer::kMaxGroupChannelTitle);
- result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
- result->entity()->setInstantReplacesEnabled(
- Core::App().settings().replaceEmojiValue());
- Ui::Emoji::SuggestionsController::Init(
- _wrap->window(),
- result->entity(),
- &_peer->session());
- result->entity()->submits(
- ) | rpl::start_with_next([=] {
- submitTitle();
- }, result->entity()->lifetime());
- {
- const auto field = result->entity();
- const auto container = _box->getDelegate()->outerContainer();
- using Selector = ChatHelpers::TabbedSelector;
- using PanelPtr = base::unique_qptr<ChatHelpers::TabbedPanel>;
- const auto emojiPanelPtr = field->lifetime().make_state<PanelPtr>(
- base::make_unique_q<ChatHelpers::TabbedPanel>(
- container,
- ChatHelpers::TabbedPanelDescriptor{
- .ownedSelector = object_ptr<Selector>(
- nullptr,
- ChatHelpers::TabbedSelectorDescriptor{
- .show = _navigation->uiShow(),
- .st = st::defaultComposeControls.tabbed,
- .level = Window::GifPauseReason::Layer,
- .mode = Selector::Mode::PeerTitle,
- }),
- }));
- const auto emojiPanel = emojiPanelPtr->get();
- emojiPanel->setDesiredHeightValues(
- 1.,
- st::emojiPanMinHeight / 2,
- st::emojiPanMinHeight);
- emojiPanel->hide();
- emojiPanel->selector()->setCurrentPeer(_peer);
- emojiPanel->selector()->emojiChosen(
- ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
- Ui::InsertEmojiAtCursor(field->textCursor(), data.emoji);
- field->setFocus();
- }, field->lifetime());
- emojiPanel->setDropDown(true);
- const auto emojiToggle = Ui::CreateChild<Ui::EmojiButton>(
- field,
- st::defaultComposeControls.files.emoji);
- emojiToggle->show();
- emojiToggle->installEventFilter(emojiPanel);
- emojiToggle->addClickHandler([=] { emojiPanel->toggleAnimated(); });
- const auto updateEmojiPanelGeometry = [=] {
- const auto parent = emojiPanel->parentWidget();
- const auto global = emojiToggle->mapToGlobal({ 0, 0 });
- const auto local = parent->mapFromGlobal(global);
- emojiPanel->moveTopRight(
- local.y() + emojiToggle->height(),
- local.x() + emojiToggle->width() * 3);
- };
- field->lifetime().make_state<base::unique_qptr<QObject>>([&] {
- return base::install_event_filter(container, [=](
- not_null<QEvent*> event) {
- const auto type = event->type();
- if (type == QEvent::Move || type == QEvent::Resize) {
- crl::on_main(field, [=] { updateEmojiPanelGeometry(); });
- }
- return base::EventFilterResult::Continue;
- });
- }());
- field->widthValue() | rpl::start_with_next([=](int width) {
- const auto &p = st::editPeerTitleEmojiPosition;
- emojiToggle->moveToRight(p.x(), p.y(), width);
- updateEmojiPanelGeometry();
- }, emojiToggle->lifetime());
- base::install_event_filter(emojiToggle, [=](not_null<QEvent*> event) {
- if (event->type() == QEvent::Enter) {
- updateEmojiPanelGeometry();
- }
- return base::EventFilterResult::Continue;
- });
- }
- _controls.title = result->entity();
- return result;
- }
- object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
- Expects(_wrap != nullptr);
- if (!canEditInformation()) {
- return nullptr;
- }
- auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
- _wrap,
- object_ptr<Ui::InputField>(
- _wrap,
- st::editPeerDescription,
- Ui::InputField::Mode::MultiLine,
- tr::lng_create_group_description(),
- _peer->about()),
- st::editPeerDescriptionMargins);
- result->entity()->setMaxLength(Ui::EditPeer::kMaxChannelDescription);
- result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
- result->entity()->setInstantReplacesEnabled(
- Core::App().settings().replaceEmojiValue());
- result->entity()->setSubmitSettings(
- Core::App().settings().sendSubmitWay());
- Ui::Emoji::SuggestionsController::Init(
- _wrap->window(),
- result->entity(),
- &_peer->session());
- result->entity()->submits(
- ) | rpl::start_with_next([=] {
- submitDescription();
- }, result->entity()->lifetime());
- _controls.description = result->entity();
- return result;
- }
- object_ptr<Ui::RpWidget> Controller::createManageGroupButtons() {
- Expects(_wrap != nullptr);
- auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
- _wrap,
- object_ptr<Ui::VerticalLayout>(_wrap),
- st::editPeerBottomButtonsLayoutMargins);
- _controls.buttonsLayout = result->entity();
- fillManageSection();
- return result;
- }
- object_ptr<Ui::RpWidget> Controller::createStickersEdit() {
- Expects(_wrap != nullptr);
- const auto channel = _peer->asChannel();
- const auto bottomSkip = st::editPeerTopButtonsLayoutSkipCustomBottom;
- auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _wrap,
- object_ptr<Ui::VerticalLayout>(_wrap));
- const auto container = result->entity();
- Ui::AddSubsectionTitle(
- container,
- tr::lng_group_stickers(),
- { 0, st::defaultSubsectionTitlePadding.top() - bottomSkip, 0, 0 });
- AddButtonWithCount(
- container,
- tr::lng_group_stickers_add(),
- rpl::single(QString()), //Empty count.
- [=, controller = _navigation->parentController()] {
- const auto isEmoji = false;
- controller->show(
- Box<StickersBox>(controller->uiShow(), channel, isEmoji));
- },
- { &st::menuIconStickers });
- Ui::AddSkip(container, bottomSkip);
- Ui::AddDividerText(
- container,
- tr::lng_group_stickers_description());
- Ui::AddSkip(container, bottomSkip);
- return result;
- }
- bool Controller::canEditInformation() const {
- if (_isBot) {
- return _peer->asUser()->botInfo->canEditInformation;
- } else if (const auto channel = _peer->asChannel()) {
- return channel->canEditInformation();
- } else if (const auto chat = _peer->asChat()) {
- return chat->canEditInformation();
- }
- return false;
- }
- bool Controller::canEditReactions() const {
- if (const auto channel = _peer->asChannel()) {
- return channel->amCreator()
- || (channel->adminRights() & ChatAdminRight::ChangeInfo);
- } else if (const auto chat = _peer->asChat()) {
- return chat->amCreator()
- || (chat->adminRights() & ChatAdminRight::ChangeInfo);
- }
- return false;
- }
- void Controller::refreshHistoryVisibility() {
- if (!_controls.historyVisibilityWrap) {
- return;
- }
- const auto withUsername = _typeDataSavedValue
- && (_typeDataSavedValue->privacy == Privacy::HasUsername);
- _controls.historyVisibilityWrap->toggle(
- (!withUsername
- && !_channelHasLocationOriginalValue
- && (!_linkedChatSavedValue || !*_linkedChatSavedValue)
- && (!_forumSavedValue || !*_forumSavedValue)),
- anim::type::instant);
- }
- void Controller::showEditPeerTypeBox(
- std::optional<rpl::producer<QString>> error) {
- const auto boxCallback = crl::guard(this, [=](EditPeerTypeData data) {
- _privacyTypeUpdates.fire({ data.privacy, data.noForwards });
- _typeDataSavedValue = data;
- refreshHistoryVisibility();
- });
- _typeDataSavedValue->hasLinkedChat
- = (_linkedChatSavedValue.value_or(nullptr) != nullptr);
- const auto box = _navigation->parentController()->show(
- Box<EditPeerTypeBox>(
- _navigation,
- _peer,
- _channelHasLocationOriginalValue,
- boxCallback,
- _typeDataSavedValue,
- error));
- box->boxClosing(
- ) | rpl::start_with_next([peer = _peer] {
- peer->session().api().usernames().requestToCache(peer);
- }, box->lifetime());
- }
- void Controller::showEditLinkedChatBox() {
- Expects(_peer->isChannel());
- if (_forumSavedValue && *_forumSavedValue) {
- ShowForumForDiscussionError(_navigation);
- return;
- }
- const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
- const auto channel = _peer->asChannel();
- const auto callback = [=](ChannelData *result) {
- if (*box) {
- (*box)->closeBox();
- }
- *_linkedChatSavedValue = result;
- _linkedChatUpdates.fire_copy(result);
- refreshHistoryVisibility();
- refreshForumToggleLocked();
- };
- const auto canEdit = channel->isBroadcast()
- ? channel->canEditInformation()
- : (channel->canPinMessages()
- && (channel->amCreator() || channel->adminRights() != 0)
- && (!channel->hiddenPreHistory()
- || channel->canEditPreHistoryHidden()));
- if (const auto chat = *_linkedChatSavedValue) {
- *box = _navigation->parentController()->show(EditLinkedChatBox(
- _navigation,
- channel,
- chat,
- canEdit,
- callback));
- return;
- } else if (!canEdit || _linkedChatsRequestId) {
- return;
- } else if (channel->isMegagroup()) {
- if (_forumSavedValue
- && *_forumSavedValue
- && _linkedChatOriginalValue) {
- ShowForumForDiscussionError(_navigation);
- } else {
- // Restore original linked channel.
- callback(_linkedChatOriginalValue);
- }
- return;
- }
- _linkedChatsRequestId = _api.request(
- MTPchannels_GetGroupsForDiscussion()
- ).done([=](const MTPmessages_Chats &result) {
- _linkedChatsRequestId = 0;
- const auto list = result.match([&](const auto &data) {
- return data.vchats().v;
- });
- auto chats = std::vector<not_null<PeerData*>>();
- chats.reserve(list.size());
- for (const auto &item : list) {
- chats.emplace_back(_peer->owner().processChat(item));
- }
- *box = _navigation->parentController()->show(EditLinkedChatBox(
- _navigation,
- channel,
- std::move(chats),
- callback));
- }).fail([=] {
- _linkedChatsRequestId = 0;
- }).send();
- }
- void Controller::fillPrivacyTypeButton() {
- Expects(_controls.buttonsLayout != nullptr);
- // Create Privacy Button.
- const auto hasLocation = _peer->isChannel()
- && _peer->asChannel()->hasLocation();
- _typeDataSavedValue = EditPeerTypeData{
- .privacy = ((_peer->isChannel()
- && _peer->asChannel()->hasUsername())
- ? Privacy::HasUsername
- : Privacy::NoUsername),
- .username = (_peer->isChannel()
- ? _peer->asChannel()->editableUsername()
- : QString()),
- .usernamesOrder = (_peer->isChannel()
- ? _peer->asChannel()->usernames()
- : std::vector<QString>()),
- .noForwards = !_peer->allowsForwarding(),
- .joinToWrite = (_peer->isMegagroup()
- && _peer->asChannel()->joinToWrite()),
- .requestToJoin = (_peer->isMegagroup()
- && _peer->asChannel()->requestToJoin()),
- };
- const auto isGroup = (_peer->isChat() || _peer->isMegagroup());
- AddButtonWithText(
- _controls.buttonsLayout,
- (hasLocation
- ? tr::lng_manage_peer_link_type
- : isGroup
- ? tr::lng_manage_peer_group_type
- : tr::lng_manage_peer_channel_type)(),
- _privacyTypeUpdates.events(
- ) | rpl::map([=](PrivacyAndForwards data) {
- const auto flag = data.privacy;
- if (flag == Privacy::HasUsername) {
- _peer->session().api().usernames().requestToCache(_peer);
- }
- return (flag == Privacy::HasUsername)
- ? (hasLocation
- ? tr::lng_manage_peer_link_permanent
- : isGroup
- ? tr::lng_manage_public_group_title
- : tr::lng_manage_public_peer_title)()
- : (hasLocation
- ? tr::lng_manage_peer_link_invite
- : ((!data.noForwards) && isGroup)
- ? tr::lng_manage_private_group_title
- : ((!data.noForwards) && !isGroup)
- ? tr::lng_manage_private_peer_title
- : isGroup
- ? tr::lng_manage_private_group_noforwards_title
- : tr::lng_manage_private_peer_noforwards_title)();
- }) | rpl::flatten_latest(),
- [=] { showEditPeerTypeBox(); },
- { &st::menuIconCustomize });
- _privacyTypeUpdates.fire_copy({
- _typeDataSavedValue->privacy,
- _typeDataSavedValue->noForwards,
- });
- }
- void Controller::fillLinkedChatButton() {
- Expects(_controls.buttonsLayout != nullptr);
- _linkedChatSavedValue = _linkedChatOriginalValue = _peer->isChannel()
- ? _peer->asChannel()->linkedChat()
- : nullptr;
- const auto isGroup = (_peer->isChat() || _peer->isMegagroup());
- auto text = !isGroup
- ? tr::lng_manage_discussion_group()
- : rpl::combine(
- tr::lng_manage_linked_channel(),
- tr::lng_manage_linked_channel_restore(),
- _linkedChatUpdates.events()
- ) | rpl::map([=](
- const QString &edit,
- const QString &restore,
- ChannelData *chat) {
- return chat ? edit : restore;
- });
- auto label = isGroup
- ? _linkedChatUpdates.events(
- ) | rpl::map([](ChannelData *chat) {
- return chat ? chat->name() : QString();
- }) | rpl::type_erased()
- : rpl::combine(
- tr::lng_manage_discussion_group_add(),
- _linkedChatUpdates.events()
- ) | rpl::map([=](const QString &add, ChannelData *chat) {
- return chat ? chat->name() : add;
- }) | rpl::type_erased();
- AddButtonWithText(
- _controls.buttonsLayout,
- std::move(text),
- std::move(label),
- [=] { showEditLinkedChatBox(); },
- { isGroup ? &st::menuIconChannel : &st::menuIconGroups });
- _linkedChatUpdates.fire_copy(*_linkedChatSavedValue);
- }
- //
- //void Controller::fillInviteLinkButton() {
- // Expects(_controls.buttonsLayout != nullptr);
- //
- // const auto buttonCallback = [=] {
- // Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther);
- // };
- //
- // AddButtonWithText(
- // _controls.buttonsLayout,
- // tr::lng_profile_invite_link_section(),
- // rpl::single(QString()), //Empty text.
- // buttonCallback);
- //}
- void Controller::fillForumButton() {
- Expects(_controls.buttonsLayout != nullptr);
- const auto button = _controls.forumToggle = _controls.buttonsLayout->add(
- EditPeerInfoBox::CreateButton(
- _controls.buttonsLayout,
- tr::lng_forum_topics_switch(),
- rpl::single(QString()),
- [] {},
- st::manageGroupTopicsButton,
- { &st::menuIconTopics }));
- const auto unlocks = std::make_shared<rpl::event_stream<bool>>();
- button->toggleOn(
- rpl::single(_peer->isForum()) | rpl::then(unlocks->events())
- )->toggledValue(
- ) | rpl::start_with_next([=](bool toggled) {
- if (_controls.forumToggleLocked && toggled) {
- unlocks->fire(false);
- if (_linkedChatSavedValue && *_linkedChatSavedValue) {
- ShowForumForDiscussionError(_navigation);
- } else {
- _navigation->showToast(
- tr::lng_forum_topics_not_enough(
- tr::now,
- lt_count,
- EnableForumMinMembers(_peer),
- Ui::Text::RichLangValue));
- }
- } else {
- _forumSavedValue = toggled;
- if (toggled) {
- _savingData.hiddenPreHistory = false;
- }
- refreshHistoryVisibility();
- }
- }, _controls.buttonsLayout->lifetime());
- refreshForumToggleLocked();
- }
- void Controller::refreshForumToggleLocked() {
- if (!_controls.forumToggle) {
- return;
- }
- const auto limit = EnableForumMinMembers(_peer);
- const auto chat = _peer->asChat();
- const auto channel = _peer->asChannel();
- const auto notenough = !_peer->isForum()
- && ((chat ? chat->count : channel->membersCount()) < limit);
- const auto linked = _linkedChatSavedValue
- && *_linkedChatSavedValue;
- const auto locked = _controls.forumToggleLocked = notenough || linked;
- _controls.forumToggle->setToggleLocked(locked);
- }
- void Controller::fillColorIndexButton() {
- Expects(_controls.buttonsLayout != nullptr);
- const auto show = _navigation->uiShow();
- AddPeerColorButton(
- _controls.buttonsLayout,
- _navigation->uiShow(),
- _peer,
- st::managePeerColorsButton);
- }
- void Controller::fillSignaturesButton() {
- Expects(_controls.buttonsLayout != nullptr);
- const auto channel = _peer->asChannel();
- if (!channel) {
- return;
- }
- const auto signs = AddButtonWithText(
- _controls.buttonsLayout,
- tr::lng_edit_sign_messages(),
- rpl::single(QString()),
- [] {},
- { &st::menuIconSigned }
- )->toggleOn(rpl::single(channel->addsSignature()));
- const auto profiles = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
- _controls.buttonsLayout,
- EditPeerInfoBox::CreateButton(
- _controls.buttonsLayout,
- tr::lng_edit_sign_profiles(),
- rpl::single(QString()),
- [] {},
- st::manageGroupTopButtonWithText,
- { &st::menuIconProfile })));
- profiles->toggleOn(signs->toggledValue());
- profiles->finishAnimating();
- profiles->entity()->toggleOn(rpl::single(
- channel->addsSignature() && channel->signatureProfiles()
- ))->toggledValue(
- ) | rpl::start_with_next([=](bool toggled) {
- _signatureProfilesSavedValue = toggled;
- }, profiles->entity()->lifetime());
- signs->toggledValue(
- ) | rpl::start_with_next([=](bool toggled) {
- _signaturesSavedValue = toggled;
- if (!toggled) {
- _signatureProfilesSavedValue = false;
- }
- }, _controls.buttonsLayout->lifetime());
- Ui::AddSkip(_controls.buttonsLayout);
- Ui::AddDividerText(
- _controls.buttonsLayout,
- rpl::conditional(
- signs->toggledValue(),
- tr::lng_edit_sign_profiles_about(Ui::Text::WithEntities),
- tr::lng_edit_sign_messages_about(Ui::Text::WithEntities)));
- Ui::AddSkip(_controls.buttonsLayout);
- }
- void Controller::fillHistoryVisibilityButton() {
- Expects(_controls.buttonsLayout != nullptr);
- const auto wrapLayout = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _controls.buttonsLayout,
- object_ptr<Ui::VerticalLayout>(_controls.buttonsLayout),
- st::boxOptionListPadding)); // Empty margins.
- _controls.historyVisibilityWrap = wrapLayout;
- const auto channel = _peer->asChannel();
- const auto container = wrapLayout->entity();
- _historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory())
- ? HistoryVisibility::Hidden
- : HistoryVisibility::Visible;
- _channelHasLocationOriginalValue = channel && channel->hasLocation();
- const auto updateHistoryVisibility
- = std::make_shared<rpl::event_stream<HistoryVisibility>>();
- const auto boxCallback = crl::guard(this, [=](HistoryVisibility checked) {
- updateHistoryVisibility->fire(std::move(checked));
- _historyVisibilitySavedValue = checked;
- });
- const auto buttonCallback = [=] {
- _peer->updateFull();
- const auto canEdit = [&] {
- if (const auto chat = _peer->asChat()) {
- return chat->canEditPreHistoryHidden();
- } else if (const auto channel = _peer->asChannel()) {
- return channel->canEditPreHistoryHidden();
- }
- Unexpected("User in HistoryVisibilityEdit.");
- }();
- if (!canEdit) {
- return;
- }
- _navigation->parentController()->show(Box(
- EditPeerHistoryVisibilityBox,
- _peer->isChat(),
- boxCallback,
- *_historyVisibilitySavedValue));
- };
- AddButtonWithText(
- container,
- tr::lng_manage_history_visibility_title(),
- updateHistoryVisibility->events(
- ) | rpl::map([](HistoryVisibility flag) {
- return (HistoryVisibility::Visible == flag
- ? tr::lng_manage_history_visibility_shown
- : tr::lng_manage_history_visibility_hidden)();
- }) | rpl::flatten_latest(),
- buttonCallback,
- { &st::menuIconChatBubble });
- updateHistoryVisibility->fire_copy(*_historyVisibilitySavedValue);
- refreshHistoryVisibility();
- }
- void Controller::fillManageSection() {
- Expects(_controls.buttonsLayout != nullptr);
- if (_isBot) {
- const auto &container = _controls.buttonsLayout;
- ::AddSkip(container, 0);
- fillBotUsernamesButton();
- fillBotCurrencyButton();
- fillBotCreditsButton();
- fillBotAffiliateProgram();
- fillBotEditIntroButton();
- fillBotEditCommandsButton();
- fillBotEditSettingsButton();
- Ui::AddSkip(
- container,
- st::editPeerTopButtonsLayoutSkipCustomBottom);
- container->add(object_ptr<Ui::DividerLabel>(
- container,
- object_ptr<Ui::FlatLabel>(
- container,
- tr::lng_manage_peer_bot_about(
- lt_bot,
- rpl::single(Ui::Text::Link(
- '@' + kBotManagerUsername.utf16(),
- _peer->session().createInternalLinkFull(
- kBotManagerUsername.utf16()))),
- Ui::Text::RichLangValue),
- st::boxDividerLabel),
- st::defaultBoxDividerLabelPadding));
- fillBotVerifyAccounts();
- return;
- }
- const auto chat = _peer->asChat();
- const auto channel = _peer->asChannel();
- const auto isChannel = (!chat);
- if (!chat && !channel) {
- return;
- }
- const auto canEditType = isChannel
- ? channel->amCreator()
- : chat->amCreator();
- const auto canEditSignatures = isChannel
- && channel->canEditSignatures()
- && !channel->isMegagroup();
- const auto canEditPreHistoryHidden = isChannel
- ? channel->canEditPreHistoryHidden()
- : chat->canEditPreHistoryHidden();
- const auto canEditForum = isChannel
- ? (channel->isMegagroup() && channel->amCreator())
- : chat->amCreator();
- const auto canEditPermissions = isChannel
- ? channel->canEditPermissions()
- : chat->canEditPermissions();
- const auto canEditInviteLinks = isChannel
- ? channel->canHaveInviteLink()
- : chat->canHaveInviteLink();
- const auto canViewAdmins = isChannel
- ? channel->canViewAdmins()
- : chat->amIn();
- const auto canViewMembers = isChannel
- ? channel->canViewMembers()
- : chat->amIn();
- const auto canViewKicked = isChannel
- && (channel->isMegagroup()
- ? (channel->isBroadcast() || channel->isGigagroup())
- : true);
- const auto hasRecentActions = isChannel
- && (channel->hasAdminRights() || channel->amCreator());
- const auto hasStarRef = Info::BotStarRef::Join::Allowed(_peer)
- && isChannel
- && channel->canPostMessages();
- const auto canEditStickers = isChannel && channel->canEditStickers();
- const auto canDeleteChannel = isChannel && channel->canDelete();
- const auto canEditColorIndex = isChannel && channel->canEditEmoji();
- const auto canViewOrEditLinkedChat = isChannel
- && (channel->linkedChat()
- || (channel->isBroadcast() && channel->canEditInformation()));
- ::AddSkip(_controls.buttonsLayout, 0);
- if (canEditType) {
- fillPrivacyTypeButton();
- //} else if (canEditInviteLinks) {
- // fillInviteLinkButton();
- }
- if (canViewOrEditLinkedChat) {
- fillLinkedChatButton();
- }
- if (canEditPreHistoryHidden) {
- fillHistoryVisibilityButton();
- }
- if (canEditForum) {
- fillForumButton();
- }
- if (canEditColorIndex) {
- fillColorIndexButton();
- }
- if (canEditSignatures) {
- fillSignaturesButton();
- } else if (canEditPreHistoryHidden
- || canEditForum
- || canEditColorIndex
- //|| canEditInviteLinks
- || canViewOrEditLinkedChat
- || canEditType) {
- ::AddSkip(_controls.buttonsLayout);
- }
- if (canEditReactions()) {
- auto allowedReactions = Info::Profile::MigratedOrMeValue(
- _peer
- ) | rpl::map([=](not_null<PeerData*> peer) {
- return peer->session().changes().peerFlagsValue(
- peer,
- Data::PeerUpdate::Flag::Reactions
- ) | rpl::map([=] {
- return Data::PeerAllowedReactions(peer);
- });
- }) | rpl::flatten_latest();
- auto label = std::move(
- allowedReactions
- ) | rpl::map([=](const Data::AllowedReactions &allowed) {
- const auto some = int(allowed.some.size());
- return (allowed.type != Data::AllowedReactionsType::Some)
- ? tr::lng_manage_peer_reactions_on(tr::now)
- : some
- ? QString::number(some)
- : tr::lng_manage_peer_reactions_off(tr::now);
- });
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_reactions(),
- std::move(label),
- [=] { editReactions(); },
- { &st::menuIconGroupReactions });
- }
- if (canEditPermissions) {
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_permissions(),
- Info::Profile::MigratedOrMeValue(
- _peer
- ) | rpl::map([=](not_null<PeerData*> peer) {
- return Info::Profile::RestrictionsCountValue(
- peer
- ) | rpl::map([=](int count) {
- return QString::number(count)
- + QString("/")
- + QString::number(int(Data::ListOfRestrictions(
- { .isForum = peer->isForum() }).size()));
- });
- }) | rpl::flatten_latest(),
- [=] { ShowEditPermissions(_navigation, _peer); },
- { &st::menuIconPermissions });
- }
- if (canEditInviteLinks) {
- auto count = Info::Profile::MigratedOrMeValue(
- _peer
- ) | rpl::map([=](not_null<PeerData*> peer) {
- peer->session().api().inviteLinks().requestMyLinks(peer);
- return peer->session().changes().peerUpdates(
- peer,
- Data::PeerUpdate::Flag::InviteLinks
- ) | rpl::map([=] {
- return peer->session().api().inviteLinks().myLinks(
- peer).count;
- });
- }) | rpl::flatten_latest(
- ) | rpl::start_spawning(_controls.buttonsLayout->lifetime());
- const auto wrap = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _controls.buttonsLayout,
- object_ptr<Ui::VerticalLayout>(
- _controls.buttonsLayout)));
- AddButtonWithCount(
- wrap->entity(),
- tr::lng_manage_peer_invite_links(),
- rpl::duplicate(count) | ToPositiveNumberString(),
- [=] {
- _navigation->parentController()->show(Box(
- ManageInviteLinksBox,
- _peer,
- _peer->session().user(),
- 0,
- 0));
- },
- { &st::menuIconLinks });
- wrap->toggle(true, anim::type::instant);
- }
- if (canViewAdmins) {
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_administrators(),
- Info::Profile::MigratedOrMeValue(
- _peer
- ) | rpl::map(
- Info::Profile::AdminsCountValue
- ) | rpl::flatten_latest(
- ) | ToPositiveNumberString(),
- [=] {
- ParticipantsBoxController::Start(
- _navigation,
- _peer,
- ParticipantsBoxController::Role::Admins);
- },
- { &st::menuIconAdmin });
- }
- if (canViewMembers) {
- AddButtonWithCount(
- _controls.buttonsLayout,
- (_isGroup
- ? tr::lng_manage_peer_members()
- : tr::lng_manage_peer_subscribers()),
- Info::Profile::MigratedOrMeValue(
- _peer
- ) | rpl::map(
- Info::Profile::MembersCountValue
- ) | rpl::flatten_latest(
- ) | ToPositiveNumberString(),
- [=] {
- ParticipantsBoxController::Start(
- _navigation,
- _peer,
- ParticipantsBoxController::Role::Members);
- },
- { &st::menuIconGroups });
- }
- fillPendingRequestsButton();
- if (canViewKicked) {
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_removed_users(),
- Info::Profile::KickedCountValue(channel)
- | ToPositiveNumberString(),
- [=] {
- ParticipantsBoxController::Start(
- _navigation,
- _peer,
- ParticipantsBoxController::Role::Kicked);
- },
- { &st::menuIconRemove });
- }
- if (hasRecentActions) {
- auto callback = [=] {
- _navigation->showSection(
- std::make_shared<AdminLog::SectionMemento>(channel));
- };
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_recent_actions(),
- rpl::single(QString()), // Empty count.
- std::move(callback),
- { &st::menuIconGroupLog });
- }
- if (hasStarRef) {
- auto callback = [=] {
- _navigation->showSection(Info::BotStarRef::Join::Make(_peer));
- };
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_star_ref(),
- rpl::single(QString()), // Empty count.
- std::move(callback),
- { .icon = &st::menuIconStarRefShare, .newBadge = true });
- }
- if (canEditStickers || canDeleteChannel) {
- ::AddSkip(_controls.buttonsLayout);
- }
- if (canEditStickers) {
- _controls.buttonsLayout->add(createStickersEdit());
- }
- if (canDeleteChannel) {
- AddButtonDelete(
- _controls.buttonsLayout,
- (_isGroup
- ? tr::lng_profile_delete_group
- : tr::lng_profile_delete_channel)(),
- [=]{ deleteWithConfirmation(); }
- );
- }
- if (canEditStickers || canDeleteChannel) {
- ::AddSkip(_controls.buttonsLayout);
- }
- }
- void Controller::editReactions() {
- const auto done = [=](const Data::AllowedReactions &chosen) {
- SaveAllowedReactions(_peer, chosen);
- };
- if (!_peer->isBroadcast()) {
- _navigation->uiShow()->show(Box(
- EditAllowedReactionsBox,
- EditAllowedReactionsArgs{
- .navigation = _navigation,
- .isGroup = true,
- .list = _navigation->session().data().reactions().list(
- Data::Reactions::Type::Active),
- .allowed = Data::PeerAllowedReactions(_peer),
- .save = done,
- }));
- return;
- }
- if (_controls.levelRequested) {
- return;
- }
- _controls.levelRequested = true;
- _api.request(MTPpremium_GetBoostsStatus(
- _peer->input
- )).done([=](const MTPpremium_BoostsStatus &result) {
- _controls.levelRequested = false;
- if (const auto channel = _peer->asChannel()) {
- channel->updateLevelHint(result.data().vlevel().v);
- }
- const auto link = qs(result.data().vboost_url());
- const auto weak = base::make_weak(_navigation->parentController());
- auto counters = ParseBoostCounters(result);
- counters.mine = 0; // Don't show current level as just-reached.
- const auto askForBoosts = [=](int required) {
- if (const auto strong = weak.get()) {
- const auto openStatistics = [=, peer = _peer] {
- strong->showSection(Info::Boosts::Make(peer));
- };
- strong->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
- .link = link,
- .boost = counters,
- .reason = { Ui::AskBoostCustomReactions{ required } },
- }, openStatistics, nullptr));
- }
- };
- _navigation->uiShow()->show(Box(
- EditAllowedReactionsBox,
- EditAllowedReactionsArgs{
- .navigation = _navigation,
- .allowedCustomReactions = counters.level,
- .customReactionsHardLimit = Data::PremiumLimits(
- &_peer->session()).maxBoostLevel(),
- .list = _navigation->session().data().reactions().list(
- Data::Reactions::Type::Active),
- .allowed = Data::PeerAllowedReactions(_peer),
- .askForBoosts = askForBoosts,
- .save = done,
- }));
- }).send();
- }
- void Controller::fillPendingRequestsButton() {
- auto pendingRequestsCount = Info::Profile::MigratedOrMeValue(
- _peer
- ) | rpl::map(
- Info::Profile::PendingRequestsCountValue
- ) | rpl::flatten_latest(
- ) | rpl::start_spawning(_controls.buttonsLayout->lifetime());
- const auto wrap = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _controls.buttonsLayout,
- object_ptr<Ui::VerticalLayout>(
- _controls.buttonsLayout)));
- AddButtonWithCount(
- wrap->entity(),
- (_isGroup
- ? tr::lng_manage_peer_requests()
- : tr::lng_manage_peer_requests_channel()),
- rpl::duplicate(pendingRequestsCount) | ToPositiveNumberString(),
- [=] { RequestsBoxController::Start(_navigation, _peer); },
- { &st::menuIconInvite });
- std::move(
- pendingRequestsCount
- ) | rpl::start_with_next([=](int count) {
- wrap->toggle(count > 0, anim::type::instant);
- }, wrap->lifetime());
- }
- void Controller::fillBotUsernamesButton() {
- Expects(_isBot);
- const auto user = _peer->asUser();
- auto localUsernames = rpl::single(
- user->usernames()
- ) | rpl::map([](const std::vector<QString> &usernames) {
- return ranges::views::all(
- usernames
- ) | ranges::views::transform([](const QString &u) {
- return Data::Username{ u };
- }) | ranges::to_vector;
- });
- auto usernamesValue = std::move(
- localUsernames
- ) | rpl::then(
- _peer->session().api().usernames().loadUsernames(_peer)
- );
- auto rightLabel = rpl::duplicate(
- usernamesValue
- ) | rpl::map([=](const Data::Usernames &usernames) {
- if (usernames.size() <= 1) {
- return user->session().createInternalLink(user->username());
- } else {
- const auto active = ranges::count_if(
- usernames,
- [](const Data::Username &u) { return u.active; });
- return u"%1/%2"_q.arg(active).arg(usernames.size());
- }
- });
- auto leftLabel = std::move(
- usernamesValue
- ) | rpl::map([=](const Data::Usernames &usernames) {
- return (usernames.size() <= 1)
- ? tr::lng_manage_peer_bot_public_link()
- : tr::lng_manage_peer_bot_public_links();
- }) | rpl::flatten_latest();
- _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _controls.buttonsLayout,
- object_ptr<Ui::VerticalLayout>(
- _controls.buttonsLayout)));
- AddButtonWithCount(
- _controls.buttonsLayout,
- std::move(leftLabel),
- std::move(rightLabel),
- [=] {
- _navigation->uiShow()->showBox(Box(UsernamesBox, user));
- },
- { &st::menuIconLinks });
- }
- void Controller::fillBotCurrencyButton() {
- Expects(_isBot);
- struct State final {
- rpl::variable<QString> balance;
- };
- auto &lifetime = _controls.buttonsLayout->lifetime();
- const auto state = lifetime.make_state<State>();
- const auto format = [=](uint64 balance) {
- return Info::ChannelEarn::MajorPart(balance)
- + Info::ChannelEarn::MinorPart(balance);
- };
- const auto was = _peer->session().credits().balanceCurrency(
- _peer->id);
- if (was) {
- state->balance = format(was);
- }
- const auto wrap = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
- _controls.buttonsLayout,
- EditPeerInfoBox::CreateButton(
- _controls.buttonsLayout,
- tr::lng_manage_peer_bot_balance_currency(),
- state->balance.value(),
- [controller = _navigation->parentController(), peer = _peer] {
- controller->showSection(Info::ChannelEarn::Make(peer));
- },
- st::manageGroupButton,
- {})));
- wrap->toggle(!state->balance.current().isEmpty(), anim::type::instant);
- const auto button = wrap->entity();
- {
- const auto currencyLoad
- = button->lifetime().make_state<Api::EarnStatistics>(_peer);
- currencyLoad->request(
- ) | rpl::start_with_error_done([=](const QString &error) {
- }, [=] {
- const auto balance = currencyLoad->data().currentBalance;
- if (balance) {
- wrap->toggle(true, anim::type::normal);
- }
- state->balance = format(balance);
- }, button->lifetime());
- }
- {
- const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
- icon->resize(st::menuIconLinks.size());
- const auto image = Ui::Earn::MenuIconCurrency(icon->size());
- icon->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(icon);
- p.drawImage(0, 0, image);
- }, icon->lifetime());
- button->sizeValue(
- ) | rpl::start_with_next([=](const QSize &size) {
- icon->moveToLeft(
- button->st().iconLeft,
- (size.height() - icon->height()) / 2);
- }, icon->lifetime());
- }
- }
- void Controller::fillBotCreditsButton() {
- Expects(_isBot);
- struct State final {
- rpl::variable<QString> balance;
- };
- auto &lifetime = _controls.buttonsLayout->lifetime();
- const auto state = lifetime.make_state<State>();
- if (const auto balance = _peer->session().credits().balance(_peer->id)) {
- state->balance = Lang::FormatStarsAmountDecimal(balance);
- }
- const auto wrap = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
- _controls.buttonsLayout,
- EditPeerInfoBox::CreateButton(
- _controls.buttonsLayout,
- tr::lng_manage_peer_bot_balance_credits(),
- state->balance.value(),
- [controller = _navigation->parentController(), peer = _peer] {
- controller->showSection(Info::BotEarn::Make(peer));
- },
- st::manageGroupButton,
- {})));
- wrap->toggle(!state->balance.current().isEmpty(), anim::type::instant);
- const auto button = wrap->entity();
- {
- const auto api = button->lifetime().make_state<Api::CreditsStatus>(
- _peer);
- api->request({}, [=](Data::CreditsStatusSlice data) {
- if (data.balance) {
- wrap->toggle(true, anim::type::normal);
- }
- state->balance = Lang::FormatStarsAmountDecimal(data.balance);
- });
- }
- {
- const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
- const auto image = Ui::Earn::MenuIconCredits();
- icon->resize(image.size() / style::DevicePixelRatio());
- icon->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(icon);
- p.drawImage(0, 0, image);
- }, icon->lifetime());
- button->sizeValue(
- ) | rpl::start_with_next([=](const QSize &size) {
- icon->moveToLeft(
- button->st().iconLeft,
- (size.height() - icon->height()) / 2);
- }, icon->lifetime());
- }
- }
- void Controller::fillBotAffiliateProgram() {
- Expects(_isBot);
- if (!Info::BotStarRef::Setup::Allowed(_peer)) {
- return;
- }
- const auto user = _peer->asUser();
- auto label = user->session().changes().peerFlagsValue(
- user,
- Data::PeerUpdate::Flag::StarRefProgram
- ) | rpl::map([=] {
- const auto commission = user->botInfo
- ? user->botInfo->starRefProgram.commission
- : 0;
- return commission
- ? Info::BotStarRef::FormatCommission(commission)
- : tr::lng_manage_peer_bot_star_ref_off(tr::now);
- });
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_bot_star_ref(),
- std::move(label),
- [controller = _navigation->parentController(), user] {
- controller->showSection(Info::BotStarRef::Setup::Make(user));
- },
- { .icon = &st::menuIconSharing, .newBadge = true });
- }
- void Controller::fillBotEditIntroButton() {
- Expects(_isBot);
- const auto user = _peer->asUser();
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_bot_edit_intro(),
- rpl::never<QString>(),
- [=] { toggleBotManager(u"%1-intro"_q.arg(user->username())); },
- { &st::menuIconEdit });
- }
- void Controller::fillBotEditCommandsButton() {
- Expects(_isBot);
- const auto user = _peer->asUser();
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_bot_edit_commands(),
- rpl::never<QString>(),
- [=] { toggleBotManager(u"%1-commands"_q.arg(user->username())); },
- { &st::menuIconBotCommands });
- }
- void Controller::fillBotEditSettingsButton() {
- Expects(_isBot);
- const auto user = _peer->asUser();
- AddButtonWithCount(
- _controls.buttonsLayout,
- tr::lng_manage_peer_bot_edit_settings(),
- rpl::never<QString>(),
- [=] { toggleBotManager(user->username()); },
- { &st::menuIconSettings });
- }
- void Controller::fillBotVerifyAccounts() {
- Expects(_isBot);
- const auto user = _peer->asUser();
- const auto wrap = _controls.buttonsLayout->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- _controls.buttonsLayout,
- object_ptr<Ui::VerticalLayout>(
- _controls.buttonsLayout)));
- wrap->toggleOn(rpl::single(
- rpl::empty
- ) | rpl::then(user->owner().botCommandsChanges(
- ) | rpl::filter(
- rpl::mappers::_1 == _peer
- ) | rpl::to_empty) | rpl::map([=] {
- const auto info = user->botInfo.get();
- return info && info->verifierSettings;
- }));
- const auto inner = wrap->entity();
- Ui::AddSkip(inner);
- AddButtonWithCount(
- inner,
- tr::lng_manage_peer_bot_verify(),
- rpl::never<QString>(),
- [controller = _navigation->parentController(), user] {
- controller->show(MakeVerifyPeersBox(controller, user));
- },
- { &st::menuIconFactcheck });
- Ui::AddSkip(inner);
- Ui::AddDivider(inner);
- }
- void Controller::submitTitle() {
- Expects(_controls.title != nullptr);
- if (_controls.title->getLastText().isEmpty()) {
- _controls.title->showError();
- _box->scrollToWidget(_controls.title);
- } else if (_controls.description) {
- _controls.description->setFocus();
- _box->scrollToWidget(_controls.description);
- }
- }
- void Controller::submitDescription() {
- Expects(_controls.title != nullptr);
- Expects(_controls.description != nullptr);
- if (_controls.title->getLastText().isEmpty()) {
- _controls.title->showError();
- _box->scrollToWidget(_controls.title);
- } else {
- save();
- }
- }
- std::optional<Controller::Saving> Controller::validate() const {
- auto result = Saving();
- if (validateUsernamesOrder(result)
- && validateUsername(result)
- && validateLinkedChat(result)
- && validateTitle(result)
- && validateDescription(result)
- && validateHistoryVisibility(result)
- && validateForum(result)
- && validateSignatures(result)
- && validateForwards(result)
- && validateJoinToWrite(result)
- && validateRequestToJoin(result)) {
- return result;
- }
- return {};
- }
- bool Controller::validateUsernamesOrder(Saving &to) const {
- if (!_typeDataSavedValue) {
- return true;
- } else if (_typeDataSavedValue->privacy != Privacy::HasUsername) {
- to.usernamesOrder = std::vector<QString>();
- return true;
- }
- to.usernamesOrder = _typeDataSavedValue->usernamesOrder;
- return true;
- }
- bool Controller::validateUsername(Saving &to) const {
- if (!_typeDataSavedValue) {
- return true;
- } else if (_typeDataSavedValue->privacy != Privacy::HasUsername) {
- to.username = QString();
- return true;
- }
- const auto username = _typeDataSavedValue->username;
- if (username.isEmpty()) {
- to.username = QString();
- return true;
- }
- to.username = username;
- return true;
- }
- bool Controller::validateLinkedChat(Saving &to) const {
- if (!_linkedChatSavedValue) {
- return true;
- }
- to.linkedChat = *_linkedChatSavedValue;
- return true;
- }
- bool Controller::validateTitle(Saving &to) const {
- if (!_controls.title) {
- return true;
- }
- const auto title = _controls.title->getLastText().trimmed();
- if (title.isEmpty()) {
- _controls.title->showError();
- _box->scrollToWidget(_controls.title);
- return false;
- }
- to.title = title;
- return true;
- }
- bool Controller::validateDescription(Saving &to) const {
- if (!_controls.description) {
- return true;
- }
- to.description = _controls.description->getLastText().trimmed();
- return true;
- }
- bool Controller::validateHistoryVisibility(Saving &to) const {
- if (!_controls.historyVisibilityWrap
- || !_controls.historyVisibilityWrap->toggled()
- || _channelHasLocationOriginalValue
- || (_typeDataSavedValue
- && _typeDataSavedValue->privacy == Privacy::HasUsername)) {
- return true;
- }
- to.hiddenPreHistory
- = (_historyVisibilitySavedValue == HistoryVisibility::Hidden);
- return true;
- }
- bool Controller::validateForum(Saving &to) const {
- if (!_forumSavedValue.has_value()) {
- return true;
- }
- to.forum = _forumSavedValue;
- return true;
- }
- bool Controller::validateSignatures(Saving &to) const {
- Expects(_signaturesSavedValue.has_value()
- == _signatureProfilesSavedValue.has_value());
- if (!_signaturesSavedValue.has_value()) {
- return true;
- }
- to.signatures = _signaturesSavedValue;
- to.signatureProfiles = _signatureProfilesSavedValue;
- return true;
- }
- bool Controller::validateForwards(Saving &to) const {
- if (!_typeDataSavedValue) {
- return true;
- }
- to.noForwards = _typeDataSavedValue->noForwards;
- return true;
- }
- bool Controller::validateJoinToWrite(Saving &to) const {
- if (!_typeDataSavedValue) {
- return true;
- }
- to.joinToWrite = _typeDataSavedValue->joinToWrite;
- return true;
- }
- bool Controller::validateRequestToJoin(Saving &to) const {
- if (!_typeDataSavedValue) {
- return true;
- }
- to.requestToJoin = _typeDataSavedValue->requestToJoin;
- return true;
- }
- void Controller::save() {
- Expects(_wrap != nullptr);
- if (!_saveStagesQueue.empty()) {
- return;
- }
- if (const auto saving = validate()) {
- _savingData = *saving;
- pushSaveStage([=] { saveUsernamesOrder(); });
- pushSaveStage([=] { saveUsername(); });
- pushSaveStage([=] { saveLinkedChat(); });
- pushSaveStage([=] { saveTitle(); });
- pushSaveStage([=] { saveDescription(); });
- pushSaveStage([=] { saveHistoryVisibility(); });
- pushSaveStage([=] { saveForum(); });
- pushSaveStage([=] { saveSignatures(); });
- pushSaveStage([=] { saveForwards(); });
- pushSaveStage([=] { saveJoinToWrite(); });
- pushSaveStage([=] { saveRequestToJoin(); });
- pushSaveStage([=] { savePhoto(); });
- continueSave();
- }
- }
- void Controller::pushSaveStage(FnMut<void()> &&lambda) {
- _saveStagesQueue.push_back(std::move(lambda));
- }
- void Controller::continueSave() {
- if (!_saveStagesQueue.empty()) {
- auto next = std::move(_saveStagesQueue.front());
- _saveStagesQueue.pop_front();
- next();
- }
- }
- void Controller::cancelSave() {
- _saveStagesQueue.clear();
- }
- void Controller::saveUsernamesOrder() {
- const auto channel = _peer->asChannel();
- if (!_savingData.usernamesOrder || !channel) {
- return continueSave();
- }
- if (_savingData.usernamesOrder->empty()) {
- _api.request(MTPchannels_DeactivateAllUsernames(
- channel->inputChannel
- )).done([=] {
- channel->setUsernames(channel->editableUsername().isEmpty()
- ? Data::Usernames()
- : Data::Usernames{
- { channel->editableUsername(), true, true }
- });
- continueSave();
- }).send();
- } else {
- const auto lifetime = std::make_shared<rpl::lifetime>();
- const auto newUsernames = (*_savingData.usernamesOrder);
- _peer->session().api().usernames().reorder(
- _peer,
- newUsernames
- ) | rpl::start_with_done([=] {
- channel->setUsernames(ranges::views::all(
- newUsernames
- ) | ranges::views::transform([&](QString username) {
- const auto editable
- = (channel->editableUsername() == username);
- return Data::Username{
- .username = std::move(username),
- .active = true,
- .editable = editable,
- };
- }) | ranges::to_vector);
- continueSave();
- lifetime->destroy();
- }, *lifetime);
- }
- }
- void Controller::saveUsername() {
- const auto channel = _peer->asChannel();
- const auto username = (channel ? channel->editableUsername() : QString());
- if (!_savingData.username || *_savingData.username == username) {
- return continueSave();
- } else if (!channel) {
- const auto saveForChannel = [=](not_null<ChannelData*> channel) {
- if (_peer->asChannel() == channel) {
- saveUsername();
- } else {
- cancelSave();
- }
- };
- _peer->session().api().migrateChat(
- _peer->asChat(),
- crl::guard(this, saveForChannel));
- return;
- }
- const auto newUsername = (*_savingData.username);
- _api.request(MTPchannels_UpdateUsername(
- channel->inputChannel,
- MTP_string(newUsername)
- )).done([=] {
- channel->setName(
- TextUtilities::SingleLine(channel->name()),
- newUsername);
- continueSave();
- }).fail([=](const MTP::Error &error) {
- const auto &type = error.type();
- if (type == u"USERNAME_NOT_MODIFIED"_q) {
- channel->setName(
- TextUtilities::SingleLine(channel->name()),
- TextUtilities::SingleLine(*_savingData.username));
- continueSave();
- return;
- }
- // Very rare case.
- showEditPeerTypeBox([&] {
- if (type == u"USERNAME_INVALID"_q) {
- return tr::lng_create_channel_link_invalid();
- } else if (type == u"USERNAME_OCCUPIED"_q
- || type == u"USERNAMES_UNAVAILABLE"_q) {
- return tr::lng_create_channel_link_occupied();
- }
- return tr::lng_create_channel_link_invalid();
- }());
- cancelSave();
- }).send();
- }
- void Controller::saveLinkedChat() {
- const auto channel = _peer->asChannel();
- if (!channel) {
- return continueSave();
- }
- if (!_savingData.linkedChat
- || *_savingData.linkedChat == channel->linkedChat()) {
- return continueSave();
- }
- const auto chat = *_savingData.linkedChat;
- if (channel->isBroadcast() && chat && chat->hiddenPreHistory()) {
- togglePreHistoryHidden(
- chat,
- false,
- [=] { saveLinkedChat(); },
- [=] { cancelSave(); });
- return;
- }
- const auto input = *_savingData.linkedChat
- ? (*_savingData.linkedChat)->inputChannel
- : MTP_inputChannelEmpty();
- _api.request(MTPchannels_SetDiscussionGroup(
- (channel->isBroadcast() ? channel->inputChannel : input),
- (channel->isBroadcast() ? input : channel->inputChannel)
- )).done([=] {
- channel->setLinkedChat(*_savingData.linkedChat);
- continueSave();
- }).fail([=] {
- cancelSave();
- }).send();
- }
- void Controller::saveTitle() {
- if (!_savingData.title || *_savingData.title == _peer->name()) {
- return continueSave();
- }
- const auto onDone = [=](const MTPUpdates &result) {
- _peer->session().api().applyUpdates(result);
- continueSave();
- };
- const auto onFail = [=](const MTP::Error &error) {
- const auto &type = error.type();
- if (type == u"CHAT_NOT_MODIFIED"_q
- || type == u"CHAT_TITLE_NOT_MODIFIED"_q) {
- if (const auto channel = _peer->asChannel()) {
- channel->setName(
- *_savingData.title,
- channel->editableUsername());
- } else if (const auto chat = _peer->asChat()) {
- chat->setName(*_savingData.title);
- }
- continueSave();
- return;
- }
- _controls.title->showError();
- if (type == u"NO_CHAT_TITLE"_q) {
- _box->scrollToWidget(_controls.title);
- }
- cancelSave();
- };
- if (const auto channel = _peer->asChannel()) {
- _api.request(MTPchannels_EditTitle(
- channel->inputChannel,
- MTP_string(*_savingData.title)
- )).done(std::move(onDone)
- ).fail(std::move(onFail)
- ).send();
- } else if (const auto chat = _peer->asChat()) {
- _api.request(MTPmessages_EditChatTitle(
- chat->inputChat,
- MTP_string(*_savingData.title)
- )).done(std::move(onDone)
- ).fail(std::move(onFail)
- ).send();
- } else if (_isBot) {
- _api.request(MTPbots_GetBotInfo(
- MTP_flags(MTPbots_GetBotInfo::Flag::f_bot),
- _peer->asUser()->inputUser,
- MTPstring() // Lang code.
- )).done([=](const MTPbots_BotInfo &result) {
- const auto was = qs(result.data().vname());
- const auto now = *_savingData.title;
- if (was == now) {
- return continueSave();
- }
- using Flag = MTPbots_SetBotInfo::Flag;
- _api.request(MTPbots_SetBotInfo(
- MTP_flags(Flag::f_bot | Flag::f_name),
- _peer->asUser()->inputUser,
- MTPstring(), // Lang code.
- MTP_string(now), // Name.
- MTPstring(), // About.
- MTPstring() // Description.
- )).done([=] {
- continueSave();
- }).fail(std::move(onFail)
- ).send();
- }).fail(std::move(onFail)
- ).send();
- } else {
- continueSave();
- }
- }
- void Controller::saveDescription() {
- if (!_savingData.description
- || *_savingData.description == _peer->about()) {
- return continueSave();
- }
- const auto successCallback = [=] {
- _peer->setAbout(*_savingData.description);
- continueSave();
- };
- if (_isBot) {
- _api.request(MTPbots_GetBotInfo(
- MTP_flags(MTPbots_GetBotInfo::Flag::f_bot),
- _peer->asUser()->inputUser,
- MTPstring() // Lang code.
- )).done([=](const MTPbots_BotInfo &result) {
- const auto was = qs(result.data().vabout());
- const auto now = *_savingData.description;
- if (was == now) {
- return continueSave();
- }
- using Flag = MTPbots_SetBotInfo::Flag;
- _api.request(MTPbots_SetBotInfo(
- MTP_flags(Flag::f_bot | Flag::f_about),
- _peer->asUser()->inputUser,
- MTPstring(), // Lang code.
- MTPstring(), // Name.
- MTP_string(now), // About.
- MTPstring() // Description.
- )).done([=] {
- successCallback();
- }).fail([=] {
- _controls.description->showError();
- cancelSave();
- }).send();
- }).fail([=] {
- continueSave();
- }).send();
- return;
- }
- _api.request(MTPmessages_EditChatAbout(
- _peer->input,
- MTP_string(*_savingData.description)
- )).done([=] {
- successCallback();
- }).fail([=](const MTP::Error &error) {
- const auto &type = error.type();
- if (type == u"CHAT_ABOUT_NOT_MODIFIED"_q) {
- successCallback();
- return;
- }
- _controls.description->showError();
- cancelSave();
- }).send();
- }
- void Controller::saveHistoryVisibility() {
- const auto channel = _peer->asChannel();
- const auto hidden = channel ? channel->hiddenPreHistory() : true;
- if (!_savingData.hiddenPreHistory
- || *_savingData.hiddenPreHistory == hidden) {
- return continueSave();
- } else if (!channel) {
- const auto saveForChannel = [=](not_null<ChannelData*> channel) {
- if (_peer->asChannel() == channel) {
- saveHistoryVisibility();
- } else {
- cancelSave();
- }
- };
- _peer->session().api().migrateChat(
- _peer->asChat(),
- crl::guard(this, saveForChannel));
- return;
- }
- togglePreHistoryHidden(
- channel,
- *_savingData.hiddenPreHistory,
- [=] { continueSave(); },
- [=] { cancelSave(); });
- }
- void Controller::toggleBotManager(const QString &command) {
- const auto controller = _navigation->parentController();
- _api.request(MTPcontacts_ResolveUsername(
- MTP_flags(0),
- MTP_string(kBotManagerUsername.utf16()),
- MTP_string()
- )).done([=](const MTPcontacts_ResolvedPeer &result) {
- _peer->owner().processUsers(result.data().vusers());
- _peer->owner().processChats(result.data().vchats());
- const auto botPeer = _peer->owner().peerLoaded(
- peerFromMTP(result.data().vpeer()));
- if (const auto bot = botPeer ? botPeer->asUser() : nullptr) {
- const auto show = controller->uiShow();
- _peer->session().api().sendBotStart(show, bot, bot, command);
- controller->showPeerHistory(bot);
- }
- }).send();
- }
- void Controller::togglePreHistoryHidden(
- not_null<ChannelData*> channel,
- bool hidden,
- Fn<void()> done,
- Fn<void()> fail) {
- const auto apply = [=] {
- // Update in the result doesn't contain the
- // channelFull:flags field which holds this value.
- // So after saving we need to update it manually.
- const auto flags = channel->flags();
- const auto flag = ChannelDataFlag::PreHistoryHidden;
- channel->setFlags(hidden ? (flags | flag) : (flags & ~flag));
- done();
- };
- _api.request(MTPchannels_TogglePreHistoryHidden(
- channel->inputChannel,
- MTP_bool(hidden)
- )).done([=](const MTPUpdates &result) {
- channel->session().api().applyUpdates(result);
- apply();
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
- apply();
- } else {
- fail();
- }
- }).send();
- }
- void Controller::saveForum() {
- const auto channel = _peer->asChannel();
- if (!_savingData.forum
- || *_savingData.forum == _peer->isForum()) {
- return continueSave();
- } else if (!channel) {
- const auto saveForChannel = [=](not_null<ChannelData*> channel) {
- if (_peer->asChannel() == channel) {
- saveForum();
- } else {
- cancelSave();
- }
- };
- _peer->session().api().migrateChat(
- _peer->asChat(),
- crl::guard(this, saveForChannel));
- return;
- }
- _api.request(MTPchannels_ToggleForum(
- channel->inputChannel,
- MTP_bool(*_savingData.forum)
- )).done([=](const MTPUpdates &result) {
- const auto weak = base::make_weak(this);
- channel->session().api().applyUpdates(result);
- if (weak) { // todo better to be able to save in closed already box.
- continueSave();
- }
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
- continueSave();
- } else {
- cancelSave();
- }
- }).send();
- }
- void Controller::saveSignatures() {
- Expects(_savingData.signatures.has_value()
- == _savingData.signatureProfiles.has_value());
- const auto channel = _peer->asChannel();
- if (!_savingData.signatures
- || !channel
- || ((*_savingData.signatures == channel->addsSignature())
- && (*_savingData.signatureProfiles
- == channel->signatureProfiles()))) {
- return continueSave();
- }
- using Flag = MTPchannels_ToggleSignatures::Flag;
- _api.request(MTPchannels_ToggleSignatures(
- MTP_flags(Flag()
- | (*_savingData.signatures
- ? Flag::f_signatures_enabled
- : Flag())
- | (*_savingData.signatureProfiles
- ? Flag::f_profiles_enabled
- : Flag())),
- channel->inputChannel
- )).done([=](const MTPUpdates &result) {
- channel->session().api().applyUpdates(result);
- continueSave();
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
- continueSave();
- } else {
- cancelSave();
- }
- }).send();
- }
- void Controller::saveForwards() {
- if (!_savingData.noForwards
- || *_savingData.noForwards != _peer->allowsForwarding()) {
- return continueSave();
- }
- _api.request(MTPmessages_ToggleNoForwards(
- _peer->input,
- MTP_bool(*_savingData.noForwards)
- )).done([=](const MTPUpdates &result) {
- _peer->session().api().applyUpdates(result);
- continueSave();
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
- continueSave();
- } else {
- cancelSave();
- }
- }).send();
- }
- void Controller::saveJoinToWrite() {
- const auto joinToWrite = _peer->isMegagroup()
- && _peer->asChannel()->joinToWrite();
- if (!_savingData.joinToWrite
- || *_savingData.joinToWrite == joinToWrite) {
- return continueSave();
- }
- _api.request(MTPchannels_ToggleJoinToSend(
- _peer->asChannel()->inputChannel,
- MTP_bool(*_savingData.joinToWrite)
- )).done([=](const MTPUpdates &result) {
- _peer->session().api().applyUpdates(result);
- continueSave();
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
- continueSave();
- } else {
- cancelSave();
- }
- }).send();
- }
- void Controller::saveRequestToJoin() {
- const auto requestToJoin = _peer->isMegagroup()
- && _peer->asChannel()->requestToJoin();
- if (!_savingData.requestToJoin
- || *_savingData.requestToJoin == requestToJoin) {
- return continueSave();
- }
- _api.request(MTPchannels_ToggleJoinRequest(
- _peer->asChannel()->inputChannel,
- MTP_bool(*_savingData.requestToJoin)
- )).done([=](const MTPUpdates &result) {
- _peer->session().api().applyUpdates(result);
- continueSave();
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
- continueSave();
- } else {
- cancelSave();
- }
- }).send();
- }
- void Controller::savePhoto() {
- auto image = _controls.photo
- ? _controls.photo->takeResultImage()
- : QImage();
- if (!image.isNull()) {
- _peer->session().api().peerPhoto().upload(
- _peer,
- { std::move(image) });
- }
- _box->closeBox();
- }
- void Controller::deleteWithConfirmation() {
- const auto channel = _peer->asChannel();
- Assert(channel != nullptr);
- const auto text = (_isGroup
- ? tr::lng_sure_delete_group
- : tr::lng_sure_delete_channel)(tr::now);
- const auto deleteCallback = crl::guard(this, [=] {
- deleteChannel();
- });
- _navigation->parentController()->show(
- Ui::MakeConfirmBox({
- .text = text,
- .confirmed = deleteCallback,
- .confirmText = tr::lng_box_delete(),
- .confirmStyle = &st::attentionBoxButton,
- }));
- }
- void Controller::deleteChannel() {
- Expects(_peer->isChannel());
- const auto channel = _peer->asChannel();
- const auto chat = channel->migrateFrom();
- const auto session = &_peer->session();
- _navigation->parentController()->hideLayer();
- Core::App().closeChatFromWindows(channel);
- if (chat) {
- session->api().deleteConversation(chat, false);
- }
- session->api().request(MTPchannels_DeleteChannel(
- channel->inputChannel
- )).done([=](const MTPUpdates &result) {
- session->api().applyUpdates(result);
- //}).fail([=](const MTP::Error &error) {
- // if (error.type() == u"CHANNEL_TOO_LARGE"_q) {
- // Ui::show(Box<Ui::InformBox>(tr::lng_cant_delete_channel(tr::now)));
- // }
- }).send();
- }
- } // namespace
- EditPeerInfoBox::EditPeerInfoBox(
- QWidget*,
- not_null<Window::SessionNavigation*> navigation,
- not_null<PeerData*> peer)
- : _navigation(navigation)
- , _peer(peer->migrateToOrMe()) {
- }
- void EditPeerInfoBox::prepare() {
- const auto controller = Ui::CreateChild<Controller>(
- this,
- _navigation,
- this,
- _peer);
- _focusRequests.events(
- ) | rpl::start_with_next(
- [=] { controller->setFocus(); },
- lifetime());
- auto content = controller->createContent();
- setDimensionsToContent(st::boxWideWidth, content);
- setInnerWidget(object_ptr<Ui::OverrideMargins>(
- this,
- std::move(content)));
- }
- object_ptr<Ui::SettingsButton> EditPeerInfoBox::CreateButton(
- not_null<QWidget*> parent,
- rpl::producer<QString> &&text,
- rpl::producer<QString> &&count,
- Fn<void()> callback,
- const style::SettingsCountButton &st,
- Settings::IconDescriptor &&descriptor) {
- auto result = object_ptr<Ui::SettingsButton>(
- parent,
- rpl::duplicate(text),
- st.button);
- const auto button = result.data();
- button->addClickHandler(callback);
- const auto badge = descriptor.newBadge
- ? Ui::NewBadge::CreateNewBadge(
- button,
- tr::lng_premium_summary_new_badge()).get()
- : nullptr;
- if (descriptor) {
- AddButtonIcon(
- button,
- st.button,
- std::move(descriptor));
- }
- auto labelText = rpl::combine(
- rpl::duplicate(text),
- std::move(count),
- button->widthValue()
- ) | rpl::map([&st](const QString &text, const QString &count, int width) {
- const auto available = width
- - st.button.padding.left()
- - (st.button.style.font->spacew * 2)
- - st.button.style.font->width(text)
- - st.labelPosition.x();
- const auto required = st.label.style.font->width(count);
- return (required > available)
- ? st.label.style.font->elided(count, std::max(available, 0))
- : count;
- });
- if (badge) {
- rpl::combine(
- std::move(text),
- rpl::duplicate(labelText),
- button->widthValue()
- ) | rpl::start_with_next([=](
- const QString &text,
- const QString &label,
- int width) {
- const auto space = st.button.style.font->spacew;
- const auto left = st.button.padding.left()
- + st.button.style.font->width(text)
- + space;
- const auto right = st.labelPosition.x()
- + st.label.style.font->width(label)
- + (space * 2);
- const auto available = width - left - right;
- badge->setVisible(available >= badge->width());
- if (!badge->isHidden()) {
- const auto top = st.button.padding.top()
- + st.button.style.font->ascent
- - st::settingsPremiumNewBadge.style.font->ascent
- - st::settingsPremiumNewBadgePadding.top();
- badge->moveToLeft(left, top, width);
- }
- }, badge->lifetime());
- }
- const auto label = Ui::CreateChild<Ui::FlatLabel>(
- button,
- std::move(labelText),
- st.label);
- label->setAttribute(Qt::WA_TransparentForMouseEvents);
- label->show();
- rpl::combine(
- button->widthValue(),
- label->widthValue()
- ) | rpl::start_with_next([=, &st](int outerWidth, int width) {
- label->moveToRight(
- st.labelPosition.x(),
- st.labelPosition.y(),
- outerWidth);
- }, label->lifetime());
- return result;
- }
- bool EditPeerInfoBox::Available(not_null<PeerData*> peer) {
- if (const auto bot = peer->asUser()) {
- return bot->botInfo && bot->botInfo->canEditInformation;
- } else if (const auto chat = peer->asChat()) {
- return false
- || chat->canEditInformation()
- || chat->canEditPermissions();
- } else if (const auto channel = peer->asChannel()) {
- // canViewMembers() is removed, because in supergroups you
- // see them in profile and in channels only admins can see them.
- // canViewAdmins() is removed, because in supergroups it is
- // always true and in channels it is equal to canViewBanned().
- return false
- //|| channel->canViewMembers()
- //|| channel->canViewAdmins()
- || channel->canViewBanned()
- || channel->canEditInformation()
- || channel->canEditPermissions()
- || channel->hasAdminRights()
- || channel->amCreator();
- } else {
- return false;
- }
- }
- void ShowEditChatPermissions(
- not_null<Window::SessionNavigation*> navigation,
- not_null<PeerData*> peer) {
- ShowEditPermissions(navigation, peer);
- }
|