Add album support to SendFilesBox.

This commit is contained in:
John Preston 2017-12-22 21:42:33 +04:00
parent 8e45b09083
commit 58d21ff916
8 changed files with 959 additions and 643 deletions

View File

@ -735,15 +735,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_failed_add_not_mutual" = "Sorry, if a person leaves a group, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
"lng_failed_add_not_mutual_channel" = "Sorry, if a person leaves a channel, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
"lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
"lng_sure_delete_group_history" = "Are you sure, you want to delete all message history in «{group}»?\n\nThis action cannot be undone.";
"lng_sure_delete_and_exit" = "Are you sure, you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
"lng_sure_leave_channel" = "Are you sure, you want to leave\nthis channel?";
"lng_sure_delete_channel" = "Are you sure, you want to delete this channel? All members will be removed and all messages will be lost.";
"lng_sure_leave_group" = "Are you sure, you want to leave\nthis group?";
"lng_sure_delete_group" = "Are you sure, you want to delete this group? All members will be removed and all messages will be lost.";
"lng_sure_delete_saved_messages" = "Are you sure, you want to delete all your saved messages?\n\nThis action cannot be undone.";
"lng_sure_delete_contact" = "Are you sure you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
"lng_sure_delete_group_history" = "Are you sure you want to delete all message history in «{group}»?\n\nThis action cannot be undone.";
"lng_sure_delete_and_exit" = "Are you sure you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
"lng_sure_leave_channel" = "Are you sure you want to leave\nthis channel?";
"lng_sure_delete_channel" = "Are you sure you want to delete this channel? All members will be removed and all messages will be lost.";
"lng_sure_leave_group" = "Are you sure you want to leave\nthis group?";
"lng_sure_delete_group" = "Are you sure you want to delete this group? All members will be removed and all messages will be lost.";
"lng_sure_delete_saved_messages" = "Are you sure you want to delete all your saved messages?\n\nThis action cannot be undone.";
"lng_message_empty" = "Empty Message";
"lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the last version in Settings or install it from {link}";
@ -1092,18 +1092,23 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_forward_selected" = "Forward Selected";
"lng_context_delete_selected" = "Delete Selected";
"lng_context_clear_selection" = "Clear Selection";
"lng_send_images_compress#one" = "Compress image";
"lng_send_images_compress#other" = "Compress images";
"lng_send_image_empty" = "Could not send an empty file: {name}";
"lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}";
"lng_send_images_selected#one" = "{count} image selected";
"lng_send_images_selected#other" = "{count} images selected";
"lng_send_photos#one" = "Send {count} photo";
"lng_send_photos#other" = "Send {count} photos";
"lng_send_photos_videos#one" = "Send {count} photo and video file";
"lng_send_photos_videos#other" = "Send {count} photos and video files";
"lng_send_separate_photos" = "Send separate photos";
"lng_send_separate_photos_videos" = "Send separate photos and video files";
"lng_send_files_selected#one" = "{count} file selected";
"lng_send_files_selected#other" = "{count} files selected";
"lng_send_files#one" = "Send {count} file";
"lng_send_files#other" = "Send {count} files";
"lng_send_album" = "Send an album";
"lng_send_photo" = "Send a photo";
"lng_send_file" = "Send a file";
"lng_forward_choose" = "Choose recipient...";
"lng_forward_cant" = "Sorry, no way to forward here :(";

View File

@ -718,3 +718,9 @@ groupStickersField: InputField(contactsSearchField) {
groupStickersSubTitleHeight: 36px;
sendMediaPreviewSize: 308px;
sendMediaPreviewHeightMax: 1280;
sendMediaPreviewPhotoSkip: 10px;
sendMediaFileThumbSize: 64px;
sendMediaFileThumbSkip: 10px;
sendMediaFileNameTop: 7px;
sendMediaFileStatusTop: 37px;

File diff suppressed because it is too large Load Diff

View File

@ -20,12 +20,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <rpl/variable.h>
#include "boxes/abstract_box.h"
#include "storage/localimageloader.h"
#include "storage/storage_media_prepare.h"
namespace Ui {
class Checkbox;
template <typename Enum>
class Radioenum;
template <typename Enum>
class RadioenumGroup;
class RoundButton;
class InputArea;
struct GroupMediaLayout;
@ -33,6 +37,12 @@ struct GroupMediaLayout;
class SendFilesBox : public BoxContent {
public:
enum class SendWay {
Album,
Photos,
Files,
};
SendFilesBox(
QWidget*,
Storage::PreparedList &&list,
@ -41,7 +51,7 @@ public:
void setConfirmedCallback(
base::lambda<void(
Storage::PreparedList &&list,
bool compressed,
SendWay way,
const QString &caption,
bool ctrlShiftEnter)> callback) {
_confirmedCallback = std::move(callback);
@ -61,102 +71,57 @@ protected:
void resizeEvent(QResizeEvent *e) override;
private:
void prepareSingleFileLayout();
void prepareDocumentLayout();
void prepareGifPreview();
void clipCallback(Media::Clip::Notification notification);
class AlbumPreview;
struct AlbumThumb;
void initSendWay();
void initPreview(rpl::producer<int> desiredPreviewHeight);
void setupControls();
void setupSendWayControls();
void setupCaption();
void setupShadows(
not_null<Ui::ScrollArea*> wrap,
not_null<AlbumPreview*> content);
void prepareSingleFilePreview();
void prepareAlbumPreview();
void send(bool ctrlShiftEnter = false);
void captionResized();
void compressedChange();
void updateTitleText();
void setupTitleText();
void updateBoxSize();
void updateControlsGeometry();
base::lambda<QString()> getSendButtonText() const;
QString _titleText;
int _titleHeight = 0;
Storage::PreparedList _list;
CompressConfirm _compressConfirm = CompressConfirm::None;
bool _animated = false;
QPixmap _preview;
int _previewLeft = 0;
int _previewWidth = 0;
int _previewHeight = 0;
Media::Clip::ReaderPointer _gifPreview;
QPixmap _fileThumb;
Text _nameText;
bool _fileIsAudio = false;
bool _fileIsImage = false;
QString _statusText;
int _statusWidth = 0;
base::lambda<void(
Storage::PreparedList &&list,
bool compressed,
SendWay way,
const QString &caption,
bool ctrlShiftEnter)> _confirmedCallback;
base::lambda<void()> _cancelledCallback;
bool _confirmed = false;
object_ptr<Ui::InputArea> _caption = { nullptr };
object_ptr<Ui::Checkbox> _compressed = { nullptr };
object_ptr<Ui::Radioenum<SendWay>> _sendAlbum = { nullptr };
object_ptr<Ui::Radioenum<SendWay>> _sendPhotos = { nullptr };
object_ptr<Ui::Radioenum<SendWay>> _sendFiles = { nullptr };
std::shared_ptr<Ui::RadioenumGroup<SendWay>> _sendWay;
rpl::variable<int> _footerHeight = 0;
QWidget *_preview = nullptr;
AlbumPreview *_albumPreview = nullptr;
int _albumVideosCount = 0;
int _albumPhotosCount = 0;
QPointer<Ui::RoundButton> _send;
};
class SendAlbumBox : public BoxContent {
public:
SendAlbumBox(QWidget*, Storage::PreparedList &&list);
void setConfirmedCallback(
base::lambda<void(
Storage::PreparedList &&list,
const QString &caption,
bool ctrlShiftEnter)> callback) {
_confirmedCallback = std::move(callback);
}
void setCancelledCallback(base::lambda<void()> callback) {
_cancelledCallback = std::move(callback);
}
~SendAlbumBox();
protected:
void prepare() override;
void setInnerFocus() override;
void keyPressEvent(QKeyEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
struct Thumb;
void prepareThumbs();
Thumb prepareThumb(
const QImage &preview,
const Ui::GroupMediaLayout &layout) const;
void send(bool ctrlShiftEnter = false);
void captionResized();
void updateBoxSize();
void updateControlsGeometry();
Storage::PreparedList _list;
std::vector<Thumb> _thumbs;
int _thumbsHeight = 0;
base::lambda<void(Storage::PreparedList &&list, const QString &caption, bool ctrlShiftEnter)> _confirmedCallback;
base::lambda<void()> _cancelledCallback;
bool _confirmed = false;
object_ptr<Ui::InputArea> _caption = { nullptr };
};

View File

@ -106,6 +106,17 @@ ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() {
};
}
void ActivateWindowDelayed(not_null<Window::Controller*> controller) {
const auto window = controller->window();
const auto weak = make_weak(window.get());
window->activateWindow();
crl::on_main([=] {
if (weak) {
weak->activateWindow();
}
});
}
} // namespace
ReportSpamPanel::ReportSpamPanel(QWidget *parent) : TWidget(parent),
@ -542,11 +553,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
_attachDragDocument->setDroppedCallback([this](const QMimeData *data) {
confirmSendingFiles(data, CompressConfirm::No);
this->controller()->window()->activateWindow();
ActivateWindowDelayed(this->controller());
});
_attachDragPhoto->setDroppedCallback([this](const QMimeData *data) {
confirmSendingFiles(data, CompressConfirm::Yes);
this->controller()->window()->activateWindow();
ActivateWindowDelayed(this->controller());
});
connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField()));
@ -1275,7 +1286,7 @@ void HistoryWidget::onRecordDone(
qint32 samples) {
if (!canWriteMessage() || result.isEmpty()) return;
App::wnd()->activateWindow();
ActivateWindowDelayed(controller());
const auto duration = samples / Media::Player::kDefaultFrequency;
auto options = ApiWrap::SendOptions(_history);
options.replyTo = replyToId();
@ -3940,48 +3951,15 @@ void HistoryWidget::updateFieldPlaceholder() {
updateSendButtonType();
}
template <typename SendCallback>
bool HistoryWidget::showSendFilesBox(
object_ptr<SendFilesBox> box,
const QString &insertTextOnCancel,
SendCallback callback) {
App::wnd()->activateWindow();
const auto confirmedCallback = [=, sendCallback = std::move(callback)](
Storage::PreparedList &&list,
bool compressed,
const QString &caption,
bool ctrlShiftEnter) {
if (!canWriteMessage()) return;
sendCallback(
std::move(list),
compressed,
caption,
replyToId());
};
box->setConfirmedCallback(
base::lambda_guarded(this, std::move(confirmedCallback)));
if (!insertTextOnCancel.isEmpty()) {
box->setCancelledCallback(base::lambda_guarded(this, [=] {
_field->textCursor().insertText(insertTextOnCancel);
}));
}
Ui::show(std::move(box));
return true;
}
bool HistoryWidget::showSendingFilesError(
const Storage::PreparedList &list) const {
App::wnd()->activateWindow();
const auto text = [&] {
if (const auto megagroup = _peer ? _peer->asMegagroup() : nullptr) {
if (megagroup->restricted(ChannelRestriction::f_send_media)) {
return lang(lng_restricted_send_media);
}
} else if (!canWriteMessage()) {
}
if (!canWriteMessage()) {
return lang(lng_forward_send_files_cant);
}
using Error = Storage::PreparedList::Error;
@ -4015,70 +3993,70 @@ bool HistoryWidget::confirmSendingFiles(const QMimeData *data) {
bool HistoryWidget::confirmSendingFiles(
const QList<QUrl> &files,
CompressConfirm compressed) {
CompressConfirm compressed,
const QString &insertTextOnCancel) {
return confirmSendingFiles(
Storage::PrepareMediaList(files, st::sendMediaPreviewSize),
compressed);
compressed,
insertTextOnCancel);
}
bool HistoryWidget::confirmSendingFiles(
const QStringList &files,
CompressConfirm compressed) {
CompressConfirm compressed,
const QString &insertTextOnCancel) {
return confirmSendingFiles(
Storage::PrepareMediaList(files, st::sendMediaPreviewSize),
compressed);
compressed,
insertTextOnCancel);
}
bool HistoryWidget::confirmSendingFiles(
Storage::PreparedList &&list,
CompressConfirm compressed) {
CompressConfirm compressed,
const QString &insertTextOnCancel) {
if (showSendingFilesError(list)) {
return false;
}
if (list.albumIsPossible) {
auto box = Ui::show(Box<SendAlbumBox>(std::move(list)));
const auto confirmedCallback = [=](
Storage::PreparedList &&list,
const QString &caption,
bool ctrlShiftEnter) {
if (!canWriteMessage()) return;
uploadFilesAfterConfirmation(
std::move(list),
SendMediaType::Photo,
caption,
replyToId(),
std::make_shared<SendingAlbum>());
};
box->setConfirmedCallback(
base::lambda_guarded(this, std::move(confirmedCallback)));
return true;
} else {
const auto insertTextOnCancel = QString();
auto sendCallback = [this](
Storage::PreparedList &&list,
bool compressed,
const QString &caption,
MsgId replyTo) {
const auto type = compressed
? SendMediaType::Photo
: SendMediaType::File;
uploadFilesAfterConfirmation(
std::move(list),
type,
caption,
replyTo);
};
const auto noCompressOption = (list.files.size() > 1)
&& !list.allFilesForCompress;
const auto boxCompressConfirm = noCompressOption
? CompressConfirm::None
: compressed;
return showSendFilesBox(
Box<SendFilesBox>(std::move(list), boxCompressConfirm),
insertTextOnCancel,
std::move(sendCallback));
const auto noCompressOption = (list.files.size() > 1)
&& !list.allFilesForCompress
&& !list.albumIsPossible;
const auto boxCompressConfirm = noCompressOption
? CompressConfirm::None
: compressed;
auto box = Box<SendFilesBox>(std::move(list), boxCompressConfirm);
box->setConfirmedCallback(base::lambda_guarded(this, [=](
Storage::PreparedList &&list,
SendFilesBox::SendWay way,
const QString &caption,
bool ctrlShiftEnter) {
if (showSendingFilesError(list)) {
return;
}
const auto type = (way == SendFilesBox::SendWay::Files)
? SendMediaType::File
: SendMediaType::Photo;
const auto album = (way == SendFilesBox::SendWay::Album)
? std::make_shared<SendingAlbum>()
: nullptr;
uploadFilesAfterConfirmation(
std::move(list),
type,
caption,
replyToId(),
album);
}));
if (!insertTextOnCancel.isEmpty()) {
box->setCancelledCallback(base::lambda_guarded(this, [=] {
_field->textCursor().insertText(insertTextOnCancel);
}));
}
ActivateWindowDelayed(controller());
Ui::show(std::move(box));
return true;
}
bool HistoryWidget::confirmSendingFiles(
@ -4086,31 +4064,18 @@ bool HistoryWidget::confirmSendingFiles(
QByteArray &&content,
CompressConfirm compressed,
const QString &insertTextOnCancel) {
if (!canWriteMessage() || image.isNull()) return false;
if (image.isNull()) {
return false;
}
App::wnd()->activateWindow();
auto sendCallback = [this](
Storage::PreparedList &&list,
bool compressed,
const QString &caption,
MsgId replyTo) {
const auto type = compressed
? SendMediaType::Photo
: SendMediaType::File;
uploadFilesAfterConfirmation(
std::move(list),
type,
caption,
replyTo);
};
auto list = Storage::PrepareMediaFromImage(
std::move(image),
std::move(content),
st::sendMediaPreviewSize);
return showSendFilesBox(
Box<SendFilesBox>(std::move(list), compressed),
insertTextOnCancel,
std::move(sendCallback));
return confirmSendingFiles(
std::move(list),
compressed,
insertTextOnCancel);
}
bool HistoryWidget::confirmSendingFiles(
@ -4121,15 +4086,14 @@ bool HistoryWidget::confirmSendingFiles(
return false;
}
auto urls = data->urls();
if (!urls.isEmpty()) {
for_const (auto &url, urls) {
if (url.isLocalFile()) {
confirmSendingFiles(urls, compressed);
return true;
}
const auto urls = data->urls();
for (const auto &url : urls) {
if (url.isLocalFile()) {
confirmSendingFiles(urls, compressed, insertTextOnCancel);
return true;
}
}
if (data->hasImage()) {
auto image = qvariant_cast<QImage>(data->imageData());
if (!image.isNull()) {
@ -4147,7 +4111,7 @@ bool HistoryWidget::confirmSendingFiles(
void HistoryWidget::uploadFiles(
Storage::PreparedList &&list,
SendMediaType type) {
if (!canWriteMessage()) return;
ActivateWindowDelayed(controller());
auto caption = QString();
uploadFilesAfterConfirmation(

View File

@ -499,10 +499,12 @@ private:
bool confirmSendingFiles(
const QList<QUrl> &files,
CompressConfirm compressed);
CompressConfirm compressed,
const QString &insertTextOnCancel = QString());
bool confirmSendingFiles(
const QStringList &files,
CompressConfirm compressed);
CompressConfirm compressed,
const QString &insertTextOnCancel = QString());
bool confirmSendingFiles(
QImage &&image,
QByteArray &&content,
@ -514,15 +516,10 @@ private:
const QString &insertTextOnCancel = QString());
bool confirmSendingFiles(
Storage::PreparedList &&list,
CompressConfirm compressed);
CompressConfirm compressed,
const QString &insertTextOnCancel = QString());
bool showSendingFilesError(const Storage::PreparedList &list) const;
template <typename SendCallback>
bool showSendFilesBox(
object_ptr<SendFilesBox> box,
const QString &insertTextOnCancel,
SendCallback callback);
void uploadFiles(Storage::PreparedList &&list, SendMediaType type);
void uploadFile(const QByteArray &fileContent, SendMediaType type);

View File

@ -83,7 +83,8 @@ bool PrepareAlbumMediaIsWaiting(
&file.information->media)) {
if (ValidPhotoForAlbum(*image)) {
file.preview = image->data.scaledToWidth(
previewWidth * cIntRetinaFactor(),
std::min(previewWidth, convertScale(image->data.width()))
* cIntRetinaFactor(),
Qt::SmoothTransformation);
file.preview.setDevicePixelRatio(cRetinaFactor());
file.type = PreparedFile::AlbumType::Photo;
@ -119,6 +120,11 @@ void PrepareAlbum(PreparedList &result, int previewWidth) {
}
if (waiting > 0) {
semaphore.acquire(waiting);
const auto badIt = ranges::find(
result.files,
PreparedFile::AlbumType::None,
[](const PreparedFile &file) { return file.type; });
result.albumIsPossible = (badIt == result.files.end());
}
}

View File

@ -212,6 +212,11 @@ enum class Option {
RoundedTopRight = (1 << 6),
RoundedBottomLeft = (1 << 7),
RoundedBottomRight = (1 << 8),
RoundedAll = (None
| RoundedTopLeft
| RoundedTopRight
| RoundedBottomLeft
| RoundedBottomRight),
Colored = (1 << 9),
TransparentBackground = (1 << 10),
};