mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-13 04:37:11 +02:00
Allow sharing video at a timestamp.
This commit is contained in:
parent
2077f51084
commit
999a13358e
12 changed files with 200 additions and 16 deletions
|
@ -4157,6 +4157,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_reply_cant_forward" = "Sorry, you can't reply to a message that was sent before the group was upgraded to a supergroup. Do you wish to forward it and add your comment?";
|
||||
|
||||
"lng_share_title" = "Share to";
|
||||
"lng_share_at_time_title" = "Share at {time} to";
|
||||
"lng_share_copy_link" = "Copy share link";
|
||||
"lng_share_confirm" = "Send";
|
||||
"lng_share_wrong_user" = "This game was opened from a different user.";
|
||||
|
@ -4280,7 +4281,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_media_save_progress" = "{ready} of {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Save As...";
|
||||
"lng_mediaview_copy" = "Copy";
|
||||
"lng_mediaview_copy_frame" = "Copy Frame";
|
||||
"lng_mediaview_forward" = "Forward";
|
||||
"lng_mediaview_share_at_time" = "Share at {time}";
|
||||
"lng_mediaview_delete" = "Delete";
|
||||
"lng_mediaview_save_to_profile" = "Post to Profile";
|
||||
"lng_mediaview_pin_story_done" = "Story pinned";
|
||||
|
|
|
@ -142,6 +142,16 @@ void ShowChannelsLimitBox(not_null<PeerData*> peer) {
|
|||
action.replaceMediaOf);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatVideoTimestamp(TimeId seconds) {
|
||||
const auto minutes = seconds / 60;
|
||||
const auto hours = minutes / 60;
|
||||
return hours
|
||||
? u"%1h%2m%3s"_q.arg(hours).arg(minutes % 60).arg(seconds % 60)
|
||||
: minutes
|
||||
? u"%1m%2s"_q.arg(minutes).arg(seconds % 60)
|
||||
: QString::number(seconds);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
|
@ -717,7 +727,8 @@ void ApiWrap::finalizeMessageDataRequest(
|
|||
QString ApiWrap::exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext,
|
||||
bool forceNonPublicLink) {
|
||||
bool forceNonPublicLink,
|
||||
std::optional<TimeId> videoTimestamp) {
|
||||
Expects(item->history()->peer->isChannel());
|
||||
|
||||
const auto itemId = item->fullId();
|
||||
|
@ -790,7 +801,14 @@ QString ApiWrap::exportDirectMessageLink(
|
|||
_unlikelyMessageLinks.emplace_or_assign(itemId, link);
|
||||
}
|
||||
}).send();
|
||||
return current;
|
||||
const auto addTimestamp = channel->hasUsername()
|
||||
&& !inRepliesContext
|
||||
&& videoTimestamp.has_value();
|
||||
const auto addedSeparator = (current.indexOf('?') >= 0) ? '&' : '?';
|
||||
const auto addedTimestamp = addTimestamp
|
||||
? (addedSeparator + u"t="_q + FormatVideoTimestamp(*videoTimestamp))
|
||||
: QString();
|
||||
return current + addedTimestamp;
|
||||
}
|
||||
|
||||
QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
|
||||
|
|
|
@ -166,7 +166,8 @@ public:
|
|||
QString exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext,
|
||||
bool forceNonPublicLink = false);
|
||||
bool forceNonPublicLink = false,
|
||||
std::optional<TimeId> videoTimestamp = {});
|
||||
QString exportDirectStoryLink(not_null<Data::Story*> item);
|
||||
|
||||
void requestContacts();
|
||||
|
|
|
@ -277,7 +277,9 @@ void ShareBox::prepare() {
|
|||
_select->resizeToWidth(st::boxWideWidth);
|
||||
Ui::SendPendingMoveResizeEvents(_select);
|
||||
|
||||
setTitle(tr::lng_share_title());
|
||||
setTitle(_descriptor.titleOverride
|
||||
? std::move(_descriptor.titleOverride)
|
||||
: tr::lng_share_title());
|
||||
|
||||
_inner = setInnerWidget(
|
||||
object_ptr<Inner>(this, _descriptor, uiShow()),
|
||||
|
@ -1495,7 +1497,8 @@ ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
|
|||
ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<History*> history,
|
||||
MessageIdsList msgIds) {
|
||||
MessageIdsList msgIds,
|
||||
std::optional<TimeId> videoTimestamp) {
|
||||
struct State final {
|
||||
base::flat_set<mtpRequestId> requests;
|
||||
};
|
||||
|
@ -1531,6 +1534,9 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
: Flag(0))
|
||||
| ((forwardOptions == Data::ForwardOptions::NoNamesAndCaptions)
|
||||
? Flag::f_drop_media_captions
|
||||
: Flag(0))
|
||||
| (videoTimestamp.has_value()
|
||||
? Flag::f_video_timestamp
|
||||
: Flag(0));
|
||||
auto mtpMsgIds = QVector<MTPint>();
|
||||
mtpMsgIds.reserve(existingIds.size());
|
||||
|
@ -1588,7 +1594,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
MTP_int(options.scheduled),
|
||||
MTP_inputPeerEmpty(), // send_as
|
||||
Data::ShortcutIdToMTP(session, options.shortcutId),
|
||||
MTPint() // video_timestamp
|
||||
MTP_int(videoTimestamp.value_or(0))
|
||||
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
||||
threadHistory->session().api().applyUpdates(updates);
|
||||
state->requests.remove(reqId);
|
||||
|
|
|
@ -104,7 +104,8 @@ public:
|
|||
[[nodiscard]] static SubmitCallback DefaultForwardCallback(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<History*> history,
|
||||
MessageIdsList msgIds);
|
||||
MessageIdsList msgIds,
|
||||
std::optional<TimeId> videoTimestamp = {});
|
||||
|
||||
struct Descriptor {
|
||||
not_null<Main::Session*> session;
|
||||
|
@ -113,7 +114,9 @@ public:
|
|||
FilterCallback filterCallback;
|
||||
object_ptr<Ui::RpWidget> bottomWidget = { nullptr };
|
||||
rpl::producer<QString> copyLinkText;
|
||||
rpl::producer<QString> titleOverride;
|
||||
ShareBoxStyleOverrides st;
|
||||
std::optional<TimeId> videoTimestamp;
|
||||
struct {
|
||||
int sendersCount = 0;
|
||||
int captionsCount = 0;
|
||||
|
|
|
@ -1295,25 +1295,28 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
|
|||
void CopyPostLink(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
Context context) {
|
||||
CopyPostLink(controller->uiShow(), itemId, context);
|
||||
Context context,
|
||||
std::optional<TimeId> videoTimestamp) {
|
||||
CopyPostLink(controller->uiShow(), itemId, context, videoTimestamp);
|
||||
}
|
||||
|
||||
void CopyPostLink(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
FullMsgId itemId,
|
||||
Context context) {
|
||||
Context context,
|
||||
std::optional<TimeId> videoTimestamp) {
|
||||
const auto item = show->session().data().message(itemId);
|
||||
if (!item || !item->hasDirectLink()) {
|
||||
return;
|
||||
}
|
||||
const auto inRepliesContext = (context == Context::Replies);
|
||||
const auto forceNonPublicLink = base::IsCtrlPressed();
|
||||
const auto forceNonPublicLink = !videoTimestamp && base::IsCtrlPressed();
|
||||
QGuiApplication::clipboard()->setText(
|
||||
item->history()->session().api().exportDirectMessageLink(
|
||||
item,
|
||||
inRepliesContext,
|
||||
forceNonPublicLink));
|
||||
forceNonPublicLink,
|
||||
videoTimestamp));
|
||||
|
||||
const auto isPublicLink = [&] {
|
||||
if (forceNonPublicLink) {
|
||||
|
@ -1334,7 +1337,7 @@ void CopyPostLink(
|
|||
}
|
||||
return channel->hasUsername();
|
||||
}();
|
||||
if (isPublicLink) {
|
||||
if (isPublicLink && !videoTimestamp) {
|
||||
show->showToast({
|
||||
.text = tr::lng_channel_public_link_copied(
|
||||
tr::now, Ui::Text::Bold
|
||||
|
|
|
@ -60,11 +60,13 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
|
|||
void CopyPostLink(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
Context context);
|
||||
Context context,
|
||||
std::optional<TimeId> videoTimestamp = {});
|
||||
void CopyPostLink(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
FullMsgId itemId,
|
||||
Context context);
|
||||
Context context,
|
||||
std::optional<TimeId> videoTimestamp = {});
|
||||
void CopyStoryLink(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
FullStoryId storyId);
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#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"
|
||||
|
@ -181,4 +182,83 @@ namespace Media::Stories {
|
|||
});
|
||||
}
|
||||
|
||||
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<Ui::BoxContent> PrepareShareAtTimeBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<HistoryItem*> 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<Data::Thread*> thread) {
|
||||
if (const auto user = thread->peer()->asUser()) {
|
||||
if (user->canSendIgnoreRequirePremium()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return Data::CanSend(thread, requiredRight)
|
||||
&& (!requiresInline
|
||||
|| Data::CanSend(thread, ChatRestriction::SendInline));
|
||||
};
|
||||
auto copyLinkCallback = canCopyLink
|
||||
? Fn<void()>(std::move(copyCallback))
|
||||
: Fn<void()>();
|
||||
const auto st = ::Settings::DarkCreditsEntryBoxStyle();
|
||||
return Box<ShareBox>(ShareBox::Descriptor{
|
||||
.session = session,
|
||||
.copyCallback = std::move(copyLinkCallback),
|
||||
.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,
|
||||
},
|
||||
.premiumRequiredError = SharePremiumRequiredError(),
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Media::Stories
|
||||
|
|
|
@ -24,4 +24,11 @@ namespace Media::Stories {
|
|||
FullStoryId id,
|
||||
bool viewerStyle = false);
|
||||
|
||||
[[nodiscard]] QString FormatShareAtTime(TimeId seconds);
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareShareAtTimeBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<HistoryItem*> item,
|
||||
TimeId videoTimestamp);
|
||||
|
||||
} // namespace Media::Stories
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_attached_stickers.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "menu/menu_sponsored.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
|
@ -50,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/view/media_view_pip.h"
|
||||
#include "media/view/media_view_overlay_raster.h"
|
||||
#include "media/view/media_view_overlay_opengl.h"
|
||||
#include "media/stories/media_stories_share.h"
|
||||
#include "media/stories/media_stories_view.h"
|
||||
#include "media/streaming/media_streaming_document.h"
|
||||
#include "media/streaming/media_streaming_player.h"
|
||||
|
@ -1646,7 +1648,9 @@ void OverlayWidget::fillContextMenuActions(
|
|||
if (!hasCopyMediaRestriction()) {
|
||||
if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) {
|
||||
addAction(
|
||||
tr::lng_mediaview_copy(tr::now),
|
||||
((_document && _streamed)
|
||||
? tr::lng_mediaview_copy_frame(tr::now)
|
||||
: tr::lng_mediaview_copy(tr::now)),
|
||||
[=] { copyMedia(); },
|
||||
&st::mediaMenuIconCopy);
|
||||
}
|
||||
|
@ -1663,6 +1667,31 @@ void OverlayWidget::fillContextMenuActions(
|
|||
tr::lng_mediaview_forward(tr::now),
|
||||
[=] { forwardMedia(); },
|
||||
&st::mediaMenuIconForward);
|
||||
if (canShareAtTime()) {
|
||||
const auto now = [=] {
|
||||
return tr::lng_mediaview_share_at_time(
|
||||
tr::now,
|
||||
lt_time,
|
||||
Stories::FormatShareAtTime(shareAtVideoTimestamp()));
|
||||
};
|
||||
const auto action = addAction(
|
||||
now(),
|
||||
[=] { shareAtTime(); },
|
||||
&st::mediaMenuIconShare);
|
||||
struct State {
|
||||
rpl::variable<QString> text;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
const auto state = Ui::CreateChild<State>(action);
|
||||
state->text = rpl::single(
|
||||
rpl::empty
|
||||
) | rpl::then(
|
||||
base::timer_each(120)
|
||||
) | rpl::map(now);
|
||||
state->text.changes() | rpl::start_with_next([=](QString value) {
|
||||
action->setText(value);
|
||||
}, state->lifetime);
|
||||
}
|
||||
}
|
||||
if (story && story->canShare()) {
|
||||
addAction(tr::lng_mediaview_forward(tr::now), [=] {
|
||||
|
@ -2638,6 +2667,33 @@ void OverlayWidget::handleDocumentClick() {
|
|||
}
|
||||
}
|
||||
|
||||
bool OverlayWidget::canShareAtTime() const {
|
||||
const auto media = _message ? _message->media() : nullptr;
|
||||
return _document
|
||||
&& media
|
||||
&& _streamed
|
||||
&& (_document == media->document())
|
||||
&& _document->isVideoFile()
|
||||
&& !media->webpage();
|
||||
}
|
||||
|
||||
TimeId OverlayWidget::shareAtVideoTimestamp() const {
|
||||
return _streamedPosition / crl::time(1000);
|
||||
}
|
||||
|
||||
void OverlayWidget::shareAtTime() {
|
||||
if (!canShareAtTime()) {
|
||||
return;
|
||||
}
|
||||
if (!_streamed->instance.player().paused()
|
||||
&& !_streamed->instance.player().finished()) {
|
||||
playbackPauseResume();
|
||||
}
|
||||
const auto show = uiShow();
|
||||
const auto timestamp = shareAtVideoTimestamp();
|
||||
show->show(Stories::PrepareShareAtTimeBox(show, _message, timestamp));
|
||||
}
|
||||
|
||||
void OverlayWidget::downloadMedia() {
|
||||
if (!_photo && !_document) {
|
||||
return;
|
||||
|
|
|
@ -291,6 +291,10 @@ private:
|
|||
void handleTouchTimer();
|
||||
void handleDocumentClick();
|
||||
|
||||
[[nodiscard]] bool canShareAtTime() const;
|
||||
[[nodiscard]] TimeId shareAtVideoTimestamp() const;
|
||||
void shareAtTime();
|
||||
|
||||
void showSaveMsgToast(const QString &path, auto phrase);
|
||||
void showSaveMsgToastWith(
|
||||
const QString &path,
|
||||
|
|
|
@ -200,6 +200,7 @@ mediaMenuIconDownload: icon {{ "menu/download", mediaviewMenuFg }};
|
|||
mediaMenuIconDownloadLocked: icon {{ "menu/download_locked", mediaviewMenuFg }};
|
||||
mediaMenuIconCopy: icon {{ "menu/copy", mediaviewMenuFg }};
|
||||
mediaMenuIconForward: icon {{ "menu/forward", mediaviewMenuFg }};
|
||||
mediaMenuIconShare: icon {{ "menu/share2", mediaviewMenuFg }};
|
||||
mediaMenuIconDelete: icon {{ "menu/delete", mediaviewMenuFg }};
|
||||
mediaMenuIconShowAll: icon {{ "menu/all_media", mediaviewMenuFg }};
|
||||
mediaMenuIconProfile: icon {{ "menu/profile", mediaviewMenuFg }};
|
||||
|
|
Loading…
Add table
Reference in a new issue