diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e295b5eb25..c4c97630e6 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3815,10 +3815,22 @@ void ApiWrap::sendFiles( std::shared_ptr album, const SendAction &action) { const auto haveCaption = !caption.text.isEmpty(); - if (haveCaption - && !list.canAddCaption( + const auto captionAttached = !haveCaption + ? false + : (list.files.size() == 1) + ? list.canAddCaption( album != nullptr, - type == SendMediaType::Photo)) { + type == SendMediaType::Photo) + : Ui::CaptionWillBeAttached( + list, + [&] { + auto way = Ui::SendFilesWay(); + way.setGroupFiles(album != nullptr); + way.setSendImagesAsPhotos(type == SendMediaType::Photo); + return way; + }(), + false); + if (haveCaption && !captionAttached) { auto message = MessageToSend(action); message.textWithTags = base::take(caption); message.action.clearDraft = false; diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index b8d7e9a34c..e2aafdc72d 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -108,10 +108,9 @@ void FileDialogCallback( rpl::producer FieldPlaceholder( const Ui::PreparedList &list, - SendFilesWay way) { - return list.canAddCaption( - way.groupFiles() && way.sendImagesAsPhotos(), - way.sendImagesAsPhotos()) + SendFilesWay way, + bool slowmode) { + return Ui::CaptionWillBeAttached(list, way, slowmode) ? tr::lng_photo_caption() : tr::lng_photos_comment(); } @@ -793,9 +792,9 @@ void SendFilesBox::openDialogToAddFileToAlbum() { void SendFilesBox::refreshMessagesCount() { const auto way = _sendWay.current(); - const auto withCaption = _list.canAddCaption( - way.groupFiles() && way.sendImagesAsPhotos(), - way.sendImagesAsPhotos()); + const auto slowmode = (_limits & SendFilesAllow::OnlyOne) + && (_list.files.size() > 1); + const auto withCaption = Ui::CaptionWillBeAttached(_list, way, slowmode); const auto withComment = !withCaption && _caption && !_caption->isHidden() @@ -1082,7 +1081,9 @@ void SendFilesBox::updateCaptionPlaceholder() { _emojiToggle->hide(); } } else { - _caption->setPlaceholder(FieldPlaceholder(_list, way)); + const auto slowmode = (_limits & SendFilesAllow::OnlyOne) + && (_list.files.size() > 1); + _caption->setPlaceholder(FieldPlaceholder(_list, way, slowmode)); _caption->show(); if (_emojiToggle) { _emojiToggle->show(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index df507c71d9..201bf390c4 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -603,13 +603,13 @@ void ScheduledWidget::sendingFilesConfirmed( return; } auto groups = DivideByGroups(std::move(list), way, false); + const auto captionAttached = CaptionWillBeAttached(groups); const auto type = way.sendImagesAsPhotos() ? SendMediaType::Photo : SendMediaType::File; auto action = prepareSendAction(options); action.clearDraft = false; - if ((groups.size() != 1 || !groups.front().sentWithCaption()) - && !caption.text.isEmpty()) { + if (!captionAttached && !caption.text.isEmpty()) { auto message = Api::MessageToSend(action); message.textWithTags = base::take(caption); session().api().sendMessage(std::move(message)); diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index e566df58e6..0c833f8701 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -1416,13 +1416,13 @@ void ShortcutMessages::sendingFilesConfirmed( std::move(list), way, _history->peer->slowmodeApplied()); + const auto captionAttached = CaptionWillBeAttached(groups); const auto type = way.sendImagesAsPhotos() ? SendMediaType::Photo : SendMediaType::File; auto action = prepareSendAction(options); action.clearDraft = false; - if ((groups.size() != 1 || !groups.front().sentWithCaption()) - && !caption.text.isEmpty()) { + if (!captionAttached && !caption.text.isEmpty()) { auto message = Api::MessageToSend(action); message.textWithTags = base::take(caption); _session->api().sendMessage(std::move(message)); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index 73a13eeb80..dc1a745c3c 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -22,6 +22,89 @@ namespace { constexpr auto kMaxAlbumCount = 10; +struct GroupRange { + int from = 0; + int till = 0; + AlbumType type = AlbumType::None; + + [[nodiscard]] int size() const { + return till - from; + } + [[nodiscard]] bool sentWithCaption() const { + return (size() == 1) || (type == AlbumType::PhotoVideo); + } +}; + +[[nodiscard]] AlbumType GroupTypeForFile( + PreparedFile::Type type, + bool groupFiles, + bool sendImagesAsPhotos) { + using Type = PreparedFile::Type; + return (type == Type::Music) + ? (groupFiles ? AlbumType::Music : AlbumType::None) + : (type == Type::Video) + ? (groupFiles ? AlbumType::PhotoVideo : AlbumType::None) + : (type == Type::Photo) + ? ((groupFiles && sendImagesAsPhotos) + ? AlbumType::PhotoVideo + : (groupFiles && !sendImagesAsPhotos) + ? AlbumType::File + : AlbumType::None) + : (type == Type::File) + ? (groupFiles ? AlbumType::File : AlbumType::None) + : AlbumType::None; +} + +[[nodiscard]] std::vector GroupRanges( + const std::vector &files, + SendFilesWay way, + bool slowmode) { + const auto sendImagesAsPhotos = way.sendImagesAsPhotos(); + const auto groupFiles = way.groupFiles() || slowmode; + + auto result = std::vector(); + if (files.empty()) { + return result; + } + auto from = 0; + auto groupType = AlbumType::None; + for (auto i = 0; i != int(files.size()); ++i) { + const auto fileGroupType = GroupTypeForFile( + files[i].type, + groupFiles, + sendImagesAsPhotos); + const auto count = (i - from); + if ((i > from && groupType != fileGroupType) + || ((groupType != AlbumType::None) && (count == kMaxAlbumCount))) { + result.push_back(GroupRange{ + .from = from, + .till = i, + .type = (count > 1) ? groupType : AlbumType::None, + }); + from = i; + } + groupType = fileGroupType; + } + const auto till = int(files.size()); + const auto count = (till - from); + result.push_back(GroupRange{ + .from = from, + .till = till, + .type = (count > 1) ? groupType : AlbumType::None, + }); + return result; +} + +[[nodiscard]] bool CaptionWillBeAttachedFromRanges( + const std::vector &ranges, + int filesCount) { + const auto hasGroupedFileAlbum = ranges::any_of(ranges, [](const auto &r) { + return (r.size() > 1) && (r.type == AlbumType::File); + }); + return ((filesCount > 1) && hasGroupedFileAlbum) + || ((ranges.size() == 1) && ranges.front().sentWithCaption()); +} + } // namespace PreparedFile::PreparedFile(const QString &path) : path(path) { @@ -274,6 +357,33 @@ bool PreparedList::hasSpoilerMenu(bool compress) const { return allAreVideo || (allAreMedia && compress); } +bool AttachCaptionToFirstAsFile( + const std::vector &groups) { + auto filesCount = 0; + auto hasGroupedFileAlbum = false; + for (const auto &group : groups) { + filesCount += group.list.files.size(); + hasGroupedFileAlbum = hasGroupedFileAlbum + || ((group.list.files.size() > 1) + && (group.type == AlbumType::File)); + } + const auto result = (filesCount > 1) && hasGroupedFileAlbum; + return result; +} + +bool CaptionWillBeAttached(const std::vector &groups) { + return AttachCaptionToFirstAsFile(groups) + || ((groups.size() == 1) && groups.front().sentWithCaption()); +} + +bool CaptionWillBeAttached( + const PreparedList &list, + SendFilesWay way, + bool slowmode) { + const auto ranges = GroupRanges(list.files, way, slowmode); + return CaptionWillBeAttachedFromRanges(ranges, int(list.files.size())); +} + std::shared_ptr PrepareFilesBundle( std::vector groups, SendFilesWay way, @@ -283,8 +393,9 @@ std::shared_ptr PrepareFilesBundle( for (const auto &group : groups) { totalCount += group.list.files.size(); } + const auto captionAttached = CaptionWillBeAttached(groups); const auto sendComment = !caption.text.isEmpty() - && (groups.size() != 1 || !groups.front().sentWithCaption()); + && !captionAttached; return std::make_shared(PreparedBundle{ .groups = std::move(groups), .way = way, @@ -310,49 +421,19 @@ std::vector DivideByGroups( PreparedList &&list, SendFilesWay way, bool slowmode) { - const auto sendImagesAsPhotos = way.sendImagesAsPhotos(); - const auto groupFiles = way.groupFiles() || slowmode; - - auto group = Ui::PreparedList(); - - using Type = Ui::PreparedFile::Type; - auto groupType = AlbumType::None; - + const auto ranges = GroupRanges(list.files, way, slowmode); auto result = std::vector(); - auto pushGroup = [&] { - const auto type = (group.files.size() > 1) - ? groupType - : AlbumType::None; - result.push_back(PreparedGroup{ - .list = base::take(group), - .type = type, - }); - }; - for (auto i = 0; i != list.files.size(); ++i) { - auto &file = list.files[i]; - const auto fileGroupType = (file.type == Type::Music) - ? (groupFiles ? AlbumType::Music : AlbumType::None) - : (file.type == Type::Video) - ? (groupFiles ? AlbumType::PhotoVideo : AlbumType::None) - : (file.type == Type::Photo) - ? ((groupFiles && sendImagesAsPhotos) - ? AlbumType::PhotoVideo - : (groupFiles && !sendImagesAsPhotos) - ? AlbumType::File - : AlbumType::None) - : (file.type == Type::File) - ? (groupFiles ? AlbumType::File : AlbumType::None) - : AlbumType::None; - if ((!group.files.empty() && groupType != fileGroupType) - || ((groupType != AlbumType::None) - && (group.files.size() == Ui::MaxAlbumItems()))) { - pushGroup(); + result.reserve(ranges.size()); + for (const auto &range : ranges) { + auto grouped = Ui::PreparedList(); + grouped.files.reserve(range.size()); + for (auto i = range.from; i != range.till; ++i) { + grouped.files.push_back(std::move(list.files[i])); } - group.files.push_back(std::move(file)); - groupType = fileGroupType; - } - if (!group.files.empty()) { - pushGroup(); + result.push_back(PreparedGroup{ + .list = std::move(grouped), + .type = range.type, + }); } return result; } diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index 635be179ad..5eddcf8ad1 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -154,6 +154,12 @@ struct PreparedGroup { PreparedList &&list, SendFilesWay way, bool slowmode); +[[nodiscard]] bool CaptionWillBeAttached( + const std::vector &groups); +[[nodiscard]] bool CaptionWillBeAttached( + const PreparedList &list, + SendFilesWay way, + bool slowmode); struct PreparedBundle { std::vector groups;