From 0b2401132eb518659891bd5641ec77491bcbf037 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 9 May 2016 15:03:06 +0300 Subject: [PATCH] Message edit warning timer (up to 15 minutes). Displaying "edited" info in messages. --- Telegram/Resources/langs/lang.strings | 2 +- Telegram/SourceFiles/dialogswidget.cpp | 4 +- Telegram/SourceFiles/history.cpp | 6 +- Telegram/SourceFiles/historywidget.cpp | 76 ++++++++++++++++++++++---- Telegram/SourceFiles/historywidget.h | 7 ++- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f71a41df26..3b54f501d9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -599,7 +599,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}"; "lng_forwarded_signed" = "{channel} ({user})"; "lng_in_reply_to" = "In reply to"; -"lng_edited" = "Edited"; +"lng_edited" = "edited"; "lng_edited_date" = "Edited: {date}"; "lng_cancel_edit_post_sure" = "Cancel editing?"; "lng_cancel_edit_post_yes" = "Yes"; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index b2de4040dc..b7f48c84e0 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1395,8 +1395,8 @@ void DialogsInner::selectSkipPage(int32 pixels, int32 direction) { _sel = *i; } } else { - for (auto i = shownDialogs()->cfind(_sel), b = shownDialogs()->cbegin(); i != b && (toSkip--); --i) { - _sel = *i; + for (auto i = shownDialogs()->cfind(_sel), b = shownDialogs()->cbegin(); i != b && (toSkip--);) { + _sel = *(--i); } if (toSkip && importantDialogs) { _importantSwitchSel = true; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 4c130f4a81..2398db550d 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3095,8 +3095,7 @@ void HistoryItem::setId(MsgId newId) { } bool HistoryItem::canEdit(const QDateTime &cur) const { - auto channel = _history->peer->asChannel(); - if (!channel || id < 0 || date.secsTo(cur) >= Global::EditTimeLimit()) return false; + if (id < 0 || date.secsTo(cur) >= Global::EditTimeLimit()) return false; if (auto msg = toHistoryMessage()) { if (msg->Has() || msg->Has()) return false; @@ -3114,6 +3113,7 @@ bool HistoryItem::canEdit(const QDateTime &cur) const { } } if (isPost()) { + auto channel = _history->peer->asChannel(); return (channel->amCreator() || (channel->amEditor() && out())); } return out(); @@ -6484,7 +6484,7 @@ void HistoryMessageEdited::create(const QDateTime &editDate, const QDateTime &da _editDate = editDate; QString time = date.toString(cTimeFormat()); - _edited.setText(st::msgDateFont, time, _textNameOptions); + _edited.setText(st::msgDateFont, lang(lng_edited) + ' ' + time, _textNameOptions); } int HistoryMessageEdited::maxWidth() const { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 1242148f57..1781e4ced0 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2070,9 +2070,9 @@ QString HistoryInner::tooltipText() const { if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) { if (App::hoveredItem()) { QString dateText = App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); - //if (auto edited = App::hoveredItem()->Get()) { - // dateText += '\n' + lng_edited_date(lt_date, edited->_editDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); - //} + if (auto edited = App::hoveredItem()->Get()) { + dateText += '\n' + lng_edited_date(lt_date, edited->_editDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); + } return dateText; } } else if (_dragCursorState == HistoryInForwardedCursorState && _dragAction == NoDrag) { @@ -2985,6 +2985,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*))); connect(&_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*))); + + connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField())); } void HistoryWidget::start() { @@ -7077,9 +7079,10 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { if (_field.isEmpty() && !_editMsgId && !_replyToId) { App::contextItem(_history->lastSentMsg); onEditMessage(); + return; } } -// _scroll.keyPressEvent(e); + _scroll.keyPressEvent(e); } } else { e->ignore(); @@ -7474,8 +7477,8 @@ void HistoryWidget::onEditMessage() { _history->setEditDraft(std_::make_unique(editData, to->id, cursor, false)); applyDraft(false); - _previewData = 0; - if (HistoryMedia *media = to->getMedia()) { + _previewData = nullptr; + if (auto media = to->getMedia()) { if (media->type() == MediaTypeWebPage) { _previewData = static_cast(media)->webpage(); updatePreview(); @@ -7491,7 +7494,7 @@ void HistoryWidget::onEditMessage() { updateFieldPlaceholder(); updateMouseTracking(); updateReplyToName(); - resizeEvent(0); + resizeEvent(nullptr); updateField(); _saveDraftText = true; @@ -8089,7 +8092,7 @@ void HistoryWidget::updateField() { update(0, fy, width(), height() - fy); } -void HistoryWidget::drawField(Painter &p) { +void HistoryWidget::drawField(Painter &p, const QRect &rect) { int32 backy = _field.y() - st::sendPadding, backh = _field.height() + 2 * st::sendPadding; Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); @@ -8110,7 +8113,7 @@ void HistoryWidget::drawField(Painter &p) { backh += st::replyHeight; } bool drawPreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed; - p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor->b); + p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor); if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { int32 replyLeft = st::replySkip; p.drawSprite(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), _editMsgId ? st::editIcon : st::replyIcon); @@ -8126,8 +8129,7 @@ void HistoryWidget::drawField(Painter &p) { } p.setPen(st::replyColor); if (_editMsgId) { - p.setFont(st::msgServiceNameFont); - p.drawText(replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_edit_message)); + paintEditHeader(p, rect, replyLeft, backy); } else { _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); } @@ -8182,6 +8184,56 @@ void HistoryWidget::drawField(Painter &p) { } } +namespace { + +constexpr int DisplayEditTimeWarningMs = 900 * 1000; +constexpr int FullDayInMs = 86400 * 1000; + +} // namespace + +void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int top) const { + if (!rect.intersects(QRect(left, top, width() - left, st::normalFont->height))) { + return; + } + + p.setFont(st::msgServiceNameFont); + p.drawText(left, top + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_edit_message)); + + if (!_replyEditMsg) return; + + QString editTimeLeftText; + int updateIn = -1; + auto tmp = ::date(unixtime()); + auto timeSinceMessage = _replyEditMsg->date.msecsTo(QDateTime::currentDateTime()); + auto editTimeLeft = (Global::EditTimeLimit() * 1000LL) - timeSinceMessage; + if (editTimeLeft < 2) { + editTimeLeftText = qsl("0:00"); + } else if (editTimeLeft > DisplayEditTimeWarningMs) { + updateIn = static_cast(qMin(editTimeLeft - DisplayEditTimeWarningMs, qint64(FullDayInMs))); + } else { + updateIn = (editTimeLeft % 1000); + if (!updateIn) { + updateIn = 1000; + } + ++updateIn; + + editTimeLeft = (editTimeLeft - 1) / 1000; // seconds + editTimeLeftText = qsl("%1:%2").arg(editTimeLeft / 60).arg(editTimeLeft % 60, 2, 10, QChar('0')); + } + + // Restart timer only if we are sure that we've painted the whole timer. + if (rect.contains(QRect(left, top, width() - left, st::normalFont->height)) && updateIn > 0) { + _updateEditTimeLeftDisplay.start(updateIn); + LOG(("Time since message: %1ms, update in %2ms: %3").arg(timeSinceMessage).arg(updateIn).arg(editTimeLeftText)); + } + + if (!editTimeLeftText.isEmpty()) { + p.setFont(st::normalFont); + p.setPen(st::msgInDateFg); + p.drawText(left + st::msgServiceNameFont->width(lang(lng_edit_message)) + st::normalFont->spacew, top + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, editTimeLeftText); + } +} + void HistoryWidget::drawRecordButton(Painter &p) { if (a_recordDown.current() < 1) { p.setOpacity(st::btnAttachEmoji.opacity * (1 - a_recordOver.current()) + st::btnAttachEmoji.overOpacity * a_recordOver.current()); @@ -8308,7 +8360,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { if (_list) { if (!_field.isHidden() || _recording) { - drawField(p); + drawField(p, r); if (_send.isHidden()) { drawRecordButton(p); if (_recording) drawRecording(p); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 89ab558d71..2701452867 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -791,7 +791,6 @@ public slots: void onDraftSave(bool delayed = false); void updateStickers(); - void updateField(); void onRecordError(); void onRecordDone(QByteArray result, VoiceWaveform waveform, qint32 samples); @@ -809,6 +808,8 @@ private slots: void onMentionInsert(UserData *user); void onInlineBotCancel(); + void updateField(); + private: // Updates position of controls around the message field, @@ -834,6 +835,7 @@ private: HistoryItem *_replyEditMsg = nullptr; Text _replyEditMsgText; + mutable SingleTimer _updateEditTimeLeftDisplay; IconedButton _fieldBarCancel; void updateReplyEditTexts(bool force = false); @@ -861,7 +863,8 @@ private: void sendExistingDocument(DocumentData *doc, const QString &caption); void sendExistingPhoto(PhotoData *photo, const QString &caption); - void drawField(Painter &p); + void drawField(Painter &p, const QRect &rect); + void paintEditHeader(Painter &p, const QRect &rect, int left, int top) const; void drawRecordButton(Painter &p); void drawRecording(Painter &p); void drawPinnedBar(Painter &p);