Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ce3f9bb71 | ||
|
|
a5483a5113 | ||
|
|
7f5e646db5 | ||
|
|
666251f23e | ||
|
|
d89d8b09da | ||
|
|
0aa20b4479 | ||
|
|
8658dba97a | ||
|
|
20c911651f | ||
|
|
bef20ba4a2 | ||
|
|
ee325031a0 | ||
|
|
b57549546d | ||
|
|
1e4d278604 | ||
|
|
80aa596310 | ||
|
|
4913288061 | ||
|
|
1a43cd8a67 | ||
|
|
bcb0511083 | ||
|
|
2dda044dd1 | ||
|
|
da18ab3d41 | ||
|
|
f26cae8807 | ||
|
|
641bb01ba2 | ||
|
|
02c9b61840 | ||
|
|
99e8d22c51 | ||
|
|
cd9b3368da | ||
|
|
f918c6bb83 | ||
|
|
28dff5ba6d | ||
|
|
4d978f5b36 | ||
|
|
ef41878815 | ||
|
|
6a663932f3 | ||
|
|
67c538ae8f | ||
|
|
38137e16a0 | ||
|
|
9c01295521 | ||
|
|
c7b6db00ca | ||
|
|
7f0bdc5d36 | ||
|
|
5f6d8f74dd | ||
|
|
9086319b99 | ||
|
|
37cd4f51eb | ||
|
|
74416568d6 | ||
|
|
a8c3d6c39b | ||
|
|
5939c2dbfc | ||
|
|
4bef1e9f59 | ||
|
|
18bf48bf90 | ||
|
|
b415b293cf | ||
|
|
3a1bb1966d | ||
|
|
d4b686ff65 | ||
|
|
8148974e9f | ||
|
|
eaa2573c66 | ||
|
|
4d315f8e61 | ||
|
|
e5981ed22b |
@@ -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\".";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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([=] {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1561,7 +1561,11 @@ void ComposeControls::initTabbedSelector() {
|
||||
}
|
||||
|
||||
_tabbedSelectorToggle->addClickHandler([=] {
|
||||
toggleTabbedSelectorMode();
|
||||
if (_tabbedPanel && _tabbedPanel->isHidden()) {
|
||||
_tabbedPanel->showAnimated();
|
||||
} else {
|
||||
toggleTabbedSelectorMode();
|
||||
}
|
||||
});
|
||||
|
||||
const auto selector = _window->tabbedSelector();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -19,6 +19,7 @@ enum class Error;
|
||||
|
||||
class FileDelegate {
|
||||
public:
|
||||
[[nodiscard]] virtual Mode fileOpenMode() = 0;
|
||||
[[nodiscard]] virtual bool fileReady(
|
||||
int headerSize,
|
||||
Stream &&video,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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({});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -92,7 +92,7 @@ std::vector<Type> Compute() {
|
||||
}
|
||||
}
|
||||
|
||||
ranges::unique(result);
|
||||
result = result | ranges::views::unique | ranges::to_vector;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ WaylandIntegration *WaylandIntegration::Instance() {
|
||||
return &instance;
|
||||
}
|
||||
|
||||
QString WaylandIntegration::nativeHandle(QWindow *window) {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WaylandIntegration::skipTaskbarSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -35,6 +35,8 @@ class Controller;
|
||||
class SessionController;
|
||||
class SessionNavigation;
|
||||
|
||||
extern const char kOptionViewProfileInChatsListContextMenu[];
|
||||
|
||||
using PeerMenuCallback = Fn<QAction*(
|
||||
const QString &text,
|
||||
Fn<void()> handler,
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
2
Telegram/ThirdParty/tgcalls
vendored
2
Telegram/ThirdParty/tgcalls
vendored
Submodule Telegram/ThirdParty/tgcalls updated: 35d483f84b...6d619124c5
@@ -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 /
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Submodule Telegram/lib_base updated: 5d99fef79a...1ec9a06352
Submodule Telegram/lib_ui updated: 6af9023eb7...7e1effeeeb
@@ -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
2
cmake
Submodule cmake updated: b662d784ff...3604a7f023
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user