mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-09-06 12:03:41 +02:00
feat: aesthetic bottom bar, a bunch of fixes
This commit is contained in:
parent
f16fb42283
commit
0d13f4710d
15 changed files with 623 additions and 384 deletions
|
@ -3279,6 +3279,21 @@ void ApiWrap::forwardMessages(
|
||||||
FnMut<void()> &&successCallback) {
|
FnMut<void()> &&successCallback) {
|
||||||
Expects(!draft.items.empty());
|
Expects(!draft.items.empty());
|
||||||
|
|
||||||
|
const auto fullAyuForward = AyuForward::isFullAyuForwardNeeded(draft.items.front());
|
||||||
|
if (fullAyuForward) {
|
||||||
|
crl::async([=] {
|
||||||
|
AyuForward::forwardMessages(_session, action, false, draft);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto ayuIntelligentForwardNeeded = AyuForward::isAyuForwardNeeded(draft.items);
|
||||||
|
if (ayuIntelligentForwardNeeded) {
|
||||||
|
crl::async([=] {
|
||||||
|
AyuForward::intelligentForward(_session, action, draft);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto &histories = _session->data().histories();
|
auto &histories = _session->data().histories();
|
||||||
|
|
||||||
struct SharedCallback {
|
struct SharedCallback {
|
||||||
|
@ -3338,20 +3353,6 @@ 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>();
|
||||||
|
@ -3780,11 +3781,12 @@ 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) || AyuForward::isForwarding(peer->id)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
const bool canTexts = topic
|
||||||
|
? Data::CanSendTexts(topic)
|
||||||
|
: Data::CanSendTexts(peer);
|
||||||
|
|
||||||
|
if (!(canTexts || AyuForward::isForwarding((peer->id)))
|
||||||
|| Api::SendDice(message)) {
|
|| Api::SendDice(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
|
|
||||||
#include "ayu_forward.h"
|
#include "ayu_forward.h"
|
||||||
|
|
||||||
#include <lang_auto.h>
|
#include <lang_auto.h>
|
||||||
#include <base/random.h>
|
#include <base/random.h>
|
||||||
#include <data/data_peer.h>
|
#include <data/data_peer.h>
|
||||||
#include <history/history_item.h>
|
#include <history/history_item.h>
|
||||||
#include <styles/style_boxes.h>
|
#include <styles/style_boxes.h>
|
||||||
|
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "ayu_sync.h"
|
#include "ayu_sync.h"
|
||||||
|
#include "ayu/utils/telegram_helpers.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
|
@ -26,76 +24,97 @@
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
|
||||||
namespace AyuForward {
|
namespace AyuForward {
|
||||||
std::unordered_map<PeerId, ForwardState> forwardStates;
|
|
||||||
|
|
||||||
bool isForwarding(const PeerId &id) {
|
std::unordered_map<PeerId, std::shared_ptr<ForwardState>> forwardStates;
|
||||||
const auto state = forwardStates.find(id);
|
|
||||||
return id.value
|
bool isForwarding(const PeerId &id) {
|
||||||
&& state != forwardStates.end()
|
const auto fwState = forwardStates.find(id);
|
||||||
&& state->second.state != ForwardState::State::Finished
|
if (id.value && fwState != forwardStates.end()) {
|
||||||
&& state->second.currentChunk < state->second.totalChunks
|
const auto state = *fwState->second;
|
||||||
&& !state->second.stopRequested
|
|
||||||
&& state->second.totalChunks
|
return state.state != ForwardState::State::Finished
|
||||||
&& state->second.totalMessages;
|
&& state.currentChunk < state.totalChunks
|
||||||
|
&& !state.stopRequested
|
||||||
|
&& state.totalChunks
|
||||||
|
&& state.totalMessages;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QString stateName(const PeerId &id) {
|
void cancelForward(const PeerId &id, const Main::Session &session) {
|
||||||
|
const auto fwState = forwardStates.find(id);
|
||||||
|
if (fwState != forwardStates.end()) {
|
||||||
|
fwState->second->stopRequested = true;
|
||||||
|
fwState->second->updateBottomBar(session, &id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<QString, QString> stateName(const PeerId &id) {
|
||||||
const auto fwState = forwardStates.find(id);
|
const auto fwState = forwardStates.find(id);
|
||||||
|
|
||||||
|
|
||||||
if (fwState == forwardStates.end()) {
|
if (fwState == forwardStates.end()) {
|
||||||
return QString();
|
return std::make_pair(QString(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto state = fwState->second;
|
const auto state = fwState->second;
|
||||||
|
|
||||||
QString messagesString = tr::ayu_AyuForwardStatusChunkCount(tr::now,
|
QString messagesString = tr::ayu_AyuForwardStatusSentCount(tr::now,
|
||||||
lt_count1,
|
lt_count1,
|
||||||
QString::number(state.sentMessages),
|
QString::number(state->sentMessages),
|
||||||
lt_count2,
|
lt_count2,
|
||||||
QString::number(state.totalMessages)
|
QString::number(state->totalMessages)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
QString chunkString = tr::ayu_AyuForwardStatusChunkCount(tr::now,
|
QString chunkString = tr::ayu_AyuForwardStatusChunkCount(tr::now,
|
||||||
lt_count1,
|
lt_count1,
|
||||||
QString::number(state.currentChunk + 1),
|
QString::number(state->currentChunk + 1),
|
||||||
lt_count2,
|
lt_count2,
|
||||||
QString::number(state.totalChunks)
|
QString::number(state->totalChunks)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const auto partString = state.totalChunks <= 1 ? messagesString : (messagesString + " • " + chunkString);
|
const auto partString = state->totalChunks <= 1 ? messagesString : (messagesString + " • " + chunkString);
|
||||||
|
|
||||||
QString status;
|
QString status;
|
||||||
if (state.state == ForwardState::State::Preparing) {
|
|
||||||
|
if (state->state == ForwardState::State::Preparing) {
|
||||||
status = tr::ayu_AyuForwardStatusPreparing(tr::now);
|
status = tr::ayu_AyuForwardStatusPreparing(tr::now);
|
||||||
} else if (state.state == ForwardState::State::Downloading) {
|
} else if (state->state == ForwardState::State::Downloading) {
|
||||||
status = tr::ayu_AyuForwardStatusLoadingMedia(tr::now);
|
status = tr::ayu_AyuForwardStatusLoadingMedia(tr::now);
|
||||||
} else if (state.state == ForwardState::State::Sending) {
|
} else if (state->state == ForwardState::State::Sending) {
|
||||||
status = tr::ayu_AyuForwardStatusForwarding(tr::now);
|
status = tr::ayu_AyuForwardStatusForwarding(tr::now);
|
||||||
} else { // ForwardState::State::Finished
|
} else {
|
||||||
|
// ForwardState::State::Finished
|
||||||
status = tr::ayu_AyuForwardStatusFinished(tr::now);
|
status = tr::ayu_AyuForwardStatusFinished(tr::now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return status.append("\n").append(partString);// + "\n" + partString;
|
return std::make_pair(status, partString);
|
||||||
}
|
}
|
||||||
|
void ForwardState::updateBottomBar(const Main::Session &session, const PeerId *peer, const State &st) {
|
||||||
void ForwardState::updateBottomBar(not_null<Main::Session *> session, const PeerData *peer, const State &st) {
|
|
||||||
state = st;
|
state = st;
|
||||||
forwardStates[peer->id] = *this;
|
|
||||||
session->changes().peerUpdated(session->data().peer(peer->id), Data::PeerUpdate::Flag::Rights);
|
session.changes().peerUpdated(session.data().peer(*peer), Data::PeerUpdate::Flag::Rights);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Ui::PreparedList prepareMedia(not_null<Main::Session*> session, const std::vector<not_null<HistoryItem*>> &items, int &i) {
|
static Ui::PreparedList prepareMedia(not_null<Main::Session*> session,
|
||||||
const auto prepare = [&] (not_null<Data::Media*> media){
|
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));
|
auto prepared = Ui::PreparedFile(AyuSync::filePath(session, media));
|
||||||
Storage::PrepareDetails(prepared, st::sendMediaPreviewSize, 1280);
|
Storage::PrepareDetails(prepared, st::sendMediaPreviewSize, 1280);
|
||||||
return prepared;
|
return prepared;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ui::PreparedList list;
|
|
||||||
const auto startItem = items[i];
|
const auto startItem = items[i];
|
||||||
const auto media = startItem->media();
|
const auto media = startItem->media();
|
||||||
const auto groupId = startItem->groupId();
|
const auto groupId = startItem->groupId();
|
||||||
|
|
||||||
|
Ui::PreparedList list;
|
||||||
list.files.emplace_back(prepare(media));
|
list.files.emplace_back(prepare(media));
|
||||||
|
|
||||||
if (!groupId.value) {
|
if (!groupId.value) {
|
||||||
|
@ -107,32 +126,26 @@ namespace AyuForward {
|
||||||
if (nextItem->groupId() != groupId) {
|
if (nextItem->groupId() != groupId) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto nextMedia = nextItem->media()) {
|
if (const auto nextMedia = nextItem->media()) {
|
||||||
list.files.emplace_back(prepare(nextMedia));
|
list.files.emplace_back(prepare(nextMedia));
|
||||||
i = k;
|
i = k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMedia(
|
void sendMedia(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
Ui::PreparedList &&list,
|
std::shared_ptr<Ui::PreparedBundle> bundle,
|
||||||
not_null<Data::Media*> primaryMedia,
|
not_null<Data::Media*> primaryMedia,
|
||||||
Api::MessageToSend &&message,
|
Api::MessageToSend &&message) {
|
||||||
uint64 newGroupId) {
|
if (const auto document = primaryMedia->document(); document && document->sticker()) {
|
||||||
|
AyuSync::sendStickerSync(session, message, document);
|
||||||
|
|
||||||
if (const auto document = primaryMedia->document()) {
|
|
||||||
if (document->isGifv() || document->sticker()) {
|
|
||||||
AyuSync::sendGifOrStickSync(session, message, document);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const auto mediaType = [&] {
|
auto mediaType = [&]
|
||||||
|
{
|
||||||
if (const auto document = primaryMedia->document()) {
|
if (const auto document = primaryMedia->document()) {
|
||||||
if (document->isVoiceMessage()) {
|
if (document->isVoiceMessage()) {
|
||||||
return SendMediaType::Audio;
|
return SendMediaType::Audio;
|
||||||
|
@ -145,51 +158,92 @@ namespace AyuForward {
|
||||||
return SendMediaType::Photo;
|
return SendMediaType::Photo;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
if (mediaType == SendMediaType::Round || mediaType == SendMediaType::Audio) {
|
||||||
|
const auto path = bundle->groups.front().list.files.front().path;
|
||||||
|
|
||||||
auto group = std::make_shared<SendingAlbum>();
|
QFile file(path);
|
||||||
group->groupId = newGroupId;
|
auto failed = false;
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
LOG(("failed to open file for forward with reason: %1").arg(file.errorString()));
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
auto data = file.readAll();
|
||||||
|
|
||||||
AyuSync::sendDocumentSync(
|
if (!failed && data.size()) {
|
||||||
session,
|
file.close();
|
||||||
std::move(list),
|
AyuSync::sendVoiceSync(session,
|
||||||
mediaType,
|
data,
|
||||||
std::move(message.textWithTags),
|
primaryMedia->document()->duration(),
|
||||||
group,
|
mediaType == SendMediaType::Round,
|
||||||
message.action);
|
message.action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// at least try to send it as squared-video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// workaround
|
||||||
|
auto isTherePhotos = false;
|
||||||
|
for (auto &group : bundle->groups) {
|
||||||
|
for (Ui::PreparedFile &file : group.list.files) {
|
||||||
|
if (file.type == Ui::PreparedFile::Type::Photo) {
|
||||||
|
isTherePhotos = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mediaType == SendMediaType::File && isTherePhotos) {
|
||||||
|
mediaType = SendMediaType::Photo;
|
||||||
|
}
|
||||||
|
|
||||||
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem *>> &items) {
|
for (auto &group : bundle->groups) {
|
||||||
|
AyuSync::sendDocumentSync(
|
||||||
|
session,
|
||||||
|
group,
|
||||||
|
mediaType,
|
||||||
|
std::move(message.textWithTags),
|
||||||
|
message.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem*>> &items) {
|
||||||
for (const auto &item : items) {
|
for (const auto &item : items) {
|
||||||
if (isAyuForwardNeeded(item)) {
|
if (isAyuForwardNeeded(item)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAyuForwardNeeded(not_null<HistoryItem *> item) {
|
bool isAyuForwardNeeded(not_null<HistoryItem*> item) {
|
||||||
if (item->isDeleted() || item->isAyuNoForwards() || item->ttlDestroyAt()) {
|
if (item->isDeleted() || item->isAyuNoForwards() || item->ttlDestroyAt()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFullAyuForwardNeeded(not_null<PeerData *> peer, not_null<History*> history) {
|
bool isFullAyuForwardNeeded(not_null<HistoryItem*> item) {
|
||||||
return peer->isAyuNoForwards() || history->peer->isAyuNoForwards();
|
return item->from()->isAyuNoForwards() || item->history()->peer->isAyuNoForwards();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ForwardChunk {
|
struct ForwardChunk
|
||||||
|
{
|
||||||
bool isAyuForwardNeeded;
|
bool isAyuForwardNeeded;
|
||||||
std::vector<not_null<HistoryItem *>> items;
|
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) {
|
void intelligentForward(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
Data::ResolvedForwardDraft draft) {
|
||||||
|
const auto history = action.history;
|
||||||
history->setForwardDraft(action.replyTo.topicRootId, {});
|
history->setForwardDraft(action.replyTo.topicRootId, {});
|
||||||
|
|
||||||
auto chunks = std::vector<ForwardChunk>();
|
const auto items = draft.items;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
|
||||||
|
auto chunks = std::vector<ForwardChunk>();
|
||||||
|
auto currentArray = std::vector<not_null<HistoryItem*>>();
|
||||||
|
|
||||||
auto currentArray = std::vector<not_null<HistoryItem *>>();
|
|
||||||
auto currentChunk = ForwardChunk({
|
auto currentChunk = ForwardChunk({
|
||||||
.isAyuForwardNeeded = isAyuForwardNeeded(items[0]),
|
.isAyuForwardNeeded = isAyuForwardNeeded(items[0]),
|
||||||
.items = currentArray
|
.items = currentArray
|
||||||
|
@ -200,7 +254,8 @@ namespace AyuForward {
|
||||||
currentChunk.items = currentArray;
|
currentChunk.items = currentArray;
|
||||||
chunks.push_back(currentChunk);
|
chunks.push_back(currentChunk);
|
||||||
|
|
||||||
currentArray = std::vector<not_null<HistoryItem *>>();
|
currentArray = std::vector<not_null<HistoryItem*>>();
|
||||||
|
|
||||||
currentChunk = ForwardChunk({
|
currentChunk = ForwardChunk({
|
||||||
.isAyuForwardNeeded = isAyuForwardNeeded(item),
|
.isAyuForwardNeeded = isAyuForwardNeeded(item),
|
||||||
.items = currentArray
|
.items = currentArray
|
||||||
|
@ -213,97 +268,132 @@ namespace AyuForward {
|
||||||
chunks.push_back(currentChunk);
|
chunks.push_back(currentChunk);
|
||||||
|
|
||||||
auto state = std::make_shared<ForwardState>(chunks.size());
|
auto state = std::make_shared<ForwardState>(chunks.size());
|
||||||
forwardStates[peer->id] = *state;
|
forwardStates[peer->id] = state;
|
||||||
|
|
||||||
|
|
||||||
for (const auto &chunk : chunks) {
|
for (const auto &chunk : chunks) {
|
||||||
if (chunk.isAyuForwardNeeded) {
|
if (chunk.isAyuForwardNeeded) {
|
||||||
forwardMessages(peer, chunk.items, session, history, action, true);
|
forwardMessages(session, action, true, Data::ResolvedForwardDraft(chunk.items));
|
||||||
} else {
|
} else {
|
||||||
state->totalMessages = chunk.items.size();
|
state->totalMessages = chunk.items.size();
|
||||||
state->sentMessages = 0;
|
state->sentMessages = 0;
|
||||||
state->updateBottomBar(session, peer, ForwardState::State::Sending);
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Sending);
|
||||||
|
|
||||||
AyuSync::forwardMessagesSync(session, chunk.items, action);
|
AyuSync::forwardMessagesSync(session, chunk.items, action, draft.options);
|
||||||
|
|
||||||
state->sentMessages = state->totalMessages;
|
state->sentMessages = state->totalMessages;
|
||||||
state->updateBottomBar(session, peer, ForwardState::State::Finished);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
state->currentChunk++;
|
state->currentChunk++;
|
||||||
}
|
}
|
||||||
state->updateBottomBar(session, peer, ForwardState::State::Finished);
|
|
||||||
|
|
||||||
}
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMessages(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
bool forwardState,
|
||||||
|
Data::ResolvedForwardDraft draft) {
|
||||||
|
const auto items = draft.items;
|
||||||
|
const auto history = action.history;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
|
||||||
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, {});
|
history->setForwardDraft(action.replyTo.topicRootId, {});
|
||||||
|
|
||||||
ForwardState state;
|
std::shared_ptr<ForwardState> state;
|
||||||
|
|
||||||
if (forwardState) {
|
if (forwardState) {
|
||||||
state = forwardStates[peer->id];
|
state = std::make_shared<ForwardState>(*forwardStates[peer->id]);
|
||||||
} else {
|
} else {
|
||||||
state = ForwardState(1);
|
state = std::make_shared<ForwardState>(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
forwardStates[peer->id] = state;
|
forwardStates[peer->id] = state;
|
||||||
|
|
||||||
std::unordered_map<uint64, uint64> groupIds;
|
std::unordered_map<uint64, uint64> groupIds;
|
||||||
|
|
||||||
|
std::vector<not_null<HistoryItem*>> toBeDownloaded;
|
||||||
|
|
||||||
std::vector<not_null<HistoryItem *> > toBeDownloaded;
|
|
||||||
|
|
||||||
for (const auto item: items) {
|
for (const auto item : items) {
|
||||||
if (item->media()) {
|
if (mediaDownloadable(item->media())) {
|
||||||
toBeDownloaded.push_back(item);
|
toBeDownloaded.push_back(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->groupId()) {
|
if (item->groupId()) {
|
||||||
const auto currentId = groupIds.find(item->groupId().value);
|
const auto currentId = groupIds.find(item->groupId().value);
|
||||||
|
|
||||||
if (currentId == groupIds.end()) {
|
if (currentId == groupIds.end()) {
|
||||||
groupIds[item->groupId().value] = base::RandomValue<uint64>();
|
groupIds[item->groupId().value] = base::RandomValue<uint64>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
state->totalMessages = items.size();
|
||||||
if (toBeDownloaded.size()) {
|
if (toBeDownloaded.size()) {
|
||||||
state.updateBottomBar(session, peer, ForwardState::State::Downloading);
|
state->state = ForwardState::State::Downloading;
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Downloading);
|
||||||
AyuSync::loadDocuments(session, toBeDownloaded);
|
AyuSync::loadDocuments(session, toBeDownloaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.totalMessages = items.size();
|
|
||||||
state.sentMessages = 0;
|
state->sentMessages = 0;
|
||||||
state.updateBottomBar(session, peer, ForwardState::State::Sending);
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Sending);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
for (int i = 0; i < items.size(); i++) {
|
||||||
const auto item = items[i];
|
const auto item = items[i];
|
||||||
|
|
||||||
|
if (state->stopRequested) {
|
||||||
if (state.stopRequested) {
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
state.updateBottomBar(session, peer, ForwardState::State::Finished);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto message = Api::MessageToSend(Api::SendAction(session->data().history(peer->id)));
|
auto extractedText = extractText(item);
|
||||||
|
if (extractedText.empty() && !mediaDownloadable(item->media())) {
|
||||||
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;
|
continue;
|
||||||
}
|
}
|
||||||
sendMedia(session, prepareMedia(session, items, i), media, std::move(message), newGroupId);
|
|
||||||
} else {
|
auto message = Api::MessageToSend(Api::SendAction(session->data().history(peer->id)));
|
||||||
AyuSync::sendMessageSync(session, message);
|
message.action.replyTo = action.replyTo;
|
||||||
}
|
|
||||||
state.sentMessages += 1;
|
if (draft.options != Data::ForwardOptions::NoNamesAndCaptions) {
|
||||||
}
|
message.textWithTags = extractedText;
|
||||||
state.updateBottomBar(session, peer, ForwardState::State::Finished);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mediaDownloadable(item->media())) {
|
||||||
|
AyuSync::sendMessageSync(session, message);
|
||||||
|
} else if (const auto media = item->media()) {
|
||||||
|
if (media->poll()) {
|
||||||
|
AyuSync::sendMessageSync(session, message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto preparedMedia = prepareMedia(session, items, i);
|
||||||
|
|
||||||
|
Ui::SendFilesWay way;
|
||||||
|
way.setGroupFiles(true);
|
||||||
|
way.setSendImagesAsPhotos(true);
|
||||||
|
|
||||||
|
auto groups = Ui::DivideByGroups(
|
||||||
|
std::move(preparedMedia),
|
||||||
|
way,
|
||||||
|
peer->slowmodeApplied());
|
||||||
|
|
||||||
|
auto bundle = Ui::PrepareFilesBundle(
|
||||||
|
std::move(groups),
|
||||||
|
way,
|
||||||
|
message.textWithTags,
|
||||||
|
false);
|
||||||
|
sendMedia(session, bundle, media, std::move(message));
|
||||||
|
}
|
||||||
|
// if there are grouped messages
|
||||||
|
// "i" is incremented in prepareMedia
|
||||||
|
|
||||||
|
state->sentMessages = i + 1;
|
||||||
|
}
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AyuFeatures::AyuForward
|
} // namespace AyuFeatures::AyuForward
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include <main/main_session.h>
|
#include <main/main_session.h>
|
||||||
|
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
|
|
||||||
|
|
||||||
namespace AyuForward {
|
namespace AyuForward {
|
||||||
bool isForwarding(const PeerId &id);
|
bool isForwarding(const PeerId &id);
|
||||||
QString stateName(const PeerId &id);
|
void cancelForward(const PeerId &id, const Main::Session &session);
|
||||||
|
std::pair<QString, QString> stateName(const PeerId &id);
|
||||||
|
|
||||||
class ForwardState {
|
class ForwardState
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
enum class State {
|
enum class State
|
||||||
|
{
|
||||||
Preparing,
|
Preparing,
|
||||||
Downloading,
|
Downloading,
|
||||||
Sending,
|
Sending,
|
||||||
Finished
|
Finished
|
||||||
};
|
};
|
||||||
|
void updateBottomBar(const Main::Session &session, const PeerId *peer, const State &st);
|
||||||
void updateBottomBar(not_null<Main::Session *> session, const PeerData *peer, const State &st);
|
|
||||||
|
|
||||||
int totalChunks;
|
int totalChunks;
|
||||||
int currentChunk;
|
int currentChunk;
|
||||||
|
@ -32,13 +31,17 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem*>> &items);
|
||||||
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem *>> &items);
|
bool isAyuForwardNeeded(not_null<HistoryItem*> item);
|
||||||
bool isAyuForwardNeeded(not_null<HistoryItem *> item);
|
bool isFullAyuForwardNeeded(not_null<HistoryItem*> item);
|
||||||
bool isFullAyuForwardNeeded(not_null<PeerData *> peer, not_null<History*> history);
|
void intelligentForward(
|
||||||
void intelligentForward(const PeerData* peer,
|
not_null<Main::Session*> session,
|
||||||
const std::vector<not_null<HistoryItem *>> &item, not_null<Main::Session *> session, not_null<History *> history, const Api::SendAction &action);
|
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);
|
Data::ResolvedForwardDraft draft);
|
||||||
|
void forwardMessages(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
bool forwardState,
|
||||||
|
Data::ResolvedForwardDraft draft);
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#include "ayu_sync.h"
|
#include "ayu_sync.h"
|
||||||
|
|
||||||
#include "api/api_sending.h"
|
#include "api/api_sending.h"
|
||||||
|
@ -13,11 +12,12 @@
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "storage/file_download_mtproto.h"
|
#include "storage/file_download_mtproto.h"
|
||||||
|
|
||||||
|
|
||||||
class TimedCountDownLatch
|
class TimedCountDownLatch
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit TimedCountDownLatch(int count) : count_(count) {}
|
explicit TimedCountDownLatch(int count)
|
||||||
|
: count_(count) {
|
||||||
|
}
|
||||||
|
|
||||||
void countDown() {
|
void countDown() {
|
||||||
std::unique_lock lock(mutex_);
|
std::unique_lock lock(mutex_);
|
||||||
|
@ -45,7 +45,7 @@ private:
|
||||||
|
|
||||||
namespace AyuSync {
|
namespace AyuSync {
|
||||||
|
|
||||||
QString pathForSave(not_null<Main::Session *> session) {
|
QString pathForSave(not_null<Main::Session*> session) {
|
||||||
const auto path = Core::App().settings().downloadPath();
|
const auto path = Core::App().settings().downloadPath();
|
||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
return File::DefaultDownloadPath(session);
|
return File::DefaultDownloadPath(session);
|
||||||
|
@ -56,8 +56,7 @@ QString pathForSave(not_null<Main::Session *> session) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString filePath(not_null<Main::Session*> session, const Data::Media *media) {
|
||||||
QString filePath(not_null<Main::Session *> session, const Data::Media *media) {
|
|
||||||
if (const auto document = media->document()) {
|
if (const auto document = media->document()) {
|
||||||
if (!document->filename().isEmpty()) {
|
if (!document->filename().isEmpty()) {
|
||||||
return pathForSave(session) + media->document()->filename();
|
return pathForSave(session) + media->document()->filename();
|
||||||
|
@ -79,25 +78,21 @@ QString filePath(not_null<Main::Session *> session, const Data::Media *media) {
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
qint64 fileSize(not_null<HistoryItem *> item) {
|
|
||||||
|
qint64 fileSize(not_null<HistoryItem*> item) {
|
||||||
if (const auto path = filePath(&item->history()->session(), item->media()); !path.isEmpty()) {
|
if (const auto path = filePath(&item->history()->session(), item->media()); !path.isEmpty()) {
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
return file.size();
|
auto size = file.size();
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadDocuments(not_null<Main::Session *> session, const std::vector<not_null<HistoryItem *>> &items) {
|
void loadDocuments(not_null<Main::Session*> session, const std::vector<not_null<HistoryItem*>> &items) {
|
||||||
for (const auto &item : items) {
|
for (const auto &item : items) {
|
||||||
if (const auto data = item->media()->document()) {
|
if (const auto data = item->media()->document()) {
|
||||||
|
|
||||||
if (data->isGifv()) {
|
|
||||||
// no need to download it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto size = fileSize(item);
|
const auto size = fileSize(item);
|
||||||
|
|
||||||
if (size == data->size) {
|
if (size == data->size) {
|
||||||
|
@ -109,9 +104,7 @@ void loadDocuments(not_null<Main::Session *> session, const std::vector<not_null
|
||||||
file.remove();
|
file.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
loadDocumentSync(session, data, item);
|
loadDocumentSync(session, data, item);
|
||||||
|
|
||||||
} else if (auto photo = item->media()->photo()) {
|
} else if (auto photo = item->media()->photo()) {
|
||||||
if (fileSize(item) == photo->imageByteSize(Data::PhotoSize::Large)) {
|
if (fileSize(item) == photo->imageByteSize(Data::PhotoSize::Large)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -121,31 +114,40 @@ void loadDocuments(not_null<Main::Session *> session, const std::vector<not_null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void loadDocumentSync(not_null<Main::Session *> session, DocumentData *data, not_null<HistoryItem *> item) {
|
|
||||||
|
|
||||||
|
void loadDocumentSync(not_null<Main::Session*> session, DocumentData *data, not_null<HistoryItem*> item) {
|
||||||
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
data->save(Data::FileOriginMessage(item->fullId()), filePath(session, item->media()));
|
data->save(Data::FileOriginMessage(item->fullId()), filePath(session, item->media()));
|
||||||
|
|
||||||
|
rpl::single() | rpl::then(
|
||||||
session->downloaderTaskFinished()
|
session->downloaderTaskFinished()
|
||||||
| rpl::filter([&]{
|
) | rpl::filter([&]
|
||||||
return item->media()->document()->status == FileDownloadFailed || fileSize(item) == data->size;
|
{
|
||||||
}) | rpl::start_with_next([&]() mutable{
|
return data->status == FileDownloadFailed || fileSize(item) == data->size;
|
||||||
|
}) | rpl::start_with_next([&]() mutable
|
||||||
|
{
|
||||||
latch->countDown();
|
latch->countDown();
|
||||||
base::take(lifetime)->destroy();
|
base::take(lifetime)->destroy();
|
||||||
}, *lifetime);
|
},
|
||||||
|
*lifetime);
|
||||||
|
|
||||||
latch->await(std::chrono::minutes(5));
|
latch->await(std::chrono::minutes(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void forwardMessagesSync(not_null<Main::Session *> session,
|
void forwardMessagesSync(not_null<Main::Session*> session,
|
||||||
const std::vector<not_null<HistoryItem *>> &items,
|
const std::vector<not_null<HistoryItem*>> &items,
|
||||||
const ApiWrap::SendAction &action) {
|
const ApiWrap::SendAction &action,
|
||||||
|
Data::ForwardOptions options) {
|
||||||
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
|
||||||
crl::on_main([=, &latch]{
|
crl::on_main([=, &latch]
|
||||||
session->api().forwardMessages(Data::ResolvedForwardDraft(items), action, [&]{
|
{
|
||||||
|
session->api().forwardMessages(Data::ResolvedForwardDraft(items, options),
|
||||||
|
action,
|
||||||
|
[&]
|
||||||
|
{
|
||||||
latch->countDown();
|
latch->countDown();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -154,8 +156,7 @@ void forwardMessagesSync(not_null<Main::Session *> session,
|
||||||
latch->await(std::chrono::minutes(1));
|
latch->await(std::chrono::minutes(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadPhotoSync(not_null<Main::Session*> session, const std::pair<not_null<PhotoData*>, FullMsgId> &photo) {
|
||||||
void loadPhotoSync(not_null<Main::Session *> session, const std::pair<not_null<PhotoData *>, FullMsgId> &photo) {
|
|
||||||
const auto folderPath = pathForSave(session);
|
const auto folderPath = pathForSave(session);
|
||||||
const auto downloadPath = folderPath.isEmpty() ? Core::App().settings().downloadPath() : folderPath;
|
const auto downloadPath = folderPath.isEmpty() ? Core::App().settings().downloadPath() : folderPath;
|
||||||
|
|
||||||
|
@ -177,15 +178,18 @@ void loadPhotoSync(not_null<Main::Session *> session, const std::pair<not_null<P
|
||||||
}
|
}
|
||||||
view->wanted(Data::PhotoSize::Large, photo.second);
|
view->wanted(Data::PhotoSize::Large, photo.second);
|
||||||
|
|
||||||
const auto finalCheck = [=]{
|
const auto finalCheck = [=]
|
||||||
|
{
|
||||||
return !photo.first->loading();
|
return !photo.first->loading();
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto saveToFiles = [=]{
|
const auto saveToFiles = [=]
|
||||||
|
{
|
||||||
QDir directory(path);
|
QDir directory(path);
|
||||||
const auto dir = directory.absolutePath();
|
const auto dir = directory.absolutePath();
|
||||||
const auto nameBase = dir.endsWith('/') ? dir : dir + '/';
|
const auto nameBase = dir.endsWith('/') ? dir : dir + '/';
|
||||||
const auto fullPath = nameBase + QString::number(photo.first->getDC()) + "_" + QString::number(photo.first->id) + ".jpg";
|
const auto fullPath = nameBase + QString::number(photo.first->getDC()) + "_" + QString::number(photo.first->id)
|
||||||
|
+ ".jpg";
|
||||||
view->saveToFile(fullPath);
|
view->saveToFile(fullPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -195,20 +199,24 @@ void loadPhotoSync(not_null<Main::Session *> session, const std::pair<not_null<P
|
||||||
if (finalCheck()) {
|
if (finalCheck()) {
|
||||||
saveToFiles();
|
saveToFiles();
|
||||||
} else {
|
} else {
|
||||||
session->downloaderTaskFinished() | rpl::filter([&]{
|
session->downloaderTaskFinished() | rpl::filter([&]
|
||||||
|
{
|
||||||
return finalCheck();
|
return finalCheck();
|
||||||
}) | rpl::start_with_next([&]() mutable{
|
}) | rpl::start_with_next([&]() mutable
|
||||||
|
{
|
||||||
saveToFiles();
|
saveToFiles();
|
||||||
latch->countDown();
|
latch->countDown();
|
||||||
base::take(lifetime)->destroy();
|
base::take(lifetime)->destroy();
|
||||||
},*lifetime);
|
},
|
||||||
|
*lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
latch->await(std::chrono::minutes(5));
|
latch->await(std::chrono::minutes(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMessageSync(not_null<Main::Session *> session, Api::MessageToSend &message) {
|
void sendMessageSync(not_null<Main::Session*> session, Api::MessageToSend &message) {
|
||||||
crl::on_main([=, &message] {
|
crl::on_main([=, &message]
|
||||||
|
{
|
||||||
// we cannot send events to objects
|
// we cannot send events to objects
|
||||||
// owned by a different thread
|
// owned by a different thread
|
||||||
// because sendMessage updates UI too
|
// because sendMessage updates UI too
|
||||||
|
@ -220,35 +228,40 @@ void sendMessageSync(not_null<Main::Session *> session, Api::MessageToSend &mess
|
||||||
waitForMsgSync(session, message.action);
|
waitForMsgSync(session, message.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void waitForMsgSync(not_null<Main::Session *> session, const Api::SendAction &action) {
|
void waitForMsgSync(not_null<Main::Session*> session, const Api::SendAction &action) {
|
||||||
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
|
||||||
session->data().itemIdChanged()
|
session->data().itemIdChanged()
|
||||||
| rpl::filter([&](const Data::Session::IdChange &update){
|
| rpl::filter([&](const Data::Session::IdChange &update)
|
||||||
|
{
|
||||||
return action.history->peer->id == update.newId.peer;
|
return action.history->peer->id == update.newId.peer;
|
||||||
}) | rpl::start_with_next([&]{
|
}) | rpl::start_with_next([&]
|
||||||
|
{
|
||||||
latch->countDown();
|
latch->countDown();
|
||||||
base::take(lifetime)->destroy();
|
base::take(lifetime)->destroy();
|
||||||
},*lifetime);
|
},
|
||||||
|
*lifetime);
|
||||||
|
|
||||||
latch->await(std::chrono::minutes(2));
|
latch->await(std::chrono::minutes(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendDocumentSync(not_null<Main::Session *> session,
|
void sendDocumentSync(not_null<Main::Session*> session,
|
||||||
Ui::PreparedList &&list,
|
Ui::PreparedGroup &group,
|
||||||
SendMediaType type,
|
SendMediaType type,
|
||||||
TextWithTags &&caption,
|
TextWithTags &&caption,
|
||||||
const std::shared_ptr<SendingAlbum> &album,
|
|
||||||
const Api::SendAction &action) {
|
const Api::SendAction &action) {
|
||||||
|
const auto size = group.list.files.size();
|
||||||
const auto size = list.files.size();
|
|
||||||
auto latch = std::make_shared<TimedCountDownLatch>(size);
|
auto latch = std::make_shared<TimedCountDownLatch>(size);
|
||||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
crl::on_main([=, list = std::move(list), caption = std::move(caption)]() mutable{
|
auto groupId = std::make_shared<SendingAlbum>();
|
||||||
session->api().sendFiles(std::move(list), type, std::move(caption), album, action);
|
groupId->groupId = base::RandomValue<uint64>();
|
||||||
|
|
||||||
|
crl::on_main([=, lst = std::move(group.list), caption = std::move(caption)]() mutable
|
||||||
|
{
|
||||||
|
session->api().sendFiles(std::move(lst), type, std::move(caption), groupId, action);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -261,27 +274,45 @@ void sendDocumentSync(not_null<Main::Session *> session,
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
session->uploader().documentReady(),
|
session->uploader().documentReady(),
|
||||||
session->uploader().photoReady()
|
session->uploader().photoReady()
|
||||||
) | rpl::filter([&](const Storage::UploadedMedia &docOrPhoto){
|
) | rpl::filter([&](const Storage::UploadedMedia &docOrPhoto)
|
||||||
|
{
|
||||||
return docOrPhoto.fullId.peer == action.history->peer->id;
|
return docOrPhoto.fullId.peer == action.history->peer->id;
|
||||||
}) | rpl::start_with_next([&]{
|
}) | rpl::start_with_next([&]
|
||||||
|
{
|
||||||
latch->countDown();
|
latch->countDown();
|
||||||
}, *lifetime);
|
},
|
||||||
|
*lifetime);
|
||||||
|
|
||||||
latch->await(std::chrono::minutes(5 * size));
|
latch->await(std::chrono::minutes(5 * size));
|
||||||
base::take(lifetime)->destroy();
|
base::take(lifetime)->destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendGifOrStickSync(not_null<Main::Session *> session,
|
void sendStickerSync(not_null<Main::Session*> session,
|
||||||
Api::MessageToSend &message,
|
Api::MessageToSend &message,
|
||||||
not_null<DocumentData *> document) {
|
not_null<DocumentData*> document) {
|
||||||
auto &action = message.action;
|
auto &action = message.action;
|
||||||
crl::on_main([&] {
|
crl::on_main([&]
|
||||||
|
{
|
||||||
Api::SendExistingDocument(std::move(message), document, std::nullopt);
|
Api::SendExistingDocument(std::move(message), document, std::nullopt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
waitForMsgSync(session, action);
|
waitForMsgSync(session, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendVoiceSync(not_null<Main::Session*> session,
|
||||||
|
const QByteArray &data,
|
||||||
|
int64_t duration,
|
||||||
|
bool video,
|
||||||
|
const Api::SendAction &action) {
|
||||||
|
crl::on_main([&]
|
||||||
|
{
|
||||||
|
session->api().sendVoiceMessage(data,
|
||||||
|
QVector<signed char>(),
|
||||||
|
duration,
|
||||||
|
video,
|
||||||
|
action);
|
||||||
|
});
|
||||||
|
waitForMsgSync(session, action);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AyuSync
|
} // namespace AyuSync
|
||||||
|
|
|
@ -24,28 +24,33 @@
|
||||||
#include "storage/file_download.h"
|
#include "storage/file_download.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
|
|
||||||
|
|
||||||
namespace AyuSync {
|
namespace AyuSync {
|
||||||
|
|
||||||
QString pathForSave(not_null<Main::Session *> session);
|
QString pathForSave(not_null<Main::Session*> session);
|
||||||
QString filePath(not_null<Main::Session *> session, const Data::Media *media);
|
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);
|
void loadDocuments(not_null<Main::Session*> session, const std::vector<not_null<HistoryItem*>> &items);
|
||||||
bool isMediaDownloadable(Data::Media *media);
|
bool isMediaDownloadable(Data::Media *media);
|
||||||
void sendMessageSync(not_null<Main::Session *> session, Api::MessageToSend &message);
|
void sendMessageSync(not_null<Main::Session*> session, Api::MessageToSend &message);
|
||||||
|
|
||||||
void sendDocumentSync(not_null<Main::Session *> session,
|
void sendDocumentSync(not_null<Main::Session*> session,
|
||||||
Ui::PreparedList &&list,
|
Ui::PreparedGroup &group,
|
||||||
SendMediaType type,
|
SendMediaType type,
|
||||||
TextWithTags &&caption,
|
TextWithTags &&caption,
|
||||||
const std::shared_ptr<SendingAlbum> &album,
|
|
||||||
const Api::SendAction &action);
|
const Api::SendAction &action);
|
||||||
|
|
||||||
void sendGifOrStickSync(not_null<Main::Session *> session,
|
void sendStickerSync(not_null<Main::Session*> session,
|
||||||
Api::MessageToSend &message,
|
Api::MessageToSend &message,
|
||||||
not_null<DocumentData *> document);
|
not_null<DocumentData*> document);
|
||||||
void waitForMsgSync(not_null<Main::Session *> session, const Api::SendAction &action);
|
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 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 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);
|
void forwardMessagesSync(not_null<Main::Session*> session,
|
||||||
|
const std::vector<not_null<HistoryItem*>> &items,
|
||||||
|
const ApiWrap::SendAction &action,
|
||||||
|
Data::ForwardOptions options);
|
||||||
|
void sendVoiceSync(not_null<Main::Session*> session,
|
||||||
|
const QByteArray &data,
|
||||||
|
int64_t duration,
|
||||||
|
bool video,
|
||||||
|
const Api::SendAction &action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -742,3 +742,34 @@ ID getUserIdFromPackId(uint64 id) {
|
||||||
|
|
||||||
return ownerId;
|
return ownerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithTags extractText(not_null<HistoryItem*> item) {
|
||||||
|
TextWithTags result;
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto poll = media->poll()) {
|
||||||
|
text.append("\xF0\x9F\x93\x8A ") // 📊
|
||||||
|
.append(poll->question.text).append("\n");
|
||||||
|
for (const auto answer : poll->answers) {
|
||||||
|
text.append("• ").append(answer.text.text).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.tags = TextUtilities::ConvertEntitiesToTextTags(item->originalText().entities);
|
||||||
|
result.text = text.isEmpty() ? item->originalText().text : text;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mediaDownloadable(Data::Media *media) {
|
||||||
|
if (!media
|
||||||
|
|| media->webpage() || media->poll() || media->game()
|
||||||
|
|| media->invoice() || media->location() || media->paper()
|
||||||
|
|| media->giveawayStart() || media->giveawayResults()
|
||||||
|
|| media->sharedContact() || media->call()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "dialogs/dialogs_main_list.h"
|
#include "dialogs/dialogs_main_list.h"
|
||||||
#include "info/profile/info_profile_badge.h"
|
#include "info/profile/info_profile_badge.h"
|
||||||
#include "main/main_domain.h"
|
#include "main/main_domain.h"
|
||||||
|
#include "data/data_poll.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
using UsernameResolverCallback = Fn<void(const QString &, UserData *)>;
|
using UsernameResolverCallback = Fn<void(const QString &, UserData *)>;
|
||||||
|
|
||||||
|
@ -55,3 +57,6 @@ void searchById(ID userId, Main::Session *session, bool retry, const UsernameRes
|
||||||
void searchById(ID userId, Main::Session *session, const UsernameResolverCallback &callback);
|
void searchById(ID userId, Main::Session *session, const UsernameResolverCallback &callback);
|
||||||
|
|
||||||
ID getUserIdFromPackId(uint64 id);
|
ID getUserIdFromPackId(uint64 id);
|
||||||
|
|
||||||
|
TextWithTags extractText(not_null<HistoryItem*> item);
|
||||||
|
bool mediaDownloadable(Data::Media* media);
|
|
@ -63,6 +63,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
#include "ayu/features/forward/ayu_forward.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace Ui::Text;
|
using namespace Ui::Text;
|
||||||
|
@ -1280,6 +1282,69 @@ std::unique_ptr<Ui::AbstractButton> BoostsToLiftWriteRestriction(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::AbstractButton> AyuForwardWriteRestriction(
|
||||||
|
not_null<QWidget *> parent,
|
||||||
|
const PeerId &peer,
|
||||||
|
const Main::Session &session) {
|
||||||
|
using namespace Ui;
|
||||||
|
|
||||||
|
// status and part
|
||||||
|
const auto pair = AyuForward::stateName(peer);
|
||||||
|
|
||||||
|
auto result = std::make_unique<FlatButton>(
|
||||||
|
parent,
|
||||||
|
QString(),
|
||||||
|
st::historyComposeButton);
|
||||||
|
const auto raw = result.get();
|
||||||
|
|
||||||
|
const auto title = CreateChild<FlatLabel>(
|
||||||
|
raw,
|
||||||
|
pair.first,
|
||||||
|
st::frozenRestrictionTitle);
|
||||||
|
title->setTextColorOverride(st::historyComposeButton.color->c);
|
||||||
|
|
||||||
|
|
||||||
|
title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
title->show();
|
||||||
|
const auto subtitle = CreateChild<FlatLabel>(
|
||||||
|
raw,
|
||||||
|
pair.second,
|
||||||
|
st::frozenRestrictionSubtitle);
|
||||||
|
subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
subtitle->show();
|
||||||
|
|
||||||
|
|
||||||
|
raw->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||||
|
|
||||||
|
const auto toggle = [&](auto &&widget, bool shown) {
|
||||||
|
if (widget->isHidden() == shown) {
|
||||||
|
widget->setVisible(shown);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto small = 2 * st::defaultDialogRow.photoSize;
|
||||||
|
const auto shown = (size.width() > small);
|
||||||
|
|
||||||
|
toggle(title, shown);
|
||||||
|
toggle(subtitle, shown);
|
||||||
|
|
||||||
|
const auto skip = st::defaultDialogRow.padding.left();
|
||||||
|
const auto available = size.width() - skip * 2;
|
||||||
|
title->resizeToWidth(available);
|
||||||
|
subtitle->resizeToWidth(available);
|
||||||
|
const auto height = title->height() + subtitle->height();
|
||||||
|
const auto top = (size.height() - height) / 2;
|
||||||
|
title->moveToLeft(skip, top, size.width());
|
||||||
|
subtitle->moveToLeft(skip, top + title->height(), size.width());
|
||||||
|
|
||||||
|
}, title->lifetime());
|
||||||
|
|
||||||
|
raw->setClickedCallback([&] {
|
||||||
|
AyuForward::cancelForward(peer, session);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Ui::AbstractButton> FrozenWriteRestriction(
|
std::unique_ptr<Ui::AbstractButton> FrozenWriteRestriction(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
|
|
@ -202,7 +202,10 @@ enum class FrozenWriteRestrictionType {
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
FrozenWriteRestrictionType type,
|
FrozenWriteRestrictionType type,
|
||||||
FreezeInfoStyleOverride st = {});
|
FreezeInfoStyleOverride st = {});
|
||||||
|
std::unique_ptr<Ui::AbstractButton> AyuForwardWriteRestriction(
|
||||||
|
not_null<QWidget *> parent,
|
||||||
|
const PeerId &peer,
|
||||||
|
const Main::Session &session);
|
||||||
void SelectTextInFieldWithMargins(
|
void SelectTextInFieldWithMargins(
|
||||||
not_null<Ui::InputField*> field,
|
not_null<Ui::InputField*> field,
|
||||||
const TextSelection &selection);
|
const TextSelection &selection);
|
||||||
|
|
|
@ -75,7 +75,7 @@ enum class ChannelDataFlag : uint64 {
|
||||||
PaidMessagesAvailable = (1ULL << 37),
|
PaidMessagesAvailable = (1ULL << 37),
|
||||||
AutoTranslation = (1ULL << 38),
|
AutoTranslation = (1ULL << 38),
|
||||||
|
|
||||||
AyuNoForwards = (1ULL << 60),
|
AyuNoForwards = (1ULL << 63),
|
||||||
};
|
};
|
||||||
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>;
|
||||||
|
|
|
@ -24,7 +24,7 @@ enum class ChatDataFlag {
|
||||||
CanSetUsername = (1 << 7),
|
CanSetUsername = (1 << 7),
|
||||||
NoForwards = (1 << 8),
|
NoForwards = (1 << 8),
|
||||||
|
|
||||||
AyuNoForwards = (1 << 20),
|
AyuNoForwards = (1 << 31),
|
||||||
};
|
};
|
||||||
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>;
|
||||||
|
|
|
@ -186,7 +186,7 @@ SendError RestrictionError(
|
||||||
ChatRestriction restriction) {
|
ChatRestriction restriction) {
|
||||||
if (AyuForward::isForwarding(peer->id)) {
|
if (AyuForward::isForwarding(peer->id)) {
|
||||||
return SendError({
|
return SendError({
|
||||||
.text = AyuForward::stateName(peer->id)
|
.text = AyuForward::stateName(peer->id).first + "\n" + AyuForward::stateName(peer->id).second,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
using Flag = ChatRestriction;
|
using Flag = ChatRestriction;
|
||||||
|
|
|
@ -353,7 +353,7 @@ enum class MessageFlag : uint64 {
|
||||||
HideDisplayDate = (1ULL << 51),
|
HideDisplayDate = (1ULL << 51),
|
||||||
|
|
||||||
|
|
||||||
AyuNoForwards = (1ULL << 60),
|
AyuNoForwards = (1ULL << 63),
|
||||||
};
|
};
|
||||||
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>;
|
||||||
|
|
|
@ -6758,7 +6758,9 @@ void HistoryWidget::updateSendRestriction() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_sendRestrictionKey = restriction.text;
|
_sendRestrictionKey = restriction.text;
|
||||||
if (!restriction) {
|
if (AyuForward::isForwarding(_peer->id)) {
|
||||||
|
_sendRestriction = AyuForwardWriteRestriction(this, _peer->id, session());
|
||||||
|
} else if (!restriction) {
|
||||||
_sendRestriction = nullptr;
|
_sendRestriction = nullptr;
|
||||||
} else if (restriction.frozen) {
|
} else if (restriction.frozen) {
|
||||||
const auto show = controller()->uiShow();
|
const auto show = controller()->uiShow();
|
||||||
|
|
|
@ -563,7 +563,9 @@ bool MainWidget::setForwardDraft(
|
||||||
.forward = &items,
|
.forward = &items,
|
||||||
.ignoreSlowmodeCountdown = true,
|
.ignoreSlowmodeCountdown = true,
|
||||||
});
|
});
|
||||||
if (error) {
|
// allow opening chat that
|
||||||
|
// already have some forward task
|
||||||
|
if (error && !AyuForward::isForwarding(history->peer->id)) {
|
||||||
Data::ShowSendErrorToast(_controller, history->peer, error);
|
Data::ShowSendErrorToast(_controller, history->peer, error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue