mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-09-06 20:13:12 +02:00
feat: AyuForward initial
This commit is contained in:
parent
3be793032f
commit
f16fb42283
22 changed files with 767 additions and 13 deletions
|
@ -170,6 +170,10 @@ set(ayugram_files
|
||||||
ayu/features/streamer_mode/streamer_mode.h
|
ayu/features/streamer_mode/streamer_mode.h
|
||||||
ayu/features/messageshot/message_shot.cpp
|
ayu/features/messageshot/message_shot.cpp
|
||||||
ayu/features/messageshot/message_shot.h
|
ayu/features/messageshot/message_shot.h
|
||||||
|
ayu/features/forward/ayu_forward.cpp
|
||||||
|
ayu/features/forward/ayu_forward.h
|
||||||
|
ayu/features/forward/ayu_sync.cpp
|
||||||
|
ayu/features/forward/ayu_sync.h
|
||||||
ayu/data/messages_storage.cpp
|
ayu/data/messages_storage.cpp
|
||||||
ayu/data/messages_storage.h
|
ayu/data/messages_storage.h
|
||||||
ayu/data/entities.h
|
ayu/data/entities.h
|
||||||
|
|
|
@ -3338,6 +3338,20 @@ void ApiWrap::forwardMessages(
|
||||||
sendFlags |= SendFlag::f_top_msg_id;
|
sendFlags |= SendFlag::f_top_msg_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto fullAyuForward = AyuForward::isFullAyuForwardNeeded(draft.items.front()->from(), draft.items.front()->history());
|
||||||
|
if (fullAyuForward) {
|
||||||
|
crl::async([=] {
|
||||||
|
AyuForward::forwardMessages(peer, draft.items, _session, history, action, false);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto ayuIntelligentForwardNeeded = AyuForward::isAyuForwardNeeded(draft.items);
|
||||||
|
if (ayuIntelligentForwardNeeded) {
|
||||||
|
crl::async([=] {
|
||||||
|
AyuForward::intelligentForward(peer, draft.items, _session, history, action);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto forwardFrom = draft.items.front()->history()->peer;
|
auto forwardFrom = draft.items.front()->history()->peer;
|
||||||
auto ids = QVector<MTPint>();
|
auto ids = QVector<MTPint>();
|
||||||
auto randomIds = QVector<MTPlong>();
|
auto randomIds = QVector<MTPlong>();
|
||||||
|
@ -3766,7 +3780,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
? replyTo->topicRootId()
|
? replyTo->topicRootId()
|
||||||
: Data::ForumTopic::kGeneralId;
|
: Data::ForumTopic::kGeneralId;
|
||||||
const auto topic = peer->forumTopicFor(topicRootId);
|
const auto topic = peer->forumTopicFor(topicRootId);
|
||||||
if (!(topic ? Data::CanSendTexts(topic) : Data::CanSendTexts(peer))
|
if (!(topic
|
||||||
|
? Data::CanSendTexts(topic)
|
||||||
|
: Data::CanSendTexts(peer) || AyuForward::isForwarding(peer->id)
|
||||||
|
)
|
||||||
|
|
||||||
|| Api::SendDice(message)) {
|
|| Api::SendDice(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
#include "data/stickers/data_stickers_set.h"
|
#include "data/stickers/data_stickers_set.h"
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
|
#include "ayu/features/forward/ayu_forward.h"
|
||||||
|
|
||||||
class TaskQueue;
|
class TaskQueue;
|
||||||
struct MessageGroupId;
|
struct MessageGroupId;
|
||||||
|
|
309
Telegram/SourceFiles/ayu/features/forward/ayu_forward.cpp
Normal file
309
Telegram/SourceFiles/ayu/features/forward/ayu_forward.cpp
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
|
||||||
|
#include "ayu_forward.h"
|
||||||
|
|
||||||
|
#include <lang_auto.h>
|
||||||
|
#include <base/random.h>
|
||||||
|
#include <data/data_peer.h>
|
||||||
|
#include <history/history_item.h>
|
||||||
|
#include <styles/style_boxes.h>
|
||||||
|
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "ayu_sync.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_chat.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "storage/file_download.h"
|
||||||
|
#include "storage/localimageloader.h"
|
||||||
|
#include "storage/storage_account.h"
|
||||||
|
#include "storage/storage_media_prepare.h"
|
||||||
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
|
||||||
|
namespace AyuForward {
|
||||||
|
std::unordered_map<PeerId, ForwardState> forwardStates;
|
||||||
|
|
||||||
|
bool isForwarding(const PeerId &id) {
|
||||||
|
const auto state = forwardStates.find(id);
|
||||||
|
return id.value
|
||||||
|
&& state != forwardStates.end()
|
||||||
|
&& state->second.state != ForwardState::State::Finished
|
||||||
|
&& state->second.currentChunk < state->second.totalChunks
|
||||||
|
&& !state->second.stopRequested
|
||||||
|
&& state->second.totalChunks
|
||||||
|
&& state->second.totalMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString stateName(const PeerId &id) {
|
||||||
|
const auto fwState = forwardStates.find(id);
|
||||||
|
|
||||||
|
if (fwState == forwardStates.end()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
const auto state = fwState->second;
|
||||||
|
|
||||||
|
QString messagesString = tr::ayu_AyuForwardStatusChunkCount(tr::now,
|
||||||
|
lt_count1,
|
||||||
|
QString::number(state.sentMessages),
|
||||||
|
lt_count2,
|
||||||
|
QString::number(state.totalMessages)
|
||||||
|
);
|
||||||
|
QString chunkString = tr::ayu_AyuForwardStatusChunkCount(tr::now,
|
||||||
|
lt_count1,
|
||||||
|
QString::number(state.currentChunk + 1),
|
||||||
|
lt_count2,
|
||||||
|
QString::number(state.totalChunks)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto partString = state.totalChunks <= 1 ? messagesString : (messagesString + " • " + chunkString);
|
||||||
|
|
||||||
|
QString status;
|
||||||
|
if (state.state == ForwardState::State::Preparing) {
|
||||||
|
status = tr::ayu_AyuForwardStatusPreparing(tr::now);
|
||||||
|
} else if (state.state == ForwardState::State::Downloading) {
|
||||||
|
status = tr::ayu_AyuForwardStatusLoadingMedia(tr::now);
|
||||||
|
} else if (state.state == ForwardState::State::Sending) {
|
||||||
|
status = tr::ayu_AyuForwardStatusForwarding(tr::now);
|
||||||
|
} else { // ForwardState::State::Finished
|
||||||
|
status = tr::ayu_AyuForwardStatusFinished(tr::now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return status.append("\n").append(partString);// + "\n" + partString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForwardState::updateBottomBar(not_null<Main::Session *> session, const PeerData *peer, const State &st) {
|
||||||
|
state = st;
|
||||||
|
forwardStates[peer->id] = *this;
|
||||||
|
session->changes().peerUpdated(session->data().peer(peer->id), Data::PeerUpdate::Flag::Rights);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Ui::PreparedList prepareMedia(not_null<Main::Session*> session, const std::vector<not_null<HistoryItem*>> &items, int &i) {
|
||||||
|
const auto prepare = [&] (not_null<Data::Media*> media){
|
||||||
|
auto prepared = Ui::PreparedFile(AyuSync::filePath(session, media));
|
||||||
|
Storage::PrepareDetails(prepared, st::sendMediaPreviewSize, 1280);
|
||||||
|
return prepared;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ui::PreparedList list;
|
||||||
|
const auto startItem = items[i];
|
||||||
|
const auto media = startItem->media();
|
||||||
|
const auto groupId = startItem->groupId();
|
||||||
|
|
||||||
|
list.files.emplace_back(prepare(media));
|
||||||
|
|
||||||
|
if (!groupId.value) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = i + 1; k < items.size(); ++k) {
|
||||||
|
const auto nextItem = items[k];
|
||||||
|
if (nextItem->groupId() != groupId) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto nextMedia = nextItem->media()) {
|
||||||
|
list.files.emplace_back(prepare(nextMedia));
|
||||||
|
i = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMedia(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Ui::PreparedList &&list,
|
||||||
|
not_null<Data::Media*> primaryMedia,
|
||||||
|
Api::MessageToSend &&message,
|
||||||
|
uint64 newGroupId) {
|
||||||
|
|
||||||
|
|
||||||
|
if (const auto document = primaryMedia->document()) {
|
||||||
|
if (document->isGifv() || document->sticker()) {
|
||||||
|
AyuSync::sendGifOrStickSync(session, message, document);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mediaType = [&] {
|
||||||
|
if (const auto document = primaryMedia->document()) {
|
||||||
|
if (document->isVoiceMessage()) {
|
||||||
|
return SendMediaType::Audio;
|
||||||
|
} else if (document->isVideoMessage()) {
|
||||||
|
return SendMediaType::Round;
|
||||||
|
} else {
|
||||||
|
return SendMediaType::File;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SendMediaType::Photo;
|
||||||
|
}();
|
||||||
|
|
||||||
|
|
||||||
|
auto group = std::make_shared<SendingAlbum>();
|
||||||
|
group->groupId = newGroupId;
|
||||||
|
|
||||||
|
AyuSync::sendDocumentSync(
|
||||||
|
session,
|
||||||
|
std::move(list),
|
||||||
|
mediaType,
|
||||||
|
std::move(message.textWithTags),
|
||||||
|
group,
|
||||||
|
message.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem *>> &items) {
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (isAyuForwardNeeded(item)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(not_null<HistoryItem *> item) {
|
||||||
|
if (item->isDeleted() || item->isAyuNoForwards() || item->ttlDestroyAt()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFullAyuForwardNeeded(not_null<PeerData *> peer, not_null<History*> history) {
|
||||||
|
return peer->isAyuNoForwards() || history->peer->isAyuNoForwards();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ForwardChunk {
|
||||||
|
bool isAyuForwardNeeded;
|
||||||
|
std::vector<not_null<HistoryItem *>> items;
|
||||||
|
};
|
||||||
|
|
||||||
|
void intelligentForward(const PeerData* peer, const std::vector<not_null<HistoryItem *>> &items, not_null<Main::Session *> session, not_null<History *> history, const Api::SendAction &action) {
|
||||||
|
history->setForwardDraft(action.replyTo.topicRootId, {});
|
||||||
|
|
||||||
|
auto chunks = std::vector<ForwardChunk>();
|
||||||
|
|
||||||
|
auto currentArray = std::vector<not_null<HistoryItem *>>();
|
||||||
|
auto currentChunk = ForwardChunk({
|
||||||
|
.isAyuForwardNeeded = isAyuForwardNeeded(items[0]),
|
||||||
|
.items = currentArray
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (isAyuForwardNeeded(item) != currentChunk.isAyuForwardNeeded) {
|
||||||
|
currentChunk.items = currentArray;
|
||||||
|
chunks.push_back(currentChunk);
|
||||||
|
|
||||||
|
currentArray = std::vector<not_null<HistoryItem *>>();
|
||||||
|
currentChunk = ForwardChunk({
|
||||||
|
.isAyuForwardNeeded = isAyuForwardNeeded(item),
|
||||||
|
.items = currentArray
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentArray.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentChunk.items = currentArray;
|
||||||
|
chunks.push_back(currentChunk);
|
||||||
|
|
||||||
|
auto state = std::make_shared<ForwardState>(chunks.size());
|
||||||
|
forwardStates[peer->id] = *state;
|
||||||
|
|
||||||
|
for (const auto &chunk : chunks) {
|
||||||
|
if (chunk.isAyuForwardNeeded) {
|
||||||
|
forwardMessages(peer, chunk.items, session, history, action, true);
|
||||||
|
} else {
|
||||||
|
state->totalMessages = chunk.items.size();
|
||||||
|
state->sentMessages = 0;
|
||||||
|
state->updateBottomBar(session, peer, ForwardState::State::Sending);
|
||||||
|
|
||||||
|
AyuSync::forwardMessagesSync(session, chunk.items, action);
|
||||||
|
|
||||||
|
state->sentMessages = state->totalMessages;
|
||||||
|
state->updateBottomBar(session, peer, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->currentChunk++;
|
||||||
|
}
|
||||||
|
state->updateBottomBar(session, peer, ForwardState::State::Finished);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMessages(const PeerData *peer, std::vector<not_null<HistoryItem *> > items,
|
||||||
|
not_null<Main::Session *> session,
|
||||||
|
not_null<History *> history, const Api::SendAction &action, bool forwardState) {
|
||||||
|
history->setForwardDraft(action.replyTo.topicRootId, {});
|
||||||
|
|
||||||
|
ForwardState state;
|
||||||
|
if (forwardState) {
|
||||||
|
state = forwardStates[peer->id];
|
||||||
|
} else {
|
||||||
|
state = ForwardState(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardStates[peer->id] = state;
|
||||||
|
|
||||||
|
std::unordered_map<uint64, uint64> groupIds;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<not_null<HistoryItem *> > toBeDownloaded;
|
||||||
|
|
||||||
|
for (const auto item: items) {
|
||||||
|
if (item->media()) {
|
||||||
|
toBeDownloaded.push_back(item);
|
||||||
|
}
|
||||||
|
if (item->groupId()) {
|
||||||
|
const auto currentId = groupIds.find(item->groupId().value);
|
||||||
|
if (currentId == groupIds.end()) {
|
||||||
|
groupIds[item->groupId().value] = base::RandomValue<uint64>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toBeDownloaded.size()) {
|
||||||
|
state.updateBottomBar(session, peer, ForwardState::State::Downloading);
|
||||||
|
AyuSync::loadDocuments(session, toBeDownloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.totalMessages = items.size();
|
||||||
|
state.sentMessages = 0;
|
||||||
|
state.updateBottomBar(session, peer, ForwardState::State::Sending);
|
||||||
|
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
const auto item = items[i];
|
||||||
|
|
||||||
|
|
||||||
|
if (state.stopRequested) {
|
||||||
|
state.updateBottomBar(session, peer, ForwardState::State::Finished);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message = Api::MessageToSend(Api::SendAction(session->data().history(peer->id)));
|
||||||
|
|
||||||
|
message.textWithTags.text = item->originalText().text;
|
||||||
|
message.textWithTags.tags = TextUtilities::ConvertEntitiesToTextTags(item->originalText().entities);
|
||||||
|
|
||||||
|
const auto groupId = item->groupId().value;
|
||||||
|
const auto newGroupId = groupIds.contains(groupId) ? groupIds.find(groupId)->second : 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto poll = media->poll()) {
|
||||||
|
// need to implement extract of the text
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sendMedia(session, prepareMedia(session, items, i), media, std::move(message), newGroupId);
|
||||||
|
} else {
|
||||||
|
AyuSync::sendMessageSync(session, message);
|
||||||
|
}
|
||||||
|
state.sentMessages += 1;
|
||||||
|
}
|
||||||
|
state.updateBottomBar(session, peer, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AyuFeatures::AyuForward
|
44
Telegram/SourceFiles/ayu/features/forward/ayu_forward.h
Normal file
44
Telegram/SourceFiles/ayu/features/forward/ayu_forward.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <main/main_session.h>
|
||||||
|
|
||||||
|
#include "history/history.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace AyuForward {
|
||||||
|
bool isForwarding(const PeerId &id);
|
||||||
|
QString stateName(const PeerId &id);
|
||||||
|
|
||||||
|
class ForwardState {
|
||||||
|
public:
|
||||||
|
enum class State {
|
||||||
|
Preparing,
|
||||||
|
Downloading,
|
||||||
|
Sending,
|
||||||
|
Finished
|
||||||
|
};
|
||||||
|
|
||||||
|
void updateBottomBar(not_null<Main::Session *> session, const PeerData *peer, const State &st);
|
||||||
|
|
||||||
|
int totalChunks;
|
||||||
|
int currentChunk;
|
||||||
|
int totalMessages;
|
||||||
|
int sentMessages;
|
||||||
|
|
||||||
|
State state = State::Preparing;
|
||||||
|
bool stopRequested = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem *>> &items);
|
||||||
|
bool isAyuForwardNeeded(not_null<HistoryItem *> item);
|
||||||
|
bool isFullAyuForwardNeeded(not_null<PeerData *> peer, not_null<History*> history);
|
||||||
|
void intelligentForward(const PeerData* peer,
|
||||||
|
const std::vector<not_null<HistoryItem *>> &item, not_null<Main::Session *> session, not_null<History *> history, const Api::SendAction &action);
|
||||||
|
void forwardMessages(const PeerData* peer, std::vector<not_null<HistoryItem*>> item, not_null<Main::Session *> session, not_null<History *> history, const Api::SendAction &action, bool forwardState);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
287
Telegram/SourceFiles/ayu/features/forward/ayu_sync.cpp
Normal file
287
Telegram/SourceFiles/ayu/features/forward/ayu_sync.cpp
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
|
||||||
|
#include "ayu_sync.h"
|
||||||
|
|
||||||
|
#include "api/api_sending.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_photo_media.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "storage/file_download_mtproto.h"
|
||||||
|
|
||||||
|
|
||||||
|
class TimedCountDownLatch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TimedCountDownLatch(int count) : count_(count) {}
|
||||||
|
|
||||||
|
void countDown() {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
if (count_ > 0) {
|
||||||
|
count_--;
|
||||||
|
}
|
||||||
|
if (count_ == 0) {
|
||||||
|
cv_.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool await(std::chrono::milliseconds timeout) {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
if (count_ == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return cv_.wait_for(lock, timeout, [this] { return count_ == 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cv_;
|
||||||
|
int count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace AyuSync {
|
||||||
|
|
||||||
|
QString pathForSave(not_null<Main::Session *> session) {
|
||||||
|
const auto path = Core::App().settings().downloadPath();
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return File::DefaultDownloadPath(session);
|
||||||
|
}
|
||||||
|
if (path == FileDialog::Tmp()) {
|
||||||
|
return session->local().tempDirectory();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString filePath(not_null<Main::Session *> session, const Data::Media *media) {
|
||||||
|
if (const auto document = media->document()) {
|
||||||
|
if (!document->filename().isEmpty()) {
|
||||||
|
return pathForSave(session) + media->document()->filename();
|
||||||
|
}
|
||||||
|
if (const auto name = document->filepath(true); !name.isEmpty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
if (document->isVoiceMessage()) {
|
||||||
|
return pathForSave(session) + "audio_" + QString::number(document->getDC()) + "_" +
|
||||||
|
QString::number(document->id) + ".ogg";
|
||||||
|
}
|
||||||
|
if (document->isVideoMessage()) {
|
||||||
|
return pathForSave(session) + "round_" + QString::number(document->getDC()) + "_" +
|
||||||
|
QString::number(document->id) + ".mp4";
|
||||||
|
}
|
||||||
|
} else if (const auto photo = media->photo()) {
|
||||||
|
return pathForSave(session) + QString::number(photo->getDC()) + "_" + QString::number(photo->id) + ".jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
qint64 fileSize(not_null<HistoryItem *> item) {
|
||||||
|
if (const auto path = filePath(&item->history()->session(), item->media()); !path.isEmpty()) {
|
||||||
|
QFile file(path);
|
||||||
|
if (file.exists()) {
|
||||||
|
return file.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadDocuments(not_null<Main::Session *> session, const std::vector<not_null<HistoryItem *>> &items) {
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (const auto data = item->media()->document()) {
|
||||||
|
|
||||||
|
if (data->isGifv()) {
|
||||||
|
// no need to download it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = fileSize(item);
|
||||||
|
|
||||||
|
if (size == data->size) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (size && size < data->size) {
|
||||||
|
// in case there some unfinished file
|
||||||
|
QFile file(filePath(session, item->media()));
|
||||||
|
file.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadDocumentSync(session, data, item);
|
||||||
|
|
||||||
|
} else if (auto photo = item->media()->photo()) {
|
||||||
|
if (fileSize(item) == photo->imageByteSize(Data::PhotoSize::Large)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPhotoSync(session, std::pair(photo, item->fullId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void loadDocumentSync(not_null<Main::Session *> session, DocumentData *data, not_null<HistoryItem *> item) {
|
||||||
|
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
data->save(Data::FileOriginMessage(item->fullId()), filePath(session, item->media()));
|
||||||
|
|
||||||
|
session->downloaderTaskFinished()
|
||||||
|
| rpl::filter([&]{
|
||||||
|
return item->media()->document()->status == FileDownloadFailed || fileSize(item) == data->size;
|
||||||
|
}) | rpl::start_with_next([&]() mutable{
|
||||||
|
latch->countDown();
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
}, *lifetime);
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMessagesSync(not_null<Main::Session *> session,
|
||||||
|
const std::vector<not_null<HistoryItem *>> &items,
|
||||||
|
const ApiWrap::SendAction &action) {
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
|
||||||
|
crl::on_main([=, &latch]{
|
||||||
|
session->api().forwardMessages(Data::ResolvedForwardDraft(items), action, [&]{
|
||||||
|
latch->countDown();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loadPhotoSync(not_null<Main::Session *> session, const std::pair<not_null<PhotoData *>, FullMsgId> &photo) {
|
||||||
|
const auto folderPath = pathForSave(session);
|
||||||
|
const auto downloadPath = folderPath.isEmpty() ? Core::App().settings().downloadPath() : folderPath;
|
||||||
|
|
||||||
|
const auto path = downloadPath.isEmpty()
|
||||||
|
? File::DefaultDownloadPath(session)
|
||||||
|
: downloadPath == FileDialog::Tmp()
|
||||||
|
? session->local().tempDirectory()
|
||||||
|
: downloadPath;
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!QDir().mkpath(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto view = photo.first->createMediaView();
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view->wanted(Data::PhotoSize::Large, photo.second);
|
||||||
|
|
||||||
|
const auto finalCheck = [=]{
|
||||||
|
return !photo.first->loading();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto saveToFiles = [=]{
|
||||||
|
QDir directory(path);
|
||||||
|
const auto dir = directory.absolutePath();
|
||||||
|
const auto nameBase = dir.endsWith('/') ? dir : dir + '/';
|
||||||
|
const auto fullPath = nameBase + QString::number(photo.first->getDC()) + "_" + QString::number(photo.first->id) + ".jpg";
|
||||||
|
view->saveToFile(fullPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
if (finalCheck()) {
|
||||||
|
saveToFiles();
|
||||||
|
} else {
|
||||||
|
session->downloaderTaskFinished() | rpl::filter([&]{
|
||||||
|
return finalCheck();
|
||||||
|
}) | rpl::start_with_next([&]() mutable{
|
||||||
|
saveToFiles();
|
||||||
|
latch->countDown();
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
},*lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMessageSync(not_null<Main::Session *> session, Api::MessageToSend &message) {
|
||||||
|
crl::on_main([=, &message] {
|
||||||
|
// we cannot send events to objects
|
||||||
|
// owned by a different thread
|
||||||
|
// because sendMessage updates UI too
|
||||||
|
|
||||||
|
session->api().sendMessage(std::move(message));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
waitForMsgSync(session, message.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitForMsgSync(not_null<Main::Session *> session, const Api::SendAction &action) {
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
|
||||||
|
session->data().itemIdChanged()
|
||||||
|
| rpl::filter([&](const Data::Session::IdChange &update){
|
||||||
|
return action.history->peer->id == update.newId.peer;
|
||||||
|
}) | rpl::start_with_next([&]{
|
||||||
|
latch->countDown();
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
},*lifetime);
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendDocumentSync(not_null<Main::Session *> session,
|
||||||
|
Ui::PreparedList &&list,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
const std::shared_ptr<SendingAlbum> &album,
|
||||||
|
const Api::SendAction &action) {
|
||||||
|
|
||||||
|
const auto size = list.files.size();
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(size);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
crl::on_main([=, list = std::move(list), caption = std::move(caption)]() mutable{
|
||||||
|
session->api().sendFiles(std::move(list), type, std::move(caption), album, action);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// probably need to handle
|
||||||
|
// session->uploader().photoFailed()
|
||||||
|
// and
|
||||||
|
// session->uploader().documentFailed()
|
||||||
|
// too
|
||||||
|
|
||||||
|
rpl::merge(
|
||||||
|
session->uploader().documentReady(),
|
||||||
|
session->uploader().photoReady()
|
||||||
|
) | rpl::filter([&](const Storage::UploadedMedia &docOrPhoto){
|
||||||
|
return docOrPhoto.fullId.peer == action.history->peer->id;
|
||||||
|
}) | rpl::start_with_next([&]{
|
||||||
|
latch->countDown();
|
||||||
|
}, *lifetime);
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(5 * size));
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendGifOrStickSync(not_null<Main::Session *> session,
|
||||||
|
Api::MessageToSend &message,
|
||||||
|
not_null<DocumentData *> document) {
|
||||||
|
auto &action = message.action;
|
||||||
|
crl::on_main([&] {
|
||||||
|
Api::SendExistingDocument(std::move(message), document, std::nullopt);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
waitForMsgSync(session, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace AyuSync
|
51
Telegram/SourceFiles/ayu/features/forward/ayu_sync.h
Normal file
51
Telegram/SourceFiles/ayu/features/forward/ayu_sync.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma once
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include <base/random.h>
|
||||||
|
#include <data/data_histories.h>
|
||||||
|
#include <data/data_peer.h>
|
||||||
|
#include <history/history_item.h>
|
||||||
|
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "storage/file_download_mtproto.h"
|
||||||
|
#include "storage/file_upload.h"
|
||||||
|
#include "api/api_peer_photo.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/core_settings.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_chat.h"
|
||||||
|
#include "storage/localimageloader.h"
|
||||||
|
#include "storage/storage_media_prepare.h"
|
||||||
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
|
#include "storage/file_download.h"
|
||||||
|
#include "storage/storage_account.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace AyuSync {
|
||||||
|
|
||||||
|
QString pathForSave(not_null<Main::Session *> session);
|
||||||
|
QString filePath(not_null<Main::Session *> session, const Data::Media *media);
|
||||||
|
void loadDocuments(not_null<Main::Session *> session, const std::vector<not_null<HistoryItem *> > &items);
|
||||||
|
bool isMediaDownloadable(Data::Media *media);
|
||||||
|
void sendMessageSync(not_null<Main::Session *> session, Api::MessageToSend &message);
|
||||||
|
|
||||||
|
void sendDocumentSync(not_null<Main::Session *> session,
|
||||||
|
Ui::PreparedList &&list,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
const std::shared_ptr<SendingAlbum> &album,
|
||||||
|
const Api::SendAction &action);
|
||||||
|
|
||||||
|
void sendGifOrStickSync(not_null<Main::Session *> session,
|
||||||
|
Api::MessageToSend &message,
|
||||||
|
not_null<DocumentData *> document);
|
||||||
|
void waitForMsgSync(not_null<Main::Session *> session, const Api::SendAction &action);
|
||||||
|
void loadPhotoSync(not_null<Main::Session *> session, const std::pair<not_null<PhotoData *>, FullMsgId> &photos);
|
||||||
|
void loadDocumentSync(not_null<Main::Session *> session, DocumentData *data, not_null<HistoryItem *> item);
|
||||||
|
void forwardMessagesSync(not_null<Main::Session *> session, const std::vector<not_null<HistoryItem *> > &items, const ApiWrap::SendAction &action);
|
||||||
|
|
||||||
|
}
|
|
@ -619,7 +619,9 @@ bool ChannelData::canAddAdmins() const {
|
||||||
return amCreator()
|
return amCreator()
|
||||||
|| (adminRights() & AdminRight::AddAdmins);
|
|| (adminRights() & AdminRight::AddAdmins);
|
||||||
}
|
}
|
||||||
|
bool ChannelData::isAyuNoForwards() const {
|
||||||
|
return flags() & Flag::AyuNoForwards;
|
||||||
|
}
|
||||||
bool ChannelData::allowsForwarding() const {
|
bool ChannelData::allowsForwarding() const {
|
||||||
return !(flags() & Flag::NoForwards);
|
return !(flags() & Flag::NoForwards);
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ enum class ChannelDataFlag : uint64 {
|
||||||
StargiftsAvailable = (1ULL << 36),
|
StargiftsAvailable = (1ULL << 36),
|
||||||
PaidMessagesAvailable = (1ULL << 37),
|
PaidMessagesAvailable = (1ULL << 37),
|
||||||
AutoTranslation = (1ULL << 38),
|
AutoTranslation = (1ULL << 38),
|
||||||
|
|
||||||
|
AyuNoForwards = (1ULL << 60),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
|
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
|
||||||
using ChannelDataFlags = base::flags<ChannelDataFlag>;
|
using ChannelDataFlags = base::flags<ChannelDataFlag>;
|
||||||
|
@ -365,6 +367,7 @@ public:
|
||||||
|
|
||||||
// Like in ChatData.
|
// Like in ChatData.
|
||||||
[[nodiscard]] bool allowsForwarding() const;
|
[[nodiscard]] bool allowsForwarding() const;
|
||||||
|
[[nodiscard]] bool isAyuNoForwards() const;
|
||||||
[[nodiscard]] bool canEditInformation() const;
|
[[nodiscard]] bool canEditInformation() const;
|
||||||
[[nodiscard]] bool canEditPermissions() const;
|
[[nodiscard]] bool canEditPermissions() const;
|
||||||
[[nodiscard]] bool canEditUsername() const;
|
[[nodiscard]] bool canEditUsername() const;
|
||||||
|
|
|
@ -63,6 +63,9 @@ ChatAdminRightsInfo ChatData::defaultAdminRights(not_null<UserData*> user) {
|
||||||
| Flag::ManageCall
|
| Flag::ManageCall
|
||||||
| (isCreator ? Flag::AddAdmins : Flag(0)));
|
| (isCreator ? Flag::AddAdmins : Flag(0)));
|
||||||
}
|
}
|
||||||
|
bool ChatData::isAyuNoForwards() const {
|
||||||
|
return flags() & Flag::AyuNoForwards;
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatData::allowsForwarding() const {
|
bool ChatData::allowsForwarding() const {
|
||||||
return !(flags() & Flag::NoForwards);
|
return !(flags() & Flag::NoForwards);
|
||||||
|
|
|
@ -23,6 +23,8 @@ enum class ChatDataFlag {
|
||||||
CallNotEmpty = (1 << 6),
|
CallNotEmpty = (1 << 6),
|
||||||
CanSetUsername = (1 << 7),
|
CanSetUsername = (1 << 7),
|
||||||
NoForwards = (1 << 8),
|
NoForwards = (1 << 8),
|
||||||
|
|
||||||
|
AyuNoForwards = (1 << 20),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(ChatDataFlag) { return true; };
|
inline constexpr bool is_flag_type(ChatDataFlag) { return true; };
|
||||||
using ChatDataFlags = base::flags<ChatDataFlag>;
|
using ChatDataFlags = base::flags<ChatDataFlag>;
|
||||||
|
@ -99,6 +101,7 @@ public:
|
||||||
|
|
||||||
// Like in ChannelData.
|
// Like in ChannelData.
|
||||||
[[nodiscard]] bool allowsForwarding() const;
|
[[nodiscard]] bool allowsForwarding() const;
|
||||||
|
[[nodiscard]] bool isAyuNoForwards() const;
|
||||||
[[nodiscard]] bool canEditInformation() const;
|
[[nodiscard]] bool canEditInformation() const;
|
||||||
[[nodiscard]] bool canEditPermissions() const;
|
[[nodiscard]] bool canEditPermissions() const;
|
||||||
[[nodiscard]] bool canEditUsername() const;
|
[[nodiscard]] bool canEditUsername() const;
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "data/data_chat_participant_status.h"
|
#include "data/data_chat_participant_status.h"
|
||||||
|
|
||||||
|
#include "ayu/features/forward/ayu_forward.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "boxes/peers/edit_peer_permissions_box.h"
|
#include "boxes/peers/edit_peer_permissions_box.h"
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
|
@ -118,6 +119,9 @@ bool CanSendAnyOf(
|
||||||
not_null<const PeerData*> peer,
|
not_null<const PeerData*> peer,
|
||||||
ChatRestrictions rights,
|
ChatRestrictions rights,
|
||||||
bool forbidInForums) {
|
bool forbidInForums) {
|
||||||
|
if (AyuForward::isForwarding(peer->id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (peer->session().frozen()
|
if (peer->session().frozen()
|
||||||
&& !peer->isFreezeAppealChat()) {
|
&& !peer->isFreezeAppealChat()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -180,6 +184,11 @@ bool CanSendAnyOf(
|
||||||
SendError RestrictionError(
|
SendError RestrictionError(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
ChatRestriction restriction) {
|
ChatRestriction restriction) {
|
||||||
|
if (AyuForward::isForwarding(peer->id)) {
|
||||||
|
return SendError({
|
||||||
|
.text = AyuForward::stateName(peer->id)
|
||||||
|
});
|
||||||
|
}
|
||||||
using Flag = ChatRestriction;
|
using Flag = ChatRestriction;
|
||||||
if (peer->session().frozen()
|
if (peer->session().frozen()
|
||||||
&& !peer->isFreezeAppealChat()) {
|
&& !peer->isFreezeAppealChat()) {
|
||||||
|
|
|
@ -1385,6 +1385,17 @@ Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const {
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
bool PeerData::isAyuNoForwards() const {
|
||||||
|
if (const auto user = asUser()) {
|
||||||
|
return false;
|
||||||
|
} else if (const auto channel = asChannel()) {
|
||||||
|
return channel->isAyuNoForwards();
|
||||||
|
} else if (const auto chat = asChat()) {
|
||||||
|
return chat->isAyuNoForwards();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PeerData::allowsForwarding() const {
|
bool PeerData::allowsForwarding() const {
|
||||||
if (isUser()) {
|
if (isUser()) {
|
||||||
|
|
|
@ -264,6 +264,7 @@ public:
|
||||||
return _notify;
|
return _notify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isAyuNoForwards() const;
|
||||||
[[nodiscard]] bool allowsForwarding() const;
|
[[nodiscard]] bool allowsForwarding() const;
|
||||||
[[nodiscard]] Data::RestrictionCheckResult amRestricted(
|
[[nodiscard]] Data::RestrictionCheckResult amRestricted(
|
||||||
ChatRestriction right) const;
|
ChatRestriction right) const;
|
||||||
|
|
|
@ -860,7 +860,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
||||||
&& chat->groupCall()->fullCount() > 0))
|
&& chat->groupCall()->fullCount() > 0))
|
||||||
? Flag::CallNotEmpty
|
? Flag::CallNotEmpty
|
||||||
: Flag())
|
: Flag())
|
||||||
| (data.is_noforwards() ? Flag::NoForwards : Flag());
|
| (data.is_noforwards() ? Flag::NoForwards : Flag())
|
||||||
|
| (data.is_ayuNoforwards() ? Flag::AyuNoForwards : Flag());
|
||||||
chat->setFlags((chat->flags() & ~flagsMask) | flagsSet);
|
chat->setFlags((chat->flags() & ~flagsMask) | flagsSet);
|
||||||
chat->count = data.vparticipants_count().v;
|
chat->count = data.vparticipants_count().v;
|
||||||
|
|
||||||
|
@ -1017,7 +1018,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
||||||
&& data.is_stories_hidden())
|
&& data.is_stories_hidden())
|
||||||
? Flag::StoriesHidden
|
? Flag::StoriesHidden
|
||||||
: Flag())
|
: Flag())
|
||||||
| (data.is_autotranslation() ? Flag::AutoTranslation : Flag());
|
| (data.is_autotranslation() ? Flag::AutoTranslation : Flag())
|
||||||
|
| (data.is_ayuNoforwards() ? Flag::AyuNoForwards : Flag());
|
||||||
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
|
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
|
||||||
channel->setBotVerifyDetailsIcon(
|
channel->setBotVerifyDetailsIcon(
|
||||||
data.vbot_verification_icon().value_or_empty());
|
data.vbot_verification_icon().value_or_empty());
|
||||||
|
|
|
@ -351,6 +351,9 @@ enum class MessageFlag : uint64 {
|
||||||
ReactionsAllowed = (1ULL << 50),
|
ReactionsAllowed = (1ULL << 50),
|
||||||
|
|
||||||
HideDisplayDate = (1ULL << 51),
|
HideDisplayDate = (1ULL << 51),
|
||||||
|
|
||||||
|
|
||||||
|
AyuNoForwards = (1ULL << 60),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||||
using MessageFlags = base::flags<MessageFlag>;
|
using MessageFlags = base::flags<MessageFlag>;
|
||||||
|
|
|
@ -2776,7 +2776,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
const auto itemId = item->fullId();
|
const auto itemId = item->fullId();
|
||||||
const auto blockSender = item->history()->peer->isRepliesChat();
|
const auto blockSender = item->history()->peer->isRepliesChat();
|
||||||
if (isUponSelected != -2) {
|
if (isUponSelected != -2) {
|
||||||
if (item->allowsForward() && !item->isDeleted()) {
|
if (item->allowsForward()) {
|
||||||
_menu->addAction(tr::lng_context_forward_msg(tr::now), [=] {
|
_menu->addAction(tr::lng_context_forward_msg(tr::now), [=] {
|
||||||
forwardItem(itemId);
|
forwardItem(itemId);
|
||||||
}, &st::menuIconForward);
|
}, &st::menuIconForward);
|
||||||
|
@ -3018,7 +3018,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
}, &st::menuIconSelect);
|
}, &st::menuIconSelect);
|
||||||
} else if (item && ((isUponSelected != -2 && (canForward || canDelete)) || item->isRegular())) {
|
} else if (item && ((isUponSelected != -2 && (canForward || canDelete)) || item->isRegular())) {
|
||||||
if (isUponSelected != -2) {
|
if (isUponSelected != -2) {
|
||||||
if (canForward && !item->isDeleted()) {
|
if (canForward) {
|
||||||
_menu->addAction(tr::lng_context_forward_msg(tr::now), [=] {
|
_menu->addAction(tr::lng_context_forward_msg(tr::now), [=] {
|
||||||
forwardAsGroup(itemId);
|
forwardAsGroup(itemId);
|
||||||
}, &st::menuIconForward);
|
}, &st::menuIconForward);
|
||||||
|
@ -3945,7 +3945,7 @@ auto HistoryInner::getSelectionState() const
|
||||||
if (selected.first->canDelete()) {
|
if (selected.first->canDelete()) {
|
||||||
++result.canDeleteCount;
|
++result.canDeleteCount;
|
||||||
}
|
}
|
||||||
if (selected.first->allowsForward() && !selected.first->isDeleted()) {
|
if (selected.first->allowsForward()) {
|
||||||
++result.canForwardCount;
|
++result.canForwardCount;
|
||||||
}
|
}
|
||||||
} else if (selected.second.from != selected.second.to) {
|
} else if (selected.second.from != selected.second.to) {
|
||||||
|
|
|
@ -1766,6 +1766,9 @@ bool HistoryItem::isScheduled() const {
|
||||||
bool HistoryItem::isSponsored() const {
|
bool HistoryItem::isSponsored() const {
|
||||||
return _flags & MessageFlag::Sponsored;
|
return _flags & MessageFlag::Sponsored;
|
||||||
}
|
}
|
||||||
|
bool HistoryItem::isAyuNoForwards() const {
|
||||||
|
return _flags & MessageFlag::AyuNoForwards;
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryItem::skipNotification() const {
|
bool HistoryItem::skipNotification() const {
|
||||||
if (isSilent() && (_flags & MessageFlag::IsContactSignUp)) {
|
if (isSilent() && (_flags & MessageFlag::IsContactSignUp)) {
|
||||||
|
|
|
@ -207,6 +207,9 @@ public:
|
||||||
[[nodiscard]] bool isFromScheduled() const;
|
[[nodiscard]] bool isFromScheduled() const;
|
||||||
[[nodiscard]] bool isScheduled() const;
|
[[nodiscard]] bool isScheduled() const;
|
||||||
[[nodiscard]] bool isSponsored() const;
|
[[nodiscard]] bool isSponsored() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isAyuNoForwards() const;
|
||||||
|
|
||||||
[[nodiscard]] bool skipNotification() const;
|
[[nodiscard]] bool skipNotification() const;
|
||||||
[[nodiscard]] bool isUserpicSuggestion() const;
|
[[nodiscard]] bool isUserpicSuggestion() const;
|
||||||
[[nodiscard]] BusinessShortcutId shortcutId() const;
|
[[nodiscard]] BusinessShortcutId shortcutId() const;
|
||||||
|
|
|
@ -767,6 +767,7 @@ MessageFlags FlagsFromMTP(
|
||||||
| ((flags & MTP::f_views) ? Flag::HasViews : Flag())
|
| ((flags & MTP::f_views) ? Flag::HasViews : Flag())
|
||||||
// AyuGram: removed
|
// AyuGram: removed
|
||||||
// | ((flags & MTP::f_noforwards) ? Flag::NoForwards : Flag())
|
// | ((flags & MTP::f_noforwards) ? Flag::NoForwards : Flag())
|
||||||
|
| (flags & MTP::f_noforwards ? Flag::AyuNoForwards : Flag())
|
||||||
| ((flags & MTP::f_invert_media) ? Flag::InvertMedia : Flag())
|
| ((flags & MTP::f_invert_media) ? Flag::InvertMedia : Flag())
|
||||||
| ((flags & MTP::f_video_processing_pending)
|
| ((flags & MTP::f_video_processing_pending)
|
||||||
? Flag::EstimatedDate
|
? Flag::EstimatedDate
|
||||||
|
|
|
@ -413,10 +413,6 @@ bool AddForwardMessageAction(
|
||||||
const ContextMenuRequest &request,
|
const ContextMenuRequest &request,
|
||||||
not_null<ListWidget*> list) {
|
not_null<ListWidget*> list) {
|
||||||
const auto item = request.item;
|
const auto item = request.item;
|
||||||
if (item && item->isDeleted()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request.selectedItems.empty()) {
|
if (!request.selectedItems.empty()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (!item || !item->allowsForward()) {
|
} else if (!item || !item->allowsForward()) {
|
||||||
|
@ -641,7 +637,7 @@ bool AddReplyToMessageAction(
|
||||||
? Data::CanSendAnything(topic)
|
? Data::CanSendAnything(topic)
|
||||||
: Data::CanSendAnything(peer);
|
: Data::CanSendAnything(peer);
|
||||||
const auto canReply = canSendReply || item->allowsForward();
|
const auto canReply = canSendReply || item->allowsForward();
|
||||||
if (!canReply || item->isDeleted()) {
|
if (!canReply) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1358,7 +1358,7 @@ bool ListWidget::addToSelection(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
iterator->second.canDelete = item->canDelete();
|
iterator->second.canDelete = item->canDelete();
|
||||||
iterator->second.canForward = item->allowsForward() && !item->isDeleted();
|
iterator->second.canForward = item->allowsForward();
|
||||||
iterator->second.canSendNow = item->allowsSendNow();
|
iterator->second.canSendNow = item->allowsSendNow();
|
||||||
iterator->second.canReschedule = item->allowsReschedule();
|
iterator->second.canReschedule = item->allowsReschedule();
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Add table
Reference in a new issue