From 7902e96d1a5f15a11daf3d20d3c28abbccb0847f Mon Sep 17 00:00:00 2001 From: RadRussianRus Date: Sun, 11 Sep 2022 03:07:21 +0300 Subject: [PATCH] [Improvement] Jump to date and time --- .../Resources/icons/menu/to_beginning.png | Bin 0 -> 195 bytes .../Resources/icons/menu/to_beginning@2x.png | Bin 0 -> 313 bytes .../Resources/icons/menu/to_beginning@3x.png | Bin 0 -> 418 bytes Telegram/Resources/langs/rewrites/en.json | 4 + Telegram/SourceFiles/apiwrap.cpp | 8 +- Telegram/SourceFiles/apiwrap.h | 6 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 2 +- .../history/history_inner_widget.cpp | 4 +- .../SourceFiles/ui/boxes/calendar_box.cpp | 74 ++++++++++- .../SourceFiles/ui/boxes/choose_date_time.cpp | 10 +- Telegram/SourceFiles/ui/menu_icons.style | 1 + .../window/window_session_controller.cpp | 124 ++++++++++++++---- .../window/window_session_controller.h | 9 +- 13 files changed, 188 insertions(+), 54 deletions(-) create mode 100644 Telegram/Resources/icons/menu/to_beginning.png create mode 100644 Telegram/Resources/icons/menu/to_beginning@2x.png create mode 100644 Telegram/Resources/icons/menu/to_beginning@3x.png diff --git a/Telegram/Resources/icons/menu/to_beginning.png b/Telegram/Resources/icons/menu/to_beginning.png new file mode 100644 index 0000000000000000000000000000000000000000..dc573c5356808ccb113be6f9e654959435f932d1 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^5+KY0Bp8m$B&h%?&H|6fVg?3oVGw3ym^BBag1yAk z*OmPl6E6#+p4)QY3qYYHPZ!4!jq}L~3$#6|ziRQ$t~hr4|Nr}G30=?kRF}TLx4PUi z<4{lK`*^o`*5W@u)ygS%v0VQxzL4knIm4EH73W)5CGWLoKD@J-hv(mGNyD|4<$Rro nTTXv;_Gx16texL@j)CEyzrw7ovaxAE+Za4u{an^LB{Ts5M#4k^ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/menu/to_beginning@2x.png b/Telegram/Resources/icons/menu/to_beginning@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a1dc66a984b08e8c86acf4dc585789ee75ea6176 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZAFdh=jER?pk@v{~n?*pO8=fwXAs)xyPEq7L!`>fq{Ag-()P}IO4}5Z(~}ege&2ob&%8eEsF!2?nweXT>$hK&O3;1my@X-H zuW$E1Tsfw%%-mPNVl@9>bb?{iMW!2d=T02dGi}>AO{{49?xnIfR(d%Eb6anXIl|TD zBD!En!_?FAj)oIgGA#CF3_N>jW$5x^bJ?y{E{E?uC@Xp=F#Y@|8Be#5xB0%F%KHB* zKJ87aY(qPfKm$V!%ezZ^e=fUQQ}b5-+4TIT-ar3;Es%e~sQ<3`{H3YwOM!l5@O1Ta JS?83{1OR79eYgMs literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/menu/to_beginning@3x.png b/Telegram/Resources/icons/menu/to_beginning@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..62c8babeb24d0a4130ed7fa5fd16e72a8294f58a GIT binary patch literal 418 zcmV;T0bTxyP)?BOU0003&NklR5kz;6TqARY${$Ha|is_^kZ@gNvsdxG;ViNHb5^b=a58u0Cwu) zh`4xBgCf!`&VIh16}uu5y!`?{SyZar@cQt!7v*K@x7Z(jG6$IrRG& zz1uuiNFjw3Qb-|%{uHv0i_bz+n1-%yd$_v8d3k(&e%HTNP5UQ(0Ji$$NHrx&9{>OV M07*qoM6N<$g8yr#eE void ApiWrap::requestMessageAfterDate( not_null peer, - const QDate &date, + const QDateTime &date, Callback &&callback) { // API returns a message with date <= offset_date. // So we request a message with offset_date = desired_date - 1 and add_offset = -1. // This should give us the first message with date >= desired_date. const auto offsetId = 0; - const auto offsetDate = static_cast(date.startOfDay().toSecsSinceEpoch()) - 1; + const auto offsetDate = static_cast(date.toSecsSinceEpoch()) - 1; const auto addOffset = -1; const auto limit = 1; const auto maxId = 0; @@ -2878,7 +2878,7 @@ void ApiWrap::requestMessageAfterDate( }).send(); } -void ApiWrap::jumpToHistoryDate(not_null peer, const QDate &date) { +void ApiWrap::jumpToHistoryDate(not_null peer, const QDateTime &date) { if (const auto channel = peer->migrateTo()) { jumpToHistoryDate(channel, date); return; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 95ff033c0..281ed3b92 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -250,7 +250,7 @@ public: bool isQuitPrevent(); - void jumpToDate(Dialogs::Key chat, const QDate &date); + void jumpToDate(Dialogs::Key chat, const QDateTime &date); using SliceType = Data::LoadDirection; void requestSharedMedia( @@ -440,11 +440,11 @@ private: void requestSavedGifs(TimeId now); void readFeaturedSets(); - void jumpToHistoryDate(not_null peer, const QDate &date); + void jumpToHistoryDate(not_null peer, const QDateTime &date); template void requestMessageAfterDate( not_null peer, - const QDate &date, + const QDateTime &date, Callback &&callback); void sharedMediaDone( diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index e88389221..98587e4b9 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1486,7 +1486,7 @@ void Widget::clearSearchCache() { void Widget::showCalendar() { if (_searchInChat) { - controller()->showCalendar(_searchInChat, QDate()); + controller()->showCalendar(_searchInChat, QDateTime()); } } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 689754bec..eedcd4cb6 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -3335,9 +3335,9 @@ void HistoryInner::mouseActionUpdate() { if (point.x() >= dateLeft && point.x() < dateLeft + dateWidth) { if (!_scrollDateLink) { - _scrollDateLink = std::make_shared(item->history(), view->dateTime().date()); + _scrollDateLink = std::make_shared(item->history(), view->dateTime()); } else { - static_cast(_scrollDateLink.get())->setDate(view->dateTime().date()); + static_cast(_scrollDateLink.get())->setDate(view->dateTime()); } dragState = TextState( nullptr, diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 95a1d8cb6..1e01b1d4f 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -47,6 +47,7 @@ public: void skipMonth(int skip); void showMonth(QDate month); + void skipDays(int skip); [[nodiscard]] bool showsMonthOf(QDate date) const; [[nodiscard]] int highlightedIndex() const { @@ -67,6 +68,9 @@ public: [[nodiscard]] bool isEnabled(int index) const { return (index >= _minDayIndex) && (index <= _maxDayIndex); } + QDate highlighted() const { + return _highlighted; + } [[nodiscard]] QDate month() const { return _month.current(); @@ -98,6 +102,7 @@ private: static int DaysShiftForMonth(QDate month, QDate min); static int RowsCountForMonth(QDate month, QDate min, QDate max); + void setHighlightedDate(QDate highlighted); bool _allowsSelection = false; @@ -196,6 +201,36 @@ void CalendarBox::Context::skipMonth(int skip) { showMonth(QDate(year, month, 1)); } +void CalendarBox::Context::skipDays(int skip) { + auto date = _highlighted; + + if (_month.current().month() == _highlighted.month() + && _month.current().year() == _highlighted.year()) { + date = date.addDays(skip); + } else if (skip < 0) { + date = QDate( + _month.current().year(), + _month.current().month(), + _month.current().daysInMonth()); + } else { + date = QDate( + _month.current().year(), + _month.current().month(), + 1); + } + + if (date.isValid() && date >= _min && date <= _max) { + auto needMonthChange = (date.month() != _highlighted.month() + || date.year() != _highlighted.year()); + + setHighlightedDate(date); + + if (needMonthChange) { + showMonth(date); + } + } +} + int CalendarBox::Context::DaysShiftForMonth(QDate month, QDate min) { Expects(!month.isNull()); @@ -244,6 +279,12 @@ int CalendarBox::Context::RowsCountForMonth( return cellsFull / kDaysInWeek; } +void CalendarBox::Context::setHighlightedDate(QDate highlighted) { + _highlighted = highlighted; + _highlightedIndex = _month.current().daysTo(_highlighted); + applyMonth(_month.current(), true); +} + QDate CalendarBox::Context::dateFromIndex(int index) const { constexpr auto kMonthsCount = 12; auto month = _month.current().month(); @@ -348,6 +389,9 @@ public: [[nodiscard]] int countMaxHeight() const; void setDateChosenCallback(Fn callback); + void selectBeginning(); + void selectEnd(); + void selectHighlighted(); ~Inner(); @@ -720,6 +764,18 @@ void CalendarBox::Inner::setDateChosenCallback(Fn callback) { _dateChosenCallback = std::move(callback); } +void CalendarBox::Inner::selectBeginning() { + _dateChosenCallback(_context->dateFromIndex(_context->minDayIndex())); +} + +void CalendarBox::Inner::selectEnd() { + _dateChosenCallback(_context->dateFromIndex(_context->maxDayIndex())); +} + +void CalendarBox::Inner::selectHighlighted() { + _dateChosenCallback(_context->highlighted()); +} + CalendarBox::Inner::~Inner() = default; class CalendarBox::Title final : public RpWidget { @@ -1109,14 +1165,20 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) { jump(_previous.data()); } else if (e->key() == Qt::Key_End) { jump(_next.data()); - } else if (e->key() == Qt::Key_Left - || e->key() == Qt::Key_Up - || e->key() == Qt::Key_PageUp) { + } else if (e->key() == Qt::Key_PageUp) { goPreviousMonth(); - } else if (e->key() == Qt::Key_Right - || e->key() == Qt::Key_Down - || e->key() == Qt::Key_PageDown) { + } else if (e->key() == Qt::Key_PageDown) { goNextMonth(); + } else if (e->key() == Qt::Key_Left) { + _context->skipDays(-1); + } else if (e->key() == Qt::Key_Right) { + _context->skipDays(1); + } else if (e->key() == Qt::Key_Up) { + _context->skipDays(-7); + } else if (e->key() == Qt::Key_Down) { + _context->skipDays(7); + } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { + _inner->selectHighlighted(); } } diff --git a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp index 023f7a94c..dd8fb5ba2 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp @@ -22,7 +22,7 @@ namespace Ui { namespace { constexpr auto kMinimalSchedule = TimeId(10); - +/* tr::phrase<> MonthDay(int index) { switch (index) { case 1: return tr::lng_month_day1; @@ -40,14 +40,10 @@ tr::phrase<> MonthDay(int index) { } Unexpected("Index in MonthDay."); } +*/ QString DayString(const QDate &date) { - return tr::lng_month_day( - tr::now, - lt_month, - MonthDay(date.month())(tr::now), - lt_day, - QString::number(date.day())); + return langDayOfMonthFull(date); } QString TimeString(QTime time) { diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style index d7a7e0ee6..0890a171e 100644 --- a/Telegram/SourceFiles/ui/menu_icons.style +++ b/Telegram/SourceFiles/ui/menu_icons.style @@ -89,6 +89,7 @@ menuIconSettings: icon {{ "menu/settings", menuSubmenuArrowFg }}; menuIconSearch: icon {{ "menu/search", menuSubmenuArrowFg }}; menuIconHide: icon {{ "menu/hide", menuSubmenuArrowFg }}; menuIconMention: icon {{ "menu/mention", menuSubmenuArrowFg }}; +menuIconToBeginning: icon {{ "menu/to_beginning", menuSubmenuArrowFg }}; mediaMenuIconStickers: icon {{ "menu/stickers", mediaviewMenuFg }}; mediaMenuIconCancel: icon {{ "menu/cancel", mediaviewMenuFg }}; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index a8f70cf4e..f9d50f55a 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "kotato/kotato_settings.h" +#include "kotato/kotato_lang.h" #include "boxes/add_contact_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "boxes/peer_list_controllers.h" @@ -58,8 +59,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toasts/common_toasts.h" #include "calls/calls_instance.h" // Core::App().calls().inCall(). #include "calls/group/calls_group_call.h" +#include "ui/boxes/choose_date_time.h" #include "ui/boxes/calendar_box.h" #include "ui/boxes/confirm_box.h" +#include "ui/widgets/popup_menu.h" #include "mainwidget.h" #include "mainwindow.h" #include "main/main_domain.h" @@ -77,6 +80,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_dialogs.h" #include "styles/style_layers.h" // st::boxLabel #include "styles/style_chat.h" // st::historyMessageRadius +#include "styles/style_info.h" +#include "styles/style_menu_icons.h" namespace Window { namespace { @@ -128,6 +133,53 @@ constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs; }; } +void ChooseJumpDateTimeBox( + not_null box, + QDateTime minDate, + QDateTime maxDate, + QDateTime highlighted, + Fn onDone, + Fn onBegnning, + Fn onCalendar, + bool hasCalendar) { + + auto descriptor = Ui::ChooseDateTimeBox(box, + Ui::ChooseDateTimeBoxArgs{ + .title = rktr("ktg_jump_to_date_title"), + .submit = rktr("ktg_jump_to_date_button"), + .done = std::move(onDone), + .min = [=] { return base::unixtime::serialize(minDate); }, + .time = base::unixtime::serialize(highlighted), + .max = [=] { return base::unixtime::serialize(maxDate); }, + }); + const auto topMenuButton = box->addTopButton(st::infoTopBarMenu); + const auto menu = std::make_shared>(); + topMenuButton.data()->setClickedCallback([=] { + *menu = base::make_unique_q( + topMenuButton.data(), + st::popupMenuWithIcons); + (*menu)->addAction( + ktr("ktg_jump_to_beginning"), + std::move(onBegnning), + &st::menuIconToBeginning); + if (hasCalendar) { + (*menu)->addAction( + ktr("ktg_show_calendar"), + std::move(onCalendar), + &st::menuIconSchedule); + } + + (*menu)->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight); + const auto buttonTopLeft = topMenuButton.data()->mapToGlobal(QPoint()); + const auto buttonRect = QRect(buttonTopLeft, topMenuButton.data()->size()); + const auto pos = QPoint( + buttonRect.x() + buttonRect.width(), + buttonRect.y() + buttonRect.height()); + (*menu)->popup(pos); + return true; + }); +} + } // namespace void ActivateWindow(not_null controller) { @@ -145,12 +197,12 @@ bool operator!=(const PeerThemeOverride &a, const PeerThemeOverride &b) { return !(a == b); } -DateClickHandler::DateClickHandler(Dialogs::Key chat, QDate date) +DateClickHandler::DateClickHandler(Dialogs::Key chat, QDateTime date) : _chat(chat) , _date(date) { } -void DateClickHandler::setDate(QDate date) { +void DateClickHandler::setDate(QDateTime date) { _date = date; } @@ -1196,14 +1248,14 @@ void SessionController::startOrJoinGroupCall( } } -void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { +void SessionController::showCalendar(Dialogs::Key chat, QDateTime requestedDateTime) { const auto history = chat.history(); if (!history) { return; } const auto currentPeerDate = [&] { if (history->scrollTopItem) { - return history->scrollTopItem->dateTime().date(); + return history->scrollTopItem->dateTime(); } else if (history->loadedAtTop() && !history->isEmpty() && history->peer->migrateFrom()) { @@ -1211,33 +1263,33 @@ void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { if (migrated->scrollTopItem) { // We're up in the migrated history. // So current date is the date of first message here. - return history->blocks.front()->messages.front()->dateTime().date(); + return history->blocks.front()->messages.front()->dateTime(); } } } else if (const auto item = history->lastMessage()) { - return base::unixtime::parse(item->date()).date(); + return base::unixtime::parse(item->date()); } - return QDate(); + return QDateTime(); }(); const auto maxPeerDate = [&] { const auto check = history->peer->migrateTo() ? history->owner().historyLoaded(history->peer->migrateTo()) : history; if (const auto item = check ? check->lastMessage() : nullptr) { - return base::unixtime::parse(item->date()).date(); + return base::unixtime::parse(item->date()); } - return QDate(); + return QDateTime(); }(); const auto minPeerDate = [&] { const auto startDate = [] { // Telegram was launched in August 2013 :) - return QDate(2013, 8, 1); + return QDate(2013, 8, 1).startOfDay(); }; if (const auto chat = history->peer->migrateFrom()) { if (const auto history = chat->owner().historyLoaded(chat)) { if (history->loadedAtTop()) { if (!history->isEmpty()) { - return history->blocks.front()->messages.front()->dateTime().date(); + return history->blocks.front()->messages.front()->dateTime(); } } else { return startDate(); @@ -1246,17 +1298,18 @@ void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { } if (history->loadedAtTop()) { if (!history->isEmpty()) { - return history->blocks.front()->messages.front()->dateTime().date(); + return history->blocks.front()->messages.front()->dateTime(); } - return QDate::currentDate(); + return QDateTime::currentDateTime(); } return startDate(); }(); - const auto highlighted = !requestedDate.isNull() - ? requestedDate + const auto highlighted = !requestedDateTime.isNull() + ? requestedDateTime : !currentPeerDate.isNull() ? currentPeerDate - : QDate::currentDate(); + : QDateTime::currentDateTime(); + struct ButtonState { enum class Type { None, @@ -1315,17 +1368,34 @@ void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { button->setPointerCursor(false); } }; - show(Box(Ui::CalendarBoxArgs{ - .month = highlighted, - .highlighted = highlighted, - .callback = [=](const QDate &date) { - session().api().jumpToDate(chat, date); - }, - .minDate = minPeerDate, - .maxDate = maxPeerDate, - .allowsSelection = history->peer->isUser(), - .selectionChanged = selectionChanged, - })); + + const auto showCalendarCallback = [=] { + show(Box(Ui::CalendarBoxArgs{ + .month = highlighted.date(), + .highlighted = highlighted.date(), + .callback = [=](const QDate &date) { + session().api().jumpToDate(chat, date.startOfDay()); + }, + .minDate = minPeerDate.date(), + .maxDate = maxPeerDate.date(), + .allowsSelection = history->peer->isUser(), + .selectionChanged = selectionChanged, + }), Ui::LayerOption::CloseOther); + }; + + show(Box(ChooseJumpDateTimeBox, + minPeerDate, + maxPeerDate, + highlighted, + [=](TimeId result) { + session().api().jumpToDate(chat, base::unixtime::parse(result)); + }, + [=] { + session().api().jumpToDate(chat, minPeerDate); + }, + std::move(showCalendarCallback), + history->peer->isUser()), + Ui::LayerOption::KeepOther); } void SessionController::showPassportForm(const Passport::FormRequest &request) { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index f797748a8..f440d53e0 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -88,14 +88,14 @@ bool operator!=(const PeerThemeOverride &a, const PeerThemeOverride &b); class DateClickHandler : public ClickHandler { public: - DateClickHandler(Dialogs::Key chat, QDate date); + DateClickHandler(Dialogs::Key chat, QDateTime date); - void setDate(QDate date); + void setDate(QDateTime date); void onClick(ClickContext context) const override; private: Dialogs::Key _chat; - QDate _date; + QDateTime _date; }; @@ -173,6 +173,7 @@ public: QString startToken; std::optional voicechatHash; FullMsgId clickFromMessageId; + QString searchQuery; }; void showPeerByLink(const PeerByLinkInfo &info); @@ -377,7 +378,7 @@ public: void showCalendar( Dialogs::Key chat, - QDate requestedDate); + QDateTime requestedDateTime); void showAddContact(); void showNewGroup();