/* 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 "media/stories/media_stories_share.h" #include "api/api_common.h" #include "apiwrap.h" #include "base/random.h" #include "boxes/share_box.h" #include "chat_helpers/compose/compose_show.h" #include "data/business/data_shortcut_messages.h" #include "data/data_channel.h" #include "data/data_chat_participant_status.h" #include "data/data_forum_topic.h" #include "data/data_histories.h" #include "data/data_peer.h" #include "data/data_session.h" #include "data/data_stories.h" #include "data/data_thread.h" #include "data/data_user.h" #include "history/history.h" #include "history/history_item_helpers.h" // GetErrorForSending. #include "history/view/history_view_context_menu.h" // CopyStoryLink. #include "lang/lang_keys.h" #include "main/main_session.h" #include "settings/settings_credits_graphics.h" #include "ui/boxes/confirm_box.h" #include "ui/text/text_utilities.h" #include "styles/style_calls.h" namespace Media::Stories { [[nodiscard]] object_ptr PrepareShareBox( std::shared_ptr show, FullStoryId id, bool viewerStyle) { const auto session = &show->session(); const auto resolve = [=] { const auto maybeStory = session->data().stories().lookup(id); return maybeStory ? maybeStory->get() : nullptr; }; const auto story = resolve(); if (!story) { return { nullptr }; } const auto canCopyLink = story->hasDirectLink(); auto copyCallback = [=] { const auto story = resolve(); if (!story) { return; } if (story->hasDirectLink()) { using namespace HistoryView; CopyStoryLink(show, story->fullId()); } }; struct State { int requests = 0; }; const auto state = std::make_shared(); auto filterCallback = [=](not_null thread) { if (const auto user = thread->peer()->asUser()) { if (user->canSendIgnoreMoneyRestrictions()) { return true; } } return Data::CanSend(thread, ChatRestriction::SendPhotos) && Data::CanSend(thread, ChatRestriction::SendVideos); }; auto copyLinkCallback = canCopyLink ? Fn(std::move(copyCallback)) : Fn(); auto countMessagesCallback = [=](const TextWithTags &comment) { return comment.text.isEmpty() ? 1 : 2; }; auto submitCallback = [=]( std::vector> &&result, Fn checkPaid, TextWithTags &&comment, Api::SendOptions options, Data::ForwardOptions forwardOptions) { if (state->requests) { return; // Share clicked already. } const auto story = resolve(); if (!story) { return; } const auto peer = story->peer(); const auto error = GetErrorForSending( result, { .story = story, .text = &comment }); if (error.error) { show->showBox(MakeSendErrorBox(error, result.size() > 1)); return; } else if (!checkPaid()) { return; } const auto api = &story->owner().session().api(); auto &histories = story->owner().histories(); for (const auto thread : result) { const auto action = Api::SendAction(thread, options); if (!comment.text.isEmpty()) { auto message = Api::MessageToSend(action); message.textWithTags = comment; message.action.clearDraft = false; api->sendMessage(std::move(message)); } const auto session = &thread->session(); const auto threadPeer = thread->peer(); const auto threadHistory = thread->owningHistory(); const auto randomId = base::RandomValue(); using SendFlag = MTPmessages_SendMedia::Flag; auto sendFlags = SendFlag(0) | SendFlag(0); if (action.replyTo) { sendFlags |= SendFlag::f_reply_to; } const auto silentPost = ShouldSendSilent(threadPeer, options); if (silentPost) { sendFlags |= SendFlag::f_silent; } if (options.scheduled) { sendFlags |= SendFlag::f_schedule_date; } if (options.shortcutId) { sendFlags |= SendFlag::f_quick_reply_shortcut; } if (options.effectId) { sendFlags |= SendFlag::f_effect; } if (options.invertCaption) { sendFlags |= SendFlag::f_invert_media; } const auto starsPaid = std::min( threadHistory->peer->starsPerMessageChecked(), options.starsApproved); if (starsPaid) { options.starsApproved -= starsPaid; sendFlags |= SendFlag::f_allow_paid_stars; } const auto done = [=] { if (!--state->requests) { if (show->valid()) { show->showToast(tr::lng_share_done(tr::now)); show->hideLayer(); } } }; histories.sendPreparedMessage( threadHistory, action.replyTo, randomId, Data::Histories::PrepareMessage( MTP_flags(sendFlags), threadPeer->input, Data::Histories::ReplyToPlaceholder(), MTP_inputMediaStory(peer->input, MTP_int(id.story)), MTPstring(), MTP_long(randomId), MTPReplyMarkup(), MTPVector(), MTP_int(options.scheduled), MTP_inputPeerEmpty(), Data::ShortcutIdToMTP(session, options.shortcutId), MTP_long(options.effectId), MTP_long(starsPaid) ), [=]( const MTPUpdates &result, const MTP::Response &response) { done(); }, [=]( const MTP::Error &error, const MTP::Response &response) { api->sendMessageFail(error, threadPeer, randomId); done(); }); ++state->requests; } }; const auto st = viewerStyle ? ::Settings::DarkCreditsEntryBoxStyle() : ::Settings::CreditsEntryBoxStyleOverrides(); return Box(ShareBox::Descriptor{ .session = session, .copyCallback = std::move(copyLinkCallback), .countMessagesCallback = std::move(countMessagesCallback), .submitCallback = std::move(submitCallback), .filterCallback = std::move(filterCallback), .st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(), }); } QString FormatShareAtTime(TimeId seconds) { const auto minutes = seconds / 60; const auto h = minutes / 60; const auto m = minutes % 60; const auto s = seconds % 60; const auto zero = QChar('0'); return h ? u"%1:%2:%3"_q.arg(h).arg(m, 2, 10, zero).arg(s, 2, 10, zero) : u"%1:%2"_q.arg(m).arg(s, 2, 10, zero); } object_ptr PrepareShareAtTimeBox( std::shared_ptr show, not_null item, TimeId videoTimestamp) { const auto id = item->fullId(); const auto history = item->history(); const auto owner = &history->owner(); const auto session = &history->session(); const auto canCopyLink = item->hasDirectLink() && history->peer->isBroadcast() && history->peer->asBroadcast()->hasUsername(); const auto hasCaptions = item->media() && !item->originalText().text.isEmpty() && item->media()->allowsEditCaption(); const auto hasOnlyForcedForwardedInfo = !hasCaptions && item->media() && item->media()->forceForwardedInfo(); auto copyCallback = [=] { const auto item = owner->message(id); if (!item) { return; } CopyPostLink( show, item->fullId(), HistoryView::Context::History, videoTimestamp); }; const auto requiredRight = item->requiredSendRight(); const auto requiresInline = item->requiresSendInlineRight(); auto filterCallback = [=](not_null thread) { if (const auto user = thread->peer()->asUser()) { if (user->canSendIgnoreMoneyRestrictions()) { return true; } } return Data::CanSend(thread, requiredRight) && (!requiresInline || Data::CanSend(thread, ChatRestriction::SendInline)); }; auto copyLinkCallback = canCopyLink ? Fn(std::move(copyCallback)) : Fn(); const auto st = ::Settings::DarkCreditsEntryBoxStyle(); return Box(ShareBox::Descriptor{ .session = session, .copyCallback = std::move(copyLinkCallback), .countMessagesCallback = ShareBox::DefaultForwardCountMessages( history, { id }), .submitCallback = ShareBox::DefaultForwardCallback( show, history, { id }, videoTimestamp), .filterCallback = std::move(filterCallback), .titleOverride = tr::lng_share_at_time_title( lt_time, rpl::single(FormatShareAtTime(videoTimestamp))), .st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(), .forwardOptions = { .sendersCount = ItemsForwardSendersCount({ item }), .captionsCount = ItemsForwardCaptionsCount({ item }), .show = !hasOnlyForcedForwardedInfo, }, .moneyRestrictionError = ShareMessageMoneyRestrictionError(), }); } } // namespace Media::Stories