Compare commits

..

48 Commits

Author SHA1 Message Date
John Preston
6ce3f9bb71 Version 3.5.2.
- Fix a freeze in audio playback on Linux.
- Fix a crash in screen sharing initialization on Linux.
2022-02-08 19:18:14 +03:00
John Preston
a5483a5113 Fix build on Linux. 2022-02-08 19:17:44 +03:00
John Preston
7f5e646db5 Disable PortalPreventAppSuspention for now.
A lot of freezing reports:
- https://bugs.telegram.org/c/14426
- XFCE X11, Ubuntu X11, XCinnamon X11 reports to support
- tdesktop/issues/24032
- tdesktop/issues/24043
2022-02-08 16:47:37 +03:00
John Preston
666251f23e Allow to set custom app icon on macOS. 2022-02-08 00:24:11 +03:00
John Preston
d89d8b09da Preload chats in support switch. 2022-02-08 00:24:11 +03:00
John Preston
0aa20b4479 Decrypt shiftedDcId / protocolDcId in mtp_ logs. 2022-02-07 15:26:26 +03:00
John Preston
8658dba97a Improve mtproto connections logging. 2022-02-07 14:44:00 +03:00
23rd
20c911651f Replaced universal initializers with constructors in Ui::Text::Link. 2022-02-06 18:29:38 +03:00
John Preston
bef20ba4a2 Fix loading libdrm before first use.
Fixes #24022.
2022-02-06 13:50:23 +03:00
John Preston
ee325031a0 Fix build without wayland integration. 2022-02-05 19:20:40 +03:00
John Preston
b57549546d Update lib_ui for the indexed links fixes. 2022-02-05 19:19:54 +03:00
23rd
1e4d278604 Fixed formatting text of pinned messages in service messages. 2022-02-05 18:39:43 +03:00
23rd
80aa596310 Fixed possible incorrect order of links in Ui. 2022-02-05 18:39:43 +03:00
John Preston
4913288061 Fix "Fix chats order" toggle in support mode. 2022-02-05 15:09:18 +03:00
John Preston
1a43cd8a67 Fix the date in the latest changelog entry. 2022-02-05 01:41:41 +03:00
John Preston
bcb0511083 Version 3.5.1: Fix build with MSVC. 2022-02-04 22:56:45 +03:00
John Preston
2dda044dd1 Fix crash in video message without a thumbnail. 2022-02-04 22:56:29 +03:00
John Preston
da18ab3d41 Fix group call settings button position. 2022-02-04 22:56:28 +03:00
John Preston
f26cae8807 Version 3.5.1.
- Keep the screen on while watching a video
or participating in a video chat.
- Save experimental settings between relaunches.
- Bug fixes and other minor improvements.
2022-02-04 22:20:17 +03:00
23rd
641bb01ba2 Added ability to set forward options from ShareBox. 2022-02-04 22:20:17 +03:00
John Preston
02c9b61840 Fix PowerSaveBlocker in case QWindow was destroyed. 2022-02-04 16:44:20 +03:00
Ilya Fedin
99e8d22c51 Move xdg-foreign support to lib_base 2022-02-04 16:31:40 +03:00
John Preston
cd9b3368da Don't request scheduled messages in channels I can't write to. 2022-02-04 09:50:12 +03:00
John Preston
f918c6bb83 Pass QWindow to PowerSaveBlocker. 2022-02-04 09:43:56 +03:00
gasinvein
28dff5ba6d Correct year in changelog for 2022 releases 2022-02-03 11:44:20 +03:00
John Preston
4d978f5b36 Add PowerSaveBlocker implementation for macOS. 2022-02-02 17:07:31 +03:00
John Preston
ef41878815 Fix date tooltip on outgoing messages. 2022-02-02 15:46:55 +03:00
John Preston
6a663932f3 Fix message viewers display in message reactions box. 2022-02-02 15:40:39 +03:00
John Preston
67c538ae8f Add AllowLinuxNvidiaOpenGL option. 2022-02-02 15:33:41 +03:00
John Preston
38137e16a0 Save / restore experimental settings in a json. 2022-02-02 12:05:33 +03:00
John Preston
9c01295521 Create PowerSaveBlocker-s on calls / video / audio. 2022-02-02 12:05:33 +03:00
Ilya Fedin
c7b6db00ca Build WebRTC without PipeWire support in snap
WebRTC no more supports PipeWire 0.2, so it's impossible to build PipeWire support until core22 runtime is released
2022-02-02 09:32:44 +03:00
Ilya Fedin
7f0bdc5d36 Fix desktop environment list deduplication 2022-02-02 09:23:14 +03:00
John Preston
5f6d8f74dd Warn about supergroup clearing history just for me. 2022-02-01 19:41:51 +03:00
John Preston
9086319b99 Update lib_ui for accounts reordering. 2022-02-01 19:41:29 +03:00
23rd
37cd4f51eb Added view-profile-in-chats-list-context-menu option. 2022-02-01 18:48:02 +03:00
23rd
74416568d6 Distributed tabbed-panel-show-on-click option to sections as well. 2022-02-01 18:48:02 +03:00
23rd
a8c3d6c39b Added ability to reorder accounts in MainMenu. 2022-02-01 18:48:02 +03:00
John Preston
5939c2dbfc Don't crash on no-frame with SUCCESS status. 2022-02-01 16:51:51 +03:00
John Preston
4bef1e9f59 Use unreliable video duration if open with audio. 2022-02-01 16:51:51 +03:00
John Preston
18bf48bf90 Fix possible crash in view button click handler. 2022-02-01 16:51:51 +03:00
John Preston
b415b293cf Fix crash from background access to style::icon-s. 2022-02-01 16:51:51 +03:00
John Preston
3a1bb1966d Fix comparator in tgcalls. 2022-02-01 16:51:51 +03:00
Ilya Fedin
d4b686ff65 Update tg_owt 2022-02-01 15:30:02 +03:00
Ilya Fedin
8148974e9f Update cmake_helpers 2022-02-01 14:42:21 +03:00
Ilya Fedin
eaa2573c66 Build ffmpeg without xlib as it was needed only for libva/vdpau 2022-02-01 14:42:21 +03:00
GitHub Action
4d315f8e61 Update User-Agent for DNS to Chrome 97.0.4692.99. 2022-02-01 14:42:05 +03:00
Ilya Fedin
e5981ed22b Move jemalloc initialization code to cmake_helpers 2022-02-01 14:41:54 +03:00
97 changed files with 1246 additions and 438 deletions

View File

@@ -395,6 +395,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_experimental" = "Experimental settings";
"lng_settings_experimental_about" = "Warning! Those are experimental settings. Some may not work. Others may break the app. Any of them may disappear in the next version without a trace. Use at your own risk.";
"lng_settings_experimental_restore" = "Restore default values";
"lng_settings_experimental_irrelevant" = "This option isn't relevant for your system.";
"lng_settings_section_chat_settings" = "Chat Settings";
"lng_settings_replace_emojis" = "Replace emoji";
@@ -734,7 +736,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sure_logout" = "Are you sure you want to log out?";
"lng_settings_need_restart" = "You need to restart for applying some of the new settings. Restart now?";
"lng_settings_restart_now" = "RESTART";
"lng_settings_restart_now" = "Restart";
"lng_settings_restart_later" = "Later";
"lng_sessions_header" = "Current session";
"lng_sessions_other_header" = "Active sessions";
@@ -1913,6 +1916,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_delete_for_me_chat_hint#other" = "This will delete them just for you, not for other participants of the chat.";
"lng_delete_for_me_hint#one" = "This will delete it just for you.";
"lng_delete_for_me_hint#other" = "This will delete them just for you.";
"lng_delete_clear_for_me" = "This will clear history just for you, not for other participants of the chat.";
"lng_edit_auto_delete_settings" = "Edit Auto-Delete Settings";
"lng_enable_auto_delete" = "Enable Auto-Delete";
"lng_selected_unsend_about_user_one" = "You can also delete the message you sent from {user}'s inbox by checking \"Unsend my messages\".";

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="3.5.0.0" />
Version="3.5.2.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,5,0,0
PRODUCTVERSION 3,5,0,0
FILEVERSION 3,5,2,0
PRODUCTVERSION 3,5,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "3.5.0.0"
VALUE "FileVersion", "3.5.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.5.0.0"
VALUE "ProductVersion", "3.5.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,5,0,0
PRODUCTVERSION 3,5,0,0
FILEVERSION 3,5,2,0
PRODUCTVERSION 3,5,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "3.5.0.0"
VALUE "FileVersion", "3.5.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.5.0.0"
VALUE "ProductVersion", "3.5.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -508,7 +508,11 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
state->current.fullReadCount = int(peers.read.size());
state->current.fullReactionsCount = peers.fullReactionsCount;
if (whoReadIds) {
whoReadIds->list = (peers.read.size() > peers.list.size())
const auto reacted = peers.list.size() - ranges::count(
peers.list,
QString(),
&PeerWithReaction::reaction);
whoReadIds->list = (peers.read.size() > reacted)
? std::move(peers.read)
: std::vector<PeerId>();
}

View File

@@ -318,7 +318,7 @@ void ProxyRow::updateFields(View &&view) {
TextWithEntities()
.append(_view.type)
.append(' ')
.append(Ui::Text::Link(endpoint, {})),
.append(Ui::Text::Link(endpoint, QString())),
Ui::ItemTextDefaultOptions());
const auto state = _view.state;

View File

@@ -156,6 +156,12 @@ void DeleteMessagesBox::prepare() {
: tr::lng_box_leave();
}, _revoke->lifetime());
}
} else if (canDelete
&& _wipeHistoryJustClear
&& (peer->isMegagroup() || peer->isChat())) {
appendDetails({
tr::lng_delete_clear_for_me(tr::now)
});
}
} else if (_moderateFrom) {
Assert(_moderateInChannel != nullptr);

View File

@@ -1116,7 +1116,8 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) {
auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&result,
TextWithTags &&comment,
Api::SendOptions options) {
Api::SendOptions options,
Data::ForwardOptions) {
if (*sending || result.empty()) {
return;
}

View File

@@ -15,11 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_account.h"
#include "ui/boxes/confirm_box.h"
#include "apiwrap.h"
#include "ui/chat/forward_options_box.h"
#include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/multi_select.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/text/text_options.h"
#include "chat_helpers/message_field.h"
@@ -40,6 +44,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_menu_icons.h"
#include "styles/style_media_player.h"
namespace {
class ForwardOptionItem final : public Ui::Menu::Action {
public:
using Ui::Menu::Action::Action;
void init(bool checked) {
enableMouseSelecting();
AbstractButton::setDisabled(true);
_checkView = std::make_unique<Ui::CheckView>(st::defaultCheck, false);
_checkView->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
setIcon(checked ? &st::mediaPlayerMenuCheck : nullptr);
}, lifetime());
_checkView->setChecked(checked, anim::type::normal);
AbstractButton::clicks(
) | rpl::start_with_next([=] {
_checkView->setChecked(
!_checkView->checked(),
anim::type::normal);
}, lifetime());
}
not_null<Ui::CheckView*> checkView() const {
return _checkView.get();
}
private:
std::unique_ptr<Ui::CheckView> _checkView;
};
} // namespace
class ShareBox::Inner final : public Ui::RpWidget {
public:
@@ -442,17 +484,68 @@ SendMenu::Type ShareBox::sendMenuType() const {
: SendMenu::Type::Scheduled;
}
void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
if (_menu) {
_menu = nullptr;
return;
}
_menu.emplace(parent, st::popupMenuWithIcons);
if (_descriptor.forwardOptions.show) {
auto createView = [&](rpl::producer<QString> &&text, bool checked) {
auto item = base::make_unique_q<ForwardOptionItem>(
_menu->menu(),
st::popupMenuWithIcons.menu,
new QAction(QString(), _menu->menu()),
nullptr,
nullptr);
std::move(
text
) | rpl::start_with_next([action = item->action()](QString text) {
action->setText(text);
}, item->lifetime());
item->init(checked);
const auto view = item->checkView();
_menu->addAction(std::move(item));
return view;
};
Ui::FillForwardOptions(
std::move(createView),
_descriptor.forwardOptions.messagesCount,
_forwardOptions,
[=](Ui::ForwardOptions value) { _forwardOptions = value; },
_menu->lifetime());
_menu->addSeparator();
}
const auto result = SendMenu::FillSendMenu(
_menu.get(),
sendMenuType(),
[=] { submitSilent(); },
[=] { submitScheduled(); });
const auto success = (result == SendMenu::FillMenuResult::Success);
if (_descriptor.forwardOptions.show || success) {
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomRight);
_menu->popup(QCursor::pos());
}
}
void ShareBox::createButtons() {
clearButtons();
if (_hasSelected) {
const auto send = addButton(tr::lng_share_confirm(), [=] {
submit({});
});
SendMenu::SetupMenuAndShortcuts(
send,
[=] { return sendMenuType(); },
[=] { submitSilent(); },
[=] { submitScheduled(); });
_forwardOptions.hasCaptions = _descriptor.forwardOptions.hasCaptions;
send->setAcceptBoth();
send->clicks(
) | rpl::start_with_next([=](Qt::MouseButton button) {
if (button == Qt::RightButton) {
showMenu(send);
}
}, send->lifetime());
} else if (_descriptor.copyCallback) {
addButton(_copyLinkText.value(), [=] { copyLink(); });
}
@@ -488,10 +581,17 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) {
void ShareBox::submit(Api::SendOptions options) {
if (const auto onstack = _descriptor.submitCallback) {
const auto forwardOptions = (_forwardOptions.hasCaptions
&& _forwardOptions.dropCaptions)
? Data::ForwardOptions::NoNamesAndCaptions
: _forwardOptions.dropNames
? Data::ForwardOptions::NoSenderNames
: Data::ForwardOptions::PreserveInfo;
onstack(
_inner->selected(),
_comment->entity()->getTextWithAppliedMarkdown(),
options);
options,
forwardOptions);
}
}

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "base/observer.h"
#include "base/timer.h"
#include "ui/chat/forward_options_box.h"
#include "ui/effects/animations.h"
#include "ui/effects/round_checkbox.h"
#include "mtproto/sender.h"
@@ -41,12 +42,17 @@ class Row;
class IndexedList;
} // namespace Dialogs
namespace Data {
enum class ForwardOptions;
} // namespace Data
namespace Ui {
class MultiSelect;
class InputField;
struct ScrollToRequest;
template <typename Widget>
class SlideWrap;
class PopupMenu;
} // namespace Ui
QString AppendShareGameScoreUrl(
@@ -63,7 +69,8 @@ public:
using SubmitCallback = Fn<void(
std::vector<not_null<PeerData*>>&&,
TextWithTags&&,
Api::SendOptions)>;
Api::SendOptions,
Data::ForwardOptions option)>;
using FilterCallback = Fn<bool(PeerData*)>;
struct Descriptor {
@@ -79,6 +86,11 @@ public:
const style::MultiSelect *stMultiSelect = nullptr;
const style::InputField *stComment = nullptr;
const style::PeerList *st = nullptr;
struct {
int messagesCount = 0;
bool show = false;
bool hasCaptions = false;
} forwardOptions;
};
ShareBox(QWidget*, Descriptor &&descriptor);
@@ -119,6 +131,8 @@ private:
mtpRequestId requestId);
void peopleFail(const MTP::Error &error, mtpRequestId requestId);
void showMenu(not_null<Ui::RpWidget*> parent);
Descriptor _descriptor;
MTP::Sender _api;
@@ -126,6 +140,9 @@ private:
object_ptr<Ui::SlideWrap<Ui::InputField>> _comment;
object_ptr<Ui::RpWidget> _bottomWidget;
base::unique_qptr<Ui::PopupMenu> _menu;
Ui::ForwardOptions _forwardOptions;
class Inner;
QPointer<Inner> _inner;

View File

@@ -162,14 +162,13 @@ Call::Call(
, _user(user)
, _api(&_user->session().mtp())
, _type(type)
, _discardByTimeoutTimer([=] { hangup(); })
, _videoIncoming(
std::make_unique<Webrtc::VideoTrack>(
StartVideoState(video)))
, _videoOutgoing(
std::make_unique<Webrtc::VideoTrack>(
StartVideoState(video))) {
_discardByTimeoutTimer.setCallback([=] { hangup(); });
if (_type == Type::Outgoing) {
setState(State::Requesting);
} else {

View File

@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "platform/platform_specific.h"
#include "base/platform/base_platform_info.h"
#include "base/power_save_blocker.h"
#include "window/main_window.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h"
@@ -357,6 +358,7 @@ void Panel::reinitWithCall(Call *call) {
if (!_call) {
_incoming = nullptr;
_outgoingVideoBubble = nullptr;
_powerSaveBlocker = nullptr;
return;
}
@@ -497,6 +499,11 @@ void Panel::reinitWithCall(Call *call) {
_camera->raise();
_mute->raise();
_powerSaveBlocker = std::make_unique<base::PowerSaveBlocker>(
base::PowerSaveBlockType::PreventDisplaySleep,
u"Video call is active"_q,
window()->windowHandle());
_incoming->widget()->lower();
}
@@ -804,6 +811,10 @@ void Panel::stateChanged(State state) {
&& (state != State::EndedByOtherDevice)
&& (state != State::FailedHangingUp)
&& (state != State::Failed)) {
if (state == State::Busy) {
_powerSaveBlocker = nullptr;
}
auto toggleButton = [&](auto &&button, bool visible) {
button->toggle(
visible,

View File

@@ -19,6 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class Image;
namespace base {
class PowerSaveBlocker;
} // namespace base
namespace Data {
class PhotoMedia;
class CloudImageView;
@@ -130,6 +134,8 @@ private:
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
#endif // !Q_OS_MAC
std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
QSize _incomingFrameSize;
rpl::lifetime _callLifetime;

View File

@@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "base/qt_signal_producer.h"
#include "base/timer_rpl.h"
#include "base/power_save_blocker.h"
#include "apiwrap.h" // api().kick.
#include "api/api_chat_participants.h" // api().kick.
#include "webrtc/webrtc_video_track.h"
@@ -92,6 +93,10 @@ Panel::Panel(not_null<GroupCall*> call)
window(),
st::groupCallTitle))
#endif // !Q_OS_MAC
, _powerSaveBlocker(std::make_unique<base::PowerSaveBlocker>(
base::PowerSaveBlockType::PreventDisplaySleep,
u"Video chat is active"_q,
window()->windowHandle()))
, _viewport(
std::make_unique<Viewport>(widget(), PanelMode::Wide, _window.backend()))
, _mute(std::make_unique<Ui::CallMuteButton>(
@@ -302,9 +307,9 @@ void Panel::initWidget() {
updateControlsGeometry();
}
// title geometry depends on _controls->controls.geometry,
// some geometries depends on _controls->controls.geometry,
// which is not updated here yet.
crl::on_main(widget(), [=] { refreshTitle(); });
crl::on_main(widget(), [=] { updateControlsGeometry(); });
}, lifetime());
}

View File

@@ -22,6 +22,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class Image;
namespace base {
class PowerSaveBlocker;
} // namespace base
namespace Data {
class PhotoMedia;
class CloudImageView;
@@ -198,6 +202,8 @@ private:
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
#endif // !Q_OS_MAC
const std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
rpl::lifetime _callLifetime;
object_ptr<Ui::FlatLabel> _title = { nullptr };

View File

@@ -129,7 +129,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&result,
TextWithTags &&comment,
Api::SendOptions options) {
Api::SendOptions options,
Data::ForwardOptions) {
if (*sending || result.empty()) {
return;
}

View File

@@ -113,7 +113,8 @@ QByteArray Settings::serialize() const {
+ sizeof(qint64)
+ sizeof(qint32) * 2
+ Serialize::bytearraySize(windowPosition)
+ sizeof(qint32);
+ sizeof(qint32) * 2
+ (_accountsOrder.size() * sizeof(quint64));
for (const auto &[id, rating] : recentEmojiPreloadData) {
size += Serialize::stringSize(id) + sizeof(quint16);
}
@@ -229,6 +230,12 @@ QByteArray Settings::serialize() const {
<< qint32(_playerRepeatMode.current())
<< qint32(_playerOrderMode.current())
<< qint32(_macWarnBeforeQuit ? 1 : 0);
stream
<< qint32(_accountsOrder.size());
for (const auto &id : _accountsOrder) {
stream << quint64(id);
}
}
return result;
}
@@ -316,6 +323,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 playerRepeatMode = static_cast<qint32>(_playerRepeatMode.current());
qint32 playerOrderMode = static_cast<qint32>(_playerOrderMode.current());
qint32 macWarnBeforeQuit = _macWarnBeforeQuit ? 1 : 0;
qint32 accountsOrderCount = 0;
std::vector<uint64> accountsOrder;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -487,6 +496,16 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> macWarnBeforeQuit;
}
if (!stream.atEnd()) {
stream >> accountsOrderCount;
if (stream.status() == QDataStream::Ok) {
for (auto i = 0; i != accountsOrderCount; ++i) {
quint64 sessionUniqueId;
stream >> sessionUniqueId;
accountsOrder.emplace_back(sessionUniqueId);
}
}
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -629,6 +648,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_photoEditorBrush = photoEditorBrush;
_closeToTaskbar = (closeToTaskbar == 1);
_customDeviceModel = customDeviceModel;
_accountsOrder = accountsOrder;
const auto uncheckedPlayerRepeatMode = static_cast<Media::Player::RepeatMode>(playerRepeatMode);
switch (uncheckedPlayerRepeatMode) {
case Media::Player::RepeatMode::None:
@@ -922,6 +942,8 @@ void Settings::resetOnLastLogout() {
_emojiVariants.clear();
_workMode = WorkMode::WindowAndTray;
_accountsOrder.clear();
}
bool Settings::ThirdColumnByDefault() {

View File

@@ -656,6 +656,12 @@ public:
[[nodiscard]] rpl::producer<Media::Player::OrderMode> playerOrderModeChanges() const {
return _playerOrderMode.changes();
}
[[nodiscard]] std::vector<uint64> accountsOrder() const {
return _accountsOrder;
}
void setAccountsOrder(const std::vector<uint64> &order) {
_accountsOrder = order;
}
void setMacWarnBeforeQuit(bool value) {
_macWarnBeforeQuit = value;
@@ -768,6 +774,7 @@ private:
rpl::variable<Media::Player::RepeatMode> _playerRepeatMode;
rpl::variable<Media::Player::OrderMode> _playerOrderMode;
bool _macWarnBeforeQuit = true;
std::vector<uint64> _accountsOrder;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/update_checker.h"
#include "core/sandbox.h"
#include "base/concurrent_timer.h"
#include "base/options.h"
#include <QtCore/QLoggingCategory>
@@ -320,6 +321,7 @@ int Launcher::exec() {
// Must be started before Platform is started.
Logs::start(this);
base::options::init(cWorkingDir() + "tdata/experimental_options.json");
if (Logs::DebugEnabled()) {
const auto openalLogPath = QDir::toNativeSeparators(

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 3005000;
constexpr auto AppVersionStr = "3.5";
constexpr auto AppVersion = 3005002;
constexpr auto AppVersionStr = "3.5.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -375,6 +375,10 @@ Data::MessagesSlice ScheduledMessages::list(not_null<History*> history) {
}
void ScheduledMessages::request(not_null<History*> history) {
const auto peer = history->peer;
if (peer->isBroadcast() && !peer->canWrite()) {
return;
}
auto &request = _requests[history];
if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
return;
@@ -384,9 +388,7 @@ void ScheduledMessages::request(not_null<History*> history) {
? countListHash(i->second)
: uint64(0);
request.requestId = _session->api().request(
MTPmessages_GetScheduledHistory(
history->peer->input,
MTP_long(hash))
MTPmessages_GetScheduledHistory(peer->input, MTP_long(hash))
).done([=](const MTPmessages_Messages &result) {
parse(history, result);
}).fail([=] {

View File

@@ -3025,6 +3025,24 @@ void InnerWidget::updateRowCornerStatusShown(
}
}
RowDescriptor InnerWidget::resolveChatNext(RowDescriptor from) const {
const auto row = from.key ? from : _controller->activeChatEntryCurrent();
return row.key
? computeJump(
chatListEntryAfter(row),
JumpSkip::NextOrEnd)
: row;
}
RowDescriptor InnerWidget::resolveChatPrevious(RowDescriptor from) const {
const auto row = from.key ? from : _controller->activeChatEntryCurrent();
return row.key
? computeJump(
chatListEntryBefore(row),
JumpSkip::PreviousOrBegin)
: row;
}
void InnerWidget::setupShortcuts() {
Shortcuts::Requests(
) | rpl::filter([=] {
@@ -3197,7 +3215,7 @@ void InnerWidget::setupShortcuts() {
RowDescriptor InnerWidget::computeJump(
const RowDescriptor &to,
JumpSkip skip) {
JumpSkip skip) const {
auto result = to;
if (result.key) {
const auto down = (skip == JumpSkip::NextOrEnd)

View File

@@ -125,6 +125,9 @@ public:
[[nodiscard]] rpl::producer<ChosenRow> chosenRow() const;
[[nodiscard]] rpl::producer<> updated() const;
[[nodiscard]] RowDescriptor resolveChatNext(RowDescriptor from = {}) const;
[[nodiscard]] RowDescriptor resolveChatPrevious(RowDescriptor from = {}) const;
~InnerWidget();
public Q_SLOTS:
@@ -238,7 +241,7 @@ private:
void setupShortcuts();
RowDescriptor computeJump(
const RowDescriptor &to,
JumpSkip skip);
JumpSkip skip) const;
bool jumpToDialogRow(RowDescriptor to);
RowDescriptor chatListEntryBefore(const RowDescriptor &which) const;

View File

@@ -1685,6 +1685,14 @@ void Widget::updateForwardBar() {
update();
}
RowDescriptor Widget::resolveChatNext(RowDescriptor from) const {
return _inner->resolveChatNext(from);
}
RowDescriptor Widget::resolveChatPrevious(RowDescriptor from) const {
return _inner->resolveChatPrevious(from);
}
void Widget::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
if (_openedFolder) {

View File

@@ -91,6 +91,9 @@ public:
[[nodiscard]] rpl::producer<> closeForwardBarRequests() const;
[[nodiscard]] RowDescriptor resolveChatNext(RowDescriptor from = {}) const;
[[nodiscard]] RowDescriptor resolveChatPrevious(RowDescriptor from = {}) const;
// Float player interface.
bool floatPlayerHandleWheelEvent(QEvent *e) override;
QRect floatPlayerAvailableRect() override;

View File

@@ -675,7 +675,7 @@ void GenerateItems(
const auto fromName = from->name;
const auto fromLink = from->createOpenLink();
const auto fromLinkText = Ui::Text::Link(fromName, {});
const auto fromLinkText = Ui::Text::Link(fromName, QString());
const auto addSimpleServiceMessage = [&](
const TextWithEntities &text,
@@ -987,7 +987,7 @@ void GenerateItems(
lt_sticker_set,
Ui::Text::Link(
tr::lng_admin_log_changed_stickers_set(tr::now),
{}),
QString()),
Ui::Text::WithEntities);
const auto setLink = std::make_shared<LambdaClickHandler>([=](
ClickContext context) {
@@ -1072,7 +1072,7 @@ void GenerateItems(
lt_from,
fromLinkText,
lt_chat,
Ui::Text::Link(now->name, {}),
Ui::Text::Link(now->name, QString()),
Ui::Text::WithEntities);
const auto chatLink = std::make_shared<LambdaClickHandler>([=] {
Ui::showPeerHistory(now, ShowAtUnreadMsgId);
@@ -1196,7 +1196,7 @@ void GenerateItems(
const auto participantPeerLink = participantPeer->createOpenLink();
const auto participantPeerLinkText = Ui::Text::Link(
participantPeer->name,
{});
QString());
const auto text = (broadcast
? tr::lng_admin_log_muted_participant_channel
: tr::lng_admin_log_muted_participant)(
@@ -1215,7 +1215,7 @@ void GenerateItems(
const auto participantPeerLink = participantPeer->createOpenLink();
const auto participantPeerLinkText = Ui::Text::Link(
participantPeer->name,
{});
QString());
const auto text = (broadcast
? tr::lng_admin_log_unmuted_participant_channel
: tr::lng_admin_log_unmuted_participant)(
@@ -1319,7 +1319,7 @@ void GenerateItems(
const auto participantPeerLink = participantPeer->createOpenLink();
const auto participantPeerLinkText = Ui::Text::Link(
participantPeer->name,
{});
QString());
const auto volume = data.vparticipant().match([&](
const MTPDgroupCallParticipant &data) {
return data.vvolume().value_or(10000);
@@ -1395,7 +1395,7 @@ void GenerateItems(
lt_link,
linkText,
lt_user,
Ui::Text::Link(user->name, {}),
Ui::Text::Link(user->name, QString()),
Ui::Text::WithEntities),
data.vinvite(),
user->createOpenLink());

View File

@@ -131,17 +131,17 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {
phrase = tr::lng_forwarded_channel_via(
tr::now,
lt_channel,
Ui::Text::Link(phrase.text, QString()), // Link 1.
Ui::Text::Link(phrase.text, 1), // Link 1.
lt_inline_bot,
Ui::Text::Link('@' + via->bot->username, {}), // Link 2.
Ui::Text::Link('@' + via->bot->username, 2), // Link 2.
Ui::Text::WithEntities);
} else {
phrase = tr::lng_forwarded_via(
tr::now,
lt_user,
Ui::Text::Link(phrase.text, QString()), // Link 1.
Ui::Text::Link(phrase.text, 1), // Link 1.
lt_inline_bot,
Ui::Text::Link('@' + via->bot->username, {}), // Link 2.
Ui::Text::Link('@' + via->bot->username, 2), // Link 2.
Ui::Text::WithEntities);
}
} else {

View File

@@ -209,6 +209,18 @@ void FastShareMessage(not_null<HistoryItem*> item) {
&& (item->media()->game() != nullptr);
const auto canCopyLink = item->hasDirectLink() || isGame;
const auto items = owner->idsToItems(data->msgIds);
const auto hasCaptions = ranges::any_of(items, [](auto item) {
return item->media()
&& !item->originalText().text.isEmpty()
&& item->media()->allowsEditCaption();
});
const auto hasOnlyForcedForwardedInfo = hasCaptions
? false
: ranges::all_of(items, [](auto item) {
return item->media() && item->media()->forceForwardedInfo();
});
auto copyCallback = [=]() {
if (const auto item = owner->message(data->msgIds[0])) {
if (item->hasDirectLink()) {
@@ -235,7 +247,8 @@ void FastShareMessage(not_null<HistoryItem*> item) {
auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&result,
TextWithTags &&comment,
Api::SendOptions options) {
Api::SendOptions options,
Data::ForwardOptions forwardOptions) {
if (!data->requests.empty()) {
return; // Share clicked already.
}
@@ -274,6 +287,12 @@ void FastShareMessage(not_null<HistoryItem*> item) {
| MTPmessages_ForwardMessages::Flag::f_with_my_score
| (options.scheduled
? MTPmessages_ForwardMessages::Flag::f_schedule_date
: MTPmessages_ForwardMessages::Flag(0))
| ((forwardOptions != Data::ForwardOptions::PreserveInfo)
? MTPmessages_ForwardMessages::Flag::f_drop_author
: MTPmessages_ForwardMessages::Flag(0))
| ((forwardOptions == Data::ForwardOptions::NoNamesAndCaptions)
? MTPmessages_ForwardMessages::Flag::f_drop_media_captions
: MTPmessages_ForwardMessages::Flag(0));
auto msgIds = QVector<MTPint>();
msgIds.reserve(data->msgIds.size());
@@ -346,7 +365,13 @@ void FastShareMessage(not_null<HistoryItem*> item) {
.copyCallback = std::move(copyLinkCallback),
.submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback),
.navigation = App::wnd()->sessionController() }));
.navigation = App::wnd()->sessionController(),
.forwardOptions = {
.messagesCount = int(data->msgIds.size()),
.show = !hasOnlyForcedForwardedInfo,
.hasCaptions = hasCaptions,
},
}));
}
void RequestDependentMessageData(

View File

@@ -125,7 +125,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
lt_from,
fromLinkText(), // Link 1.
lt_user,
Ui::Text::Link(u->name, {}), // Link 2.
Ui::Text::Link(u->name, 2), // Link 2.
Ui::Text::WithEntities);
}
} else if (users.isEmpty()) {
@@ -143,7 +143,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto user = history()->owner().user(users[i].v);
result.links.push_back(user->createOpenLink());
auto linkText = Ui::Text::Link(user->name, {});
auto linkText = Ui::Text::Link(user->name, QString());
if (i == 0) {
result.text = linkText;
} else if (i + 1 == l) {
@@ -253,7 +253,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
lt_from,
fromLinkText(), // Link 1.
lt_user,
Ui::Text::Link(user->name, {}), // Link 2.
Ui::Text::Link(user->name, 2), // Link 2.
Ui::Text::WithEntities);
}
return result;
@@ -364,7 +364,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
result.text = tr::lng_action_secure_values_sent(
tr::now,
lt_user,
Ui::Text::Link(history()->peer->name, {}), // Link 1.
Ui::Text::Link(history()->peer->name, QString()), // Link 1.
lt_documents,
{ .text = documents.join(", ") },
Ui::Text::WithEntities);
@@ -412,14 +412,14 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
lt_distance,
{ .text = distance },
lt_user,
Ui::Text::Link(toPeer->name, {}), // Link 1.
Ui::Text::Link(toPeer->name, QString()), // Link 1.
Ui::Text::WithEntities);
} else if (toId == selfId) {
result.links.push_back(fromPeer->createOpenLink());
return tr::lng_action_proximity_reached_you(
tr::now,
lt_from,
Ui::Text::Link(fromPeer->name, {}), // Link 1.
Ui::Text::Link(fromPeer->name, QString()), // Link 1.
lt_distance,
{ .text = distance },
Ui::Text::WithEntities);
@@ -429,11 +429,11 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
return tr::lng_action_proximity_reached(
tr::now,
lt_from,
Ui::Text::Link(fromPeer->name, {}), // Link 1.
Ui::Text::Link(fromPeer->name, 1), // Link 1.
lt_distance,
{ .text = distance },
lt_user,
Ui::Text::Link(toPeer->name, {}), // Link 2.
Ui::Text::Link(toPeer->name, 2), // Link 2.
Ui::Text::WithEntities);
}
}();
@@ -796,10 +796,11 @@ HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
Ui::Text::WithEntities);
auto result = PreparedText{};
result.links.push_back(fromLink());
auto linkIndex = 1;
if (linkCallId) {
const auto peer = history()->peer;
result.links.push_back(GroupCallClickHandler(peer, linkCallId));
chatText = Ui::Text::Link(chatText.text, {});
chatText = Ui::Text::Link(chatText.text, ++linkIndex);
}
if (users.size() == 1) {
auto user = owner->user(users[0].v);
@@ -809,7 +810,7 @@ HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
lt_from,
fromLinkText(), // Link 1.
lt_user,
Ui::Text::Link(user->name, {}), // Link N.
Ui::Text::Link(user->name, ++linkIndex), // Link N.
lt_chat,
chatText,
Ui::Text::WithEntities);
@@ -828,7 +829,7 @@ HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
auto user = owner->user(users[i].v);
result.links.push_back(user->createOpenLink());
auto linkText = Ui::Text::Link(user->name, {});
auto linkText = Ui::Text::Link(user->name, ++linkIndex);
if (i == 0) {
result.text = linkText;
} else if (i + 1 == l) {
@@ -902,8 +903,11 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
Ui::kQEllipsis);
}
original = Ui::Text::Wrapped(
std::move(original),
EntityType::CustomUrl);
Ui::Text::Filtered(
std::move(original),
{ EntityType::Spoiler, EntityType::StrikeOut }),
EntityType::CustomUrl,
Ui::Text::Link({}, 2).entities.front().data());
result.text = tr::lng_action_pinned_message(
tr::now,
lt_from,
@@ -917,7 +921,7 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
lt_from,
fromLinkText(), // Link 1.
lt_media,
Ui::Text::Link(mediaText, {}), // Link 2.
Ui::Text::Link(mediaText, 2), // Link 2.
Ui::Text::WithEntities);
}
} else if (pinned && pinned->msgId) {
@@ -928,7 +932,7 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
lt_from,
fromLinkText(), // Link 1.
lt_media,
Ui::Text::Link(tr::lng_contacts_loading(tr::now), {}), // Link 2.
Ui::Text::Link(tr::lng_contacts_loading(tr::now), 2), // Link 2.
Ui::Text::WithEntities);
} else {
result.links.push_back(fromLink());
@@ -960,7 +964,7 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
column,
gamescore->msg->fullId()));
auto titleText = game->title;
return Ui::Text::Link(titleText, {});
return Ui::Text::Link(titleText, QString());
}
}
return tr::lng_deleted_message(tr::now, Ui::Text::WithEntities);
@@ -1023,7 +1027,7 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
if (payment->msg) {
if (const auto media = payment->msg->media()) {
if (const auto invoice = media->invoice()) {
return Ui::Text::Link(invoice->title, {});
return Ui::Text::Link(invoice->title, QString());
}
}
}
@@ -1207,7 +1211,7 @@ std::unique_ptr<HistoryView::Element> HistoryService::createView(
}
TextWithEntities HistoryService::fromLinkText() const {
return Ui::Text::Link(_from->name, {});
return Ui::Text::Link(_from->name, 1);
}
ClickHandlerPtr HistoryService::fromLink() const {
@@ -1515,7 +1519,7 @@ HistoryService::PreparedText GenerateJoinedText(
: tr::lng_action_add_you)(
tr::now,
lt_from,
Ui::Text::Link(inviter->name, {}),
Ui::Text::Link(inviter->name, QString()),
Ui::Text::WithEntities);
return result;
} else if (history->peer->isMegagroup()) {
@@ -1530,7 +1534,7 @@ HistoryService::PreparedText GenerateJoinedText(
result.text = tr::lng_action_user_joined(
tr::now,
lt_from,
Ui::Text::Link(self->name, {}),
Ui::Text::Link(self->name, QString()),
Ui::Text::WithEntities);
return result;
}

View File

@@ -818,6 +818,13 @@ HistoryWidget::HistoryWidget(
}
}, lifetime());
if (session().supportMode()) {
session().data().chatListEntryRefreshes(
) | rpl::start_with_next([=] {
crl::on_main(this, [=] { checkSupportPreload(true); });
}, lifetime());
}
setupScheduledToggle();
setupSendAsToggle();
orderWidgets();
@@ -2299,9 +2306,11 @@ void HistoryWidget::setHistory(History *history) {
history->forceFullResize();
}
};
if (_history) {
unregisterDraftSources();
clearAllLoadRequests();
clearSupportPreloadRequest();
const auto wasHistory = base::take(_history);
const auto wasMigrated = base::take(_migrated);
unloadHeavyViewParts(wasHistory);
@@ -2371,6 +2380,16 @@ void HistoryWidget::clearDelayedShowAtRequest() {
}
}
void HistoryWidget::clearSupportPreloadRequest() {
Expects(_history != nullptr);
if (_supportPreloadRequest) {
auto &histories = _history->owner().histories();
histories.cancelRequest(_supportPreloadRequest);
_supportPreloadRequest = 0;
}
}
void HistoryWidget::clearAllLoadRequests() {
Expects(_history != nullptr);
@@ -2990,6 +3009,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages
setMsgId(_delayedShowAtMsgId);
historyLoaded();
}
if (session().supportMode()) {
crl::on_main(this, [=] { checkSupportPreload(); });
}
}
void HistoryWidget::historyLoaded() {
@@ -3337,6 +3359,88 @@ void HistoryWidget::preloadHistoryByScroll() {
if (scrollTop <= kPreloadHeightsCount * scrollHeight) {
loadMessages();
}
if (session().supportMode()) {
crl::on_main(this, [=] { checkSupportPreload(); });
}
}
void HistoryWidget::checkSupportPreload(bool force) {
if (!_history
|| _firstLoadRequest
|| _preloadRequest
|| _preloadDownRequest
|| (_supportPreloadRequest && !force)
|| controller()->activeChatEntryCurrent().key.history() != _history) {
return;
}
const auto setting = session().settings().supportSwitch();
const auto command = Support::GetSwitchCommand(setting);
const auto descriptor = !command
? Dialogs::RowDescriptor()
: (*command == Shortcuts::Command::ChatNext)
? controller()->resolveChatNext()
: controller()->resolveChatPrevious();
auto history = descriptor.key.history();
if (!history || _supportPreloadHistory == history) {
return;
}
clearSupportPreloadRequest();
_supportPreloadHistory = history;
auto offsetId = MsgId();
auto offset = 0;
auto loadCount = kMessagesPerPage;
if (const auto around = history->loadAroundId()) {
history->getReadyFor(ShowAtUnreadMsgId);
offset = -loadCount / 2;
offsetId = around;
}
const auto offsetDate = 0;
const auto maxId = 0;
const auto minId = 0;
const auto historyHash = uint64(0);
const auto type = Data::Histories::RequestType::History;
auto &histories = history->owner().histories();
_supportPreloadRequest = histories.sendRequest(history, type, [=](Fn<void()> finish) {
return history->session().api().request(MTPmessages_GetHistory(
history->peer->input,
MTP_int(offsetId),
MTP_int(offsetDate),
MTP_int(offset),
MTP_int(loadCount),
MTP_int(maxId),
MTP_int(minId),
MTP_long(historyHash)
)).done([=](const MTPmessages_Messages &result) {
if (const auto around = history->loadAroundId()) {
if (around != offsetId) {
_supportPreloadRequest = 0;
_supportPreloadHistory = nullptr;
crl::on_main(this, [=] { checkSupportPreload(); });
return;
}
history->clear(History::ClearType::Unload);
history->getReadyFor(ShowAtUnreadMsgId);
} else if (offsetId) {
_supportPreloadRequest = 0;
_supportPreloadHistory = nullptr;
crl::on_main(this, [=] { checkSupportPreload(); });
return;
} else {
history->clear(History::ClearType::Unload);
history->getReadyFor(ShowAtTheEndMsgId);
}
result.match([](const MTPDmessages_messagesNotModified&) {
}, [&](const auto &data) {
history->owner().processUsers(data.vusers());
history->owner().processChats(data.vchats());
history->addOlderSlice(data.vmessages().v);
});
finish();
}).fail([=](const MTP::Error &error) {
finish();
}).send();
});
}
void HistoryWidget::checkReplyReturns() {
@@ -7534,10 +7638,7 @@ HistoryWidget::~HistoryWidget() {
// Saving a draft on account switching.
saveFieldToHistoryLocalDraft();
session().api().saveDraftToCloudDelayed(_history);
clearAllLoadRequests();
setHistory(nullptr);
unregisterDraftSources();
}
setTabbedPanel(nullptr);
}

View File

@@ -234,6 +234,7 @@ public:
Ui::ReportReason reason,
Fn<void(MessageIdsList)> callback);
void clearAllLoadRequests();
void clearSupportPreloadRequest();
void clearDelayedShowAtRequest();
void clearDelayedShowAt();
void saveFieldToHistoryLocalDraft();
@@ -598,6 +599,7 @@ private:
bool readyToForward() const;
bool hasSilentToggle() const;
void checkSupportPreload(bool force = false);
void handleSupportSwitch(not_null<History*> updated);
void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result);
@@ -685,6 +687,9 @@ private:
MsgId _delayedShowAtMsgId = -1;
int _delayedShowAtRequest = 0; // Not real mtpRequestId.
History *_supportPreloadHistory = nullptr;
int _supportPreloadRequest = 0; // Not real mtpRequestId.
object_ptr<HistoryView::TopBarWidget> _topBar;
object_ptr<Ui::ContinuousScroll> _scroll;
QPointer<HistoryInner> _list;

View File

@@ -1561,7 +1561,11 @@ void ComposeControls::initTabbedSelector() {
}
_tabbedSelectorToggle->addClickHandler([=] {
toggleTabbedSelectorMode();
if (_tabbedPanel && _tabbedPanel->isHidden()) {
_tabbedPanel->showAnimated();
} else {
toggleTabbedSelectorMode();
}
});
const auto selector = _window->tabbedSelector();

View File

@@ -115,10 +115,15 @@ TextState BottomInfo::textState(
result.link = link;
return result;
}
const auto textWidth = _authorEditedDate.maxWidth();
auto withTicksWidth = textWidth;
if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
withTicksWidth += st::historySendStateSpace;
}
const auto inTime = QRect(
width() - _dateWidth,
width() - withTicksWidth,
0,
_dateWidth,
withTicksWidth,
st::msgDateFont->height
).contains(position);
if (inTime) {
@@ -412,7 +417,6 @@ void BottomInfo::layoutDateText() {
const auto author = _data.author;
const auto prefix = !author.isEmpty() ? qsl(", ") : QString();
const auto date = edited + _data.date.toString(cTimeFormat());
_dateWidth = st::msgDateFont->width(date);
const auto afterAuthor = prefix + date;
const auto afterAuthorWidth = st::msgDateFont->width(afterAuthor);
const auto authorWidth = st::msgDateFont->width(author);
@@ -424,7 +428,11 @@ void BottomInfo::layoutDateText() {
: author;
const auto full = (_data.flags & Data::Flag::Sponsored)
? tr::lng_sponsored(tr::now)
: name.isEmpty() ? date : (name + afterAuthor);
: (_data.flags & Data::Flag::Imported)
? (date + ' ' + tr::lng_imported(tr::now))
: name.isEmpty()
? date
: (name + afterAuthor);
_authorEditedDate.setText(
st::msgDateTextStyle,
full,
@@ -605,6 +613,10 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
if (item->isSending() || item->hasFailed()) {
result.flags |= Flag::Sending;
}
const auto forwarded = item->Get<HistoryMessageForwarded>();
if (forwarded && forwarded->imported) {
result.flags |= Flag::Imported;
}
// We don't want to pass and update it in Date for now.
//if (item->unread()) {
// result.flags |= Flag::Unread;

View File

@@ -40,6 +40,7 @@ public:
RepliesContext = 0x08,
Sponsored = 0x10,
Pinned = 0x20,
Imported = 0x40,
//Unread, // We don't want to pass and update it in Date for now.
};
friend inline constexpr bool is_flag_type(Flag) { return true; };
@@ -127,7 +128,6 @@ private:
std::vector<Reaction> _reactions;
mutable ClickHandlerPtr _revokeLink;
int _reactionsMaxWidth = 0;
int _dateWidth = 0;
bool _authorElided = false;
};

View File

@@ -73,6 +73,19 @@ inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
: QString());
}
[[nodiscard]] ClickHandlerPtr MakeWebPageClickHandler(
not_null<Data::Media*> media) {
Expects(media->webpage() != nullptr);
const auto url = media->webpage()->url;
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) {
HiddenUrlClickHandler::Open(url, context.other);
}
});
}
} // namespace
struct ViewButton::Inner {
@@ -146,16 +159,7 @@ ViewButton::Inner::Inner(
not_null<Data::Media*> media,
Fn<void()> updateCallback)
: margins(st::historyViewButtonMargins)
, link(std::make_shared<LambdaClickHandler>([=](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) {
const auto webpage = media->webpage();
if (!webpage) {
return;
}
HiddenUrlClickHandler::Open(webpage->url, context.other);
}
}))
, link(MakeWebPageClickHandler(media))
, updateCallback(std::move(updateCallback))
, belowInfo(false)
, text(st::historyViewButtonTextStyle, WebPageToPhrase(media->webpage())) {

View File

@@ -473,16 +473,23 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
p.drawPixmap(rthumb.topLeft(), blurred->pixSingle(size, args.blurred()));
} else if (!unwrapped) {
const auto roundTop = (roundCorners & RectPart::TopLeft);
const auto roundBottom = (roundCorners & RectPart::BottomLeft);
const auto margin = inWebPage
? st::roundRadiusSmall
: st::historyMessageRadius;
const auto parts = roundCorners
| RectPart::NoTopBottom
| (roundTop ? RectPart::Top : RectPart::None)
| (roundBottom ? RectPart::Bottom : RectPart::None);
Ui::FillRoundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st->imageBg(), roundRadius, parts);
if (roundRadius == ImageRoundRadius::Ellipse) {
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(st->imageBg());
p.drawEllipse(rthumb);
} else {
const auto roundTop = (roundCorners & RectPart::TopLeft);
const auto roundBottom = (roundCorners & RectPart::BottomLeft);
const auto margin = inWebPage
? st::roundRadiusSmall
: st::historyMessageRadius;
const auto parts = roundCorners
| RectPart::NoTopBottom
| (roundTop ? RectPart::Top : RectPart::None)
| (roundBottom ? RectPart::Bottom : RectPart::None);
Ui::FillRoundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st->imageBg(), roundRadius, parts);
}
} else {
paintPath(p, context, rthumb);
}

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_specific.h"
#include "core/crash_reports.h"
#include "core/launcher.h"
#include "mtproto/facade.h"
namespace {
@@ -569,10 +570,43 @@ void writeTcp(const QString &v) {
}
void writeMtp(int32 dc, const QString &v) {
const auto msg = QString("%1 (dc:%2) %3\n").arg(
_logsEntryStart(),
QString::number(dc),
v);
const auto expanded = [&] {
const auto bare = MTP::isTemporaryDcId(dc)
? MTP::getRealIdFromTemporaryDcId(dc)
: MTP::BareDcId(dc);
const auto base = (MTP::isTemporaryDcId(dc) ? "temporary_" : "")
+ QString::number(bare);
const auto shift = MTP::GetDcIdShift(dc);
if (shift == 0) {
return base + "_main";
} else if (shift == MTP::kExportDcShift) {
return base + "_export";
} else if (shift == MTP::kExportMediaDcShift) {
return base + "_export_download";
} else if (shift == MTP::kConfigDcShift) {
return base + "_config_enumeration";
} else if (shift == MTP::kLogoutDcShift) {
return base + "_logout_guest";
} else if (shift == MTP::kUpdaterDcShift) {
return base + "_download_update";
} else if (shift == MTP::kGroupCallStreamDcShift) {
return base + "_stream";
} else if (MTP::isDownloadDcId(dc)) {
const auto index = shift - MTP::kBaseDownloadDcShift;
return base + "_download" + QString::number(index);
} else if (MTP::isUploadDcId(dc)) {
const auto index = shift - MTP::kBaseUploadDcShift;
return base + "_upload" + QString::number(index);
} else if (shift >= MTP::kDestroyKeyStartDcShift) {
const auto index = shift - MTP::kDestroyKeyStartDcShift;
return base + "_key_destroyer" + QString::number(index);
}
return base + "_unknown" + QString::number(shift);
}();
const auto msg = _logsEntryStart()
+ u" (dc:%1) "_q.arg(expanded)
+ v
+ '\n';
_logsWrite(LogDataMtp, msg);
}

View File

@@ -1779,6 +1779,16 @@ bool MainWidget::isThirdSectionShown() const {
return _thirdSection != nullptr;
}
Dialogs::RowDescriptor MainWidget::resolveChatNext(
Dialogs::RowDescriptor from) const {
return _dialogs ? _dialogs->resolveChatNext(from) : Dialogs::RowDescriptor();
}
Dialogs::RowDescriptor MainWidget::resolveChatPrevious(
Dialogs::RowDescriptor from) const {
return _dialogs ? _dialogs->resolveChatPrevious(from) : Dialogs::RowDescriptor();
}
bool MainWidget::stackIsEmpty() const {
return _stack.empty();
}

View File

@@ -128,6 +128,11 @@ public:
[[nodiscard]] bool isMainSectionShown() const;
[[nodiscard]] bool isThirdSectionShown() const;
[[nodiscard]] Dialogs::RowDescriptor resolveChatNext(
Dialogs::RowDescriptor from) const;
[[nodiscard]] Dialogs::RowDescriptor resolveChatPrevious(
Dialogs::RowDescriptor from) const;
void returnTabbedSelector();
void showAnimated(const QPixmap &bgAnimCache, bool back = false);

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_streaming.h"
#include "data/data_file_click_handler.h"
#include "base/random.h"
#include "base/power_save_blocker.h"
#include "media/audio/media_audio.h"
#include "media/audio/media_audio_capture.h"
#include "media/streaming/media_streaming_instance.h"
@@ -24,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h"
#include "data/data_file_origin.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "core/shortcuts.h"
#include "core/application.h"
#include "main/main_domain.h" // Domain::activeSessionValue.
@@ -568,6 +570,32 @@ bool Instance::moveInPlaylist(
return false;
}
void Instance::updatePowerSaveBlocker(
not_null<Data*> data,
const TrackState &state) {
const auto block = !IsPausedOrPausing(state.state)
&& !IsStoppedOrStopping(state.state);
const auto blockVideo = block
&& data->current.audio()
&& data->current.audio()->isVideoMessage();
const auto windowResolver = [] {
const auto window = Core::App().activeWindow();
return window ? window->widget()->windowHandle() : nullptr;
};
base::UpdatePowerSaveBlocker(
data->powerSaveBlocker,
block,
base::PowerSaveBlockType::PreventAppSuspension,
[] { return u"Audio playback is active"_q; },
windowResolver);
base::UpdatePowerSaveBlocker(
data->powerSaveBlockerVideo,
blockVideo,
base::PowerSaveBlockType::PreventDisplaySleep,
[] { return u"Video playback is active"_q; },
windowResolver);
}
void Instance::ensureShuffleMove(not_null<Data*> data, int delta) {
const auto raw = data->shuffleData.get();
if (delta < 0) {
@@ -1170,6 +1198,8 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) {
streamed->progress.updateState(state);
}
}
updatePowerSaveBlocker(data, state);
auto finished = false;
_updatedNotifier.fire_copy({state});
if (data->isPlaying && state.state == State::StoppedAtEnd) {

View File

@@ -35,6 +35,10 @@ enum class Error;
} // namespace Streaming
} // namespace Media
namespace base {
class PowerSaveBlocker;
} // namespace base
namespace Media {
namespace Player {
@@ -195,6 +199,8 @@ private:
bool resumeOnCallEnd = false;
std::unique_ptr<Streamed> streamed;
std::unique_ptr<ShuffleData> shuffleData;
std::unique_ptr<base::PowerSaveBlocker> powerSaveBlocker;
std::unique_ptr<base::PowerSaveBlocker> powerSaveBlockerVideo;
};
struct SeekingChanges {
@@ -234,6 +240,9 @@ private:
void validateOtherPlaylist(not_null<Data*> data);
void playlistUpdated(not_null<Data*> data);
bool moveInPlaylist(not_null<Data*> data, int delta, bool autonext);
void updatePowerSaveBlocker(
not_null<Data*> data,
const TrackState &state);
HistoryItem *itemByIndex(not_null<Data*> data, int index);
void stopAndClear(not_null<Data*> data);

View File

@@ -20,8 +20,10 @@ constexpr auto kMaxQueuedPackets = 1024;
[[nodiscard]] bool UnreliableFormatDuration(
not_null<AVFormatContext*> format,
not_null<AVStream*> stream) {
return stream->codec
not_null<AVStream*> stream,
Mode mode) {
return (mode == Mode::Video || mode == Mode::Inspection)
&& stream->codec
&& (stream->codec->codec_id == AV_CODEC_ID_VP9)
&& format->iformat
&& format->iformat->name
@@ -145,7 +147,8 @@ void File::Context::logFatal(
Stream File::Context::initStream(
not_null<AVFormatContext*> format,
AVMediaType type) {
AVMediaType type,
Mode mode) {
auto result = Stream();
const auto index = result.index = av_find_best_stream(
format,
@@ -186,7 +189,7 @@ Stream File::Context::initStream(
result.timeBase = info->time_base;
result.duration = (info->duration != AV_NOPTS_VALUE)
? FFmpeg::PtsToTime(info->duration, result.timeBase)
: UnreliableFormatDuration(format, info)
: UnreliableFormatDuration(format, info, mode)
? kTimeUnknown
: FFmpeg::PtsToTime(format->duration, FFmpeg::kUniversalTimeBase);
if (result.duration == kTimeUnknown) {
@@ -276,12 +279,13 @@ void File::Context::start(crl::time position) {
return logFatal(qstr("avformat_find_stream_info"), error);
}
auto video = initStream(format.get(), AVMEDIA_TYPE_VIDEO);
const auto mode = _delegate->fileOpenMode();
auto video = initStream(format.get(), AVMEDIA_TYPE_VIDEO, mode);
if (unroll()) {
return;
}
auto audio = initStream(format.get(), AVMEDIA_TYPE_AUDIO);
auto audio = initStream(format.get(), AVMEDIA_TYPE_AUDIO, mode);
if (unroll()) {
return;
}

View File

@@ -72,9 +72,10 @@ private:
void logFatal(QLatin1String method, FFmpeg::AvErrorWrap error);
void fail(Error error);
Stream initStream(
[[nodiscard]] Stream initStream(
not_null<AVFormatContext *> format,
AVMediaType type);
AVMediaType type,
Mode mode);
void seekToPosition(
not_null<AVFormatContext *> format,
const Stream &stream,

View File

@@ -19,6 +19,7 @@ enum class Error;
class FileDelegate {
public:
[[nodiscard]] virtual Mode fileOpenMode() = 0;
[[nodiscard]] virtual bool fileReady(
int headerSize,
Stream &&video,

View File

@@ -234,6 +234,10 @@ void Player::videoPlayedTill(crl::time position) {
trackPlayedTill(*_video, _information.video.state, position);
}
Mode Player::fileOpenMode() {
return _options.mode;
}
bool Player::fileReady(int headerSize, Stream &&video, Stream &&audio) {
_waitingForData = false;

View File

@@ -97,6 +97,7 @@ private:
not_null<FileDelegate*> delegate();
// FileDelegate methods are called only from the File thread.
Mode fileOpenMode() override;
bool fileReady(int headerSize, Stream &&video, Stream &&audio) override;
void fileError(Error error) override;
void fileWaitingForData() override;

View File

@@ -60,6 +60,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "base/platform/base_platform_info.h"
#include "base/power_save_blocker.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "base/qt_signal_producer.h"
@@ -227,6 +228,7 @@ struct OverlayWidget::Streamed {
Streaming::Instance instance;
PlaybackControls controls;
std::unique_ptr<base::PowerSaveBlocker> powerSaveBlocker;
bool withSound = false;
bool pausedBySeek = false;
@@ -2791,6 +2793,22 @@ bool OverlayWidget::createStreamingObjects() {
return true;
}
void OverlayWidget::updatePowerSaveBlocker(
const Player::TrackState &state) {
Expects(_streamed != nullptr);
const auto block = (_document != nullptr)
&& _document->isVideoFile()
&& !IsPausedOrPausing(state.state)
&& !IsStoppedOrStopping(state.state);
base::UpdatePowerSaveBlocker(
_streamed->powerSaveBlocker,
block,
base::PowerSaveBlockType::PreventDisplaySleep,
[] { return u"Video playback is active"_q; },
[=] { return window(); });
}
QImage OverlayWidget::transformedShownContent() const {
return transformShownContent(
videoShown() ? currentVideoFrameImage() : _staticContent,
@@ -3259,6 +3277,7 @@ void OverlayWidget::updatePlaybackState() {
const auto state = _streamed->instance.player().prepareLegacyState();
if (state.position != kTimeUnknown && state.length != kTimeUnknown) {
_streamed->controls.updatePlayback(state);
updatePowerSaveBlocker(state);
_touchbarTrackState.fire_copy(state);
}
}

View File

@@ -310,6 +310,7 @@ private:
[[nodiscard]] bool createStreamingObjects();
void handleStreamingUpdate(Streaming::Update &&update);
void handleStreamingError(Streaming::Error &&error);
void updatePowerSaveBlocker(const Player::TrackState &state);
void initThemePreview();
void destroyThemePreview();

View File

@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "core/application.h"
#include "base/platform/base_platform_info.h"
#include "base/power_save_blocker.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/fade_wrap.h"
@@ -1512,6 +1513,7 @@ void Pip::updatePlaybackState() {
return;
}
_playbackProgress->updateState(state);
updatePowerSaveBlocker(state);
qint64 position = 0;
if (Player::IsStoppedAtEnd(state.state)) {
@@ -1529,6 +1531,18 @@ void Pip::updatePlaybackState() {
}
}
void Pip::updatePowerSaveBlocker(const Player::TrackState &state) {
const auto block = _data->isVideoFile()
&& !IsPausedOrPausing(state.state)
&& !IsStoppedOrStopping(state.state);
base::UpdatePowerSaveBlocker(
_powerSaveBlocker,
block,
base::PowerSaveBlockType::PreventDisplaySleep,
[] { return u"Video playback is active"_q; },
[=] { return _panel.widget()->windowHandle(); });
}
void Pip::updatePlaybackTexts(
int64 position,
int64 length,

View File

@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QPointer>
namespace base {
class PowerSaveBlocker;
} // namespace base
namespace Data {
class DocumentMedia;
} // namespace Data
@@ -187,6 +191,7 @@ private:
void saveGeometry();
void updatePlaybackState();
void updatePowerSaveBlocker(const Player::TrackState &state);
void updatePlayPauseResumeState(const Player::TrackState &state);
void restartAtSeekPosition(crl::time position);
@@ -244,12 +249,13 @@ private:
void seekFinish(float64 value);
const not_null<Delegate*> _delegate;
not_null<DocumentData*> _data;
const not_null<DocumentData*> _data;
FullMsgId _contextId;
Streaming::Instance _instance;
bool _opengl = false;
PipPanel _panel;
QSize _size;
std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
std::unique_ptr<PlaybackProgress> _playbackProgress;
std::shared_ptr<Data::DocumentMedia> _dataMedia;

View File

@@ -16,6 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP {
namespace details {
namespace {
std::atomic<int> GlobalConnectionCounter/* = 0*/;
} // namespace
ConnectionPointer::ConnectionPointer() = default;
@@ -158,7 +163,8 @@ std::optional<MTPResPQ> AbstractConnection::readPQFakeReply(
AbstractConnection::AbstractConnection(
QThread *thread,
const ProxyData &proxy)
: _proxy(proxy) {
: _proxy(proxy)
, _debugId(QString::number(++GlobalConnectionCounter)) {
moveToThread(thread);
}
@@ -188,6 +194,24 @@ ConnectionPointer AbstractConnection::Create(
return result;
}
QString AbstractConnection::ProtocolDcDebugId(int16 protocolDcId) {
const auto postfix = (protocolDcId < 0) ? "_media" : "";
protocolDcId = (protocolDcId < 0) ? (-protocolDcId) : protocolDcId;
const auto prefix = (protocolDcId > kTestModeDcIdShift) ? "test_" : "";
protocolDcId = (protocolDcId > kTestModeDcIdShift)
? (protocolDcId - kTestModeDcIdShift)
: protocolDcId;
return prefix + QString::number(protocolDcId) + postfix;
}
void AbstractConnection::logInfo(const QString &message) {
DEBUG_LOG(("Connection %1 Info: ").arg(_debugId) + message);
}
void AbstractConnection::logError(const QString &message) {
DEBUG_LOG(("Connection %1 Error: ").arg(_debugId) + message);
}
uint32 AbstractConnection::extendedNotSecurePadding() const {
return uint32(base::RandomValue<uchar>() & 0x3F);
}

View File

@@ -24,6 +24,8 @@ struct ConnectionOptions;
class AbstractConnection;
inline constexpr auto kTestModeDcIdShift = 10000;
class ConnectionPointer {
public:
ConnectionPointer();
@@ -122,6 +124,13 @@ public:
[[nodiscard]] gsl::span<const mtpPrime> parseNotSecureResponse(
const mtpBuffer &buffer) const;
[[nodiscard]] static QString ProtocolDcDebugId(int16 protocolDcId);
[[nodiscard]] QString debugId() const {
return _debugId;
}
void logInfo(const QString &message);
void logError(const QString &message);
// Used to emit error(...) with no real code from the server.
static constexpr auto kErrorCodeOther = -499;
@@ -141,6 +150,8 @@ protected:
int _pingTime = 0;
ProxyData _proxy;
QString _debugId;
// first we always send fake MTPReq_pq to see if connection works at all
// we send them simultaneously through TCP/HTTP/IPv4/IPv6 to choose the working one
[[nodiscard]] mtpBuffer preparePQFake(const MTPint128 &nonce) const;
@@ -193,5 +204,8 @@ mtpBuffer AbstractConnection::prepareNotSecurePacket(
return result;
}
#define CONNECTION_LOG_INFO(x) if (Logs::DebugEnabled()) { logInfo(x); }
#define CONNECTION_LOG_ERROR(x) if (Logs::DebugEnabled()) { logError(x); }
} // namespace details
} // namespace MTP

View File

@@ -43,7 +43,7 @@ void HttpConnection::sendData(mtpBuffer &&buffer) {
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
TCP_LOG(("HTTP Info: sending %1 len request").arg(requestSize));
CONNECTION_LOG_INFO(u"Sending %1 len request."_q.arg(requestSize));
_requests.insert(_manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
}
@@ -78,10 +78,12 @@ void HttpConnection::connectToServer(
auto buffer = preparePQFake(_checkNonce);
DEBUG_LOG(("HTTP Info: "
"dc:%1 - Sending fake req_pq to '%2'"
).arg(protocolDcId
).arg(url().toDisplayString()));
if (Logs::DebugEnabled()) {
_debugId = u"%1(dc:%2,%3)"_q
.arg(_debugId.toInt())
.arg(ProtocolDcDebugId(protocolDcId))
.arg(url().toDisplayString());
}
_pingTime = crl::now();
sendData(std::move(buffer));
@@ -89,12 +91,12 @@ void HttpConnection::connectToServer(
mtpBuffer HttpConnection::handleResponse(QNetworkReply *reply) {
QByteArray response = reply->readAll();
TCP_LOG(("HTTP Info: read %1 bytes").arg(response.size()));
CONNECTION_LOG_INFO(u"Read %1 bytes."_q.arg(response.size()));
if (response.isEmpty()) return mtpBuffer();
if (response.size() & 0x03 || response.size() < 8) {
LOG(("HTTP Error: bad response size %1").arg(response.size()));
CONNECTION_LOG_ERROR(u"Bad response size %1."_q.arg(response.size()));
return mtpBuffer(1, -500);
}
@@ -104,31 +106,46 @@ mtpBuffer HttpConnection::handleResponse(QNetworkReply *reply) {
return data;
}
qint32 HttpConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key"
// Returns "maybe bad key".
qint32 HttpConnection::handleError(QNetworkReply *reply) {
auto result = qint32(kErrorCodeOther);
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant statusCode = reply->attribute(
QNetworkRequest::HttpStatusCodeAttribute);
if (statusCode.isValid()) {
int status = statusCode.toInt();
result = -status;
}
switch (reply->error()) {
case QNetworkReply::ConnectionRefusedError: LOG(("HTTP Error: connection refused - %1").arg(reply->errorString())); break;
case QNetworkReply::RemoteHostClosedError: LOG(("HTTP Error: remote host closed - %1").arg(reply->errorString())); break;
case QNetworkReply::HostNotFoundError: LOG(("HTTP Error: host not found - %1").arg(reply->errorString())); break;
case QNetworkReply::TimeoutError: LOG(("HTTP Error: timeout - %1").arg(reply->errorString())); break;
case QNetworkReply::OperationCanceledError: LOG(("HTTP Error: cancelled - %1").arg(reply->errorString())); break;
case QNetworkReply::ConnectionRefusedError:
CONNECTION_LOG_ERROR(u"Connection refused - %1."_q
.arg(reply->errorString()));
break;
case QNetworkReply::RemoteHostClosedError:
CONNECTION_LOG_ERROR(u"Remote host closed - %1."_q
.arg(reply->errorString()));
break;
case QNetworkReply::HostNotFoundError:
CONNECTION_LOG_ERROR(u"Host not found - %1."_q
.arg(reply->errorString()));
break;
case QNetworkReply::TimeoutError:
CONNECTION_LOG_ERROR(u"Timeout - %1."_q
.arg(reply->errorString()));
break;
case QNetworkReply::OperationCanceledError:
CONNECTION_LOG_ERROR(u"Cancelled - %1."_q
.arg(reply->errorString()));
break;
case QNetworkReply::SslHandshakeFailedError:
case QNetworkReply::TemporaryNetworkFailureError:
case QNetworkReply::NetworkSessionFailedError:
case QNetworkReply::BackgroundRequestNotAllowedError:
case QNetworkReply::UnknownNetworkError:
if (reply->error() == QNetworkReply::UnknownNetworkError) {
DEBUG_LOG(("HTTP Error: network error %1 - %2").arg(reply->error()).arg(reply->errorString()));
} else {
LOG(("HTTP Error: network error %1 - %2").arg(reply->error()).arg(reply->errorString()));
}
CONNECTION_LOG_ERROR(u"Network error %1 - %2."_q
.arg(reply->error())
.arg(reply->errorString()));
break;
// proxy errors (101-199):
@@ -137,7 +154,11 @@ qint32 HttpConnection::handleError(QNetworkReply *reply) { // returnes "maybe ba
case QNetworkReply::ProxyNotFoundError:
case QNetworkReply::ProxyTimeoutError:
case QNetworkReply::ProxyAuthenticationRequiredError:
case QNetworkReply::UnknownProxyError: LOG(("HTTP Error: proxy error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
case QNetworkReply::UnknownProxyError:
CONNECTION_LOG_ERROR(u"Proxy error %1 - %2."_q
.arg(reply->error())
.arg(reply->errorString()));
break;
// content errors (201-299):
case QNetworkReply::ContentAccessDenied:
@@ -145,14 +166,21 @@ qint32 HttpConnection::handleError(QNetworkReply *reply) { // returnes "maybe ba
case QNetworkReply::ContentNotFoundError:
case QNetworkReply::AuthenticationRequiredError:
case QNetworkReply::ContentReSendError:
case QNetworkReply::UnknownContentError: LOG(("HTTP Error: content error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
case QNetworkReply::UnknownContentError:
CONNECTION_LOG_ERROR(u"Content error %1 - %2."_q
.arg(reply->error())
.arg(reply->errorString()));
break;
// protocol errors
case QNetworkReply::ProtocolUnknownError:
case QNetworkReply::ProtocolInvalidOperationError:
case QNetworkReply::ProtocolFailure: LOG(("HTTP Error: protocol error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
case QNetworkReply::ProtocolFailure:
CONNECTION_LOG_ERROR(u"Protocol error %1 - %2."_q
.arg(reply->error())
.arg(reply->errorString()));
break;
};
TCP_LOG(("HTTP Error %1, restarting! - %2").arg(reply->error()).arg(reply->errorString()));
return result;
}
@@ -178,20 +206,19 @@ void HttpConnection::requestFinished(QNetworkReply *reply) {
} else if (const auto res_pq = readPQFakeReply(data)) {
const auto &data = res_pq->c_resPQ();
if (data.vnonce() == _checkNonce) {
DEBUG_LOG(("Connection Info: "
"HTTP-transport to %1 connected by pq-response"
).arg(_address));
CONNECTION_LOG_INFO(
"HTTP-transport connected by pq-response.");
_status = Status::Ready;
_pingTime = crl::now() - _pingTime;
connected();
} else {
DEBUG_LOG(("Connection Error: "
"Wrong nonce received in HTTP fake pq-responce"));
CONNECTION_LOG_ERROR(
"Wrong nonce in HTTP fake pq-response.");
error(kErrorCodeOther);
}
} else {
DEBUG_LOG(("Connection Error: "
"Could not parse HTTP fake pq-responce"));
CONNECTION_LOG_ERROR(
"Could not parse HTTP fake pq-response.");
error(kErrorCodeOther);
}
}

View File

@@ -40,8 +40,8 @@ public:
QString transport() const override;
QString tag() const override;
static mtpBuffer handleResponse(QNetworkReply *reply);
static qint32 handleError(QNetworkReply *reply); // Returns error code.
mtpBuffer handleResponse(QNetworkReply *reply);
qint32 handleError(QNetworkReply *reply); // Returns error code.
private:
QUrl url() const;

View File

@@ -72,12 +72,6 @@ void ResolvingConnection::setChild(ConnectionPointer &&child) {
&AbstractConnection::disconnected,
this,
&ResolvingConnection::handleDisconnected);
DEBUG_LOG(("Resolving Info: dc:%1 proxy '%2' got new child '%3'").arg(
QString::number(_protocolDcId),
_proxy.host + ':' + QString::number(_proxy.port),
(_ipIndex >= 0 && _ipIndex < _proxy.resolvedIPs.size())
? _proxy.resolvedIPs[_ipIndex]
: _proxy.host));
if (_protocolDcId) {
_child->connectToServer(
_address,
@@ -85,6 +79,8 @@ void ResolvingConnection::setChild(ConnectionPointer &&child) {
_protocolSecret,
_protocolDcId,
_protocolForFiles);
CONNECTION_LOG_INFO("Resolving connected a new child: "
+ _child->debugId());
}
}
@@ -224,18 +220,13 @@ void ResolvingConnection::connectToServer(
_protocolSecret = protocolSecret;
_protocolDcId = protocolDcId;
_protocolForFiles = protocolForFiles;
DEBUG_LOG(("Resolving Info: dc:%1 proxy '%2' connects a child '%3'").arg(
QString::number(_protocolDcId),
_proxy.host +':' + QString::number(_proxy.port),
(_ipIndex >= 0 && _ipIndex < _proxy.resolvedIPs.size())
? _proxy.resolvedIPs[_ipIndex]
: _proxy.host));
return _child->connectToServer(
_child->connectToServer(
address,
port,
protocolSecret,
protocolDcId,
protocolForFiles);
CONNECTION_LOG_INFO("Resolving connected a child: " + _child->debugId());
}
bool ResolvingConnection::isConnected() const {

View File

@@ -40,6 +40,8 @@ public:
virtual int readPacketLength(bytes::const_span bytes) const = 0;
virtual bytes::const_span readPacket(bytes::const_span bytes) const = 0;
virtual QString debugPostfix() const = 0;
virtual ~Protocol() = default;
private:
@@ -60,6 +62,8 @@ public:
int readPacketLength(bytes::const_span bytes) const override;
bytes::const_span readPacket(bytes::const_span bytes) const override;
QString debugPostfix() const override;
};
uint32 TcpConnection::Protocol::Version0::id() const {
@@ -129,12 +133,18 @@ bytes::const_span TcpConnection::Protocol::Version0::readPacket(
return bytes.subspan(sizeLength, size - sizeLength);
}
QString TcpConnection::Protocol::Version0::debugPostfix() const {
return QString();
}
class TcpConnection::Protocol::Version1 : public Version0 {
public:
explicit Version1(bytes::vector &&secret);
void prepareKey(bytes::span key, bytes::const_span source) override;
QString debugPostfix() const override;
private:
bytes::vector _secret;
@@ -151,6 +161,10 @@ void TcpConnection::Protocol::Version1::prepareKey(
bytes::copy(key, openssl::Sha256(payload));
}
QString TcpConnection::Protocol::Version1::debugPostfix() const {
return u"_obf"_q;
}
class TcpConnection::Protocol::VersionD : public Version1 {
public:
using Version1::Version1;
@@ -163,6 +177,8 @@ public:
int readPacketLength(bytes::const_span bytes) const override;
bytes::const_span readPacket(bytes::const_span bytes) const override;
QString debugPostfix() const override;
};
uint32 TcpConnection::Protocol::VersionD::id() const {
@@ -209,6 +225,10 @@ bytes::const_span TcpConnection::Protocol::VersionD::readPacket(
return bytes.subspan(sizeLength, size - sizeLength);
}
QString TcpConnection::Protocol::VersionD::debugPostfix() const {
return u"_dd"_q;
}
auto TcpConnection::Protocol::Create(bytes::const_span secret)
-> std::unique_ptr<Protocol> {
// See also DcOptions::ValidateSecret.
@@ -269,7 +289,7 @@ void TcpConnection::socketRead() {
Expects(_leftBytes > 0 || !_usingLargeBuffer);
if (!_socket || !_socket->isConnected()) {
LOG(("MTP Error: Socket not connected in socketRead()"));
CONNECTION_LOG_ERROR("Socket not connected in socketRead()");
error(kErrorCodeOther);
return;
}
@@ -290,7 +310,7 @@ void TcpConnection::socketRead() {
if (readCount > 0) {
const auto read = free.subspan(0, readCount);
aesCtrEncrypt(read, _receiveKey, &_receiveState);
TCP_LOG(("TCP Info: read %1 bytes").arg(readCount));
CONNECTION_LOG_INFO(u"Read %1 bytes"_q.arg(readCount));
_readBytes += readCount;
if (_leftBytes > 0) {
@@ -306,9 +326,10 @@ void TcpConnection::socketRead() {
_largeBuffer.clear();
_offsetBytes = _readBytes = 0;
} else {
TCP_LOG(("TCP Info: not enough %1 for packet! read %2"
).arg(_leftBytes
).arg(_readBytes));
CONNECTION_LOG_INFO(
u"Not enough %1 for packet! read %2"_q
.arg(_leftBytes)
.arg(_readBytes));
receivedSome();
}
} else {
@@ -320,8 +341,9 @@ void TcpConnection::socketRead() {
// Not enough bytes yet.
break;
} else if (packetSize <= 0) {
LOG(("TCP Error: bad packet size in 4 bytes: %1"
).arg(packetSize));
CONNECTION_LOG_ERROR(
u"Bad packet size in 4 bytes: %1"_q
.arg(packetSize));
error(kErrorCodeOther);
return;
} else if (available.size() >= packetSize) {
@@ -342,22 +364,23 @@ void TcpConnection::socketRead() {
// If the next packet won't fit in the buffer.
ensureAvailableInBuffer(packetSize);
TCP_LOG(("TCP Info: not enough %1 for packet! "
"full size %2 read %3"
).arg(_leftBytes
).arg(packetSize
).arg(available.size()));
CONNECTION_LOG_INFO(u"Not enough %1 for packet! "
"full size %2 read %3"_q
.arg(_leftBytes)
.arg(packetSize)
.arg(available.size()));
receivedSome();
break;
}
}
}
} else if (readCount < 0) {
LOG(("TCP Error: socket read return %1").arg(readCount));
CONNECTION_LOG_ERROR(u"Socket read return %1."_q.arg(readCount));
error(kErrorCodeOther);
return;
} else {
TCP_LOG(("TCP Info: no bytes read, but bytes available was true..."));
CONNECTION_LOG_INFO(
"No bytes read, but bytes available was true...");
break;
}
} while (_socket
@@ -367,8 +390,7 @@ void TcpConnection::socketRead() {
mtpBuffer TcpConnection::parsePacket(bytes::const_span bytes) {
const auto packet = _protocol->readPacket(bytes);
TCP_LOG(("TCP Info: packet received, size = %1"
).arg(packet.size()));
CONNECTION_LOG_INFO(u"Packet received, size = %1."_q.arg(packet.size()));
const auto ints = gsl::make_span(
reinterpret_cast<const mtpPrime*>(packet.data()),
packet.size() / sizeof(mtpPrime));
@@ -376,13 +398,8 @@ mtpBuffer TcpConnection::parsePacket(bytes::const_span bytes) {
if (ints.size() < 3) {
// nop or error or new quickack, latter is not yet supported.
if (ints[0] != 0) {
LOG(("TCP Error: "
"error packet received, endpoint: '%1:%2', "
"protocolDcId: %3, code = %4"
).arg(_address.isEmpty() ? ("prx_" + _proxy.host) : _address
).arg(_address.isEmpty() ? _proxy.port : _port
).arg(_protocolDcId
).arg(ints[0]));
CONNECTION_LOG_ERROR(u"Error packet received, code = %1"_q
.arg(ints[0]));
}
return mtpBuffer(1, ints[0]);
}
@@ -396,10 +413,7 @@ void TcpConnection::socketConnected() {
auto buffer = preparePQFake(_checkNonce);
DEBUG_LOG(("TCP Info: "
"dc:%1 - Sending fake req_pq to '%2'"
).arg(_protocolDcId
).arg(_address + ':' + QString::number(_port)));
CONNECTION_LOG_INFO("Sending fake req_pq.");
_pingTime = crl::now();
sendData(std::move(buffer));
@@ -423,7 +437,8 @@ void TcpConnection::sendData(mtpBuffer &&buffer) {
// buffer: 2 available int-s + data + available int.
const auto bytes = _protocol->finalizePacket(buffer);
TCP_LOG(("TCP Info: write packet %1 bytes").arg(bytes.size()));
CONNECTION_LOG_INFO(u"TCP Info: write packet %1 bytes."_q
.arg(bytes.size()));
aesCtrEncrypt(bytes, _sendKey, &_sendState);
_socket->write(connectionStartPrefix, bytes);
}
@@ -506,20 +521,10 @@ void TcpConnection::connectToServer(
_address = _proxy.host;
_port = _proxy.port;
_protocol = Protocol::Create(secret);
DEBUG_LOG(("TCP Info: "
"dc:%1 - Connecting to proxy '%2'"
).arg(protocolDcId
).arg(_address + ':' + QString::number(_port)));
} else {
_address = address;
_port = port;
_protocol = Protocol::Create(secret);
DEBUG_LOG(("TCP Info: "
"dc:%1 - Connecting to '%2'"
).arg(protocolDcId
).arg(_address + ':' + QString::number(_port)));
}
_socket = AbstractSocket::Create(
thread(),
@@ -528,6 +533,18 @@ void TcpConnection::connectToServer(
protocolForFiles);
_protocolDcId = protocolDcId;
const auto postfix = _socket->debugPostfix();
_debugId = u"%1(dc:%2,%3%4:%5%6)"_q
.arg(_debugId.toInt())
.arg(ProtocolDcDebugId(_protocolDcId))
.arg((_proxy.type == ProxyData::Type::Mtproto) ? "mtproxy " : "")
.arg(_address)
.arg(_port)
.arg(postfix.isEmpty() ? _protocol->debugPostfix() : postfix);
_socket->setDebugId(_debugId);
CONNECTION_LOG_INFO("Connecting...");
_socket->connected(
) | rpl::start_with_next([=] {
socketConnected();
@@ -584,19 +601,18 @@ void TcpConnection::socketPacket(bytes::const_span bytes) {
if (const auto res_pq = readPQFakeReply(data)) {
const auto &data = res_pq->c_resPQ();
if (data.vnonce() == _checkNonce) {
DEBUG_LOG(("Connection Info: Valid pq response by TCP."));
CONNECTION_LOG_INFO("Valid pq response by TCP.");
_status = Status::Ready;
_connectedLifetime.destroy();
_pingTime = (crl::now() - _pingTime);
connected();
} else {
DEBUG_LOG(("Connection Error: "
"Wrong nonce received in TCP fake pq-responce"));
CONNECTION_LOG_ERROR(
"Wrong nonce received in TCP fake pq-responce");
error(kErrorCodeOther);
}
} else {
DEBUG_LOG(("Connection Error: "
"Could not parse TCP fake pq-responce"));
CONNECTION_LOG_ERROR("Could not parse TCP fake pq-responce");
error(kErrorCodeOther);
}
}

View File

@@ -28,4 +28,44 @@ std::unique_ptr<AbstractSocket> AbstractSocket::Create(
}
}
void AbstractSocket::logError(int errorCode, const QString &errorText) {
const auto log = [&](const QString &message) {
DEBUG_LOG(("Socket %1 Error: ").arg(_debugId) + message);
};
switch (errorCode) {
case QAbstractSocket::ConnectionRefusedError:
log(u"Socket connection refused - %1."_q.arg(errorText));
break;
case QAbstractSocket::RemoteHostClosedError:
log(u"Remote host closed socket connection - %1."_q.arg(errorText));
break;
case QAbstractSocket::HostNotFoundError:
log(u"Host not found - %1."_q.arg(errorText));
break;
case QAbstractSocket::SocketTimeoutError:
log(u"Socket timeout - %1."_q.arg(errorText));
break;
case QAbstractSocket::NetworkError: {
log(u"Network - %1."_q.arg(errorText));
} break;
case QAbstractSocket::ProxyAuthenticationRequiredError:
case QAbstractSocket::ProxyConnectionRefusedError:
case QAbstractSocket::ProxyConnectionClosedError:
case QAbstractSocket::ProxyConnectionTimeoutError:
case QAbstractSocket::ProxyNotFoundError:
case QAbstractSocket::ProxyProtocolError:
log(u"Proxy (%1) - %2."_q.arg(errorCode).arg(errorText));
break;
default:
log(u"Other (%1) - %2."_q.arg(errorCode).arg(errorText));
break;
}
}
} // namespace MTP::details

View File

@@ -20,6 +20,10 @@ public:
const QNetworkProxy &proxy,
bool protocolForFiles);
void setDebugId(const QString &id) {
_debugId = id;
}
explicit AbstractSocket(not_null<QThread*> thread) {
moveToThread(thread);
}
@@ -52,11 +56,15 @@ public:
bytes::const_span buffer) = 0;
virtual int32 debugState() = 0;
[[nodiscard]] virtual QString debugPostfix() const = 0;
protected:
static const int kFilesSendBufferSize = 2 * 1024 * 1024;
static const int kFilesReceiveBufferSize = 2 * 1024 * 1024;
void logError(int errorCode, const QString &errorText);
QString _debugId;
rpl::event_stream<> _connected;
rpl::event_stream<> _disconnected;
rpl::event_stream<> _readyRead;

View File

@@ -65,7 +65,7 @@ QByteArray DnsUserAgent() {
static const auto kResult = QByteArray(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/96.0.4664.110 Safari/537.36");
"Chrome/97.0.4692.99 Safari/537.36");
return kResult;
}

View File

@@ -114,50 +114,12 @@ int32 TcpSocket::debugState() {
return _socket.state();
}
void TcpSocket::LogError(int errorCode, const QString &errorText) {
switch (errorCode) {
case QAbstractSocket::ConnectionRefusedError:
LOG(("TCP Error: socket connection refused - %1").arg(errorText));
break;
case QAbstractSocket::RemoteHostClosedError:
TCP_LOG(("TCP Info: remote host closed socket connection - %1"
).arg(errorText));
break;
case QAbstractSocket::HostNotFoundError:
LOG(("TCP Error: host not found - %1").arg(errorText));
break;
case QAbstractSocket::SocketTimeoutError:
LOG(("TCP Error: socket timeout - %1").arg(errorText));
break;
case QAbstractSocket::NetworkError: {
DEBUG_LOG(("TCP Error: network - %1").arg(errorText));
} break;
case QAbstractSocket::ProxyAuthenticationRequiredError:
case QAbstractSocket::ProxyConnectionRefusedError:
case QAbstractSocket::ProxyConnectionClosedError:
case QAbstractSocket::ProxyConnectionTimeoutError:
case QAbstractSocket::ProxyNotFoundError:
case QAbstractSocket::ProxyProtocolError:
LOG(("TCP Error: proxy (%1) - %2").arg(errorCode).arg(errorText));
break;
default:
LOG(("TCP Error: other (%1) - %2").arg(errorCode).arg(errorText));
break;
}
TCP_LOG(("TCP Error %1, restarting! - %2"
).arg(errorCode
).arg(errorText));
QString TcpSocket::debugPostfix() const {
return QString();
}
void TcpSocket::handleError(int errorCode) {
LogError(errorCode, _socket.errorString());
logError(errorCode, _socket.errorString());
_error.fire({});
}

View File

@@ -27,8 +27,7 @@ public:
void write(bytes::const_span prefix, bytes::const_span buffer) override;
int32 debugState() override;
static void LogError(int errorCode, const QString &errorText);
QString debugPostfix() const override;
private:
void handleError(int errorCode);

View File

@@ -505,7 +505,7 @@ void TlsSocket::plainConnected() {
domainFromSecret(),
keyFromSecret());
if (hello.data.isEmpty()) {
LOG(("TLS Error: Could not generate Client Hello!"));
logError(888, "Could not generate Client Hello.");
_state = State::Error;
_error.fire({});
} else {
@@ -563,7 +563,7 @@ void TlsSocket::checkHelloParts12(int parts1Size) {
- kLengthSize
- kServerHelloPart1.size();
if (!CheckPart(data.subspan(part1Offset), kServerHelloPart1)) {
LOG(("TLS Error: Bad Server Hello part1."));
logError(888, "Bad Server Hello part1.");
handleError();
return;
}
@@ -587,7 +587,7 @@ void TlsSocket::checkHelloParts34(int parts123Size) {
- kLengthSize
- kServerHelloPart3.size();
if (!CheckPart(data.subspan(part3Offset), kServerHelloPart3)) {
LOG(("TLS Error: Bad Server Hello part."));
logError(888, "Bad Server Hello part.");
handleError();
return;
}
@@ -611,7 +611,7 @@ void TlsSocket::checkHelloDigest() {
bytes::set_with_const(digest, bytes::type(0));
const auto check = openssl::HmacSha256(keyFromSecret(), fulldata);
if (bytes::compare(digestCopy, check) != 0) {
LOG(("TLS Error: Bad Server Hello digest."));
logError(888, "Bad Server Hello digest.");
handleError();
return;
}
@@ -649,7 +649,7 @@ bool TlsSocket::checkNextPacket() {
return true;
}
if (!CheckPart(incoming.subspan(offset), kServerHeader)) {
LOG(("TLS Error: Bad packet header."));
logError(888, "Bad packet header.");
return false;
}
const auto length = ReadPartLength(
@@ -773,12 +773,16 @@ int32 TlsSocket::debugState() {
return _socket.state();
}
QString TlsSocket::debugPostfix() const {
return u"_ee"_q;
}
void TlsSocket::handleError(int errorCode) {
if (_state != State::Connected) {
_syncTimeRequests.fire({});
}
if (errorCode) {
TcpSocket::LogError(errorCode, _socket.errorString());
logError(errorCode, _socket.errorString());
}
_state = State::Error;
_error.fire({});

View File

@@ -28,6 +28,7 @@ public:
void write(bytes::const_span prefix, bytes::const_span buffer) override;
int32 debugState() override;
QString debugPostfix() const override;
private:
enum class State {

View File

@@ -39,7 +39,6 @@ constexpr auto kPingSendAfter = 30 * crl::time(1000);
constexpr auto kPingSendAfterForce = 45 * crl::time(1000);
constexpr auto kTemporaryExpiresIn = TimeId(86400);
constexpr auto kBindKeyAdditionalExpiresTimeout = TimeId(30);
constexpr auto kTestModeDcIdShift = 10000;
constexpr auto kKeyOldEnoughForDestroy = 60 * crl::time(1000);
constexpr auto kSentContainerLives = 600 * crl::time(1000);
constexpr auto kFastRequestDuration = crl::time(500);
@@ -1252,14 +1251,10 @@ void SessionPrivate::handleReceived() {
auto ints = intsBuffer.constData();
if ((intsCount < kMinimalIntsCount) || (intsCount > kMaxMessageLength / kIntSize)) {
LOG(("TCP Error: bad message received, len %1").arg(intsCount * kIntSize));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str()));
return restart();
}
if (_keyId != *(uint64*)ints) {
LOG(("TCP Error: bad auth_key_id %1 instead of %2 received").arg(_keyId).arg(*(uint64*)ints));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str()));
return restart();
}
@@ -1297,8 +1292,6 @@ void SessionPrivate::handleReceived() {
constexpr auto kMsgKeyShift = 8U;
if (ConstTimeIsDifferent(&msgKey, sha256Buffer.data() + kMsgKeyShift, sizeof(msgKey))) {
LOG(("TCP Error: bad SHA256 hash after aesDecrypt in message"));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
return restart();
}
@@ -1307,17 +1300,19 @@ void SessionPrivate::handleReceived() {
|| (paddingSize < kMinPaddingSize)
|| (paddingSize > kMaxPaddingSize)) {
LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(messageLength).arg(encryptedBytesCount));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
return restart();
}
TCP_LOG(("TCP Info: decrypted message %1,%2,%3 is %4 len").arg(msgId).arg(seqNo).arg(Logs::b(needAck)).arg(fullDataLength));
if (Logs::DebugEnabled()) {
_connection->logInfo(u"Decrypted message %1,%2,%3 is %4 len"_q
.arg(msgId)
.arg(seqNo)
.arg(Logs::b(needAck))
.arg(fullDataLength));
}
if (session != _sessionId) {
LOG(("MTP Error: bad server session received"));
TCP_LOG(("MTP Error: bad server session %1 instead of %2 in message received").arg(session).arg(_sessionId));
return restart();
}
@@ -1360,8 +1355,8 @@ void SessionPrivate::handleReceived() {
auto sfrom = decryptedInts + 4U; // msg_id + seq_no + length + message
MTP_LOG(_shiftedDcId, ("Recv: ")
+ DumpToText(sfrom, end)
+ QString(" (protocolDcId:%1,key:%2)"
).arg(getProtocolDcId()
+ QString(" (dc:%1,key:%2)"
).arg(AbstractConnection::ProtocolDcDebugId(getProtocolDcId())
).arg(_encryptionKey->keyId()));
const auto registered = _receivedMessageIds.registerMsgId(
@@ -2619,8 +2614,8 @@ bool SessionPrivate::sendSecureRequest(
auto from = request->constData() + 4;
MTP_LOG(_shiftedDcId, ("Send: ")
+ DumpToText(from, from + messageSize)
+ QString(" (protocolDcId:%1,key:%2)"
).arg(getProtocolDcId()
+ QString(" (dc:%1,key:%2)"
).arg(AbstractConnection::ProtocolDcDebugId(getProtocolDcId())
).arg(_encryptionKey->keyId()));
uchar encryptedSHA256[32];

View File

@@ -92,7 +92,7 @@ std::vector<Type> Compute() {
}
}
ranges::unique(result);
result = result | ranges::views::unique | ranges::to_vector;
return result;
}

View File

@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <connection_thread.h>
#include <registry.h>
#include <surface.h>
#include <xdgforeign.h>
#include <plasmashell.h>
using namespace KWayland::Client;
@@ -23,7 +22,6 @@ namespace internal {
struct WaylandIntegration::Private {
std::unique_ptr<ConnectionThread> connection;
Registry registry;
std::unique_ptr<XdgExporter> xdgExporter;
std::unique_ptr<PlasmaShell> plasmaShell;
};
@@ -42,21 +40,6 @@ WaylandIntegration::WaylandIntegration()
&_private->registry,
&Registry::destroy);
QObject::connect(
&_private->registry,
&Registry::exporterUnstableV2Announced,
[=](uint name, uint version) {
_private->xdgExporter = std::unique_ptr<XdgExporter>{
_private->registry.createXdgExporter(name, version),
};
QObject::connect(
_private->connection.get(),
&ConnectionThread::connectionDied,
_private->xdgExporter.get(),
&XdgExporter::destroy);
});
QObject::connect(
&_private->registry,
&Registry::plasmaShellAnnounced,
@@ -81,26 +64,6 @@ WaylandIntegration *WaylandIntegration::Instance() {
return &instance;
}
QString WaylandIntegration::nativeHandle(QWindow *window) {
if (const auto exporter = _private->xdgExporter.get()) {
if (const auto surface = Surface::fromWindow(window)) {
if (const auto exported = exporter->exportTopLevel(
surface,
surface)) {
QEventLoop loop;
QObject::connect(
exported,
&XdgExported::done,
&loop,
&QEventLoop::quit);
loop.exec();
return exported->handle();
}
}
}
return {};
}
bool WaylandIntegration::skipTaskbarSupported() {
return _private->plasmaShell != nullptr;
}

View File

@@ -14,7 +14,6 @@ class WaylandIntegration {
public:
[[nodiscard]] static WaylandIntegration *Instance();
[[nodiscard]] QString nativeHandle(QWindow *window);
[[nodiscard]] bool skipTaskbarSupported();
void skipTaskbar(QWindow *window, bool skip);

View File

@@ -26,10 +26,6 @@ WaylandIntegration *WaylandIntegration::Instance() {
return &instance;
}
QString WaylandIntegration::nativeHandle(QWindow *window) {
return {};
}
bool WaylandIntegration::skipTaskbarSupported() {
return false;
}

View File

@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_file_utilities.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "platform/linux/linux_wayland_integration.h"
#include "base/platform/linux/base_linux_wayland_integration.h"
#include "storage/localstorage.h"
#include "base/random.h"
@@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <glibmm.h>
#include <giomm.h>
using Platform::internal::WaylandIntegration;
using base::Platform::WaylandIntegration;
namespace Platform {
namespace FileDialog {

View File

@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "platform/linux/linux_wayland_integration.h"
#include "base/platform/linux/base_linux_wayland_integration.h"
#include "core/application.h"
#include "window/window_controller.h"
#include "base/random.h"
@@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <giomm.h>
#include <private/qguiapplication_p.h>
using Platform::internal::WaylandIntegration;
using base::Platform::WaylandIntegration;
namespace Platform {
namespace File {

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/random.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_wayland_integration.h"
#include "ui/platform/linux/ui_linux_wayland_integration.h"
#include "platform/linux/linux_desktop_environment.h"
#include "platform/linux/linux_wayland_integration.h"
@@ -42,12 +43,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <private/qguiapplication_p.h>
#ifdef Q_OS_FREEBSD
#include <malloc_np.h>
#else // Q_OS_FREEBSD
#include <jemalloc/jemalloc.h>
#endif // Q_OS_FREEBSD
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include <glibmm.h>
#include <giomm.h>
@@ -63,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <iostream>
using namespace Platform;
using BaseWaylandIntegration = base::Platform::WaylandIntegration;
using UiWaylandIntegration = Ui::Platform::WaylandIntegration;
using Platform::internal::WaylandIntegration;
@@ -97,7 +93,7 @@ void PortalAutostart(bool start, bool silent) {
}
const auto window = activeWindow->widget()->windowHandle();
if (const auto integration = WaylandIntegration::Instance()) {
if (const auto integration = BaseWaylandIntegration::Instance()) {
if (const auto handle = integration->nativeHandle(window)
; !handle.isEmpty()) {
result << "wayland:" << handle.toStdString();
@@ -688,9 +684,6 @@ int psFixPrevious() {
namespace Platform {
void start() {
auto backgroundThread = true;
mallctl("background_thread", nullptr, nullptr, &backgroundThread, sizeof(bool));
// Prevent any later calls into setlocale() by Qt
QCoreApplicationPrivate::initLocale();

View File

@@ -21,6 +21,13 @@ settingsAttentionButton: SettingsButton(settingsButton) {
textFg: attentionButtonFg;
textFgOver: attentionButtonFgOver;
}
settingsOptionDisabled: SettingsButton(settingsButton) {
textFg: windowSubTextFg;
textFgOver: windowSubTextFg;
textBg: windowBg;
textBgOver: windowBg;
toggleOver: infoProfileToggle;
}
settingsSectionSkip: 9px;
settingsSectionIconLeft: 22px;
settingsSeparatorPadding: margins(22px, infoProfileSkip, 0px, infoProfileSkip);

View File

@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_common.h"
#include "api/api_updates.h"
#include "base/qt/qt_common_adapters.h"
#include "base/custom_app_icon.h"
#include "zlib.h"
@@ -283,6 +284,29 @@ auto GenerateCodes() {
Ui::Toast::Show(now ? "Testing chat theme colors!" : "Not testing..");
});
#ifdef Q_OS_MAC
codes.emplace(qsl("customicon"), [](SessionController *window) {
const auto iconFilters = qsl("Icon files (*.icns *.png);;") + FileDialog::AllFilesFilter();
const auto change = [](const QString &path) {
const auto success = path.isEmpty()
? base::ClearCustomAppIcon()
: base::SetCustomAppIcon(path);
Ui::Toast::Show(success
? (path.isEmpty()
? "Icon cleared. Restarting the Dock."
: "Icon updated. Restarting the Dock.")
: (path.isEmpty()
? "Icon clear failed. See log.txt for details."
: "Icon update failed. See log.txt for details."));
};
FileDialog::GetOpenPath(Core::App().getFileDialogParent(), "Choose custom icon", iconFilters, [=](const FileDialog::OpenResult &result) {
change(result.paths.isEmpty() ? QString() : result.paths.front());
}, [=] {
change(QString());
});
});
#endif // Q_OS_MAC
return codes;
}

View File

@@ -7,12 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "settings/settings_experimental.h"
#include "ui/boxes/confirm_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/gl/gl_detection.h"
#include "base/options.h"
#include "core/application.h"
#include "chat_helpers/tabbed_panel.h"
#include "lang/lang_keys.h"
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
@@ -20,27 +27,61 @@ namespace Settings {
namespace {
void AddOption(
not_null<Window::Controller*> window,
not_null<Ui::VerticalLayout*> container,
base::options::option<bool> &option) {
base::options::option<bool> &option,
rpl::producer<> resetClicks) {
auto &lifetime = container->lifetime();
const auto name = option.name().isEmpty() ? option.id() : option.name();
AddButton(
const auto toggles = lifetime.make_state<rpl::event_stream<bool>>();
std::move(
resetClicks
) | rpl::map_to(
option.defaultValue()
) | rpl::start_to_stream(*toggles, lifetime);
const auto button = AddButton(
container,
rpl::single(name),
st::settingsButton
)->toggleOn(rpl::single(option.value()))->toggledChanges(
option.relevant() ? st::settingsButton : st::settingsOptionDisabled
)->toggleOn(toggles->events_starting_with(option.value()));
const auto restarter = (option.relevant() && option.restartRequired())
? button->lifetime().make_state<base::Timer>()
: nullptr;
if (restarter) {
restarter->setCallback([=] {
window->show(Box<Ui::ConfirmBox>(
tr::lng_settings_need_restart(tr::now),
tr::lng_settings_restart_now(tr::now),
tr::lng_settings_restart_later(tr::now),
[] { Core::Restart(); }));
});
}
button->toggledChanges(
) | rpl::start_with_next([=, &option](bool toggled) {
if (!option.relevant() && toggled != option.defaultValue()) {
toggles->fire_copy(option.defaultValue());
window->showToast(
tr::lng_settings_experimental_irrelevant(tr::now));
return;
}
option.set(toggled);
if (restarter) {
restarter->callOnce(st::settingsButton.toggle.duration);
}
}, container->lifetime());
const auto &description = option.description();
if (!description.isEmpty()) {
AddSkip(container, st::settingsCheckboxesSkip);
AddDividerText(container, rpl::single(description));
AddSkip(container, st::settingsCheckboxesSkip);
}
}
void SetupExperimental(
not_null<Window::SessionController*> controller,
not_null<Window::Controller*> window,
not_null<Ui::VerticalLayout*> container) {
AddSkip(container, st::settingsCheckboxesSkip);
@@ -51,17 +92,42 @@ void SetupExperimental(
st::boxLabel),
st::settingsDividerLabelPadding);
AddDivider(container);
auto reset = (Button*)nullptr;
if (base::options::changed()) {
const auto wrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
const auto inner = wrap->entity();
AddDivider(inner);
AddSkip(inner, st::settingsCheckboxesSkip);
reset = AddButton(
inner,
tr::lng_settings_experimental_restore(),
st::settingsButton);
reset->addClickHandler([=] {
base::options::reset();
wrap->hide(anim::type::normal);
});
AddSkip(inner, st::settingsCheckboxesSkip);
}
AddDivider(container);
AddSkip(container, st::settingsCheckboxesSkip);
const auto addToggle = [&](const char name[]) {
AddOption(container, base::options::lookup<bool>(name));
AddOption(
window,
container,
base::options::lookup<bool>(name),
(reset
? (reset->clicks() | rpl::to_empty)
: rpl::producer<>()));
};
addToggle(ChatHelpers::kOptionTabbedPanelShowOnClick);
AddSkip(container, st::settingsCheckboxesSkip);
addToggle(Window::kOptionViewProfileInChatsListContextMenu);
addToggle(Ui::GL::kOptionAllowLinuxNvidiaOpenGL);
}
} // namespace
@@ -77,7 +143,7 @@ void Experimental::setupContent(
not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
SetupExperimental(controller, content);
SetupExperimental(&controller->window(), content);
Ui::ResizeFitChild(this, content);
}

View File

@@ -21,14 +21,19 @@ Qt::KeyboardModifiers SkipSwitchModifiers() {
return Qt::ControlModifier | Qt::ShiftModifier;
}
FnMut<bool()> GetSwitchMethod(SwitchSettings value) {
std::optional<Shortcuts::Command> GetSwitchCommand(SwitchSettings value) {
switch (value) {
case SwitchSettings::Next:
return Shortcuts::RequestHandler(Shortcuts::Command::ChatNext);
return Shortcuts::Command::ChatNext;
case SwitchSettings::Previous:
return Shortcuts::RequestHandler(Shortcuts::Command::ChatPrevious);
return Shortcuts::Command::ChatPrevious;
}
return nullptr;
return std::nullopt;
}
FnMut<bool()> GetSwitchMethod(SwitchSettings value) {
const auto command = GetSwitchCommand(value);
return command ? Shortcuts::RequestHandler(*command) : nullptr;
}
} // namespace Support

View File

@@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Shortcuts {
enum class Command;
} // namespace Shortcuts
namespace Support {
enum class SwitchSettings {
@@ -15,8 +19,10 @@ enum class SwitchSettings {
Previous,
};
Qt::KeyboardModifiers SkipSwitchModifiers();
bool HandleSwitch(Qt::KeyboardModifiers modifiers);
FnMut<bool()> GetSwitchMethod(SwitchSettings value);
[[nodiscard]] Qt::KeyboardModifiers SkipSwitchModifiers();
[[nodiscard]] bool HandleSwitch(Qt::KeyboardModifiers modifiers);
[[nodiscard]] std::optional<Shortcuts::Command> GetSwitchCommand(
SwitchSettings value);
[[nodiscard]] FnMut<bool()> GetSwitchMethod(SwitchSettings value);
} // namespace Support

View File

@@ -444,7 +444,8 @@ void ChatTheme::setBubblesBackground(QImage image) {
_bubblesBackgroundPrepared = std::move(image);
if (_bubblesBackgroundPrepared.isNull()) {
_bubblesBackgroundPattern = nullptr;
_repaintBackgroundRequests.fire({});
// setBubblesBackground called only from background thread.
//_repaintBackgroundRequests.fire({});
return;
}
_bubblesBackground = CacheBackground({
@@ -459,7 +460,14 @@ void ChatTheme::setBubblesBackground(QImage image) {
_bubblesBackgroundPattern = PrepareBubblePattern(palette());
}
_bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
_repaintBackgroundRequests.fire({});
// setBubblesBackground called only from background thread.
//_repaintBackgroundRequests.fire({});
}
void ChatTheme::finishCreateOnMain() {
if (_bubblesBackgroundPattern) {
FinishBubblePatternOnMain(_bubblesBackgroundPattern.get());
}
}
ChatPaintContext ChatTheme::preparePaintContext(

View File

@@ -163,6 +163,7 @@ public:
[[nodiscard]] const BubblePattern *bubblesBackgroundPattern() const {
return _bubblesBackgroundPattern.get();
}
void finishCreateOnMain(); // Called on_main after setBubblesBackground.
[[nodiscard]] ChatPaintContext preparePaintContext(
not_null<const ChatStyle*> st,

View File

@@ -15,6 +15,56 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
void FillForwardOptions(
Fn<not_null<AbstractCheckView*>(
rpl::producer<QString> &&,
bool)> createView,
int count,
ForwardOptions options,
Fn<void(ForwardOptions)> optionsChanged,
rpl::lifetime &lifetime) {
Expects(optionsChanged != nullptr);
const auto names = createView(
(count == 1
? tr::lng_forward_show_sender
: tr::lng_forward_show_senders)(),
!options.dropNames);
const auto captions = options.hasCaptions
? createView(
(count == 1
? tr::lng_forward_show_caption
: tr::lng_forward_show_captions)(),
!options.dropCaptions).get()
: nullptr;
const auto notify = [=] {
optionsChanged({
.dropNames = !names->checked(),
.hasCaptions = options.hasCaptions,
.dropCaptions = (captions && !captions->checked()),
});
};
names->checkedChanges(
) | rpl::start_with_next([=](bool showNames) {
if (showNames && captions && !captions->checked()) {
captions->setChecked(true, anim::type::normal);
} else {
notify();
}
}, lifetime);
if (captions) {
captions->checkedChanges(
) | rpl::start_with_next([=](bool showCaptions) {
if (!showCaptions && names->checked()) {
names->setChecked(false, anim::type::normal);
} else {
notify();
}
}, lifetime);
}
}
void ForwardOptionsBox(
not_null<GenericBox*> box,
int count,
@@ -45,51 +95,23 @@ void ForwardOptionsBox(
st::boxRowPadding.left(),
st::boxRowPadding.right(),
st::boxRowPadding.bottom());
const auto names = box->addRow(
object_ptr<Ui::Checkbox>(
box.get(),
(count == 1
? tr::lng_forward_show_sender
: tr::lng_forward_show_senders)(),
!options.dropNames,
st::defaultBoxCheckbox),
checkboxPadding);
const auto captions = options.hasCaptions
? box->addRow(
auto createView = [&](rpl::producer<QString> &&text, bool checked) {
return box->addRow(
object_ptr<Ui::Checkbox>(
box.get(),
(count == 1
? tr::lng_forward_show_caption
: tr::lng_forward_show_captions)(),
!options.dropCaptions,
std::move(text),
checked,
st::defaultBoxCheckbox),
checkboxPadding)
: nullptr;
const auto notify = [=] {
optionsChanged({
.dropNames = !names->checked(),
.hasCaptions = options.hasCaptions,
.dropCaptions = (captions && !captions->checked()),
});
checkboxPadding)->checkView();
};
names->checkedChanges(
) | rpl::start_with_next([=](bool showNames) {
if (showNames && captions && !captions->checked()) {
captions->setChecked(true);
} else {
notify();
}
}, names->lifetime());
if (captions) {
captions->checkedChanges(
) | rpl::start_with_next([=](bool showCaptions) {
if (!showCaptions && names->checked()) {
names->setChecked(false);
} else {
notify();
}
}, captions->lifetime());
}
FillForwardOptions(
std::move(createView),
count,
options,
std::move(optionsChanged),
box->lifetime());
box->addRow(
object_ptr<Ui::LinkButton>(
box.get(),

View File

@@ -11,12 +11,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
class AbstractCheckView;
struct ForwardOptions {
bool dropNames = false;
bool hasCaptions = false;
bool dropCaptions = false;
};
void FillForwardOptions(
Fn<not_null<AbstractCheckView*>(
rpl::producer<QString> &&,
bool)> createView,
int count,
ForwardOptions options,
Fn<void(ForwardOptions)> optionsChanged,
rpl::lifetime &lifetime);
void ForwardOptionsBox(
not_null<GenericBox*> box,
int count,

View File

@@ -266,11 +266,6 @@ std::unique_ptr<BubblePattern> PrepareBubblePattern(
};
addShadow(result->corners[2]);
addShadow(result->corners[3]);
result->tailLeft = st::historyBubbleTailOutLeft.instance(Qt::white);
result->tailRight = st::historyBubbleTailOutRight.instance(Qt::white);
result->tailCache = QImage(
result->tailLeft.size(),
QImage::Format_ARGB32_Premultiplied);
result->cornerTopCache = QImage(
result->corners[0].size(),
QImage::Format_ARGB32_Premultiplied);
@@ -280,6 +275,14 @@ std::unique_ptr<BubblePattern> PrepareBubblePattern(
return result;
}
void FinishBubblePatternOnMain(not_null<BubblePattern*> pattern) {
pattern->tailLeft = st::historyBubbleTailOutLeft.instance(Qt::white);
pattern->tailRight = st::historyBubbleTailOutRight.instance(Qt::white);
pattern->tailCache = QImage(
pattern->tailLeft.size(),
QImage::Format_ARGB32_Premultiplied);
}
void PaintBubble(Painter &p, const SimpleBubble &args) {
if (!args.selected
&& args.outbg

View File

@@ -33,6 +33,7 @@ struct BubblePattern {
[[nodiscard]] std::unique_ptr<BubblePattern> PrepareBubblePattern(
not_null<const style::palette*> st);
void FinishBubblePatternOnMain(not_null<BubblePattern*> pattern);
struct SimpleBubble {
not_null<const ChatStyle*> st;

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/shadow.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/vertical_layout_reorder.h"
#include "ui/text/format_values.h" // Ui::FormatPhone
#include "ui/text/text_utilities.h"
#include "ui/special_buttons.h"
@@ -140,6 +141,27 @@ void ShowCallsBox(not_null<Window::SessionController*> window) {
window->show(Box<PeerListBox>(std::move(controller), initBox));
}
[[nodiscard]] std::vector<not_null<Main::Account*>> OrderedAccounts() {
const auto order = Core::App().settings().accountsOrder();
auto accounts = ranges::views::all(
Core::App().domain().accounts()
) | ranges::views::transform([](const Main::Domain::AccountWithIndex &a) {
return not_null{ a.account.get() };
}) | ranges::to_vector;
ranges::stable_sort(accounts, [&](
not_null<Main::Account*> a,
not_null<Main::Account*> b) {
const auto aIt = a->sessionExists()
? ranges::find(order, a->session().uniqueId())
: end(order);
const auto bIt = b->sessionExists()
? ranges::find(order, b->session().uniqueId())
: end(order);
return aIt < bIt;
});
return accounts;
}
} // namespace
namespace Window {
@@ -630,11 +652,11 @@ MainMenu::MainMenu(
tr::now,
lt_version,
currentVersionText()),
{}) // Link 1.
1) // Link 1.
.append(QChar(' '))
.append(QChar(8211))
.append(QChar(' '))
.append(Ui::Text::Link(tr::lng_menu_about(tr::now), {}))); // Link 2.
.append(Ui::Text::Link(tr::lng_menu_about(tr::now), 2))); // Link 2.
_version->setLink(
1,
std::make_shared<UrlClickHandler>(Core::App().changelogLink()));
@@ -817,11 +839,37 @@ void MainMenu::setupAccounts() {
}
void MainMenu::rebuildAccounts() {
const auto inner = _accounts->entity();
const auto inner = _accounts->entity()->insert(
1, // After skip with the fixed height.
object_ptr<Ui::VerticalLayout>(_accounts.get()));
auto count = 0;
for (const auto &[index, pointer] : Core::App().domain().accounts()) {
const auto account = pointer.get();
_reorder = std::make_unique<Ui::VerticalLayoutReorder>(inner);
_reorder->updates(
) | rpl::start_with_next([=](Ui::VerticalLayoutReorder::Single data) {
using State = Ui::VerticalLayoutReorder::State;
if (data.state == State::Started) {
++_reordering;
} else {
Ui::PostponeCall(inner, [=] {
--_reordering;
});
if (data.state == State::Applied) {
std::vector<uint64> order;
order.reserve(inner->count());
for (auto i = 0; i < inner->count(); i++) {
for (const auto &[account, button] : _watched) {
if (button.get() == inner->widgetAt(i)) {
order.push_back(account->session().uniqueId());
}
}
}
Core::App().settings().setAccountsOrder(order);
Core::App().saveSettings();
}
}
}, inner->lifetime());
for (const auto &account : OrderedAccounts()) {
auto i = _watched.find(account);
Assert(i != _watched.end());
@@ -829,16 +877,19 @@ void MainMenu::rebuildAccounts() {
if (!account->sessionExists()) {
button = nullptr;
} else if (!button) {
button.reset(inner->insert(
++count,
button.reset(inner->add(
object_ptr<AccountButton>(inner, account)));
button->setClickedCallback([=] {
if (_reordering) {
return;
}
if (account == &Core::App().domain().active()) {
closeLayer();
return;
}
auto activate = [=, guard = _accountSwitchGuard.make_guard()]{
if (guard) {
_reorder->finishReordering();
Core::App().domain().maybeActivate(account);
}
};
@@ -847,15 +898,15 @@ void MainMenu::rebuildAccounts() {
account,
std::move(activate));
});
} else {
++count;
}
}
inner->resizeToWidth(_accounts->width());
_addAccount->toggle(
(count < Main::Domain::kMaxAccounts),
(inner->count() < Main::Domain::kMaxAccounts),
anim::type::instant);
_reorder->start();
}
not_null<Ui::SlideWrap<Ui::RippleButton>*> MainMenu::setupAddAccount(
@@ -954,12 +1005,20 @@ void MainMenu::refreshMenu() {
}, &st::mainMenuContacts, &st::mainMenuContactsOver);
const auto fix = std::make_shared<QPointer<QAction>>();
*fix = _menu->addAction(qsl("Fix chats order"), [=] {
auto fixCallback = [=] {
(*fix)->setChecked(!(*fix)->isChecked());
_controller->session().settings().setSupportFixChatsOrder(
(*fix)->isChecked());
_controller->session().saveSettings();
}, &st::mainMenuFixOrder, &st::mainMenuFixOrderOver);
};
auto item = base::make_unique_q<Ui::Menu::Toggle>(
_menu,
st::mainMenu,
u"Fix chats order"_q,
std::move(fixCallback),
&st::mainMenuFixOrder,
&st::mainMenuFixOrderOver);
*fix = _menu->addAction(std::move(item));
(*fix)->setCheckable(true);
(*fix)->setChecked(
_controller->session().settings().supportFixChatsOrder());

View File

@@ -20,6 +20,7 @@ class UserpicButton;
class PopupMenu;
class ScrollArea;
class VerticalLayout;
class VerticalLayoutReorder;
class RippleButton;
class PlainShadow;
template <typename Widget>
@@ -94,6 +95,9 @@ private:
base::Timer _nightThemeSwitch;
base::unique_qptr<Ui::PopupMenu> _contextMenu;
std::unique_ptr<Ui::VerticalLayoutReorder> _reorder;
int _reordering = 0;
base::binary_guard _accountSwitchGuard;
QString _phoneText;

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_participants.h"
#include "lang/lang_keys.h"
#include "ui/boxes/confirm_box.h"
#include "base/options.h"
#include "boxes/delete_messages_box.h"
#include "boxes/max_invite_box.h"
#include "boxes/mute_settings_box.h"
@@ -69,11 +70,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QAction>
namespace Window {
const char kOptionViewProfileInChatsListContextMenu[] =
"view-profile-in-chats-list-context-menu";
namespace {
constexpr auto kArchivedToastDuration = crl::time(5000);
constexpr auto kMaxUnreadWithoutConfirmation = 10000;
base::options::toggle ViewProfileInChatsListContextMenu({
.id = kOptionViewProfileInChatsListContextMenu,
.name = "Add \"View Profile\"",
.description = "Add \"View Profile\" to context menu in chats list",
});
void SetActionText(not_null<QAction*> action, rpl::producer<QString> &&text) {
const auto lifetime = Ui::CreateChild<rpl::lifetime>(action.get());
std::move(
@@ -736,6 +747,9 @@ void Filler::fillChatsListActions() {
addHidePromotion();
addToggleArchive();
addTogglePin();
if (ViewProfileInChatsListContextMenu.value()) {
addInfo();
}
addToggleMute();
addToggleUnreadMark();
// addToFolder();

View File

@@ -35,6 +35,8 @@ class Controller;
class SessionController;
class SessionNavigation;
extern const char kOptionViewProfileInChatsListContextMenu[];
using PeerMenuCallback = Fn<QAction*(
const QString &text,
Fn<void()> handler,

View File

@@ -795,6 +795,16 @@ bool SessionController::jumpToChatListEntry(Dialogs::RowDescriptor row) {
return false;
}
Dialogs::RowDescriptor SessionController::resolveChatNext(
Dialogs::RowDescriptor from) const {
return content()->resolveChatNext(from);
}
Dialogs::RowDescriptor SessionController::resolveChatPrevious(
Dialogs::RowDescriptor from) const {
return content()->resolveChatPrevious(from);
}
void SessionController::pushToChatEntryHistory(Dialogs::RowDescriptor row) {
if (!_chatEntryHistory.empty()
&& _chatEntryHistory[_chatEntryHistoryPosition] == row) {
@@ -1632,6 +1642,7 @@ void SessionController::cacheChatTheme(
this,
result = std::make_shared<Ui::ChatTheme>(std::move(descriptor))
]() mutable {
result->finishCreateOnMain();
cacheChatThemeDone(std::move(result));
});
});

View File

@@ -301,6 +301,12 @@ public:
rpl::producer<Dialogs::RowDescriptor> activeChatEntryValue() const;
rpl::producer<Dialogs::Key> activeChatValue() const;
bool jumpToChatListEntry(Dialogs::RowDescriptor row);
[[nodiscard]] Dialogs::RowDescriptor resolveChatNext(
Dialogs::RowDescriptor from = {}) const;
[[nodiscard]] Dialogs::RowDescriptor resolveChatPrevious(
Dialogs::RowDescriptor from = {}) const;
void showEditPeerBox(PeerData *peer);
void enableGifPauseReason(GifPauseReason reason);

View File

@@ -286,19 +286,6 @@ RUN make DESTDIR="$LibrariesPath/libXfixes-cache" install
WORKDIR ..
RUN rm -rf libxfixes
FROM builder AS libXv
COPY --from=libXext ${LibrariesPath}/libXext-cache /
RUN git clone -b libXv-1.0.11 --depth=1 $GIT_FREEDESKTOP/libxv.git
WORKDIR libxv
RUN ./autogen.sh --enable-static
RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/libXv-cache" install
WORKDIR ..
RUN rm -rf libxv
FROM builder AS libXrandr
RUN git clone -b libXrandr-1.5.2 --depth=1 $GIT_FREEDESKTOP/libxrandr.git
@@ -367,9 +354,6 @@ FROM builder AS ffmpeg
COPY --from=opus ${LibrariesPath}/opus-cache /
COPY --from=libvpx ${LibrariesPath}/libvpx-cache /
COPY --from=libXext ${LibrariesPath}/libXext-cache /
COPY --from=libXfixes ${LibrariesPath}/libXfixes-cache /
COPY --from=libXv ${LibrariesPath}/libXv-cache /
RUN mkdir ffmpeg
WORKDIR ffmpeg
@@ -389,7 +373,6 @@ RUN ./configure \
--disable-everything \
--enable-libopus \
--enable-libvpx \
--enable-xlib \
--enable-protocol=file \
--enable-decoder=aac \
--enable-decoder=aac_fixed \
@@ -704,6 +687,7 @@ WORKDIR ..
FROM builder AS webrtc
COPY --from=libepoxy ${LibrariesPath}/libepoxy-cache /
COPY --from=mozjpeg ${LibrariesPath}/mozjpeg-cache /
COPY --from=opus ${LibrariesPath}/opus-cache /
COPY --from=libvpx ${LibrariesPath}/libvpx-cache /
@@ -716,7 +700,7 @@ RUN mkdir tg_owt
WORKDIR tg_owt
RUN git init
RUN git remote add origin $GIT/desktop-app/tg_owt.git
RUN git fetch --depth=1 origin 1fd131d37777c445b58cad3889313a7c26ffc2ee
RUN git fetch --depth=1 origin 347400dc2377b16be702397ff8db44d5739d2650
RUN git reset --hard FETCH_HEAD
RUN git submodule init
RUN git submodule update
@@ -760,7 +744,6 @@ COPY --from=xcb-keysyms ${LibrariesPath}/xcb-keysyms-cache /
COPY --from=xcb-render-util ${LibrariesPath}/xcb-render-util-cache /
COPY --from=libXext ${LibrariesPath}/libXext-cache /
COPY --from=libXfixes ${LibrariesPath}/libXfixes-cache /
COPY --from=libXv ${LibrariesPath}/libXv-cache /
COPY --from=libXtst ${LibrariesPath}/libXtst-cache /
COPY --from=libXrandr ${LibrariesPath}/libXrandr-cache /
COPY --from=libXrender ${LibrariesPath}/libXrender-cache /

View File

@@ -4,6 +4,6 @@ pushd `dirname $0` > /dev/null
FullScriptPath=`pwd`
popd > /dev/null
python $FullScriptPath/set_version.py $1
python3 $FullScriptPath/set_version.py $1
exit

View File

@@ -1,7 +1,7 @@
AppVersion 3005000
AppVersion 3005002
AppVersionStrMajor 3.5
AppVersionStrSmall 3.5
AppVersionStr 3.5.0
AppVersionStrSmall 3.5.2
AppVersionStr 3.5.2
BetaChannel 0
AlphaVersion 0
AppVersionOriginal 3.5
AppVersionOriginal 3.5.2

View File

@@ -1,4 +1,15 @@
3.5 (31.01.21)
3.5.2 (06.02.22)
- Fix a freeze in audio playback on Linux.
- Fix a crash in screen sharing initialization on Linux.
3.5.1 (04.02.22)
- Keep the screen on while watching a video or participating in a video chat.
- Save experimental settings between relaunches.
- Bug fixes and other minor improvements.
3.5 (31.01.22)
- Use a new type of detailed stickers with smooth animations.
- Create new sets by sending .webm videos to @stickers.
@@ -11,31 +22,31 @@
- The app will warn you before closing if you are uploading photos or files to a chat.
- Enjoy better screencast quality in video chats.
3.4.8 (19.01.21)
3.4.8 (19.01.22)
- Nice animations in reactions.
- Add snap layouts support on Windows 11.
- Bug fixes and other minor improvements.
3.4.7 beta (19.01.21)
3.4.7 beta (19.01.22)
- Fix a crash on launch on Windows.
3.4.6 beta (18.01.21)
3.4.6 beta (18.01.22)
- Add snap layouts support on Windows 11.
- Fix crash in drafts after accounts switching.
3.4.5 beta (16.01.21)
3.4.5 beta (16.01.22)
- Fix crash in monospace blocks processing.
- Fix reaction animations stopping after an hour uptime.
3.4.4 beta (14.01.21)
3.4.4 beta (14.01.22)
- Nice animations in reactions.
3.4.3 (03.01.21)
3.4.3 (03.01.22)
- Bug fixes and other minor improvements.

2
cmake

Submodule cmake updated: b662d784ff...3604a7f023

View File

@@ -447,13 +447,15 @@ parts:
webrtc:
source: https://github.com/desktop-app/tg_owt.git
source-depth: 1
source-commit: 6372a0848f8dd84013e291b44f9d1427fde3a90e
source-commit: d618d0b5ff3e59bea0143e6070481f8f4316a428
plugin: cmake
build-packages:
- yasm
- libglib2.0-dev
#- libdrm-dev
#- libgbm-dev
#- libglib2.0-dev
- libopus-dev
- libpipewire-0.2-dev
#- libpipewire-0.2-dev
- libssl-dev
- libx11-dev
- libxcomposite-dev
@@ -464,9 +466,11 @@ parts:
- libxrandr-dev
- libxtst-dev
stage-packages:
- libglib2.0-0
#- libdrm2
#- libgbm1
#- libglib2.0-0
- libopus0
- libpipewire-0.2-1
#- libpipewire-0.2-1
- libssl1.1
- libx11-6
- libxcomposite1
@@ -482,11 +486,13 @@ parts:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DJPEG_LIBRARY_RELEASE=$SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so
- -DJPEG_INCLUDE_DIR=$SNAPCRAFT_STAGE/usr/include
- -DTG_OWT_USE_PIPEWIRE=OFF
prime:
- -./usr/include
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/cmake
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/*.a
after:
#- epoxy
- ffmpeg
- mozjpeg
- vpx