diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 5d224185b..f8611e0a3 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -214,6 +214,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_saved_gif_limit_more#other" = "An older GIF was replaced with this one.\nYou can {link} to {count} GIFs."; "lng_saved_gif_limit_link" = "increase the limit"; +"lng_caption_limit_title" = "Limit Reached"; +"lng_caption_limit1#one" = "Sorry, you can't use more than **{count}** character in media captions."; +"lng_caption_limit1#other" = "Sorry, you can't use more than **{count}** characters in media captions."; +"lng_caption_limit2#one" = "Make the caption shorter or subscribe to **Telegram Premium** to double the limit to **{count}** character."; +"lng_caption_limit2#other" = "Make the caption shorter or subscribe to **Telegram Premium** to double the limit to **{count}** characters."; +"lng_caption_limit_reached#one" = "You've reached the media caption limit. Please make the caption shorter by {count} character."; +"lng_caption_limit_reached#other" = "You've reached the media caption limit. Please make the caption shorter by {count} characters."; + "lng_limits_increase" = "Increase Limit"; "lng_flood_error" = "Too many tries. Please try again later."; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 5eec24275..0952f70f7 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_text_entities.h" #include "apiwrap.h" #include "base/event_filter.h" +#include "boxes/premium_limits_box.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" #include "chat_helpers/tabbed_panel.h" @@ -33,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "storage/localimageloader.h" // SendMediaType #include "storage/storage_media_prepare.h" +#include "ui/boxes/confirm_box.h" #include "ui/chat/attach/attach_item_single_file_preview.h" #include "ui/chat/attach/attach_item_single_media_preview.h" #include "ui/chat/attach/attach_single_file_preview.h" @@ -240,8 +242,6 @@ void EditCaptionBox::rebuildPreview() { void EditCaptionBox::setupField() { const auto show = std::make_shared(_controller); const auto session = &_controller->session(); - _field->setMaxLength( - _controller->session().serverConfig().captionLengthMax); _field->setSubmitSettings( Core::App().settings().sendSubmitWay()); _field->setInstantReplaces(Ui::InstantReplaces::Default()); @@ -648,6 +648,22 @@ void EditCaptionBox::setInnerFocus() { _field->setFocusFast(); } +bool EditCaptionBox::validateLength(const QString &text) const { + const auto session = &_controller->session(); + const auto limit = CurrentPremiumLimit( + session, + "caption_length_limit_default", + 1024, + "caption_length_limit_premium", + 2048); + const auto remove = int(text.size()) - limit; + if (remove <= 0) { + return true; + } + _controller->show(Box(CaptionLimitReachedBox, session, remove)); + return false; +} + void EditCaptionBox::save() { if (_saveRequestId) { return; @@ -662,6 +678,9 @@ void EditCaptionBox::save() { } const auto textWithTags = _field->getTextWithAppliedMarkdown(); + if (!validateLength(textWithTags.text)) { + return; + } const auto sending = TextWithEntities{ textWithTags.text, TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index ce9b68acc..554c9bd8b 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -62,6 +62,7 @@ private: void setupDragArea(); + bool validateLength(const QString &text) const; void save(); bool fileFromClipboard(not_null data); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index c336f8bcf..9d707d1c2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -626,6 +626,7 @@ void Controller::showEditPeerTypeBox( = (_linkedChatSavedValue.value_or(nullptr) != nullptr); _navigation->parentController()->show( Box( + _navigation, _peer, _channelHasLocationOriginalValue, boxCallback, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index e1894da92..06ab6117d 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "boxes/add_contact_box.h" #include "ui/boxes/confirm_box.h" +#include "boxes/premium_limits_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_common.h" @@ -48,6 +49,7 @@ namespace { class Controller : public base::has_weak_ptr { public: Controller( + Window::SessionNavigation *navigation, std::shared_ptr show, not_null container, not_null peer, @@ -131,6 +133,7 @@ private: const QString &text, rpl::producer about); + Window::SessionNavigation *_navigation = nullptr; std::shared_ptr _show; not_null _peer; @@ -154,12 +157,14 @@ private: }; Controller::Controller( + Window::SessionNavigation *navigation, std::shared_ptr show, not_null container, not_null peer, bool useLocationPhrases, std::optional dataSavedValue) -: _show(show) +: _navigation(navigation) +, _show(show) , _peer(peer) , _linkOnly(!dataSavedValue.has_value()) , _api(&_peer->session().mtp()) @@ -249,7 +254,7 @@ void Controller::createContent() { tr::lng_manage_peer_send_approve_members(), rpl::single(QString()), [=] {}, - st::manageGroupTopButtonWithText, + st::peerPermissionsButton, {})))->setDuration(0); requestToJoinWrap->toggleOn(rpl::duplicate(joinToWrite)); _controls.requestToJoin = requestToJoinWrap->entity(); @@ -574,9 +579,7 @@ void Controller::askUsernameRevoke() { checkUsernameAvailability(); }); _show->showBox( - Box( - &_peer->session(), - std::move(revokeCallback)), + Box(PublicLinksLimitBox, _navigation), Ui::LayerOption::KeepOther); } @@ -676,12 +679,14 @@ object_ptr Controller::createInviteLinkBlock() { EditPeerTypeBox::EditPeerTypeBox( QWidget*, + Window::SessionNavigation *navigation, not_null peer, bool useLocationPhrases, std::optional> savedCallback, std::optional dataSaved, std::optional> usernameError) -: _peer(peer) +: _navigation(navigation) +, _peer(peer) , _useLocationPhrases(useLocationPhrases) , _savedCallback(std::move(savedCallback)) , _dataSavedValue(dataSaved) @@ -691,7 +696,7 @@ EditPeerTypeBox::EditPeerTypeBox( EditPeerTypeBox::EditPeerTypeBox( QWidget*, not_null peer) -: EditPeerTypeBox(nullptr, peer, {}, {}, {}) { +: EditPeerTypeBox(nullptr, nullptr, peer, {}, {}, {}) { } void EditPeerTypeBox::setInnerFocus() { @@ -705,6 +710,7 @@ void EditPeerTypeBox::prepare() { const auto controller = Ui::CreateChild( this, + _navigation, std::make_shared(this), content.data(), _peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 8f182c6b3..d69c79e92 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -18,6 +18,10 @@ class VerticalLayout; class SettingsButton; } // namespace Ui +namespace Window { +class SessionNavigation; +} // namespace Window + enum class Privacy { HasUsername, NoUsername, @@ -42,6 +46,7 @@ class EditPeerTypeBox : public Ui::BoxContent { public: EditPeerTypeBox( QWidget*, + Window::SessionNavigation *navigation, not_null peer, bool useLocationPhrases, std::optional> savedCallback, @@ -58,7 +63,8 @@ protected: void setInnerFocus() override; private: - not_null _peer; + Window::SessionNavigation *_navigation = nullptr; + const not_null _peer; bool _useLocationPhrases = false; std::optional> _savedCallback; diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 5c07564ba..02d20b639 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/premium_limits_box.h" +#include "ui/boxes/confirm_box.h" #include "ui/controls/peer_list_dummy.h" #include "ui/widgets/buttons.h" #include "ui/wrap/padding_wrap.h" @@ -16,7 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "main/main_app_config.h" #include "boxes/peer_list_controllers.h" +#include "boxes/peers/prepare_short_info_box.h" // PrepareShortInfoBox +#include "window/window_session_controller.h" #include "data/data_user.h" +#include "data/data_channel.h" #include "data/data_session.h" #include "lang/lang_keys.h" #include "base/unixtime.h" @@ -47,6 +51,29 @@ private: }; +class PublicsController final : public PeerListController { +public: + PublicsController( + not_null navigation, + Fn closeBox); + ~PublicsController(); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + void rowRightActionClicked(not_null row) override; + +private: + void appendRow(not_null peer); + [[nodiscard]] std::unique_ptr createRow( + not_null peer) const; + + const not_null _navigation; + Fn _closeBox; + mtpRequestId _requestId = 0; + +}; + class InactiveDelegate final : public PeerListContentDelegate { public: void peerListSetTitle(rpl::producer title) override; @@ -149,7 +176,6 @@ const base::flat_set &InactiveDelegate::selected() const { return _selectedIds; } - InactiveController::InactiveController(not_null session) : _session(session) { } @@ -165,10 +191,6 @@ Main::Session &InactiveController::session() const { } void InactiveController::prepare() { - delegate()->peerListSetTitle(tr::lng_blocked_list_title()); - setDescriptionText(tr::lng_contacts_loading(tr::now)); - delegate()->peerListRefreshRows(); - _requestId = _session->api().request(MTPchannels_GetInactiveChannels( )).done([=](const MTPmessages_InactiveChats &result) { _requestId = 0; @@ -241,6 +263,102 @@ std::unique_ptr InactiveController::createRow( return result; } +PublicsController::PublicsController( + not_null navigation, + Fn closeBox) +: _navigation(navigation) +, _closeBox(std::move(closeBox)) { +} + +PublicsController::~PublicsController() { + if (_requestId) { + _navigation->session().api().request(_requestId).cancel(); + } +} + +Main::Session &PublicsController::session() const { + return _navigation->session(); +} + +void PublicsController::prepare() { + _requestId = _navigation->session().api().request( + MTPchannels_GetAdminedPublicChannels(MTP_flags(0)) + ).done([=](const MTPmessages_Chats &result) { + _requestId = 0; + + const auto &chats = result.match([](const auto &data) { + return data.vchats().v; + }); + auto &owner = _navigation->session().data(); + for (const auto &chat : chats) { + if (const auto peer = owner.processChat(chat)) { + if (!peer->isChannel() || peer->userName().isEmpty()) { + continue; + } + appendRow(peer); + } + delegate()->peerListRefreshRows(); + } + }).send(); +} + +void PublicsController::rowClicked(not_null row) { + _navigation->parentController()->show( + PrepareShortInfoBox(row->peer(), _navigation)); +} + +void PublicsController::rowRightActionClicked(not_null row) { + const auto peer = row->peer(); + const auto textMethod = peer->isMegagroup() + ? tr::lng_channels_too_much_public_revoke_confirm_group + : tr::lng_channels_too_much_public_revoke_confirm_channel; + const auto text = textMethod( + tr::now, + lt_link, + peer->session().createInternalLink(peer->userName()), + lt_group, + peer->name); + const auto confirmText = tr::lng_channels_too_much_public_revoke( + tr::now); + const auto closeBox = _closeBox; + const auto once = std::make_shared(false); + auto callback = crl::guard(_navigation, [=](Fn &&close) { + if (*once) { + return; + } + *once = true; + peer->session().api().request(MTPchannels_UpdateUsername( + peer->asChannel()->inputChannel, + MTP_string() + )).done([=, close = std::move(close)] { + closeBox(); + close(); + }).send(); + }); + _navigation->parentController()->show( + Ui::MakeConfirmBox({ + .text = text, + .confirmed = std::move(callback), + .confirmText = confirmText, + }), + Ui::LayerOption::KeepOther); +} + +void PublicsController::appendRow(not_null participant) { + if (!delegate()->peerListFindRow(participant->id.value)) { + delegate()->peerListAppendRow(createRow(participant)); + } +} + +std::unique_ptr PublicsController::createRow( + not_null peer) const { + auto result = std::make_unique(peer); + result->setActionLink(tr::lng_channels_too_much_public_revoke(tr::now)); + result->setCustomStatus( + _navigation->session().createInternalLink(peer->userName())); + return result; +} + [[nodiscard]] float64 Limit( not_null session, const QString &key, @@ -385,7 +503,7 @@ void ChannelsLimitBox( delegate->setContent(content); controller->setDelegate(delegate); - const auto count = 50; + const auto count = 100; const auto placeholder = box->addRow( object_ptr(box, count, st::defaultPeerList), {}); @@ -431,7 +549,8 @@ void ChannelsLimitBox( void PublicLinksLimitBox( not_null box, - not_null session) { + not_null navigation) { + const auto session = &navigation->session(); const auto premium = session->user()->isPremium(); auto text = rpl::combine( @@ -462,7 +581,30 @@ void PublicLinksLimitBox( session, tr::lng_links_limit_title(), std::move(text), - premium); + premium, + true); + + const auto delegate = box->lifetime().make_state(); + const auto controller = box->lifetime().make_state( + navigation, + crl::guard(box, [=] { box->closeBox(); })); + + const auto content = box->addRow( + object_ptr(box, controller), + {}); + delegate->setContent(content); + controller->setDelegate(delegate); + + const auto count = Limit(session, "channels_public_limit_default", 10); + const auto placeholder = box->addRow( + object_ptr(box, count, st::defaultPeerList), + {}); + + using namespace rpl::mappers; + content->heightValue( + ) | rpl::filter(_1 > 0) | rpl::start_with_next([=] { + delete placeholder; + }, placeholder->lifetime()); } void FilterChatsLimitBox( @@ -575,6 +717,58 @@ void PinsLimitBox( 10); } +void CaptionLimitBox( + not_null box, + not_null session) { + const auto premium = session->user()->isPremium(); + + auto text = rpl::combine( + tr::lng_caption_limit1( + lt_count, + rpl::single(Limit( + session, + (premium + ? "caption_length_limit_premium" + : "caption_length_limit_default"), + premium ? 2048 : 1024)), + Ui::Text::RichLangValue), + tr::lng_caption_limit2( + lt_count, + rpl::single( + Limit(session, "caption_length_limit_premium", 2048)), + Ui::Text::RichLangValue) + ) | rpl::map([](TextWithEntities &&a, TextWithEntities &&b) { + return a.append(QChar(' ')).append(std::move(b)); + }); + + SimpleLimitBox( + box, + session, + tr::lng_caption_limit_title(), + std::move(text), + premium); +} + +void CaptionLimitReachedBox( + not_null box, + not_null session, + int remove) { + Ui::ConfirmBox(box, Ui::ConfirmBoxArgs{ + .text = tr::lng_caption_limit_reached(tr::now, lt_count, remove), + .inform = true, + }); + if (!session->user()->isPremium()) { + box->addLeftButton(tr::lng_limits_increase(), [=] { + box->getDelegate()->showBox( + Box(CaptionLimitBox, session), + Ui::LayerOption::KeepOther, + anim::type::normal); + box->closeBox(); + }); + } +} + + int AppConfigLimit( not_null session, const QString &key, diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.h b/Telegram/SourceFiles/boxes/premium_limits_box.h index 92b3b04b9..80fad40d2 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.h +++ b/Telegram/SourceFiles/boxes/premium_limits_box.h @@ -13,12 +13,16 @@ namespace Main { class Session; } // namespace Main +namespace Window { +class SessionNavigation; +} // namespace Window + void ChannelsLimitBox( not_null box, not_null session); void PublicLinksLimitBox( not_null box, - not_null session); + not_null navigation); void FilterChatsLimitBox( not_null box, not_null session); @@ -34,6 +38,13 @@ void FolderPinsLimitBox( void PinsLimitBox( not_null box, not_null session); +void CaptionLimitBox( + not_null box, + not_null session); +void CaptionLimitReachedBox( + not_null box, + not_null session, + int remove); [[nodiscard]] int AppConfigLimit( not_null session, diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 0d44396fe..78c3cc6bc 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -25,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "core/mime_type.h" #include "base/event_filter.h" +#include "boxes/premium_limits_box.h" +#include "ui/boxes/confirm_box.h" #include "ui/effects/animations.h" #include "ui/effects/scroll_content_shadow.h" #include "ui/widgets/checkbox.h" @@ -60,6 +62,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { +constexpr auto kMaxMessageLength = 4096; + using Ui::SendFilesWay; inline bool CanAddUrls(const QList &urls) { @@ -660,8 +664,7 @@ void SendFilesBox::updateSendWayControlsVisibility() { } void SendFilesBox::setupCaption() { - _caption->setMaxLength( - _controller->session().serverConfig().captionLengthMax); + _caption->setMaxLength(kMaxMessageLength); _caption->setSubmitSettings( Core::App().settings().sendSubmitWay()); connect(_caption, &Ui::InputField::resized, [=] { @@ -973,6 +976,25 @@ void SendFilesBox::saveSendWaySettings() { } } +bool SendFilesBox::validateLength(const QString &text) const { + const auto session = &_controller->session(); + const auto limit = CurrentPremiumLimit( + session, + "caption_length_limit_default", + 1024, + "caption_length_limit_premium", + 2048); + const auto remove = int(text.size()) - limit; + const auto way = _sendWay.current(); + if (remove <= 0 + || !_list.canAddCaption( + way.groupFiles() && way.sendImagesAsPhotos())) { + return true; + } + _controller->show(Box(CaptionLimitReachedBox, session, remove)); + return false; +} + void SendFilesBox::send( Api::SendOptions options, bool ctrlShiftEnter) { @@ -1001,6 +1023,9 @@ void SendFilesBox::send( auto caption = (_caption && !_caption->isHidden()) ? _caption->getTextWithAppliedMarkdown() : TextWithTags(); + if (!validateLength(caption.text)) { + return; + } _confirmedCallback( std::move(_list), _sendWay.current(), diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index e2089fb14..add87e2a9 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -120,6 +120,7 @@ private: void initSendWay(); void initPreview(); + bool validateLength(const QString &text) const; void refreshControls(); void setupSendWayControls(); void setupCaption(); diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index bc2e1e8c8..aa032df6e 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -392,7 +392,6 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) { .canSaveCredentials = data.is_can_save_credentials(), .passwordMissing = data.is_password_missing(), }; - //_invoice.isTest = data.is_test(); _invoice.cover.title = qs(data.vtitle()); _invoice.cover.description = qs(data.vdescription()); if (_invoice.cover.thumbnail.isNull() && !_thumbnailLoadProcess) { diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index dcc10a404..6fa99aefd 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_sending.h" #include "data/data_document.h" #include "data/data_session.h" +#include "data/data_user.h" #include "core/file_utilities.h" #include "core/mime_type.h" #include "base/unixtime.h" @@ -1042,6 +1043,11 @@ void FileLoadTask::process(Args &&args) { } void FileLoadTask::finish() { + const auto session = _session.get(); + if (!session) { + return; + } + const auto premium = session->user()->isPremium(); if (!_result || !_result->filesize || _result->filesize < 0) { Ui::show( Ui::MakeInformBox( @@ -1054,13 +1060,13 @@ void FileLoadTask::finish() { tr::lng_send_image_too_large(tr::now, lt_name, _filepath)), Ui::LayerOption::KeepOther); removeFromAlbum(); - } else if (_result->filesize > kFileSizeLimit) { + } else if (_result->filesize > kFileSizeLimit && !premium) { Ui::show( Ui::MakeInformBox( tr::lng_send_image_too_large(tr::now, lt_name, _filepath)), Ui::LayerOption::KeepOther); removeFromAlbum(); - } else if (const auto session = _session.get()) { + } else { Api::SendConfirmedFile(session, _result); } }