mirror of https://github.com/procxx/kepka.git
Allow schedule of files, stickers, GIFs.
This commit is contained in:
parent
77ebdd3576
commit
f690f93f32
|
@ -129,7 +129,7 @@ template <std::size_t... I>
|
||||||
inline void GenericBox::Initer<InitMethod, InitArgs...>::call(
|
inline void GenericBox::Initer<InitMethod, InitArgs...>::call(
|
||||||
not_null<GenericBox*> box,
|
not_null<GenericBox*> box,
|
||||||
std::index_sequence<I...>) {
|
std::index_sequence<I...>) {
|
||||||
std::invoke(method, box, std::get<I>(args)...);
|
std::invoke(method, box, std::get<I>(std::move(args))...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename InitMethod, typename ...InitArgs>
|
template <typename InitMethod, typename ...InitArgs>
|
||||||
|
|
|
@ -50,7 +50,7 @@ inline void CallDelayed(int duration, Guard &&object, Lambda &&lambda) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Guard, typename Lambda>
|
template <typename Guard, typename Lambda>
|
||||||
inline auto LambdaDelayed(int duration, Guard &&object, Lambda &&lambda) {
|
[[nodiscard]] inline auto LambdaDelayed(int duration, Guard &&object, Lambda &&lambda) {
|
||||||
auto guarded = crl::guard(
|
auto guarded = crl::guard(
|
||||||
std::forward<Guard>(object),
|
std::forward<Guard>(object),
|
||||||
std::forward<Lambda>(lambda));
|
std::forward<Lambda>(lambda));
|
||||||
|
|
|
@ -83,6 +83,26 @@ rpl::producer<> ComposeControls::sendRequests() const {
|
||||||
return _send->clicks() | rpl::map([] { return rpl::empty_value(); });
|
return _send->clicks() | rpl::map([] { return rpl::empty_value(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> ComposeControls::attachRequests() const {
|
||||||
|
return _attachToggle->clicks(
|
||||||
|
) | rpl::map([] {
|
||||||
|
return rpl::empty_value();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<DocumentData*>> ComposeControls::fileChosen() const {
|
||||||
|
return _fileChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<PhotoData*>> ComposeControls::photoChosen() const {
|
||||||
|
return _photoChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ComposeControls::inlineResultChosen() const
|
||||||
|
->rpl::producer<ChatHelpers::TabbedSelector::InlineChosen> {
|
||||||
|
return _inlineResultChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
void ComposeControls::showStarted() {
|
void ComposeControls::showStarted() {
|
||||||
if (_inlineResults) {
|
if (_inlineResults) {
|
||||||
_inlineResults->hideFast();
|
_inlineResults->hideFast();
|
||||||
|
@ -202,20 +222,13 @@ void ComposeControls::initTabbedSelector() {
|
||||||
}, wrap->lifetime());
|
}, wrap->lifetime());
|
||||||
|
|
||||||
selector->fileChosen(
|
selector->fileChosen(
|
||||||
) | rpl::start_with_next([=](not_null<DocumentData*> document) {
|
) | rpl::start_to_stream(_fileChosen, wrap->lifetime());
|
||||||
//sendExistingDocument(document);
|
|
||||||
}, wrap->lifetime());
|
|
||||||
|
|
||||||
selector->photoChosen(
|
selector->photoChosen(
|
||||||
) | rpl::start_with_next([=](not_null<PhotoData*> photo) {
|
) | rpl::start_to_stream(_photoChosen, wrap->lifetime());
|
||||||
//sendExistingPhoto(photo);
|
|
||||||
}, wrap->lifetime());
|
|
||||||
|
|
||||||
selector->inlineResultChosen(
|
selector->inlineResultChosen(
|
||||||
) | rpl::start_with_next([=](
|
) | rpl::start_to_stream(_inlineResultChosen, wrap->lifetime());
|
||||||
ChatHelpers::TabbedSelector::InlineChosen data) {
|
|
||||||
//sendInlineResult(data.result, data.bot);
|
|
||||||
}, wrap->lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::initSendButton() {
|
void ComposeControls::initSendButton() {
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/unique_qptr.h"
|
#include "base/unique_qptr.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
class TabbedPanel;
|
class TabbedPanel;
|
||||||
|
@ -65,6 +66,11 @@ public:
|
||||||
void focus();
|
void focus();
|
||||||
[[nodiscard]] rpl::producer<> cancelRequests() const;
|
[[nodiscard]] rpl::producer<> cancelRequests() const;
|
||||||
[[nodiscard]] rpl::producer<> sendRequests() const;
|
[[nodiscard]] rpl::producer<> sendRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<> attachRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<not_null<DocumentData*>> fileChosen() const;
|
||||||
|
[[nodiscard]] rpl::producer<not_null<PhotoData*>> photoChosen() const;
|
||||||
|
[[nodiscard]] auto inlineResultChosen() const
|
||||||
|
-> rpl::producer<ChatHelpers::TabbedSelector::InlineChosen>;
|
||||||
|
|
||||||
void pushTabbedSelectorToThirdSection(const Window::SectionShow ¶ms);
|
void pushTabbedSelectorToThirdSection(const Window::SectionShow ¶ms);
|
||||||
bool returnTabbedSelector();
|
bool returnTabbedSelector();
|
||||||
|
@ -108,6 +114,9 @@ private:
|
||||||
std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel;
|
std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel;
|
||||||
|
|
||||||
rpl::event_stream<> _cancelRequests;
|
rpl::event_stream<> _cancelRequests;
|
||||||
|
rpl::event_stream<not_null<DocumentData*>> _fileChosen;
|
||||||
|
rpl::event_stream<not_null<PhotoData*>> _photoChosen;
|
||||||
|
rpl::event_stream<ChatHelpers::TabbedSelector::InlineChosen> _inlineResultChosen;
|
||||||
|
|
||||||
bool _recording = false;
|
bool _recording = false;
|
||||||
bool _inField = false;
|
bool _inField = false;
|
||||||
|
|
|
@ -512,7 +512,7 @@ TimeId DefaultScheduleTime() {
|
||||||
|
|
||||||
void ScheduleBox(
|
void ScheduleBox(
|
||||||
not_null<GenericBox*> box,
|
not_null<GenericBox*> box,
|
||||||
Fn<void(Api::SendOptions)> done,
|
FnMut<void(Api::SendOptions)> done,
|
||||||
TimeId time) {
|
TimeId time) {
|
||||||
box->setTitle(tr::lng_schedule_title());
|
box->setTitle(tr::lng_schedule_title());
|
||||||
box->setWidth(st::boxWideWidth);
|
box->setWidth(st::boxWideWidth);
|
||||||
|
@ -582,6 +582,8 @@ void ScheduleBox(
|
||||||
}), (*calendar)->lifetime());
|
}), (*calendar)->lifetime());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const auto shared = std::make_shared<FnMut<void(Api::SendOptions)>>(
|
||||||
|
std::move(done));
|
||||||
const auto save = [=] {
|
const auto save = [=] {
|
||||||
auto result = Api::SendOptions();
|
auto result = Api::SendOptions();
|
||||||
|
|
||||||
|
@ -602,9 +604,9 @@ void ScheduleBox(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto copy = done;
|
auto copy = shared;
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
copy(result);
|
(*copy)(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
box->setFocusCallback([=] { timeInput->setFocusFast(); });
|
box->setFocusCallback([=] { timeInput->setFocusFast(); });
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace HistoryView {
|
||||||
[[nodiscard]] TimeId DefaultScheduleTime();
|
[[nodiscard]] TimeId DefaultScheduleTime();
|
||||||
void ScheduleBox(
|
void ScheduleBox(
|
||||||
not_null<GenericBox*> box,
|
not_null<GenericBox*> box,
|
||||||
Fn<void(Api::SendOptions)> done,
|
FnMut<void(Api::SendOptions)> done,
|
||||||
TimeId time);
|
TimeId time);
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -15,22 +15,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "api/api_common.h"
|
#include "api/api_common.h"
|
||||||
|
#include "api/api_sending.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
|
#include "boxes/send_files_box.h"
|
||||||
|
#include "boxes/generic_box.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "window/window_peer_menu.h"
|
#include "window/window_peer_menu.h"
|
||||||
#include "core/event_filter.h"
|
#include "core/event_filter.h"
|
||||||
|
#include "core/file_utilities.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_scheduled_messages.h"
|
#include "data/data_scheduled_messages.h"
|
||||||
|
#include "storage/storage_media_prepare.h"
|
||||||
|
#include "storage/localstorage.h"
|
||||||
|
#include "inline_bots/inline_bot_result.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void ShowErrorToast(const QString &text) {
|
||||||
|
auto config = Ui::Toast::Config();
|
||||||
|
config.multiline = true;
|
||||||
|
config.minWidth = st::msgMinWidth;
|
||||||
|
config.text = text;
|
||||||
|
Ui::Toast::Show(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
object_ptr<Window::SectionWidget> ScheduledMemento::createWidget(
|
object_ptr<Window::SectionWidget> ScheduledMemento::createWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
@ -111,6 +131,256 @@ void ScheduledWidget::setupComposeControls() {
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
send();
|
send();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_composeControls->attachRequests(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return !_choosingAttach;
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
_choosingAttach = true;
|
||||||
|
App::CallDelayed(
|
||||||
|
st::historyAttach.ripple.hideDuration,
|
||||||
|
this,
|
||||||
|
[=] { _choosingAttach = false; chooseAttach(); });
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_composeControls->fileChosen(
|
||||||
|
) | rpl::start_with_next([=](not_null<DocumentData*> document) {
|
||||||
|
sendExistingDocument(document);
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_composeControls->photoChosen(
|
||||||
|
) | rpl::start_with_next([=](not_null<PhotoData*> photo) {
|
||||||
|
sendExistingPhoto(photo);
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_composeControls->inlineResultChosen(
|
||||||
|
) | rpl::start_with_next([=](
|
||||||
|
ChatHelpers::TabbedSelector::InlineChosen chosen) {
|
||||||
|
sendInlineResult(chosen.result, chosen.bot);
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::chooseAttach() {
|
||||||
|
if (const auto error = Data::RestrictionError(
|
||||||
|
_history->peer,
|
||||||
|
ChatRestriction::f_send_media)) {
|
||||||
|
Ui::Toast::Show(*error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto filter = FileDialog::AllFilesFilter()
|
||||||
|
+ qsl(";;Image files (*")
|
||||||
|
+ cImgExtensions().join(qsl(" *"))
|
||||||
|
+ qsl(")");
|
||||||
|
|
||||||
|
FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=](
|
||||||
|
FileDialog::OpenResult &&result) {
|
||||||
|
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.remoteContent.isEmpty()) {
|
||||||
|
auto animated = false;
|
||||||
|
auto image = App::readImage(
|
||||||
|
result.remoteContent,
|
||||||
|
nullptr,
|
||||||
|
false,
|
||||||
|
&animated);
|
||||||
|
if (!image.isNull() && !animated) {
|
||||||
|
confirmSendingFiles(
|
||||||
|
std::move(image),
|
||||||
|
std::move(result.remoteContent),
|
||||||
|
CompressConfirm::Auto);
|
||||||
|
} else {
|
||||||
|
uploadFile(result.remoteContent, SendMediaType::File);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto list = Storage::PrepareMediaList(
|
||||||
|
result.paths,
|
||||||
|
st::sendMediaPreviewSize);
|
||||||
|
if (list.allFilesForCompress || list.albumIsPossible) {
|
||||||
|
confirmSendingFiles(std::move(list), CompressConfirm::Auto);
|
||||||
|
} else if (!showSendingFilesError(list)) {
|
||||||
|
confirmSendingFiles(std::move(list), CompressConfirm::No);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::confirmSendingFiles(
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel) {
|
||||||
|
if (showSendingFilesError(list)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto noCompressOption = (list.files.size() > 1)
|
||||||
|
&& !list.allFilesForCompress
|
||||||
|
&& !list.albumIsPossible;
|
||||||
|
const auto boxCompressConfirm = noCompressOption
|
||||||
|
? CompressConfirm::None
|
||||||
|
: compressed;
|
||||||
|
|
||||||
|
//const auto cursor = _field->textCursor();
|
||||||
|
//const auto position = cursor.position();
|
||||||
|
//const auto anchor = cursor.anchor();
|
||||||
|
const auto text = _composeControls->getTextWithAppliedMarkdown();//_field->getTextWithTags();
|
||||||
|
using SendLimit = SendFilesBox::SendLimit;
|
||||||
|
auto box = Box<SendFilesBox>(
|
||||||
|
controller(),
|
||||||
|
std::move(list),
|
||||||
|
text,
|
||||||
|
boxCompressConfirm,
|
||||||
|
_history->peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many);
|
||||||
|
//_field->setTextWithTags({});
|
||||||
|
|
||||||
|
box->setConfirmedCallback(crl::guard(this, [=](
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
SendFilesWay way,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
Api::SendOptions options,
|
||||||
|
bool ctrlShiftEnter) {
|
||||||
|
if (showSendingFilesError(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto type = (way == SendFilesWay::Files)
|
||||||
|
? SendMediaType::File
|
||||||
|
: SendMediaType::Photo;
|
||||||
|
const auto album = (way == SendFilesWay::Album)
|
||||||
|
? std::make_shared<SendingAlbum>()
|
||||||
|
: nullptr;
|
||||||
|
uploadFilesAfterConfirmation(
|
||||||
|
std::move(list),
|
||||||
|
type,
|
||||||
|
std::move(caption),
|
||||||
|
MsgId(0),//replyToId(),
|
||||||
|
options,
|
||||||
|
album);
|
||||||
|
}));
|
||||||
|
//box->setCancelledCallback(crl::guard(this, [=] {
|
||||||
|
// _field->setTextWithTags(text);
|
||||||
|
// auto cursor = _field->textCursor();
|
||||||
|
// cursor.setPosition(anchor);
|
||||||
|
// if (position != anchor) {
|
||||||
|
// cursor.setPosition(position, QTextCursor::KeepAnchor);
|
||||||
|
// }
|
||||||
|
// _field->setTextCursor(cursor);
|
||||||
|
// if (!insertTextOnCancel.isEmpty()) {
|
||||||
|
// _field->textCursor().insertText(insertTextOnCancel);
|
||||||
|
// }
|
||||||
|
//}));
|
||||||
|
|
||||||
|
//ActivateWindow(controller());
|
||||||
|
const auto shown = Ui::show(std::move(box));
|
||||||
|
shown->setCloseByOutsideClick(false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::confirmSendingFiles(
|
||||||
|
QImage &&image,
|
||||||
|
QByteArray &&content,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel) {
|
||||||
|
if (image.isNull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto list = Storage::PrepareMediaFromImage(
|
||||||
|
std::move(image),
|
||||||
|
std::move(content),
|
||||||
|
st::sendMediaPreviewSize);
|
||||||
|
return confirmSendingFiles(
|
||||||
|
std::move(list),
|
||||||
|
compressed,
|
||||||
|
insertTextOnCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::uploadFilesAfterConfirmation(
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
MsgId replyTo,
|
||||||
|
Api::SendOptions options,
|
||||||
|
std::shared_ptr<SendingAlbum> album) {
|
||||||
|
const auto isAlbum = (album != nullptr);
|
||||||
|
const auto compressImages = (type == SendMediaType::Photo);
|
||||||
|
if (_history->peer->slowmodeApplied()
|
||||||
|
&& ((list.files.size() > 1 && !album)
|
||||||
|
|| (!list.files.empty()
|
||||||
|
&& !caption.text.isEmpty()
|
||||||
|
&& !list.canAddCaption(isAlbum, compressImages)))) {
|
||||||
|
ShowErrorToast(tr::lng_slowmode_no_many(tr::now));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto callback = crl::guard(this, [
|
||||||
|
=,
|
||||||
|
list = std::move(list),
|
||||||
|
caption = std::move(caption),
|
||||||
|
// Strange thing, otherwise std::is_copy_constructible is true. O_o
|
||||||
|
msvc_bug_workaround = std::make_unique<int>()
|
||||||
|
](Api::SendOptions options) mutable {
|
||||||
|
auto action = Api::SendAction(_history);
|
||||||
|
action.replyTo = replyTo;
|
||||||
|
action.options = options;
|
||||||
|
session().api().sendFiles(
|
||||||
|
std::move(list),
|
||||||
|
type,
|
||||||
|
std::move(caption),
|
||||||
|
album,
|
||||||
|
action);
|
||||||
|
});
|
||||||
|
Ui::show(
|
||||||
|
Box(ScheduleBox, std::move(callback), DefaultScheduleTime()),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::uploadFile(
|
||||||
|
const QByteArray &fileContent,
|
||||||
|
SendMediaType type) {
|
||||||
|
const auto callback = crl::guard(this, [=](Api::SendOptions options) {
|
||||||
|
auto action = Api::SendAction(_history);
|
||||||
|
//action.replyTo = replyToId();
|
||||||
|
action.options = options;
|
||||||
|
session().api().sendFile(fileContent, type, action);
|
||||||
|
});
|
||||||
|
Ui::show(
|
||||||
|
Box(ScheduleBox, callback, DefaultScheduleTime()),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::showSendingFilesError(
|
||||||
|
const Storage::PreparedList &list) const {
|
||||||
|
const auto text = [&] {
|
||||||
|
const auto error = Data::RestrictionError(
|
||||||
|
_history->peer,
|
||||||
|
ChatRestriction::f_send_media);
|
||||||
|
if (error) {
|
||||||
|
return *error;
|
||||||
|
}
|
||||||
|
using Error = Storage::PreparedList::Error;
|
||||||
|
switch (list.error) {
|
||||||
|
case Error::None: return QString();
|
||||||
|
case Error::EmptyFile:
|
||||||
|
case Error::Directory:
|
||||||
|
case Error::NonLocalUrl: return tr::lng_send_image_empty(
|
||||||
|
tr::now,
|
||||||
|
lt_name,
|
||||||
|
list.errorData);
|
||||||
|
case Error::TooLargeFile: return tr::lng_send_image_too_large(
|
||||||
|
tr::now,
|
||||||
|
lt_name,
|
||||||
|
list.errorData);
|
||||||
|
}
|
||||||
|
return tr::lng_forward_send_files_cant(tr::now);
|
||||||
|
}();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowErrorToast(text);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::send() {
|
void ScheduledWidget::send() {
|
||||||
|
@ -160,6 +430,123 @@ void ScheduledWidget::send(Api::SendOptions options) {
|
||||||
_composeControls->focus();
|
_composeControls->focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::sendExistingDocument(
|
||||||
|
not_null<DocumentData*> document) {
|
||||||
|
const auto callback = crl::guard(this, [=](Api::SendOptions options) {
|
||||||
|
sendExistingDocument(document, options);
|
||||||
|
});
|
||||||
|
Ui::show(
|
||||||
|
Box(ScheduleBox, callback, DefaultScheduleTime()),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::sendExistingDocument(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Api::SendOptions options) {
|
||||||
|
const auto error = Data::RestrictionError(
|
||||||
|
_history->peer,
|
||||||
|
ChatRestriction::f_send_stickers);
|
||||||
|
if (error) {
|
||||||
|
Ui::show(Box<InformBox>(*error), LayerOption::KeepOther);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message = Api::MessageToSend(_history);
|
||||||
|
//message.action.replyTo = replyToId();
|
||||||
|
message.action.options = options;
|
||||||
|
Api::SendExistingDocument(std::move(message), document);
|
||||||
|
|
||||||
|
//if (_fieldAutocomplete->stickersShown()) {
|
||||||
|
// clearFieldText();
|
||||||
|
// //_saveDraftText = true;
|
||||||
|
// //_saveDraftStart = crl::now();
|
||||||
|
// //onDraftSave();
|
||||||
|
// onCloudDraftSave(); // won't be needed if SendInlineBotResult will clear the cloud draft
|
||||||
|
//}
|
||||||
|
|
||||||
|
_composeControls->hidePanelsAnimated();
|
||||||
|
_composeControls->focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::sendExistingPhoto(not_null<PhotoData*> photo) {
|
||||||
|
const auto callback = crl::guard(this, [=](Api::SendOptions options) {
|
||||||
|
sendExistingPhoto(photo, options);
|
||||||
|
});
|
||||||
|
Ui::show(
|
||||||
|
Box(ScheduleBox, callback, DefaultScheduleTime()),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::sendExistingPhoto(
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
Api::SendOptions options) {
|
||||||
|
const auto error = Data::RestrictionError(
|
||||||
|
_history->peer,
|
||||||
|
ChatRestriction::f_send_media);
|
||||||
|
if (error) {
|
||||||
|
Ui::show(Box<InformBox>(*error), LayerOption::KeepOther);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message = Api::MessageToSend(_history);
|
||||||
|
//message.action.replyTo = replyToId();
|
||||||
|
message.action.options = options;
|
||||||
|
Api::SendExistingPhoto(std::move(message), photo);
|
||||||
|
|
||||||
|
_composeControls->hidePanelsAnimated();
|
||||||
|
_composeControls->focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::sendInlineResult(
|
||||||
|
not_null<InlineBots::Result*> result,
|
||||||
|
not_null<UserData*> bot) {
|
||||||
|
const auto errorText = result->getErrorOnSend(_history);
|
||||||
|
if (!errorText.isEmpty()) {
|
||||||
|
Ui::show(Box<InformBox>(errorText));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto callback = crl::guard(this, [=](Api::SendOptions options) {
|
||||||
|
sendInlineResult(result, bot, options);
|
||||||
|
});
|
||||||
|
Ui::show(
|
||||||
|
Box(ScheduleBox, callback, DefaultScheduleTime()),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::sendInlineResult(
|
||||||
|
not_null<InlineBots::Result*> result,
|
||||||
|
not_null<UserData*> bot,
|
||||||
|
Api::SendOptions options) {
|
||||||
|
auto action = Api::SendAction(_history);
|
||||||
|
action.clearDraft = true;
|
||||||
|
//action.replyTo = replyToId();
|
||||||
|
action.options = options;
|
||||||
|
action.generateLocal = true;
|
||||||
|
session().api().sendInlineResult(bot, result, action);
|
||||||
|
|
||||||
|
_composeControls->clear();
|
||||||
|
//_saveDraftText = true;
|
||||||
|
//_saveDraftStart = crl::now();
|
||||||
|
//onDraftSave();
|
||||||
|
|
||||||
|
auto &bots = cRefRecentInlineBots();
|
||||||
|
const auto index = bots.indexOf(bot);
|
||||||
|
if (index) {
|
||||||
|
if (index > 0) {
|
||||||
|
bots.removeAt(index);
|
||||||
|
} else if (bots.size() >= RecentInlineBotsLimit) {
|
||||||
|
bots.resize(RecentInlineBotsLimit - 1);
|
||||||
|
}
|
||||||
|
bots.push_front(bot);
|
||||||
|
Local::writeRecentHashtagsAndBots();
|
||||||
|
}
|
||||||
|
|
||||||
|
_composeControls->hidePanelsAnimated();
|
||||||
|
_composeControls->focus();
|
||||||
|
}
|
||||||
|
|
||||||
void ScheduledWidget::setupScrollDownButton() {
|
void ScheduledWidget::setupScrollDownButton() {
|
||||||
_scrollDown->setClickedCallback([=] {
|
_scrollDown->setClickedCallback([=] {
|
||||||
scrollDownClicked();
|
scrollDownClicked();
|
||||||
|
@ -470,14 +857,14 @@ void ScheduledWidget::highlightSingleNewMessage(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto firstDifferent = 0;
|
auto firstDifferent = 0;
|
||||||
for (; firstDifferent != _lastSlice.ids.size(); ++firstDifferent) {
|
while (firstDifferent != _lastSlice.ids.size()) {
|
||||||
if (slice.ids[firstDifferent] != _lastSlice.ids[firstDifferent]) {
|
if (slice.ids[firstDifferent] != _lastSlice.ids[firstDifferent]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++firstDifferent;
|
++firstDifferent;
|
||||||
}
|
}
|
||||||
auto lastDifferent = slice.ids.size() - 1;
|
auto lastDifferent = slice.ids.size() - 1;
|
||||||
for (; lastDifferent != firstDifferent;) {
|
while (lastDifferent != firstDifferent) {
|
||||||
if (slice.ids[lastDifferent] != _lastSlice.ids[lastDifferent - 1]) {
|
if (slice.ids[lastDifferent] != _lastSlice.ids[lastDifferent - 1]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
|
enum class CompressConfirm;
|
||||||
|
enum class SendMediaType;
|
||||||
|
struct SendingAlbum;
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
struct SendOptions;
|
struct SendOptions;
|
||||||
|
@ -22,6 +25,10 @@ namespace Notify {
|
||||||
struct PeerUpdate;
|
struct PeerUpdate;
|
||||||
} // namespace Notify
|
} // namespace Notify
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
struct PreparedList;
|
||||||
|
} // namespace Storage
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ScrollArea;
|
class ScrollArea;
|
||||||
class PlainShadow;
|
class PlainShadow;
|
||||||
|
@ -33,6 +40,10 @@ namespace Profile {
|
||||||
class BackButton;
|
class BackButton;
|
||||||
} // namespace Profile
|
} // namespace Profile
|
||||||
|
|
||||||
|
namespace InlineBots {
|
||||||
|
class Result;
|
||||||
|
} // namespace InlineBots
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class Element;
|
class Element;
|
||||||
|
@ -133,6 +144,42 @@ private:
|
||||||
void send();
|
void send();
|
||||||
void send(Api::SendOptions options);
|
void send(Api::SendOptions options);
|
||||||
void highlightSingleNewMessage(const Data::MessagesSlice &slice);
|
void highlightSingleNewMessage(const Data::MessagesSlice &slice);
|
||||||
|
void chooseAttach();
|
||||||
|
|
||||||
|
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
||||||
|
bool confirmSendingFiles(
|
||||||
|
QImage &&image,
|
||||||
|
QByteArray &&content,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel = QString());
|
||||||
|
bool confirmSendingFiles(
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel = QString());
|
||||||
|
bool showSendingFilesError(const Storage::PreparedList &list) const;
|
||||||
|
void uploadFilesAfterConfirmation(
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
MsgId replyTo,
|
||||||
|
Api::SendOptions options,
|
||||||
|
std::shared_ptr<SendingAlbum> album);
|
||||||
|
|
||||||
|
void sendExistingDocument(not_null<DocumentData*> document);
|
||||||
|
bool sendExistingDocument(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Api::SendOptions options);
|
||||||
|
void sendExistingPhoto(not_null<PhotoData*> photo);
|
||||||
|
bool sendExistingPhoto(
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
Api::SendOptions options);
|
||||||
|
void sendInlineResult(
|
||||||
|
not_null<InlineBots::Result*> result,
|
||||||
|
not_null<UserData*> bot);
|
||||||
|
void sendInlineResult(
|
||||||
|
not_null<InlineBots::Result*> result,
|
||||||
|
not_null<UserData*> bot,
|
||||||
|
Api::SendOptions options);
|
||||||
|
|
||||||
const not_null<History*> _history;
|
const not_null<History*> _history;
|
||||||
object_ptr<Ui::ScrollArea> _scroll;
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
|
@ -151,6 +198,7 @@ private:
|
||||||
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
||||||
|
|
||||||
Data::MessagesSlice _lastSlice;
|
Data::MessagesSlice _lastSlice;
|
||||||
|
bool _choosingAttach = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue