From 29fefbc4bb0b33bdd218020d8238a65d8159a90b Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 21 Feb 2016 15:30:16 +0300 Subject: [PATCH] post edit started, sign messages checkbox added in channel edit by creator --- Telegram/Resources/lang.strings | 5 + Telegram/Resources/style.txt | 5 + Telegram/SourceFiles/boxes/addcontactbox.cpp | 82 ++++- Telegram/SourceFiles/boxes/addcontactbox.h | 5 +- Telegram/SourceFiles/boxes/photosendbox.cpp | 306 +++++++++++++++++++ Telegram/SourceFiles/boxes/photosendbox.h | 57 +++- Telegram/SourceFiles/facades.cpp | 10 +- Telegram/SourceFiles/history.cpp | 33 +- Telegram/SourceFiles/history.h | 2 + Telegram/SourceFiles/historywidget.cpp | 36 ++- Telegram/SourceFiles/historywidget.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/structs.h | 2 + 13 files changed, 508 insertions(+), 38 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index be3832f2a1..a1badd427f 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -123,6 +123,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_server_error" = "Internal server error."; "lng_flood_error" = "Too many tries. Please try again later."; "lng_gif_error" = "An error has occured while reading GIF animation :("; +"lng_edit_error" = "You cannot edit this message"; "lng_deleted" = "Unknown"; "lng_deleted_message" = "Deleted message"; @@ -737,6 +738,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_context_save_gif" = "Save GIF"; "lng_context_to_msg" = "Go To Message"; "lng_context_reply_msg" = "Reply"; +"lng_context_edit_msg" = "Edit"; "lng_context_forward_msg" = "Forward Message"; "lng_context_delete_msg" = "Delete Message"; "lng_context_select_msg" = "Select Message"; @@ -753,6 +755,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_send_image_too_large" = "Could not send a file, because it is larger than 1.5 GB :("; "lng_send_folder" = "Could not send «{name}» because it is a directory :("; +"lng_edit_placeholder" = "Message text"; + "lng_forward_choose" = "Choose recipient.."; "lng_forward_cant" = "Sorry, no way to forward here :("; "lng_forward_confirm" = "Forward to {recipient}?"; @@ -773,6 +777,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_edit_group_title" = "Edit group name"; "lng_edit_contact_title" = "Edit contact name"; "lng_edit_channel_title" = "Edit channel"; +"lng_edit_sign_messages" = "Sign messages"; "lng_edit_group" = "Edit group"; "lng_edit_self_title" = "Edit your name"; "lng_confirm_contact_data" = "New Contact"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 3a9794fe14..a8f7e3c32e 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -2438,3 +2438,8 @@ inlineRowBorder: linksBorder; inlineRowBorderFg: linksBorderFg; inlineResultsMinWidth: 64px; inlineDurationMargin: 3px; + +editTextArea: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 256px; +} diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 2a710d7aa8..86accc4bc9 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -1150,14 +1150,17 @@ void EditNameTitleBox::onSaveChatDone(const MTPUpdates &updates) { emit closed(); } -EditChannelBox::EditChannelBox(ChannelData *channel) : -_channel(channel), -_save(this, lang(lng_settings_save), st::defaultBoxButton), -_cancel(this, lang(lng_cancel), st::cancelBoxButton), -_title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name), -_description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about), -_publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton), -_saveTitleRequestId(0), _saveDescriptionRequestId(0) { +EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox() +, _channel(channel) +, _save(this, lang(lng_settings_save), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name) +, _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about) +, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature()) +, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton) +, _saveTitleRequestId(0) +, _saveDescriptionRequestId(0) +, _saveSignRequestId(0) { connect(App::main(), SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*))); setMouseTracking(true); @@ -1183,6 +1186,7 @@ _saveTitleRequestId(0), _saveDescriptionRequestId(0) { void EditChannelBox::hideAll() { _title.hide(); _description.hide(); + _sign.hide(); _save.hide(); _cancel.hide(); _publicLink.hide(); @@ -1195,8 +1199,10 @@ void EditChannelBox::showAll() { _cancel.show(); if (_channel->isMegagroup()) { _publicLink.hide(); + _sign.hide(); } else { _publicLink.show(); + _sign.show(); } } @@ -1224,6 +1230,7 @@ void EditChannelBox::paintEvent(QPaintEvent *e) { void EditChannelBox::peerUpdated(PeerData *peer) { if (peer == _channel) { _publicLink.setText(lang(_channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link)); + _sign.setChecked(_channel->addsSignature()); } } @@ -1235,6 +1242,9 @@ void EditChannelBox::onDescriptionResized() { void EditChannelBox::updateMaxHeight() { int32 h = st::boxTitleHeight + st::newGroupInfoPadding.top() + _title.height(); h += st::newGroupDescriptionPadding.top() + _description.height() + st::newGroupDescriptionPadding.bottom(); + if (!_channel->isMegagroup()) { + h += st::newGroupPublicLinkPadding.top() + _sign.height() + st::newGroupPublicLinkPadding.bottom(); + } h += st::newGroupPublicLinkPadding.top() + _publicLink.height() + st::newGroupPublicLinkPadding.bottom(); h += st::boxPadding.bottom() + st::newGroupInfoPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); setMaxHeight(h); @@ -1246,14 +1256,16 @@ void EditChannelBox::resizeEvent(QResizeEvent *e) { _description.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _title.y() + _title.height() + st::newGroupDescriptionPadding.top()); - _publicLink.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); + _sign.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); + + _publicLink.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _sign.y() + _sign.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); } void EditChannelBox::onSave() { - if (_saveTitleRequestId || _saveDescriptionRequestId) return; + if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId) return; QString title = prepareText(_title.getLastText()), description = prepareText(_description.getLastText(), true); if (title.isEmpty()) { @@ -1263,7 +1275,11 @@ void EditChannelBox::onSave() { } _sentTitle = title; _sentDescription = description; - _saveTitleRequestId = MTP::send(MTPchannels_EditTitle(_channel->inputChannel, MTP_string(_sentTitle)), rpcDone(&EditChannelBox::onSaveTitleDone), rpcFail(&EditChannelBox::onSaveFail)); + if (_sentTitle == _channel->name) { + saveDescription(); + } else { + _saveTitleRequestId = MTP::send(MTPchannels_EditTitle(_channel->inputChannel, MTP_string(_sentTitle)), rpcDone(&EditChannelBox::onSaveTitleDone), rpcFail(&EditChannelBox::onSaveFail)); + } } void EditChannelBox::onPublicLink() { @@ -1271,7 +1287,19 @@ void EditChannelBox::onPublicLink() { } void EditChannelBox::saveDescription() { - _saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail)); + if (_sentDescription == _channel->about) { + saveSign(); + } else { + _saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail)); + } +} + +void EditChannelBox::saveSign() { + if (_channel->isMegagroup() || _channel->addsSignature() == _sign.checked()) { + onClose(); + } else { + _saveSignRequestId = MTP::send(MTPchannels_ToggleSignatures(_channel->inputChannel, MTP_bool(_sign.checked())), rpcDone(&EditChannelBox::onSaveSignDone), rpcFail(&EditChannelBox::onSaveFail)); + } } bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) { @@ -1295,24 +1323,46 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) { _saveDescriptionRequestId = 0; if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) { _channel->about = _sentDescription; - if (App::api()) emit App::api()->fullPeerUpdated(_channel); - onClose(); + if (App::api()) { + emit App::api()->fullPeerUpdated(_channel); + } + saveSign(); + return true; } else { _description.setFocus(); } + } else if (req == _saveSignRequestId) { + _saveSignRequestId = 0; + if (err == qstr("CHAT_NOT_MODIFIED")) { + onClose(); + return true; + } } return true; } void EditChannelBox::onSaveTitleDone(const MTPUpdates &updates) { _saveTitleRequestId = 0; - App::main()->sentUpdatesReceived(updates); + if (App::main()) { + App::main()->sentUpdatesReceived(updates); + } saveDescription(); } void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) { _saveDescriptionRequestId = 0; _channel->about = _sentDescription; - if (App::api()) emit App::api()->fullPeerUpdated(_channel); + if (App::api()) { + emit App::api()->fullPeerUpdated(_channel); + } + saveSign(); +} + +void EditChannelBox::onSaveSignDone(const MTPUpdates &updates) { + _saveSignRequestId = 0; + if (App::main()) { + App::main()->sentUpdatesReceived(updates); + } onClose(); } + diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 0289bc3d53..55e89f3e68 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -315,18 +315,21 @@ private: void onSaveTitleDone(const MTPUpdates &updates); void onSaveDescriptionDone(const MTPBool &result); + void onSaveSignDone(const MTPUpdates &updates); bool onSaveFail(const RPCError &e, mtpRequestId req); void saveDescription(); + void saveSign(); ChannelData *_channel; BoxButton _save, _cancel; InputField _title; InputArea _description; + Checkbox _sign; LinkButton _publicLink; - mtpRequestId _saveTitleRequestId, _saveDescriptionRequestId; + mtpRequestId _saveTitleRequestId, _saveDescriptionRequestId, _saveSignRequestId; QString _sentTitle, _sentDescription; }; diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 1a25dd55ec..4bc63c021d 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -359,3 +359,309 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) { _confirmed = true; onClose(); } + +EditPostBox::EditPostBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) +, _msg(msg) +, _animated(false) +, _photo(false) +, _doc(false) +, _text(0) +, _save(this, lang(lng_settings_save), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _thumbx(0) +, _thumby(0) +, _thumbw(0) +, _thumbh(0) +, _statusw(0) +, _isImage(false) +, _previewCancelled(false) +, _saveRequestId(0) { + connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + + QSize dimensions; + ImagePtr image; + QString caption; + DocumentData *doc = 0; + if (HistoryMedia *media = _msg->getMedia()) { + HistoryMediaType t = media->type(); + switch (t) { + case MediaTypeGif: { + _animated = true; + doc = static_cast(media)->getDocument(); + dimensions = doc->dimensions; + image = doc->thumb; + } break; + + case MediaTypePhoto: { + _photo = true; + PhotoData *photo = static_cast(media)->photo(); + dimensions = QSize(photo->full->width(), photo->full->height()); + image = photo->full; + } break; + + case MediaTypeVideo: { + _animated = true; + doc = static_cast(media)->getDocument(); + dimensions = doc->dimensions; + image = doc->thumb; + } break; + + case MediaTypeFile: + case MediaTypeMusicFile: + case MediaTypeVoiceFile: { + _doc = true; + doc = static_cast(media)->getDocument(); + image = doc->thumb; + } break; + } + caption = media->getCaption(); + } + if (!_animated && (dimensions.isEmpty() || doc) || image->isNull()) { + _animated = false; + if (image->isNull()) { + _thumbw = 0; + } else { + int32 tw = image->width(), th = image->height(); + if (tw > th) { + _thumbw = (tw * st::msgFileThumbSize) / th; + } else { + _thumbw = st::msgFileThumbSize; + } + _thumb = imagePix(image->pix().toImage(), _thumbw * cIntRetinaFactor(), 0, true, false, true, st::msgFileThumbSize, st::msgFileThumbSize); + } + + if (doc) { + if (doc->voice()) { + _name.setText(st::semiboldFont, lang(lng_media_audio), _textNameOptions); + } else { + _name.setText(st::semiboldFont, documentName(doc), _textNameOptions); + } + _status = formatSizeText(doc->size); + _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status)); + _isImage = doc->isImage(); + } + } else { + int32 maxW = 0, maxH = 0; + if (_animated) { + int32 limitW = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + int32 limitH = st::confirmMaxHeight; + maxW = dimensions.width(); + maxH = dimensions.height(); + if (maxW * limitH > maxH * limitW) { + if (maxW < limitW) { + maxH = maxH * limitW / maxW; + maxW = limitW; + } + } else { + if (maxH < limitH) { + maxW = maxW * limitH / maxH; + maxH = limitH; + } + } + _thumb = image->pixNoCache(maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), true, true, false, maxW, maxH); + } else { + maxW = dimensions.width(); + maxH = dimensions.height(); + _thumb = image->pixNoCache(maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), true, false, false, maxW, maxH); + } + int32 tw = _thumb.width(), th = _thumb.height(); + if (!tw || !th) { + tw = th = 1; + } + _thumbw = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + if (_thumb.width() < _thumbw) { + _thumbw = (_thumb.width() > 20) ? _thumb.width() : 20; + } + int32 maxthumbh = qMin(qRound(1.5 * _thumbw), int(st::confirmMaxHeight)); + _thumbh = qRound(th * float64(_thumbw) / tw); + if (_thumbh > maxthumbh) { + _thumbw = qRound(_thumbw * float64(maxthumbh) / _thumbh); + _thumbh = maxthumbh; + if (_thumbw < 10) { + _thumbw = 10; + } + } + _thumbx = (width() - _thumbw) / 2; + + _thumb = QPixmap::fromImage(_thumb.toImage().scaled(_thumbw * cIntRetinaFactor(), _thumbh * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly); + _thumb.setDevicePixelRatio(cRetinaFactor()); + } + + if (_animated || _photo || _doc) { + _text = new InputArea(this, st::confirmCaptionArea, lang(lng_photo_caption), caption); + _text->setMaxLength(MaxPhotoCaption); + _text->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + } else { + _text = new InputArea(this, st::editTextArea, lang(lng_edit_placeholder), msg->originalText()); + _text->setMaxLength(MaxMessageSize); + _text->setCtrlEnterSubmit(cCtrlEnter() ? CtrlEnterSubmitCtrlEnter : CtrlEnterSubmitEnter); + } + updateBoxSize(); + connect(_text, SIGNAL(resized()), this, SLOT(onCaptionResized())); + connect(_text, SIGNAL(submitted(bool)), this, SLOT(onSave(bool))); + connect(_text, SIGNAL(cancelled()), this, SLOT(onClose())); + + QTextCursor c(_text->textCursor()); + c.movePosition(QTextCursor::End); + _text->setTextCursor(c); + + prepare(); +} + +void EditPostBox::onCaptionResized() { + updateBoxSize(); + resizeEvent(0); + update(); +} + +void EditPostBox::updateBoxSize() { + if (_photo || _animated) { + setMaxHeight(st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.bottom() + _text->height() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + } else if (_thumbw) { + setMaxHeight(st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.bottom() + _text->height() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + } else if (_doc) { + setMaxHeight(st::boxPhotoPadding.top() + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.bottom() + _text->height() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + } else { + setMaxHeight(st::boxPhotoPadding.top() + _text->height() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + } +} + +void EditPostBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + if (_photo || _animated) { + if (_thumbx > st::boxPhotoPadding.left()) { + p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _thumbx - st::boxPhotoPadding.left(), _thumbh, st::confirmBg->b); + } + if (_thumbx + _thumbw < width() - st::boxPhotoPadding.right()) { + p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, _thumbh, st::confirmBg->b); + } + p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), _thumb); + if (_animated) { + QRect inner(_thumbx + (_thumbw - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_thumbh - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + p.setPen(Qt::NoPen); + p.setBrush(st::msgDateImgBg); + + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + p.drawSpriteCenter(inner, st::msgFileInPlay); + } + } else if (_doc) { + int32 w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + int32 h = _thumbw ? (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()) : (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()); + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + if (_thumbw) { + nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); + nametop = st::msgFileThumbNameTop; + nameright = st::msgFileThumbPadding.left(); + statustop = st::msgFileThumbStatusTop; + linktop = st::msgFileThumbLinkTop; + } else { + nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + nametop = st::msgFileNameTop; + nameright = st::msgFilePadding.left(); + statustop = st::msgFileStatusTop; + } + int32 namewidth = w - nameleft - (_thumbw ? st::msgFileThumbPadding.left() : st::msgFilePadding.left()); + if (namewidth > _statusw) { + w -= (namewidth - _statusw); + namewidth = _statusw; + } + int32 x = (width() - w) / 2, y = st::boxPhotoPadding.top(); + + App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow); + + if (_thumbw) { + QRect rthumb(rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width())); + p.drawPixmap(rthumb.topLeft(), _thumb); + } else { + QRect inner(rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width())); + p.setPen(Qt::NoPen); + p.setBrush(st::msgFileOutBg); + + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + p.drawSpriteCenter(inner, _isImage ? st::msgFileOutImage : st::msgFileOutFile); + } + p.setFont(st::semiboldFont); + p.setPen(st::black); + _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); + + style::color status(st::mediaOutFg); + p.setFont(st::normalFont); + p.setPen(status); + p.drawTextLeft(x + nameleft, y + statustop, width(), _status); + } +} + +void EditPostBox::resizeEvent(QResizeEvent *e) { + _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); + _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _text->resize(st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), _text->height()); + _text->moveToLeft(st::boxPhotoPadding.left(), _save.y() - st::boxButtonPadding.top() - _text->height()); +} + +void EditPostBox::hideAll() { + _save.hide(); + _cancel.hide(); + _text->hide(); +} + +void EditPostBox::showAll() { + _save.show(); + _cancel.show(); + _text->show(); +} + +void EditPostBox::showDone() { + setInnerFocus(); +} + +void EditPostBox::onSave(bool ctrlShiftEnter) { + if (_saveRequestId) return; + + int32 flags = 0; + if (_previewCancelled) { + flags |= MTPchannels_EditMessage::flag_no_webpage; + } + EntitiesInText sendingEntities; + MTPVector sentEntities = linksToMTP(sendingEntities, true); + if (!sentEntities.c_vector().v.isEmpty()) { + flags |= MTPmessages_SendMessage::flag_entities; + } + _saveRequestId = MTP::send(MTPchannels_EditMessage(MTP_int(flags), _msg->history()->peer->asChannel()->inputChannel, MTP_int(_msg->id), MTP_string(_text->getLastText()), sentEntities), rpcDone(&EditPostBox::saveDone), rpcFail(&EditPostBox::saveFail)); +} + +void EditPostBox::saveDone(const MTPUpdates &updates) { + _saveRequestId = 0; + onClose(); + if (App::main()) { + App::main()->sentUpdatesReceived(updates); + } +} + +bool EditPostBox::saveFail(const RPCError &error) { + if (mtpIsFlood(error)) return false; + + _saveRequestId = 0; + QString err = error.type(); + if (err == qstr("MESSAGE_ID_INVALID") || err == qstr("CHAT_ADMIN_REQUIRED") || err == qstr("MESSAGE_EDIT_TIME_EXPIRED")) { + _error = lang(lng_edit_error); + } else if (err == qstr("MESSAGE_NOT_MODIFIED")) { + onClose(); + return true; + } else if (err == qstr("MESSAGE_EMPTY")) { + _text->setFocus(); + _text->showError(); + } else { + _error = lang(lng_edit_error); + } + update(); + return true; +} diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h index 1cd4bbf00e..462e8bc503 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/photosendbox.h @@ -42,11 +42,6 @@ public: } } -signals: - - void confirmed(); - void cancelled(); - public slots: void onCompressedChange(); @@ -87,3 +82,55 @@ private: bool _confirmed; }; + +class EditPostBox : public AbstractBox, public RPCSender { + Q_OBJECT + +public: + + EditPostBox(HistoryItem *msg); + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); + + void setInnerFocus() { + _text->setFocus(); + } + +public slots: + + void onCaptionResized(); + void onSave(bool ctrlShiftEnter = false); + +protected: + + void hideAll(); + void showAll(); + void showDone(); + +private: + + void updateBoxSize(); + + void saveDone(const MTPUpdates &updates); + bool saveFail(const RPCError &error); + + HistoryItem *_msg; + bool _animated, _photo, _doc; + + QPixmap _thumb; + + InputArea *_text; + BoxButton _save, _cancel; + + int32 _thumbx, _thumby, _thumbw, _thumbh; + Text _name; + QString _status; + int32 _statusw; + bool _isImage; + + bool _previewCancelled; + mtpRequestId _saveRequestId; + + QString _error; + +}; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 70c257e9a2..6e7a621c3e 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -364,11 +364,11 @@ struct GlobalDataStruct { int32 OnlineCloudTimeout = 300000; int32 NotifyCloudDelay = 30000; int32 NotifyDefaultDelay = 1500; - int32 ChatBigSize = 190; // ? - int32 PushChatPeriod = 0; // ? - int32 PushChatLimit = 0; // ? - int32 SavedGifsLimit = 100; - int32 EditTimeLimit = 0; // ? + int32 ChatBigSize = 10; + int32 PushChatPeriod = 60000; + int32 PushChatLimit = 2; + int32 SavedGifsLimit = 200; + int32 EditTimeLimit = 172800; }; GlobalDataStruct *GlobalData = 0; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 6ef6b30c57..ffa79820b9 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2911,6 +2911,33 @@ bool HistoryItem::displayFromPhoto() const { return (Adaptive::Wide() || (!out() && !history()->peer->isUser())) && !isPost(); } +bool HistoryItem::canEdit(const QDateTime &cur) const { + ChannelData *channel = _history->peer->asChannel(); + int32 s = date.secsTo(cur); + if (!channel || id < 0 || date.secsTo(cur) >= Global::EditTimeLimit()) return false; + + if (const HistoryMessage *msg = toHistoryMessage()) { + if (msg->Is() || msg->Is()) return false; + + if (HistoryMedia *media = msg->getMedia()) { + HistoryMediaType t = media->type(); + if (t != MediaTypePhoto && + t != MediaTypeVideo && + t != MediaTypeFile && + t != MediaTypeGif && + t != MediaTypeMusicFile && + t != MediaTypeVoiceFile) { + return false; + } + } + if (isPost()) { + return (channel->amCreator() || (channel->amEditor() && out())); + } + return out(); + } + return false; +} + void HistoryItem::clipCallback(ClipReaderNotification notification) { HistoryMedia *media = getMedia(); if (!media) return; @@ -3829,13 +3856,13 @@ void HistoryDocument::create(bool caption) { mask |= HistoryDocumentVoice::Bit(); } else { mask |= HistoryDocumentNamed::Bit(); - if (caption) { - mask |= HistoryDocumentCaptioned::Bit(); - } if (!_data->song() && !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height()) { mask |= HistoryDocumentThumbed::Bit(); } } + if (caption) { + mask |= HistoryDocumentCaptioned::Bit(); + } UpdateInterfaces(mask); if (HistoryDocumentThumbed *thumbed = Get()) { thumbed->_linksavel.reset(new DocumentSaveLink(_data)); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 6b20310bed..d1e410d1fd 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -987,6 +987,8 @@ public: return (channel->amEditor() || channel->amModerator() || out()); } + bool canEdit(const QDateTime &cur) const; + int32 y; MsgId id; QDateTime date; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 30048f24f1..8835be63bf 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -870,8 +870,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } - if (item && item->id > 0 && isUponSelected != 2 && isUponSelected != -2 && canSendMessages) { - _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); + if (item && item->id > 0 && isUponSelected != 2 && isUponSelected != -2) { + if (canSendMessages) { + _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); + } + if (item->canEdit(::date(unixtime()))) { + _menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage())); + } } if (lnkPhoto) { _menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true); @@ -920,12 +925,22 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); - if (item && item->id > 0 && isUponSelected != 2 && canSendMessages) { - _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); + if (item && item->id > 0 && isUponSelected != 2) { + if (canSendMessages) { + _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); + } + if (item->canEdit(::date(unixtime()))) { + _menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage())); + } } } else { - if (item && item->id > 0 && isUponSelected != -2 && canSendMessages) { - _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); + if (item && item->id > 0 && isUponSelected != -2) { + if (canSendMessages) { + _menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage())); + } + if (item->canEdit(::date(unixtime()))) { + _menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage())); + } } if (item && !isUponSelected && !_contextMenuLnk) { if (HistoryMedia *media = (msg ? msg->getMedia() : 0)) { @@ -4422,7 +4437,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(_channel, replyTo)); - WebPageId webPageId = _previewCancelled ? 0xFFFFFFFFFFFFFFFFULL : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0); + WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0); App::main()->sendMessage(_history, _field.getLastText(), replyTo, _broadcast.checked(), webPageId); setFieldText(QString()); @@ -6617,6 +6632,13 @@ void HistoryWidget::onReplyToMessage() { _field.setFocus(); } +void HistoryWidget::onEditMessage() { + HistoryItem *to = App::contextItem(); + if (!to || !to->history()->peer->isChannel()) return; + + Ui::showLayer(new EditPostBox(to)); +} + bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const { if (replyTo.msg > 0 && replyTo.channel != _channel) return false; return _keyboard.forceReply() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _keyboard.forMsgId().msg == (replyTo.msg < 0 ? replyToId() : replyTo.msg); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index fcf4c53771..d18f081035 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -594,6 +594,7 @@ public slots: void onCancel(); void onReplyToMessage(); + void onEditMessage(); void onReplyForwardPreviewCancel(); void onCancelSendAction(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index aef60bc1dc..27175c2f3f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1300,7 +1300,7 @@ void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo, sendFlags |= MTPmessages_SendMessage::flag_reply_to_msg_id; } MTPMessageMedia media = MTP_messageMediaEmpty(); - if (webPageId == 0xFFFFFFFFFFFFFFFFULL) { + if (webPageId == CancelledWebPageId) { sendFlags |= MTPmessages_SendMessage::flag_no_webpage; } else if (webPageId) { WebPageData *page = App::webPage(webPageId); diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 3afef5e2b1..69604e5017 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -131,6 +131,8 @@ typedef uint64 VideoId; typedef uint64 AudioId; typedef uint64 DocumentId; typedef uint64 WebPageId; +static const WebPageId CancelledWebPageId = 0xFFFFFFFFFFFFFFFFULL; + typedef int32 MsgId; struct FullMsgId { FullMsgId() : channel(NoChannel), msg(0) {