Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3660f1ed8 | ||
|
|
70570e0987 | ||
|
|
baccec623d | ||
|
|
093d89db83 | ||
|
|
48fea47d16 | ||
|
|
8500bf6073 | ||
|
|
69b41fadef | ||
|
|
d44f076f0b | ||
|
|
3637fec397 | ||
|
|
e109da037e | ||
|
|
3a659b4b54 | ||
|
|
7e4dff25e9 | ||
|
|
da74fe4248 | ||
|
|
294f849775 | ||
|
|
88951e9e5c | ||
|
|
153b91248d | ||
|
|
7977331d8b | ||
|
|
8ec60e0321 | ||
|
|
dcebefe2bb | ||
|
|
93a88b54ad | ||
|
|
07f94cc184 | ||
|
|
9a0edbd0c5 | ||
|
|
c935f1bb16 | ||
|
|
3cd05a34d9 | ||
|
|
223681d2da | ||
|
|
c3c1759f3c | ||
|
|
bff3291631 | ||
|
|
e85394b520 | ||
|
|
5cea5fc4e6 | ||
|
|
39742d22d9 | ||
|
|
d60a89f354 | ||
|
|
90f90a4ca3 | ||
|
|
776c099a25 | ||
|
|
f2b434d82b | ||
|
|
03e8d28456 | ||
|
|
9b70f24e91 | ||
|
|
8fd1d16db6 | ||
|
|
36acf60f7e | ||
|
|
ce1b94eb16 | ||
|
|
e8affa85b0 | ||
|
|
d782ea63f8 | ||
|
|
11b965e82e | ||
|
|
a2187a1d2b | ||
|
|
7cd626c9fd | ||
|
|
37b8551760 | ||
|
|
0cd8453b00 | ||
|
|
0b4d0b83c2 | ||
|
|
0783a682dc | ||
|
|
fb9a34a069 | ||
|
|
b4af805521 | ||
|
|
1f80c297ec | ||
|
|
019e691fbb | ||
|
|
683d78c64a | ||
|
|
0aaa88cb79 | ||
|
|
0cb8f2cc85 | ||
|
|
03a5619d61 | ||
|
|
f1236edf5b | ||
|
|
0b98cfbfec | ||
|
|
062c451c27 | ||
|
|
b13e5ddce9 | ||
|
|
4ad0837661 | ||
|
|
4695ccfdb8 | ||
|
|
813470ff25 | ||
|
|
2d50c61703 | ||
|
|
2dd99a535c | ||
|
|
57ca6e23b9 | ||
|
|
153c949a88 | ||
|
|
76457c1e52 | ||
|
|
bcc333c2e1 | ||
|
|
fe8bc30645 | ||
|
|
f7b72bffe2 | ||
|
|
5538c5eace | ||
|
|
34ec1c371c | ||
|
|
cb2d77d386 | ||
|
|
eb11185de7 | ||
|
|
4e5c81dac2 | ||
|
|
5feb381cb2 | ||
|
|
2a1096d83c | ||
|
|
d202a0cd06 | ||
|
|
3251b8bf6e | ||
|
|
f51055d606 | ||
|
|
9c86755546 | ||
|
|
0b5213a9cb |
@@ -840,6 +840,8 @@ PRIVATE
|
||||
platform/linux/linux_open_with_dialog.h
|
||||
platform/linux/linux_wayland_integration.cpp
|
||||
platform/linux/linux_wayland_integration.h
|
||||
platform/linux/linux_xdp_file_dialog.cpp
|
||||
platform/linux/linux_xdp_file_dialog.h
|
||||
platform/linux/linux_xlib_helper.cpp
|
||||
platform/linux/linux_xlib_helper.h
|
||||
platform/linux/file_utilities_linux.cpp
|
||||
@@ -1132,6 +1134,8 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
platform/linux/linux_gsd_media_keys.h
|
||||
platform/linux/linux_notification_service_watcher.cpp
|
||||
platform/linux/linux_notification_service_watcher.h
|
||||
platform/linux/linux_xdp_file_dialog.cpp
|
||||
platform/linux/linux_xdp_file_dialog.h
|
||||
platform/linux/notifications_manager_linux.cpp
|
||||
)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 100 B |
|
Before Width: | Height: | Size: 125 B |
|
Before Width: | Height: | Size: 139 B |
|
Before Width: | Height: | Size: 103 B |
|
Before Width: | Height: | Size: 127 B |
|
Before Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 295 B |
|
Before Width: | Height: | Size: 559 B |
|
Before Width: | Height: | Size: 927 B |
|
Before Width: | Height: | Size: 572 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -827,6 +827,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_delete_contact" = "Delete";
|
||||
"lng_profile_set_group_photo" = "Set Photo";
|
||||
"lng_profile_add_participant" = "Add Members";
|
||||
"lng_profile_add_via_link" = "Invite via Link";
|
||||
"lng_profile_view_channel" = "View Channel";
|
||||
"lng_profile_view_discussion" = "View discussion";
|
||||
"lng_profile_join_channel" = "Join Channel";
|
||||
@@ -1582,6 +1583,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_copy_selected_items" = "Copy Selected as Text";
|
||||
"lng_context_forward_selected" = "Forward Selected";
|
||||
"lng_context_send_now_selected" = "Send selected now";
|
||||
"lng_context_reschedule_selected" = "Reschedule Selected";
|
||||
"lng_context_delete_selected" = "Delete Selected";
|
||||
"lng_context_clear_selection" = "Clear Selection";
|
||||
"lng_send_image_empty" = "Could not send an empty file: {name}";
|
||||
@@ -1720,7 +1722,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_mediaview_downloads" = "Downloads";
|
||||
"lng_mediaview_video_loading" = "Loading - {percent}";
|
||||
"lng_mediaview_playback_speed" = "Playback speed";
|
||||
"lng_mediaview_playback_speed_normal" = "Normal";
|
||||
"lng_mediaview_rotate_video" = "Rotate video";
|
||||
|
||||
"lng_theme_preview_title" = "Theme Preview";
|
||||
|
||||
@@ -1196,7 +1196,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2
|
||||
|
||||
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
|
||||
|
||||
groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant;
|
||||
groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant;
|
||||
|
||||
phone.groupCall#66ab0bfc call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string users:Vector<User> = phone.GroupCall;
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.5.8.0" />
|
||||
Version="2.5.9.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
<Description>Telegram Desktop official messenger</Description>
|
||||
<Logo>Assets\logo\logo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,5,8,0
|
||||
PRODUCTVERSION 2,5,8,0
|
||||
FILEVERSION 2,5,9,0
|
||||
PRODUCTVERSION 2,5,9,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.5.8.0"
|
||||
VALUE "FileVersion", "2.5.9.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.5.8.0"
|
||||
VALUE "ProductVersion", "2.5.9.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,5,8,0
|
||||
PRODUCTVERSION 2,5,8,0
|
||||
FILEVERSION 2,5,9,0
|
||||
PRODUCTVERSION 2,5,9,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", "2.5.8.0"
|
||||
VALUE "FileVersion", "2.5.9.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.5.8.0"
|
||||
VALUE "ProductVersion", "2.5.9.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -128,9 +128,19 @@ void Authorizations::requestTerminate(
|
||||
const auto send = [&](auto request) {
|
||||
_api.request(
|
||||
std::move(request)
|
||||
).done(
|
||||
std::move(done)
|
||||
).fail(
|
||||
).done([=, done = std::move(done)](const MTPBool &result) {
|
||||
done(result);
|
||||
if (mtpIsTrue(result)) {
|
||||
if (hash) {
|
||||
_list.erase(
|
||||
ranges::remove(_list, *hash, &Entry::hash),
|
||||
end(_list));
|
||||
} else {
|
||||
_list.clear();
|
||||
}
|
||||
_listChanges.fire({});
|
||||
}
|
||||
}).fail(
|
||||
std::move(fail)
|
||||
).send();
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/application.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -109,7 +110,7 @@ void AboutBox::showVersionHistory() {
|
||||
|
||||
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
|
||||
} else {
|
||||
UrlClickHandler::Open(qsl("https://desktop.telegram.org/changelog"));
|
||||
UrlClickHandler::Open(Core::App().changelogLink());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -906,6 +906,29 @@ pollResultsShowMore: SettingsButton(defaultSettingsButton) {
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
|
||||
inviteViaLinkButton: SettingsButton(defaultSettingsButton) {
|
||||
textFg: lightButtonFg;
|
||||
textFgOver: lightButtonFgOver;
|
||||
textBg: windowBg;
|
||||
textBgOver: windowBgOver;
|
||||
|
||||
font: font(14px semibold);
|
||||
|
||||
height: 20px;
|
||||
padding: margins(74px, 8px, 8px, 9px);
|
||||
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
inviteViaLinkIcon: icon {{ "info/edit/group_manage_links", lightButtonFg }};
|
||||
inviteViaLinkIconPosition: point(23px, 2px);
|
||||
peerListWithInviteViaLink: PeerList(peerListBox) {
|
||||
padding: margins(
|
||||
0px,
|
||||
0px,
|
||||
0px,
|
||||
membersMarginBottom);
|
||||
}
|
||||
|
||||
scheduleHeight: 95px;
|
||||
scheduleDateTop: 38px;
|
||||
scheduleDateField: InputField(defaultInputField) {
|
||||
|
||||
@@ -1161,11 +1161,7 @@ void PeerListContent::enterEventHook(QEvent *e) {
|
||||
|
||||
void PeerListContent::leaveEventHook(QEvent *e) {
|
||||
setMouseTracking(false);
|
||||
if (_mouseSelection) {
|
||||
setSelected(Selected());
|
||||
_mouseSelection = false;
|
||||
_lastMousePosition = std::nullopt;
|
||||
}
|
||||
mouseLeftGeometry();
|
||||
}
|
||||
|
||||
void PeerListContent::mouseMoveEvent(QMouseEvent *e) {
|
||||
@@ -1500,6 +1496,14 @@ void PeerListContent::clearSelection() {
|
||||
setSelected(Selected());
|
||||
}
|
||||
|
||||
void PeerListContent::mouseLeftGeometry() {
|
||||
if (_mouseSelection) {
|
||||
setSelected(Selected());
|
||||
_mouseSelection = false;
|
||||
_lastMousePosition = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListContent::loadProfilePhotos() {
|
||||
if (_visibleTop >= _visibleBottom) return;
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ public:
|
||||
virtual void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetAboveSearchWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) = 0;
|
||||
virtual void peerListMouseLeftGeometry() = 0;
|
||||
virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
|
||||
virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
|
||||
virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0;
|
||||
@@ -541,6 +542,8 @@ public:
|
||||
void setHideEmpty(bool hide);
|
||||
void refreshRows();
|
||||
|
||||
void mouseLeftGeometry();
|
||||
|
||||
void setSearchMode(PeerListSearchMode mode);
|
||||
void changeCheckState(
|
||||
not_null<PeerListRow*> row,
|
||||
@@ -804,6 +807,9 @@ public:
|
||||
void peerListSetSearchMode(PeerListSearchMode mode) override {
|
||||
_content->setSearchMode(mode);
|
||||
}
|
||||
void peerListMouseLeftGeometry() override {
|
||||
_content->mouseLeftGeometry();
|
||||
}
|
||||
void peerListSortRows(
|
||||
Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) override {
|
||||
_content->reorderRows([&](
|
||||
|
||||
@@ -113,7 +113,7 @@ object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
|
||||
box->addLeftButton(
|
||||
tr::lng_profile_add_contact(),
|
||||
[=] { controller->widget()->showAddContact(); });
|
||||
[=] { controller->showAddContact(); });
|
||||
};
|
||||
return Box<PeerListBox>(
|
||||
std::make_unique<ContactsBoxController>(
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
#include "boxes/peers/edit_peer_type_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_channel.h"
|
||||
@@ -18,15 +19,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_changes.h"
|
||||
#include "history/history.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "apiwrap.h"
|
||||
#include "facades.h" // Ui::showPeerHistory
|
||||
#include "app.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -68,6 +73,9 @@ AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
: ContactsBoxController(&peer->session())
|
||||
, _peer(peer)
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
if (needsInviteLinkButton()) {
|
||||
setStyleOverrides(&st::peerListWithInviteViaLink);
|
||||
}
|
||||
subscribeToMigration();
|
||||
}
|
||||
|
||||
@@ -166,6 +174,44 @@ void AddParticipantsBoxController::updateTitle() {
|
||||
).arg(session().serverConfig().megagroupSizeMax);
|
||||
delegate()->peerListSetTitle(tr::lng_profile_add_participant());
|
||||
delegate()->peerListSetAdditionalTitle(rpl::single(additional));
|
||||
|
||||
addInviteLinkButton();
|
||||
}
|
||||
|
||||
bool AddParticipantsBoxController::needsInviteLinkButton() {
|
||||
if (!_peer) {
|
||||
return false;
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
return channel->canHaveInviteLink();
|
||||
}
|
||||
return _peer->asChat()->canHaveInviteLink();
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::addInviteLinkButton() {
|
||||
if (!needsInviteLinkButton()) {
|
||||
return;
|
||||
}
|
||||
auto button = object_ptr<Ui::PaddingWrap<Ui::SettingsButton>>(
|
||||
nullptr,
|
||||
object_ptr<Ui::SettingsButton>(
|
||||
nullptr,
|
||||
tr::lng_profile_add_via_link(),
|
||||
st::inviteViaLinkButton),
|
||||
style::margins(0, st::membersMarginTop, 0, 0));
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
button->entity(),
|
||||
st::inviteViaLinkIcon,
|
||||
st::inviteViaLinkIconPosition);
|
||||
button->entity()->setClickedCallback([=] {
|
||||
Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther);
|
||||
});
|
||||
button->entity()->events(
|
||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::Enter);
|
||||
}) | rpl::start_with_next([=] {
|
||||
delegate()->peerListMouseLeftGeometry();
|
||||
}, button->lifetime());
|
||||
delegate()->peerListSetAboveWidget(std::move(button));
|
||||
}
|
||||
|
||||
bool AddParticipantsBoxController::inviteSelectedUsers(
|
||||
@@ -283,15 +329,21 @@ Main::Session &AddSpecialBoxController::session() const {
|
||||
}
|
||||
|
||||
void AddSpecialBoxController::subscribeToMigration() {
|
||||
const auto chat = _peer->asChat();
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
chat,
|
||||
lifetime(),
|
||||
[=](not_null<ChannelData*> channel) { migrate(channel); });
|
||||
[=](not_null<ChannelData*> channel) { migrate(chat, channel); });
|
||||
}
|
||||
|
||||
void AddSpecialBoxController::migrate(not_null<ChannelData*> channel) {
|
||||
void AddSpecialBoxController::migrate(
|
||||
not_null<ChatData*> chat,
|
||||
not_null<ChannelData*> channel) {
|
||||
_peer = channel;
|
||||
_additional.migrate(channel);
|
||||
_additional.migrate(chat, channel);
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> AddSpecialBoxController::createSearchRow(
|
||||
|
||||
@@ -44,6 +44,7 @@ protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) override;
|
||||
virtual bool needsInviteLinkButton();
|
||||
|
||||
private:
|
||||
static void Start(
|
||||
@@ -52,6 +53,7 @@ private:
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated);
|
||||
|
||||
void addInviteLinkButton();
|
||||
bool inviteSelectedUsers(not_null<PeerListBox*> box) const;
|
||||
void subscribeToMigration();
|
||||
int alreadyInCount() const;
|
||||
@@ -119,7 +121,7 @@ private:
|
||||
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
|
||||
|
||||
void subscribeToMigration();
|
||||
void migrate(not_null<ChannelData*> channel);
|
||||
void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel);
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
MTP::Sender _api;
|
||||
|
||||
@@ -208,10 +208,10 @@ EditAdminBox::EditAdminBox(
|
||||
, _oldRank(rank) {
|
||||
}
|
||||
|
||||
MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) {
|
||||
const auto defaultRights = peer->isChat()
|
||||
? ChatData::DefaultAdminRights()
|
||||
: peer->isMegagroup()
|
||||
MTPChatAdminRights EditAdminBox::defaultRights() const {
|
||||
const auto flags = peer()->isChat()
|
||||
? peer()->asChat()->defaultAdminRights(user())
|
||||
: peer()->isMegagroup()
|
||||
? (Flag::f_change_info
|
||||
| Flag::f_delete_messages
|
||||
| Flag::f_ban_users
|
||||
@@ -223,7 +223,7 @@ MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) {
|
||||
| Flag::f_edit_messages
|
||||
| Flag::f_delete_messages
|
||||
| Flag::f_invite_users);
|
||||
return MTP_chatAdminRights(MTP_flags(defaultRights));
|
||||
return MTP_chatAdminRights(MTP_flags(flags));
|
||||
}
|
||||
|
||||
void EditAdminBox::prepare() {
|
||||
@@ -242,7 +242,7 @@ void EditAdminBox::prepare() {
|
||||
|
||||
const auto chat = peer()->asChat();
|
||||
const auto channel = peer()->asChannel();
|
||||
const auto prepareRights = hadRights ? _oldRights : Defaults(peer());
|
||||
const auto prepareRights = hadRights ? _oldRights : defaultRights();
|
||||
const auto disabledByDefaults = (channel && !channel->isMegagroup())
|
||||
? MTPDchatAdminRights::Flags(0)
|
||||
: DisabledByDefaultRestrictions(peer());
|
||||
@@ -264,12 +264,12 @@ void EditAdminBox::prepare() {
|
||||
result.emplace(
|
||||
disabledByDefaults,
|
||||
tr::lng_rights_permission_for_all(tr::now));
|
||||
if (const auto channel = peer()->asChannel()) {
|
||||
if (amCreator() && user()->isSelf()) {
|
||||
result.emplace(
|
||||
~Flag::f_anonymous,
|
||||
tr::lng_rights_permission_cant_edit(tr::now));
|
||||
} else if (!channel->amCreator()) {
|
||||
if (amCreator() && user()->isSelf()) {
|
||||
result.emplace(
|
||||
~Flag::f_anonymous,
|
||||
tr::lng_rights_permission_cant_edit(tr::now));
|
||||
} else if (const auto channel = peer()->asChannel()) {
|
||||
if (!channel->amCreator()) {
|
||||
result.emplace(
|
||||
~channel->adminRights(),
|
||||
tr::lng_rights_permission_cant_edit(tr::now));
|
||||
@@ -611,9 +611,9 @@ void EditRestrictedBox::prepare() {
|
||||
const auto defaultRestrictions = chat
|
||||
? chat->defaultRestrictions()
|
||||
: channel->defaultRestrictions();
|
||||
const auto prepareRights = (_oldRights.c_chatBannedRights().vflags().v
|
||||
const auto prepareRights = _oldRights.c_chatBannedRights().vflags().v
|
||||
? _oldRights
|
||||
: Defaults(peer()));
|
||||
: defaultRights();
|
||||
const auto prepareFlags = FixDependentRestrictions(
|
||||
prepareRights.c_chatBannedRights().vflags().v
|
||||
| defaultRestrictions
|
||||
@@ -680,7 +680,7 @@ void EditRestrictedBox::prepare() {
|
||||
}
|
||||
}
|
||||
|
||||
MTPChatBannedRights EditRestrictedBox::Defaults(not_null<PeerData*> peer) {
|
||||
MTPChatBannedRights EditRestrictedBox::defaultRights() const {
|
||||
return MTP_chatBannedRights(MTP_flags(0), MTP_int(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
using Flag = MTPDchatAdminRights::Flag;
|
||||
using Flags = MTPDchatAdminRights::Flags;
|
||||
|
||||
static MTPChatAdminRights Defaults(not_null<PeerData*> peer);
|
||||
[[nodiscard]] MTPChatAdminRights defaultRights() const;
|
||||
|
||||
not_null<Ui::InputField*> addRankInput();
|
||||
void transferOwnership();
|
||||
@@ -144,7 +144,7 @@ private:
|
||||
using Flag = MTPDchatBannedRights::Flag;
|
||||
using Flags = MTPDchatBannedRights::Flags;
|
||||
|
||||
static MTPChatBannedRights Defaults(not_null<PeerData*> peer);
|
||||
[[nodiscard]] MTPChatBannedRights defaultRights() const;
|
||||
|
||||
bool canSave() const {
|
||||
return !!_saveCallback;
|
||||
|
||||
@@ -224,7 +224,7 @@ Fn<void(
|
||||
const MTPDchatAdminRights &data) {
|
||||
return data.vflags().v;
|
||||
});
|
||||
if (flags == ChatData::DefaultAdminRights() && rank.isEmpty()) {
|
||||
if (flags == chat->defaultAdminRights(user) && rank.isEmpty()) {
|
||||
saveChatAdmin(true);
|
||||
} else if (!flags) {
|
||||
saveChatAdmin(false);
|
||||
@@ -359,7 +359,9 @@ bool ParticipantsAdditionalData::canRemoveUser(
|
||||
if (canRestrictUser(user)) {
|
||||
return true;
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
return !user->isSelf() && chat->invitedByMe.contains(user);
|
||||
return !user->isSelf()
|
||||
&& chat->invitedByMe.contains(user)
|
||||
&& (chat->amCreator() || !_admins.contains(user));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -370,7 +372,7 @@ auto ParticipantsAdditionalData::adminRights(
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
return _admins.contains(user)
|
||||
? std::make_optional(MTPChatAdminRights(MTP_chatAdminRights(
|
||||
MTP_flags(ChatData::DefaultAdminRights()))))
|
||||
MTP_flags(chat->defaultAdminRights(user)))))
|
||||
: std::nullopt;
|
||||
}
|
||||
const auto i = _adminRights.find(user);
|
||||
@@ -671,14 +673,16 @@ UserData *ParticipantsAdditionalData::applyBanned(
|
||||
return user;
|
||||
}
|
||||
|
||||
void ParticipantsAdditionalData::migrate(not_null<ChannelData*> channel) {
|
||||
void ParticipantsAdditionalData::migrate(
|
||||
not_null<ChatData*> chat,
|
||||
not_null<ChannelData*> channel) {
|
||||
_peer = channel;
|
||||
fillFromChannel(channel);
|
||||
|
||||
for (const auto user : _admins) {
|
||||
_adminRights.emplace(
|
||||
user,
|
||||
MTP_chatAdminRights(MTP_flags(ChatData::DefaultAdminRights())));
|
||||
MTP_chatAdminRights(MTP_flags(chat->defaultAdminRights(user))));
|
||||
if (channel->amCreator()) {
|
||||
_adminCanEdit.emplace(user);
|
||||
}
|
||||
@@ -1889,15 +1893,21 @@ void ParticipantsBoxController::refreshCustomStatus(
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::subscribeToMigration() {
|
||||
const auto chat = _peer->asChat();
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
chat,
|
||||
lifetime(),
|
||||
[=](not_null<ChannelData*> channel) { migrate(channel); });
|
||||
[=](not_null<ChannelData*> channel) { migrate(chat, channel); });
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::migrate(not_null<ChannelData*> channel) {
|
||||
void ParticipantsBoxController::migrate(
|
||||
not_null<ChatData*> chat,
|
||||
not_null<ChannelData*> channel) {
|
||||
_peer = channel;
|
||||
_additional.migrate(channel);
|
||||
_additional.migrate(chat, channel);
|
||||
subscribeToCreatorChange(channel);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
[[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const;
|
||||
[[nodiscard]] UserData *restrictedBy(not_null<UserData*> user) const;
|
||||
|
||||
void migrate(not_null<ChannelData*> channel);
|
||||
void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel);
|
||||
|
||||
private:
|
||||
UserData *applyCreator(const MTPDchannelParticipantCreator &data);
|
||||
@@ -242,7 +242,7 @@ private:
|
||||
void recomputeTypeFor(not_null<UserData*> user);
|
||||
|
||||
void subscribeToMigration();
|
||||
void migrate(not_null<ChannelData*> channel);
|
||||
void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel);
|
||||
void subscribeToCreatorChange(not_null<ChannelData*> channel);
|
||||
void fullListRefresh();
|
||||
|
||||
|
||||
@@ -332,6 +332,7 @@ private:
|
||||
void showEditLinkedChatBox();
|
||||
void fillPrivacyTypeButton();
|
||||
void fillLinkedChatButton();
|
||||
void fillInviteLinkButton();
|
||||
void fillSignaturesButton();
|
||||
void fillHistoryVisibilityButton();
|
||||
void fillManageSection();
|
||||
@@ -640,8 +641,6 @@ void Controller::refreshHistoryVisibility(anim::type animated) {
|
||||
|
||||
void Controller::showEditPeerTypeBox(
|
||||
std::optional<rpl::producer<QString>> error) {
|
||||
Expects(_privacySavedValue.has_value());
|
||||
|
||||
const auto boxCallback = crl::guard(this, [=](
|
||||
Privacy checked, QString publicLink) {
|
||||
_privacyTypeUpdates.fire(std::move(checked));
|
||||
@@ -654,7 +653,7 @@ void Controller::showEditPeerTypeBox(
|
||||
_peer,
|
||||
_channelHasLocationOriginalValue,
|
||||
boxCallback,
|
||||
*_privacySavedValue,
|
||||
_privacySavedValue,
|
||||
_usernameSavedValue,
|
||||
error),
|
||||
Ui::LayerOption::KeepOther);
|
||||
@@ -800,6 +799,20 @@ void Controller::fillLinkedChatButton() {
|
||||
_linkedChatUpdates.fire_copy(*_linkedChatSavedValue);
|
||||
}
|
||||
|
||||
void Controller::fillInviteLinkButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
const auto buttonCallback = [=] {
|
||||
Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther);
|
||||
};
|
||||
|
||||
AddButtonWithText(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_profile_invite_link_section(),
|
||||
rpl::single(QString()), //Empty text.
|
||||
buttonCallback);
|
||||
}
|
||||
|
||||
void Controller::fillSignaturesButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
@@ -880,13 +893,6 @@ void Controller::fillManageSection() {
|
||||
? channel->canEditUsername()
|
||||
: chat->canEditUsername();
|
||||
}();
|
||||
const auto canEditInviteLink = [&] {
|
||||
return isChannel
|
||||
? (channel->amCreator()
|
||||
|| (channel->adminRights() & ChatAdminRight::f_invite_users))
|
||||
: (chat->amCreator()
|
||||
|| (chat->adminRights() & ChatAdminRight::f_invite_users));
|
||||
}();
|
||||
const auto canEditSignatures = [&] {
|
||||
return isChannel
|
||||
? (channel->canEditSignatures() && !channel->isMegagroup())
|
||||
@@ -953,6 +959,8 @@ void Controller::fillManageSection() {
|
||||
|
||||
if (canEditUsername) {
|
||||
fillPrivacyTypeButton();
|
||||
} else if (canEditInviteLinks) {
|
||||
fillInviteLinkButton();
|
||||
}
|
||||
if (canViewOrEditLinkedChat) {
|
||||
fillLinkedChatButton();
|
||||
@@ -965,7 +973,7 @@ void Controller::fillManageSection() {
|
||||
}
|
||||
if (canEditPreHistoryHidden
|
||||
|| canEditSignatures
|
||||
|| canEditInviteLink
|
||||
|| canEditInviteLinks
|
||||
|| canViewOrEditLinkedChat
|
||||
|| canEditUsername) {
|
||||
AddSkip(
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
Privacy privacySavedValue,
|
||||
std::optional<Privacy> privacySavedValue,
|
||||
std::optional<QString> usernameSavedValue);
|
||||
|
||||
void createContent();
|
||||
@@ -72,7 +72,9 @@ public:
|
||||
void setFocusUsername();
|
||||
|
||||
rpl::producer<QString> getTitle() {
|
||||
return _isGroup
|
||||
return !_privacySavedValue
|
||||
? tr::lng_create_invite_link_title()
|
||||
: _isGroup
|
||||
? tr::lng_manage_peer_group_type()
|
||||
: tr::lng_manage_peer_channel_type();
|
||||
}
|
||||
@@ -120,7 +122,7 @@ private:
|
||||
|
||||
void fillPrivaciesButtons(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
Privacy savedValue);
|
||||
std::optional<Privacy> savedValue);
|
||||
void addRoundButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Privacy value,
|
||||
@@ -131,8 +133,10 @@ private:
|
||||
QString inviteLinkText();
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
bool _linkOnly = false;
|
||||
|
||||
MTP::Sender _api;
|
||||
Privacy _privacySavedValue = Privacy();
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
|
||||
bool _useLocationPhrases = false;
|
||||
@@ -153,9 +157,10 @@ Controller::Controller(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
Privacy privacySavedValue,
|
||||
std::optional<Privacy> privacySavedValue,
|
||||
std::optional<QString> usernameSavedValue)
|
||||
: _peer(peer)
|
||||
, _linkOnly(!privacySavedValue.has_value())
|
||||
, _api(&_peer->session().mtp())
|
||||
, _privacySavedValue(privacySavedValue)
|
||||
, _usernameSavedValue(usernameSavedValue)
|
||||
@@ -172,10 +177,14 @@ void Controller::createContent() {
|
||||
|
||||
fillPrivaciesButtons(_wrap, _privacySavedValue);
|
||||
// Skip.
|
||||
_wrap->add(object_ptr<Ui::BoxContentDivider>(_wrap));
|
||||
if (!_linkOnly) {
|
||||
_wrap->add(object_ptr<Ui::BoxContentDivider>(_wrap));
|
||||
}
|
||||
//
|
||||
_wrap->add(createInviteLinkBlock());
|
||||
_wrap->add(createUsernameEdit());
|
||||
if (!_linkOnly) {
|
||||
_wrap->add(createUsernameEdit());
|
||||
}
|
||||
|
||||
//using namespace Settings; // #TODO links
|
||||
//AddSkip(_wrap.get());
|
||||
@@ -192,15 +201,20 @@ void Controller::createContent() {
|
||||
//AddSkip(_wrap.get());
|
||||
//AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about());
|
||||
|
||||
if (_controls.privacy->value() == Privacy::NoUsername) {
|
||||
checkUsernameAvailability();
|
||||
if (_linkOnly) {
|
||||
_controls.inviteLinkWrap->show(anim::type::instant);
|
||||
} else {
|
||||
if (_controls.privacy->value() == Privacy::NoUsername) {
|
||||
checkUsernameAvailability();
|
||||
}
|
||||
const auto forShowing = _privacySavedValue.value_or(Privacy::NoUsername);
|
||||
_controls.inviteLinkWrap->toggle(
|
||||
(forShowing != Privacy::HasUsername),
|
||||
anim::type::instant);
|
||||
_controls.usernameWrap->toggle(
|
||||
(forShowing == Privacy::HasUsername),
|
||||
anim::type::instant);
|
||||
}
|
||||
_controls.inviteLinkWrap->toggle(
|
||||
(_privacySavedValue != Privacy::HasUsername),
|
||||
anim::type::instant);
|
||||
_controls.usernameWrap->toggle(
|
||||
(_privacySavedValue == Privacy::HasUsername),
|
||||
anim::type::instant);
|
||||
}
|
||||
|
||||
void Controller::addRoundButton(
|
||||
@@ -228,7 +242,7 @@ void Controller::addRoundButton(
|
||||
|
||||
void Controller::fillPrivaciesButtons(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
Privacy savedValue) {
|
||||
std::optional<Privacy> savedValue) {
|
||||
const auto canEditUsername = [&] {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
return chat->canEditUsername();
|
||||
@@ -237,7 +251,7 @@ void Controller::fillPrivaciesButtons(
|
||||
}
|
||||
Unexpected("Peer type in Controller::createPrivaciesEdit.");
|
||||
}();
|
||||
if (!canEditUsername) {
|
||||
if (!canEditUsername || _linkOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -251,7 +265,8 @@ void Controller::fillPrivaciesButtons(
|
||||
const auto isPublic = _peer->isChannel()
|
||||
&& _peer->asChannel()->hasUsername();
|
||||
_controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>(
|
||||
savedValue);
|
||||
savedValue.value_or(
|
||||
isPublic ? Privacy::HasUsername : Privacy::NoUsername));
|
||||
|
||||
addRoundButton(
|
||||
container,
|
||||
@@ -546,9 +561,11 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() {
|
||||
const auto container = result->entity();
|
||||
|
||||
using namespace Settings;
|
||||
AddSkip(container);
|
||||
if (_privacySavedValue) {
|
||||
AddSkip(container);
|
||||
|
||||
AddSubsectionTitle(container, tr::lng_create_invite_link_title());
|
||||
AddSubsectionTitle(container, tr::lng_create_invite_link_title());
|
||||
}
|
||||
// tr::lng_create_permanent_link_title()); // #TODO links
|
||||
AddPermanentLinkBlock(container, _peer);
|
||||
|
||||
@@ -585,7 +602,7 @@ EditPeerTypeBox::EditPeerTypeBox(
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback,
|
||||
Privacy privacySaved,
|
||||
std::optional<Privacy> privacySaved,
|
||||
std::optional<QString> usernameSaved,
|
||||
std::optional<rpl::producer<QString>> usernameError)
|
||||
: _peer(peer)
|
||||
@@ -596,6 +613,12 @@ EditPeerTypeBox::EditPeerTypeBox(
|
||||
, _usernameError(usernameError) {
|
||||
}
|
||||
|
||||
EditPeerTypeBox::EditPeerTypeBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer)
|
||||
: EditPeerTypeBox(nullptr, peer, {}, {}, {}, {}, {}) {
|
||||
}
|
||||
|
||||
void EditPeerTypeBox::setInnerFocus() {
|
||||
_focusRequests.fire({});
|
||||
}
|
||||
@@ -641,8 +664,10 @@ void EditPeerTypeBox::prepare() {
|
||||
: QString()); // We don't need username with private type.
|
||||
closeBox();
|
||||
});
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
} else {
|
||||
addButton(tr::lng_close(), [=] { closeBox(); });
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
setDimensionsToContent(st::boxWideWidth, content.data());
|
||||
setInnerWidget(std::move(content));
|
||||
|
||||
@@ -37,10 +37,15 @@ public:
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback,
|
||||
Privacy privacySaved,
|
||||
std::optional<Privacy> privacySaved,
|
||||
std::optional<QString> usernameSaved,
|
||||
std::optional<rpl::producer<QString>> usernameError = {});
|
||||
|
||||
// For invite link only.
|
||||
EditPeerTypeBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
@@ -50,7 +55,7 @@ private:
|
||||
bool _useLocationPhrases = false;
|
||||
std::optional<FnMut<void(Privacy, QString)>> _savedCallback;
|
||||
|
||||
Privacy _privacySavedValue = Privacy();
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
std::optional<rpl::producer<QString>> _usernameError;
|
||||
|
||||
|
||||
@@ -262,13 +262,13 @@ void SessionsContent::terminateOne(uint64 hash) {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
auto callback = [=] {
|
||||
auto done = crl::guard(weak, [=](const MTPBool &result) {
|
||||
if (mtpIsFalse(result)) {
|
||||
return;
|
||||
}
|
||||
_inner->terminatingOne(hash, false);
|
||||
const auto getHash = [](const Entry &entry) {
|
||||
return entry.hash;
|
||||
};
|
||||
const auto removeByHash = [&](std::vector<Entry> &list) {
|
||||
list.erase(
|
||||
ranges::remove(list, hash, getHash),
|
||||
ranges::remove(list, hash, &Entry::hash),
|
||||
end(list));
|
||||
};
|
||||
removeByHash(_data.incomplete);
|
||||
|
||||
@@ -16,56 +16,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
SingleChoiceBox::SingleChoiceBox(
|
||||
QWidget*,
|
||||
rpl::producer<QString> title,
|
||||
const std::vector<QString> &optionTexts,
|
||||
int initialSelection,
|
||||
Fn<void(int)> callback,
|
||||
const style::Checkbox *st,
|
||||
const style::Radio *radioSt)
|
||||
: _title(std::move(title))
|
||||
, _optionTexts(optionTexts)
|
||||
, _initialSelection(initialSelection)
|
||||
, _callback(callback)
|
||||
, _st(st ? *st : st::defaultBoxCheckbox)
|
||||
, _radioSt(radioSt ? *radioSt : st::defaultRadio) {
|
||||
}
|
||||
void SingleChoiceBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
SingleChoiceBoxArgs &&args) {
|
||||
box->setTitle(std::move(args.title));
|
||||
|
||||
void SingleChoiceBox::prepare() {
|
||||
setTitle(std::move(_title));
|
||||
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
|
||||
|
||||
addButton(tr::lng_box_ok(), [=] { closeBox(); });
|
||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>(
|
||||
args.initialSelection);
|
||||
|
||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>(_initialSelection);
|
||||
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
content->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
content,
|
||||
const auto layout = box->verticalLayout();
|
||||
layout->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
layout,
|
||||
st::boxOptionListPadding.top() + st::autolockButton.margin.top()));
|
||||
auto &&ints = ranges::view::ints(0, ranges::unreachable);
|
||||
for (const auto &[i, text] : ranges::view::zip(ints, _optionTexts)) {
|
||||
content->add(
|
||||
for (const auto &[i, text] : ranges::view::zip(ints, args.options)) {
|
||||
layout->add(
|
||||
object_ptr<Ui::Radiobutton>(
|
||||
content,
|
||||
layout,
|
||||
group,
|
||||
i,
|
||||
text,
|
||||
_st,
|
||||
_radioSt),
|
||||
args.st ? *args.st : st::defaultBoxCheckbox,
|
||||
args.radioSt ? *args.radioSt : st::defaultRadio),
|
||||
QMargins(
|
||||
st::boxPadding.left() + st::boxOptionListPadding.left(),
|
||||
0,
|
||||
st::boxPadding.right(),
|
||||
st::boxOptionListSkip));
|
||||
}
|
||||
const auto callback = args.callback;
|
||||
group->setChangedCallback([=](int value) {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
_callback(value);
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
callback(value);
|
||||
if (weak) {
|
||||
closeBox();
|
||||
box->closeBox();
|
||||
}
|
||||
});
|
||||
setDimensionsToContent(st::boxWidth, content);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,38 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Ui {
|
||||
class Radiobutton;
|
||||
} // namespace Ui
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "base/required.h"
|
||||
|
||||
namespace style {
|
||||
struct Checkbox;
|
||||
struct Radio;
|
||||
} // namespace style
|
||||
|
||||
class SingleChoiceBox : public Ui::BoxContent {
|
||||
public:
|
||||
SingleChoiceBox(
|
||||
QWidget*,
|
||||
rpl::producer<QString> title,
|
||||
const std::vector<QString> &optionTexts,
|
||||
int initialSelection,
|
||||
Fn<void(int)> callback,
|
||||
const style::Checkbox *st = nullptr,
|
||||
const style::Radio *radioSt = nullptr);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
rpl::producer<QString> _title;
|
||||
std::vector<QString> _optionTexts;
|
||||
int _initialSelection = 0;
|
||||
Fn<void(int)> _callback;
|
||||
const style::Checkbox &_st;
|
||||
const style::Radio &_radioSt;
|
||||
struct SingleChoiceBoxArgs {
|
||||
template <typename T>
|
||||
using required = base::required<T>;
|
||||
|
||||
required<rpl::producer<QString>> title;
|
||||
const std::vector<QString> &options;
|
||||
int initialSelection = 0;
|
||||
Fn<void(int)> callback;
|
||||
const style::Checkbox *st = nullptr;
|
||||
const style::Radio *radioSt = nullptr;
|
||||
};
|
||||
|
||||
void SingleChoiceBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
SingleChoiceBoxArgs &&args);
|
||||
|
||||
@@ -473,11 +473,9 @@ void StickerSetBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||
int index = stickerFromGlobalPos(e->globalPos());
|
||||
if (index >= 0 && index < _pack.size() && index != _previewShown) {
|
||||
_previewShown = index;
|
||||
if (const auto w = App::wnd()) {
|
||||
w->showMediaPreview(
|
||||
Data::FileOriginStickerSet(_setId, _setAccess),
|
||||
_pack[_previewShown]);
|
||||
}
|
||||
_controller->widget()->showMediaPreview(
|
||||
Data::FileOriginStickerSet(_setId, _setAccess),
|
||||
_pack[_previewShown]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -536,11 +534,9 @@ void StickerSetBox::Inner::showPreview() {
|
||||
int index = stickerFromGlobalPos(QCursor::pos());
|
||||
if (index >= 0 && index < _pack.size()) {
|
||||
_previewShown = index;
|
||||
if (const auto w = App::wnd()) {
|
||||
w->showMediaPreview(
|
||||
Data::FileOriginStickerSet(_setId, _setAccess),
|
||||
_pack[_previewShown]);
|
||||
}
|
||||
_controller->widget()->showMediaPreview(
|
||||
Data::FileOriginStickerSet(_setId, _setAccess),
|
||||
_pack[_previewShown]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,20 +22,6 @@ CallSignalBars {
|
||||
inactiveOpacity: double;
|
||||
}
|
||||
|
||||
callRadius: 6px;
|
||||
callShadow: Shadow {
|
||||
left: icon {{ "calls/call_shadow_left", windowShadowFg }};
|
||||
topLeft: icon {{ "calls/call_shadow_top_left", windowShadowFg }};
|
||||
top: icon {{ "calls/call_shadow_top", windowShadowFg }};
|
||||
topRight: icon {{ "calls/call_shadow_top_left-flip_horizontal", windowShadowFg }};
|
||||
right: icon {{ "calls/call_shadow_left-flip_horizontal", windowShadowFg }};
|
||||
bottomRight: icon {{ "calls/call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }};
|
||||
bottom: icon {{ "calls/call_shadow_top-flip_vertical", windowShadowFg }};
|
||||
bottomLeft: icon {{ "calls/call_shadow_top_left-flip_vertical", windowShadowFg }};
|
||||
extend: margins(9px, 8px, 9px, 10px);
|
||||
fallback: windowShadowFgFallback;
|
||||
}
|
||||
|
||||
callWidthMin: 300px;
|
||||
callHeightMin: 440px;
|
||||
callWidth: 720px;
|
||||
|
||||
@@ -48,10 +48,7 @@ constexpr auto kHangupTimeoutMs = 5000;
|
||||
constexpr auto kSha256Size = 32;
|
||||
const auto kDefaultVersion = "2.4.4"_q;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION
|
||||
const auto RegisterTag = tgcalls::Register<tgcalls::InstanceImpl>();
|
||||
//const auto RegisterTagReference = tgcalls::Register<tgcalls::InstanceImplReference>();
|
||||
#endif // DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION
|
||||
const auto RegisterTagLegacy = tgcalls::Register<tgcalls::InstanceImplLegacy>();
|
||||
|
||||
void AppendEndpoint(
|
||||
@@ -380,7 +377,6 @@ void Call::setupOutgoingVideo() {
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
} else if (state != Webrtc::VideoState::Inactive) {
|
||||
// Paused not supported right now.
|
||||
#ifndef DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION
|
||||
Assert(state == Webrtc::VideoState::Active);
|
||||
if (!_videoCapture) {
|
||||
_videoCapture = _delegate->getVideoCapture();
|
||||
@@ -390,7 +386,6 @@ void Call::setupOutgoingVideo() {
|
||||
_instance->setVideoCapture(_videoCapture);
|
||||
}
|
||||
_videoCapture->setState(tgcalls::VideoState::Active);
|
||||
#endif // DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION
|
||||
} else if (_videoCapture) {
|
||||
_videoCapture->setState(tgcalls::VideoState::Inactive);
|
||||
}
|
||||
|
||||
@@ -377,10 +377,15 @@ void GroupCall::applySelfInCallLocally() {
|
||||
const auto lastActive = (i != end(participants))
|
||||
? i->lastActive
|
||||
: TimeId(0);
|
||||
const auto volume = (i != end(participants))
|
||||
? i->volume
|
||||
: Group::kDefaultVolume;
|
||||
const auto canSelfUnmute = (muted() != MuteState::ForceMuted);
|
||||
const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
|
||||
| (lastActive ? Flag::f_active_date : Flag(0))
|
||||
| (_mySsrc ? Flag(0) : Flag::f_left)
|
||||
| Flag::f_volume // Without flag the volume is reset to 100%.
|
||||
| Flag::f_volume_by_admin // Self volume can only be set by admin.
|
||||
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0));
|
||||
call->applyUpdateChecked(
|
||||
MTP_updateGroupCallParticipants(
|
||||
@@ -393,7 +398,7 @@ void GroupCall::applySelfInCallLocally() {
|
||||
MTP_int(date),
|
||||
MTP_int(lastActive),
|
||||
MTP_int(_mySsrc),
|
||||
MTP_int(Group::kDefaultVolume))), // volume
|
||||
MTP_int(volume))),
|
||||
MTP_int(0)).c_updateGroupCallParticipants());
|
||||
}
|
||||
|
||||
@@ -410,6 +415,9 @@ void GroupCall::applyParticipantLocally(
|
||||
using Flag = MTPDgroupCallParticipant::Flag;
|
||||
const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
|
||||
| Flag::f_volume // Without flag the volume is reset to 100%.
|
||||
| ((participant->applyVolumeFromMin && !volume)
|
||||
? Flag::f_volume_by_admin
|
||||
: Flag(0))
|
||||
| (participant->lastActive ? Flag::f_active_date : Flag(0))
|
||||
| (!mute
|
||||
? Flag(0)
|
||||
@@ -595,6 +603,10 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
||||
|
||||
const auto handleOtherParticipants = [=](
|
||||
const MTPDgroupCallParticipant &data) {
|
||||
if (data.is_min()) {
|
||||
// No real information about mutedByMe or my custom volume.
|
||||
return;
|
||||
}
|
||||
const auto user = _peer->owner().user(data.vuser_id().v);
|
||||
const auto participant = LookupParticipant(_peer, _id, user);
|
||||
if (!participant) {
|
||||
|
||||
@@ -19,6 +19,7 @@ struct MuteRequest {
|
||||
bool mute = false;
|
||||
bool locallyOnly = false;
|
||||
};
|
||||
|
||||
struct VolumeRequest {
|
||||
not_null<UserData*> user;
|
||||
int volume = kDefaultVolume;
|
||||
|
||||
@@ -194,7 +194,7 @@ private:
|
||||
st::groupCallStatusSpeakerArcsAnimation,
|
||||
kSpeakerThreshold,
|
||||
volume,
|
||||
Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) {
|
||||
Ui::Paint::ArcsAnimation::Direction::Right)) {
|
||||
}
|
||||
const style::icon &speaker;
|
||||
const std::unique_ptr<Ui::Paint::ArcsAnimation> arcs;
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "calls/calls_group_common.h"
|
||||
#include "calls/calls_group_members.h"
|
||||
#include "calls/calls_group_settings.h"
|
||||
#include "ui/platform/ui_platform_window_title.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/widgets/call_button.h"
|
||||
@@ -38,10 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "ui/platform/win/ui_window_title_win.h"
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QWindow>
|
||||
@@ -99,6 +96,10 @@ private:
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) override;
|
||||
|
||||
bool needsInviteLinkButton() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
const not_null<const base::flat_set<not_null<UserData*>>*> _inGroup;
|
||||
rpl::producer<not_null<UserData*>> _discoveredInGroup;
|
||||
|
||||
@@ -300,11 +301,11 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
||||
, _peer(call->peer())
|
||||
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
|
||||
, _layerBg(std::make_unique<Ui::LayerManager>(_window->body()))
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
, _controls(std::make_unique<Ui::Platform::TitleControls>(
|
||||
_window.get(),
|
||||
_window->body(),
|
||||
st::groupCallTitle))
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
, _members(widget(), call)
|
||||
, _settings(widget(), st::groupCallSettings)
|
||||
, _mute(std::make_unique<Ui::CallMuteButton>(
|
||||
@@ -745,9 +746,9 @@ void GroupPanel::kickMemberSure(not_null<UserData*> user) {
|
||||
void GroupPanel::initLayout() {
|
||||
initGeometry();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
_controls->raise();
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
}
|
||||
|
||||
void GroupPanel::showControls() {
|
||||
@@ -779,14 +780,12 @@ int GroupPanel::computeMembersListTop() const {
|
||||
}
|
||||
|
||||
std::optional<QRect> GroupPanel::computeTitleRect() const {
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_OS_MAC
|
||||
return QRect(70, 0, widget()->width() - 70, 28);
|
||||
#else // Q_OS_MAC
|
||||
const auto controls = _controls->geometry();
|
||||
return QRect(0, 0, controls.x(), controls.height());
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
return QRect(70, 0, widget()->width() - 70, 28);
|
||||
#else // Q_OS_WIN || Q_OS_MAC
|
||||
return std::nullopt;
|
||||
#endif // Q_OS_WIN || Q_OS_MAC
|
||||
#endif // !Q_OS_MAC
|
||||
}
|
||||
|
||||
void GroupPanel::updateControlsGeometry() {
|
||||
|
||||
@@ -110,9 +110,9 @@ private:
|
||||
const std::unique_ptr<Ui::Window> _window;
|
||||
const std::unique_ptr<Ui::LayerManager> _layerBg;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
|
||||
rpl::lifetime _callLifetime;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "calls/calls_signal_bars.h"
|
||||
#include "calls/calls_userpic.h"
|
||||
#include "calls/calls_video_bubble.h"
|
||||
#include "ui/platform/ui_platform_window_title.h"
|
||||
#include "ui/widgets/call_button.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
@@ -45,10 +46,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "ui/platform/win/ui_window_title_win.h"
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QWindow>
|
||||
@@ -189,12 +186,12 @@ Panel::Panel(not_null<Call*> call)
|
||||
: _call(call)
|
||||
, _user(call->user())
|
||||
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
, _controls(std::make_unique<Ui::Platform::TitleControls>(
|
||||
_window.get(),
|
||||
_window->body(),
|
||||
st::callTitle,
|
||||
[=](bool maximized) { toggleFullScreen(maximized); }))
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
, _bodySt(&st::callBodyLayout)
|
||||
, _answerHangupRedial(widget(), st::callAnswer, &st::callHangup)
|
||||
, _decline(widget(), object_ptr<Ui::CallButton>(widget(), st::callHangup))
|
||||
@@ -270,11 +267,11 @@ void Panel::initWindow() {
|
||||
if (!widget()->rect().contains(widgetPoint)) {
|
||||
return Flag::None | Flag(0);
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
if (_controls->geometry().contains(widgetPoint)) {
|
||||
return Flag::None | Flag(0);
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
const auto buttonWidth = st::callCancel.button.width;
|
||||
const auto buttonsWidth = buttonWidth * 4;
|
||||
const auto inControls = (_fingerprint
|
||||
@@ -595,9 +592,9 @@ void Panel::initLayout() {
|
||||
updateControlsGeometry();
|
||||
}, widget()->lifetime());
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
_controls->raise();
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
}
|
||||
|
||||
void Panel::showControls() {
|
||||
@@ -669,10 +666,10 @@ void Panel::updateControlsGeometry() {
|
||||
refreshIncomingGeometry();
|
||||
}
|
||||
if (_fingerprint) {
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
const auto minRight = _controls->geometry().width()
|
||||
+ st::callFingerprintTop;
|
||||
#else // Q_OS_WIN
|
||||
#else // !Q_OS_MAC
|
||||
const auto minRight = 0;
|
||||
#endif // _controls
|
||||
const auto desired = (widget()->width() - _fingerprint->width()) / 2;
|
||||
|
||||
@@ -108,9 +108,9 @@ private:
|
||||
const std::unique_ptr<Ui::Window> _window;
|
||||
std::unique_ptr<Incoming> _incoming;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef Q_OS_MAC
|
||||
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
||||
#endif // Q_OS_WIN
|
||||
#endif // !Q_OS_MAC
|
||||
|
||||
QSize _incomingFrameSize;
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ MenuVolumeItem::MenuVolumeItem(
|
||||
st::groupCallSpeakerArcsAnimation,
|
||||
kSpeakerThreshold,
|
||||
_localMuted ? 0. : (startVolume / float(maxVolume)),
|
||||
Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) {
|
||||
Ui::Paint::ArcsAnimation::Direction::Right)) {
|
||||
|
||||
initResizeHook(parent->sizeValue());
|
||||
enableMouseSelecting();
|
||||
|
||||
@@ -1280,11 +1280,9 @@ void FieldAutocomplete::Inner::selectByMouse(QPoint globalPosition) {
|
||||
if (_down >= 0 && _sel >= 0 && _down != _sel) {
|
||||
_down = _sel;
|
||||
if (_down >= 0 && _down < _srows->size()) {
|
||||
if (const auto w = App::wnd()) {
|
||||
w->showMediaPreview(
|
||||
(*_srows)[_down].document->stickerSetOrigin(),
|
||||
(*_srows)[_down].document);
|
||||
}
|
||||
_controller->widget()->showMediaPreview(
|
||||
(*_srows)[_down].document->stickerSetOrigin(),
|
||||
(*_srows)[_down].document);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1302,12 +1300,10 @@ void FieldAutocomplete::Inner::onParentGeometryChanged() {
|
||||
|
||||
void FieldAutocomplete::Inner::showPreview() {
|
||||
if (_down >= 0 && _down < _srows->size()) {
|
||||
if (const auto w = App::wnd()) {
|
||||
w->showMediaPreview(
|
||||
(*_srows)[_down].document->stickerSetOrigin(),
|
||||
(*_srows)[_down].document);
|
||||
_previewShown = true;
|
||||
}
|
||||
_controller->widget()->showMediaPreview(
|
||||
(*_srows)[_down].document->stickerSetOrigin(),
|
||||
(*_srows)[_down].document);
|
||||
_previewShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1078,16 +1078,14 @@ void GifsListWidget::updateSelected() {
|
||||
_pressed = _selected;
|
||||
if (row >= 0 && col >= 0) {
|
||||
auto layout = _rows[row].items[col];
|
||||
if (const auto w = App::wnd()) {
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
w->showMediaPreview(
|
||||
Data::FileOriginSavedGifs(),
|
||||
previewDocument);
|
||||
} else if (const auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
w->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewPhoto);
|
||||
}
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
controller()->widget()->showMediaPreview(
|
||||
Data::FileOriginSavedGifs(),
|
||||
previewDocument);
|
||||
} else if (const auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
controller()->widget()->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewPhoto);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1104,16 +1102,14 @@ void GifsListWidget::showPreview() {
|
||||
int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift;
|
||||
if (row < _rows.size() && col < _rows[row].items.size()) {
|
||||
auto layout = _rows[row].items[col];
|
||||
if (const auto w = App::wnd()) {
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
_previewShown = w->showMediaPreview(
|
||||
Data::FileOriginSavedGifs(),
|
||||
previewDocument);
|
||||
} else if (const auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
_previewShown = w->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewPhoto);
|
||||
}
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
_previewShown = controller()->widget()->showMediaPreview(
|
||||
Data::FileOriginSavedGifs(),
|
||||
previewDocument);
|
||||
} else if (const auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
_previewShown = controller()->widget()->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewPhoto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,46 +45,46 @@ constexpr auto kRefreshTimeout = 7200 * crl::time(1000);
|
||||
|
||||
static const auto color1 = Lottie::ColorReplacements{
|
||||
{
|
||||
{ 0xf77e41U, 0xca907aU },
|
||||
{ 0xffb139U, 0xedc5a5U },
|
||||
{ 0xffd140U, 0xf7e3c3U },
|
||||
{ 0xffdf79U, 0xfbefd6U },
|
||||
{ 0xf77e41U, 0xcb7b55U },
|
||||
{ 0xffb139U, 0xf6b689U },
|
||||
{ 0xffd140U, 0xffcda7U },
|
||||
{ 0xffdf79U, 0xffdfc5U },
|
||||
},
|
||||
1,
|
||||
};
|
||||
static const auto color2 = Lottie::ColorReplacements{
|
||||
{
|
||||
{ 0xf77e41U, 0xaa7c60U },
|
||||
{ 0xffb139U, 0xc8a987U },
|
||||
{ 0xffd140U, 0xddc89fU },
|
||||
{ 0xffdf79U, 0xe6d6b2U },
|
||||
{ 0xf77e41U, 0xa45a38U },
|
||||
{ 0xffb139U, 0xdf986bU },
|
||||
{ 0xffd140U, 0xedb183U },
|
||||
{ 0xffdf79U, 0xf4c3a0U },
|
||||
},
|
||||
2,
|
||||
};
|
||||
static const auto color3 = Lottie::ColorReplacements{
|
||||
{
|
||||
{ 0xf77e41U, 0x8c6148U },
|
||||
{ 0xffb139U, 0xad8562U },
|
||||
{ 0xffd140U, 0xc49e76U },
|
||||
{ 0xffdf79U, 0xd4b188U },
|
||||
{ 0xf77e41U, 0x703a17U },
|
||||
{ 0xffb139U, 0xab673dU },
|
||||
{ 0xffd140U, 0xc37f4eU },
|
||||
{ 0xffdf79U, 0xd89667U },
|
||||
},
|
||||
3,
|
||||
};
|
||||
static const auto color4 = Lottie::ColorReplacements{
|
||||
{
|
||||
{ 0xf77e41U, 0x6e3c2cU },
|
||||
{ 0xffb139U, 0x925a34U },
|
||||
{ 0xffd140U, 0xa16e46U },
|
||||
{ 0xffdf79U, 0xac7a52U },
|
||||
{ 0xf77e41U, 0x4a2409U },
|
||||
{ 0xffb139U, 0x7d3e0eU },
|
||||
{ 0xffd140U, 0x965529U },
|
||||
{ 0xffdf79U, 0xa96337U },
|
||||
},
|
||||
4,
|
||||
};
|
||||
static const auto color5 = Lottie::ColorReplacements{
|
||||
{
|
||||
{ 0xf77e41U, 0x291c12U },
|
||||
{ 0xffb139U, 0x472a22U },
|
||||
{ 0xffd140U, 0x573b30U },
|
||||
{ 0xffdf79U, 0x68493cU },
|
||||
{ 0xf77e41U, 0x200f0aU },
|
||||
{ 0xffb139U, 0x412924U },
|
||||
{ 0xffd140U, 0x593d37U },
|
||||
{ 0xffdf79U, 0x63453fU },
|
||||
},
|
||||
5,
|
||||
};
|
||||
@@ -247,7 +247,7 @@ void EmojiPack::applySet(const MTPDmessages_stickerSet &data) {
|
||||
was.erase(i);
|
||||
}
|
||||
}
|
||||
for (const auto &[emoji, Document] : was) {
|
||||
for (const auto &[emoji, document] : was) {
|
||||
refreshItems(emoji);
|
||||
}
|
||||
}
|
||||
@@ -260,6 +260,13 @@ void EmojiPack::refreshAll() {
|
||||
|
||||
void EmojiPack::refreshItems(EmojiPtr emoji) {
|
||||
const auto i = _items.find(IsolatedEmoji{ { emoji } });
|
||||
if (!emoji->colored()) {
|
||||
if (const auto count = emoji->variantsCount()) {
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
refreshItems(emoji->variant(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == end(_items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2883,9 +2883,9 @@ void StickersListWidget::setSelected(OverState newSelected) {
|
||||
const auto &set = sets[sticker->section];
|
||||
Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
|
||||
const auto document = set.stickers[sticker->index].document;
|
||||
if (const auto w = App::wnd()) {
|
||||
w->showMediaPreview(document->stickerSetOrigin(), document);
|
||||
}
|
||||
controller()->widget()->showMediaPreview(
|
||||
document->stickerSetOrigin(),
|
||||
document);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2898,10 +2898,10 @@ void StickersListWidget::showPreview() {
|
||||
const auto &set = sets[sticker->section];
|
||||
Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
|
||||
const auto document = set.stickers[sticker->index].document;
|
||||
if (const auto w = App::wnd()) {
|
||||
w->showMediaPreview(document->stickerSetOrigin(), document);
|
||||
_previewShown = true;
|
||||
}
|
||||
controller()->widget()->showMediaPreview(
|
||||
document->stickerSetOrigin(),
|
||||
document);
|
||||
_previewShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,10 @@ Application::Application(not_null<Launcher*> launcher)
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
if (_saveSettingsTimer && _saveSettingsTimer->isActive()) {
|
||||
Local::writeSettings();
|
||||
}
|
||||
|
||||
// Depend on activeWindow() for now :(
|
||||
Shortcuts::Finish();
|
||||
|
||||
@@ -465,7 +469,9 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
}
|
||||
|
||||
void Application::saveSettingsDelayed(crl::time delay) {
|
||||
_saveSettingsTimer.callOnce(delay);
|
||||
if (_saveSettingsTimer) {
|
||||
_saveSettingsTimer->callOnce(delay);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::saveSettings() {
|
||||
@@ -533,7 +539,7 @@ void Application::badMtprotoConfigurationError() {
|
||||
|
||||
void Application::startLocalStorage() {
|
||||
Local::start();
|
||||
_saveSettingsTimer.setCallback([=] { saveSettings(); });
|
||||
_saveSettingsTimer.emplace([=] { saveSettings(); });
|
||||
}
|
||||
|
||||
void Application::startEmojiImageLoader() {
|
||||
@@ -764,6 +770,39 @@ bool Application::openInternalUrl(const QString &url, QVariant context) {
|
||||
return openCustomUrl("internal:", InternalUrlHandlers(), url, context);
|
||||
}
|
||||
|
||||
QString Application::changelogLink() const {
|
||||
const auto base = u"https://desktop.telegram.org/changelog"_q;
|
||||
const auto languages = {
|
||||
"id",
|
||||
"de",
|
||||
"fr",
|
||||
"nl",
|
||||
"pl",
|
||||
"tr",
|
||||
"uk",
|
||||
"fa",
|
||||
"ru",
|
||||
"ms",
|
||||
"es",
|
||||
"it",
|
||||
"uz",
|
||||
"pt-br",
|
||||
"be",
|
||||
"ar",
|
||||
"ko",
|
||||
};
|
||||
const auto current = _langpack->id().replace("-raw", "");
|
||||
if (current.isEmpty()) {
|
||||
return base;
|
||||
}
|
||||
for (const auto language : languages) {
|
||||
if (current == language || current.split(u'-')[0] == language) {
|
||||
return base + "?setln=" + language;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
bool Application::openCustomUrl(
|
||||
const QString &protocol,
|
||||
const std::vector<LocalUrlHandler> &handlers,
|
||||
|
||||
@@ -227,6 +227,7 @@ public:
|
||||
void checkStartUrl();
|
||||
bool openLocalUrl(const QString &url, QVariant context);
|
||||
bool openInternalUrl(const QString &url, QVariant context);
|
||||
[[nodiscard]] QString changelogLink() const;
|
||||
|
||||
// Float player.
|
||||
void setDefaultFloatPlayerDelegate(
|
||||
@@ -367,7 +368,7 @@ private:
|
||||
crl::time _shouldLockAt = 0;
|
||||
base::Timer _autoLockTimer;
|
||||
|
||||
base::Timer _saveSettingsTimer;
|
||||
std::optional<base::Timer> _saveSettingsTimer;
|
||||
|
||||
struct LeaveSubscription {
|
||||
LeaveSubscription(
|
||||
|
||||
@@ -185,7 +185,7 @@ void Changelogs::addLocalLogs() {
|
||||
lt_changes,
|
||||
tr::lng_new_version_minor(tr::now),
|
||||
lt_link,
|
||||
qsl("https://desktop.telegram.org/changelog"));
|
||||
Core::App().changelogLink());
|
||||
addLocalLog(text.trimmed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "facades.h"
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] WindowPosition Deserialize(const QByteArray &data) {
|
||||
QDataStream stream(data);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
||||
auto result = WindowPosition();
|
||||
stream
|
||||
>> result.x
|
||||
>> result.y
|
||||
>> result.w
|
||||
>> result.h
|
||||
>> result.moncrc
|
||||
>> result.maximized
|
||||
>> result.scale;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray Serialize(const WindowPosition &position) {
|
||||
auto result = QByteArray();
|
||||
const auto size = 7 * sizeof(qint32);
|
||||
result.reserve(size);
|
||||
{
|
||||
QDataStream stream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
stream
|
||||
<< qint32(position.x)
|
||||
<< qint32(position.y)
|
||||
<< qint32(position.w)
|
||||
<< qint32(position.h)
|
||||
<< qint32(position.moncrc)
|
||||
<< qint32(position.maximized)
|
||||
<< qint32(position.scale);
|
||||
}
|
||||
DEBUG_LOG(("Window Pos: Writing to storage %1, %2, %3, %4"
|
||||
" (scale %5%, maximized %6)")
|
||||
.arg(position.x)
|
||||
.arg(position.y)
|
||||
.arg(position.w)
|
||||
.arg(position.h)
|
||||
.arg(position.scale)
|
||||
.arg(Logs::b(position.maximized)));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Settings::Settings()
|
||||
: _sendSubmitWay(Ui::InputSubmitSettings::Enter)
|
||||
@@ -27,6 +73,8 @@ Settings::Settings()
|
||||
|
||||
QByteArray Settings::serialize() const {
|
||||
const auto themesAccentColors = _themesAccentColors.serialize();
|
||||
const auto windowPosition = Serialize(_windowPosition);
|
||||
|
||||
auto size = Serialize::bytearraySize(themesAccentColors)
|
||||
+ sizeof(qint32) * 5
|
||||
+ Serialize::stringSize(_downloadPath.current())
|
||||
@@ -40,6 +88,7 @@ QByteArray Settings::serialize() const {
|
||||
size += Serialize::stringSize(key) + Serialize::stringSize(value);
|
||||
}
|
||||
size += Serialize::bytearraySize(_videoPipGeometry);
|
||||
size += Serialize::bytearraySize(windowPosition);
|
||||
|
||||
auto result = QByteArray();
|
||||
result.reserve(size);
|
||||
@@ -115,7 +164,8 @@ QByteArray Settings::serialize() const {
|
||||
<< _groupCallPushToTalkShortcut
|
||||
<< qint64(_groupCallPushToTalkDelay)
|
||||
<< qint32(0) // Call audio backend
|
||||
<< qint32(_disableCalls ? 1 : 0);
|
||||
<< qint32(_disableCalls ? 1 : 0)
|
||||
<< windowPosition;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -188,6 +238,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
qint64 groupCallPushToTalkDelay = _groupCallPushToTalkDelay;
|
||||
qint32 callAudioBackend = 0;
|
||||
qint32 disableCalls = _disableCalls ? 1 : 0;
|
||||
QByteArray windowPosition;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (!stream.atEnd()) {
|
||||
@@ -289,6 +340,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
if (!stream.atEnd()) {
|
||||
stream >> disableCalls;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> windowPosition;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
@@ -389,6 +443,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
_groupCallPushToTalkShortcut = groupCallPushToTalkShortcut;
|
||||
_groupCallPushToTalkDelay = groupCallPushToTalkDelay;
|
||||
_disableCalls = (disableCalls == 1);
|
||||
if (!windowPosition.isEmpty()) {
|
||||
_windowPosition = Deserialize(windowPosition);
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::chatWide() const {
|
||||
|
||||
@@ -28,6 +28,18 @@ enum class Backend;
|
||||
|
||||
namespace Core {
|
||||
|
||||
struct WindowPosition {
|
||||
WindowPosition() = default;
|
||||
|
||||
int32 moncrc = 0;
|
||||
int maximized = 0;
|
||||
int scale = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
};
|
||||
|
||||
class Settings final {
|
||||
public:
|
||||
enum class ScreenCorner {
|
||||
@@ -503,14 +515,25 @@ public:
|
||||
[[nodiscard]] rpl::producer<Window::ControlsLayout> windowControlsLayoutChanges() const {
|
||||
return _windowControlsLayout.changes();
|
||||
}
|
||||
[[nodiscard]] const WindowPosition &windowPosition() const {
|
||||
return _windowPosition;
|
||||
}
|
||||
void setWindowPosition(const WindowPosition &position) {
|
||||
_windowPosition = position;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool ThirdColumnByDefault();
|
||||
[[nodiscard]] float64 DefaultDialogsWidthRatio();
|
||||
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
|
||||
return int(std::round(std::clamp(speed * 4., 2., 8.))) - 2;
|
||||
return int(std::round(std::clamp(speed, 0.5, 2.0) * 100));
|
||||
}
|
||||
[[nodiscard]] static float64 DeserializePlaybackSpeed(qint32 speed) {
|
||||
return (std::clamp(speed, 0, 6) + 2) / 4.;
|
||||
if (speed < 10) {
|
||||
// The old values in settings.
|
||||
return (std::clamp(speed, 0, 6) + 2) / 4.;
|
||||
} else {
|
||||
return std::clamp(speed, 50, 200) / 100.;
|
||||
}
|
||||
}
|
||||
|
||||
void resetOnLastLogout();
|
||||
@@ -580,6 +603,7 @@ private:
|
||||
rpl::variable<std::optional<bool>> _systemDarkMode = std::nullopt;
|
||||
rpl::variable<bool> _systemDarkModeEnabled = false;
|
||||
rpl::variable<Window::ControlsLayout> _windowControlsLayout;
|
||||
WindowPosition _windowPosition; // per-window
|
||||
|
||||
bool _tabbedReplacedWithInfo = false; // per-window
|
||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
||||
|
||||
@@ -346,6 +346,7 @@ bool ResolveSettings(
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
controller->window().activate();
|
||||
const auto section = match->captured(1).mid(1).toLower();
|
||||
if (section.isEmpty()) {
|
||||
controller->window().showSettings();
|
||||
@@ -520,8 +521,8 @@ QString TryConvertUrlToLocal(QString url) {
|
||||
auto telegramMeMatch = regex_match(qsl("^(https?://)?(www\\.)?(telegram\\.(me|dog)|t\\.me)/(.+)$"), url, matchOptions);
|
||||
if (telegramMeMatch) {
|
||||
auto query = telegramMeMatch->capturedRef(5);
|
||||
if (auto joinChatMatch = regex_match(qsl("^joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) {
|
||||
return qsl("tg://join?invite=") + url_encode(joinChatMatch->captured(1));
|
||||
if (auto joinChatMatch = regex_match(qsl("^(joinchat/|\\+|\\%20)([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) {
|
||||
return qsl("tg://join?invite=") + url_encode(joinChatMatch->captured(2));
|
||||
} else if (auto stickerSetMatch = regex_match(qsl("^addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), query, matchOptions)) {
|
||||
return qsl("tg://addstickers?set=") + url_encode(stickerSetMatch->captured(1));
|
||||
} else if (auto themeMatch = regex_match(qsl("^addtheme/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), query, matchOptions)) {
|
||||
|
||||
@@ -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 = 2005008;
|
||||
constexpr auto AppVersionStr = "2.5.8";
|
||||
constexpr auto AppVersion = 2005009;
|
||||
constexpr auto AppVersionStr = "2.5.9";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -48,14 +48,17 @@ void ChatData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) {
|
||||
});
|
||||
}
|
||||
|
||||
auto ChatData::DefaultAdminRights() -> AdminRights {
|
||||
auto ChatData::defaultAdminRights(not_null<UserData*> user) -> AdminRights {
|
||||
const auto isCreator = (creator == user->bareId())
|
||||
|| (user->isSelf() && amCreator());
|
||||
using Flag = AdminRight;
|
||||
return Flag::f_change_info
|
||||
| Flag::f_delete_messages
|
||||
| Flag::f_ban_users
|
||||
| Flag::f_invite_users
|
||||
| Flag::f_pin_messages
|
||||
| Flag::f_manage_call;
|
||||
| Flag::f_manage_call
|
||||
| (isCreator ? Flag::f_add_admins : Flag(0));
|
||||
}
|
||||
|
||||
bool ChatData::canWrite() const {
|
||||
@@ -335,7 +338,7 @@ void ApplyChatUpdate(
|
||||
}
|
||||
if (user->isSelf()) {
|
||||
chat->setAdminRights(MTP_chatAdminRights(mtpIsTrue(update.vis_admin())
|
||||
? MTP_flags(ChatData::DefaultAdminRights())
|
||||
? MTP_flags(chat->defaultAdminRights(user))
|
||||
: MTP_flags(0)));
|
||||
}
|
||||
if (mtpIsTrue(update.vis_admin())) {
|
||||
@@ -457,7 +460,7 @@ void ApplyChatUpdate(
|
||||
chat->admins.emplace(user);
|
||||
if (user->isSelf()) {
|
||||
chat->setAdminRights(MTP_chatAdminRights(
|
||||
MTP_flags(ChatData::DefaultAdminRights())));
|
||||
MTP_flags(chat->defaultAdminRights(user))));
|
||||
}
|
||||
}, [](const MTPDchatParticipant &) {
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
void setName(const QString &newName);
|
||||
|
||||
void invalidateParticipants();
|
||||
bool noParticipantInfo() const {
|
||||
[[nodiscard]] bool noParticipantInfo() const {
|
||||
return (count > 0 || amIn()) && participants.empty();
|
||||
}
|
||||
|
||||
@@ -58,10 +58,10 @@ public:
|
||||
void removeFlags(MTPDchat::Flags which) {
|
||||
_flags.remove(which);
|
||||
}
|
||||
auto flags() const {
|
||||
[[nodiscard]] auto flags() const {
|
||||
return _flags.current();
|
||||
}
|
||||
auto flagsValue() const {
|
||||
[[nodiscard]] auto flagsValue() const {
|
||||
return _flags.value();
|
||||
}
|
||||
|
||||
@@ -74,58 +74,58 @@ public:
|
||||
void removeFullFlags(MTPDchatFull::Flags which) {
|
||||
_fullFlags.remove(which);
|
||||
}
|
||||
auto fullFlags() const {
|
||||
[[nodiscard]] auto fullFlags() const {
|
||||
return _fullFlags.current();
|
||||
}
|
||||
auto fullFlagsValue() const {
|
||||
[[nodiscard]] auto fullFlagsValue() const {
|
||||
return _fullFlags.value();
|
||||
}
|
||||
|
||||
auto adminRights() const {
|
||||
[[nodiscard]] auto adminRights() const {
|
||||
return _adminRights.current();
|
||||
}
|
||||
auto adminRightsValue() const {
|
||||
[[nodiscard]] auto adminRightsValue() const {
|
||||
return _adminRights.value();
|
||||
}
|
||||
void setAdminRights(const MTPChatAdminRights &rights);
|
||||
bool hasAdminRights() const {
|
||||
[[nodiscard]] bool hasAdminRights() const {
|
||||
return (adminRights() != 0);
|
||||
}
|
||||
|
||||
auto defaultRestrictions() const {
|
||||
[[nodiscard]] auto defaultRestrictions() const {
|
||||
return _defaultRestrictions.current();
|
||||
}
|
||||
auto defaultRestrictionsValue() const {
|
||||
[[nodiscard]] auto defaultRestrictionsValue() const {
|
||||
return _defaultRestrictions.value();
|
||||
}
|
||||
void setDefaultRestrictions(const MTPChatBannedRights &rights);
|
||||
|
||||
bool isForbidden() const {
|
||||
[[nodiscard]] bool isForbidden() const {
|
||||
return flags() & MTPDchat_ClientFlag::f_forbidden;
|
||||
}
|
||||
bool amIn() const {
|
||||
[[nodiscard]] bool amIn() const {
|
||||
return !isForbidden()
|
||||
&& !isDeactivated()
|
||||
&& !haveLeft()
|
||||
&& !wasKicked();
|
||||
}
|
||||
bool haveLeft() const {
|
||||
[[nodiscard]] bool haveLeft() const {
|
||||
return flags() & MTPDchat::Flag::f_left;
|
||||
}
|
||||
bool wasKicked() const {
|
||||
[[nodiscard]] bool wasKicked() const {
|
||||
return flags() & MTPDchat::Flag::f_kicked;
|
||||
}
|
||||
bool amCreator() const {
|
||||
[[nodiscard]] bool amCreator() const {
|
||||
return flags() & MTPDchat::Flag::f_creator;
|
||||
}
|
||||
bool isDeactivated() const {
|
||||
[[nodiscard]] bool isDeactivated() const {
|
||||
return flags() & MTPDchat::Flag::f_deactivated;
|
||||
}
|
||||
bool isMigrated() const {
|
||||
[[nodiscard]] bool isMigrated() const {
|
||||
return flags() & MTPDchat::Flag::f_migrated_to;
|
||||
}
|
||||
|
||||
static AdminRights DefaultAdminRights();
|
||||
[[nodiscard]] AdminRights defaultAdminRights(not_null<UserData*> user);
|
||||
|
||||
// Like in ChannelData.
|
||||
bool canWrite() const;
|
||||
|
||||
@@ -47,6 +47,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lottie/lottie_animation.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
|
||||
namespace {
|
||||
|
||||
const auto kAnimatedStickerDimensions = QSize(
|
||||
@@ -320,21 +322,33 @@ void DocumentOpenClickHandler::Open(
|
||||
return;
|
||||
}
|
||||
|
||||
const auto openFile = [&] {
|
||||
const auto media = data->createMediaView();
|
||||
const auto openImageInApp = [&] {
|
||||
if (data->size >= App::kImageSizeLimit) {
|
||||
return false;
|
||||
}
|
||||
const auto &location = data->location(true);
|
||||
if (data->size < App::kImageSizeLimit && location.accessEnable()) {
|
||||
if (!location.isEmpty() && location.accessEnable()) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
location.accessDisable();
|
||||
});
|
||||
const auto path = location.name();
|
||||
if (Core::MimeTypeForFile(path).name().startsWith("image/") && QImageReader(path).canRead()) {
|
||||
if (Core::MimeTypeForFile(path).name().startsWith("image/")
|
||||
&& QImageReader(path).canRead()) {
|
||||
Core::App().showDocument(data, context);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
} else if (data->mimeString().startsWith("image/")
|
||||
&& !media->bytes().isEmpty()) {
|
||||
auto bytes = media->bytes();
|
||||
auto buffer = QBuffer(&bytes);
|
||||
if (QImageReader(&buffer).canRead()) {
|
||||
Core::App().showDocument(data, context);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LaunchWithWarning(&data->session(), location.name(), context);
|
||||
return false;
|
||||
};
|
||||
const auto media = data->createMediaView();
|
||||
const auto &location = data->location(true);
|
||||
if (data->isTheme() && media->loaded(true)) {
|
||||
Core::App().showDocument(data, context);
|
||||
@@ -352,11 +366,16 @@ void DocumentOpenClickHandler::Open(
|
||||
} else {
|
||||
Core::App().showDocument(data, context);
|
||||
}
|
||||
} else if (data->saveFromDataSilent()) {
|
||||
openFile();
|
||||
} else if (data->status == FileReady
|
||||
|| data->status == FileDownloadFailed) {
|
||||
DocumentSaveClickHandler::Save(origin, data);
|
||||
} else {
|
||||
data->saveFromDataSilent();
|
||||
if (!openImageInApp()) {
|
||||
if (!data->filepath(true).isEmpty()) {
|
||||
LaunchWithWarning(&data->session(), location.name(), context);
|
||||
} else if (data->status == FileReady
|
||||
|| data->status == FileDownloadFailed) {
|
||||
DocumentSaveClickHandler::Save(origin, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -276,17 +276,31 @@ void GroupCall::applyParticipantsSlice(
|
||||
&& ((was ? was->speaking : false)
|
||||
|| (!amInCall
|
||||
&& (lastActive + speakingAfterActive > now)));
|
||||
const auto defaultVolume = Calls::Group::kDefaultVolume;
|
||||
const auto volume = (was
|
||||
&& !was->applyVolumeFromMin
|
||||
&& data.is_min())
|
||||
? was->volume
|
||||
: data.vvolume().value_or(Calls::Group::kDefaultVolume);
|
||||
const auto applyVolumeFromMin = (was && data.is_min())
|
||||
? was->applyVolumeFromMin
|
||||
: (data.is_min() || data.is_volume_by_admin());
|
||||
const auto mutedByMe = (was && data.is_min())
|
||||
? was->mutedByMe
|
||||
: data.is_muted_by_you();
|
||||
const auto onlyMinLoaded = data.is_min()
|
||||
&& (!was || was->onlyMinLoaded);
|
||||
const auto value = Participant{
|
||||
.user = user,
|
||||
.date = data.vdate().v,
|
||||
.lastActive = lastActive,
|
||||
.ssrc = uint32(data.vsource().v),
|
||||
.volume = data.vvolume().value_or(defaultVolume),
|
||||
.volume = volume,
|
||||
.applyVolumeFromMin = applyVolumeFromMin,
|
||||
.speaking = canSelfUnmute && (was ? was->speaking : false),
|
||||
.muted = data.is_muted(),
|
||||
.mutedByMe = data.is_muted_by_you(),
|
||||
.mutedByMe = mutedByMe,
|
||||
.canSelfUnmute = canSelfUnmute,
|
||||
.onlyMinLoaded = onlyMinLoaded,
|
||||
};
|
||||
if (i == end(_participants)) {
|
||||
_userBySsrc.emplace(value.ssrc, user);
|
||||
@@ -358,11 +372,13 @@ void GroupCall::applyActiveUpdate(
|
||||
not_null{ userLoaded },
|
||||
&Participant::user)
|
||||
: _participants.end();
|
||||
if (i == end(_participants)) {
|
||||
const auto notFound = (i == end(_participants));
|
||||
const auto loadByUserId = notFound || i->onlyMinLoaded;
|
||||
if (loadByUserId) {
|
||||
_unknownSpokenUids[userId] = when;
|
||||
requestUnknownParticipants();
|
||||
return;
|
||||
} else if (!i->canSelfUnmute) {
|
||||
}
|
||||
if (notFound || !i->canSelfUnmute) {
|
||||
return;
|
||||
}
|
||||
const auto was = std::make_optional(*i);
|
||||
|
||||
@@ -38,11 +38,13 @@ public:
|
||||
TimeId lastActive = 0;
|
||||
uint32 ssrc = 0;
|
||||
int volume = 0;
|
||||
bool applyVolumeFromMin = true;
|
||||
bool sounding = false;
|
||||
bool speaking = false;
|
||||
bool muted = false;
|
||||
bool mutedByMe = false;
|
||||
bool canSelfUnmute = false;
|
||||
bool onlyMinLoaded = false;
|
||||
};
|
||||
struct ParticipantUpdate {
|
||||
std::optional<Participant> was;
|
||||
|
||||
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_replies_list.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_service.h"
|
||||
@@ -627,22 +626,4 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) {
|
||||
return (list.size() == skipped);
|
||||
}
|
||||
|
||||
HistoryItem *RepliesList::lastEditableMessage() {
|
||||
const auto message = [&](MsgId msgId) {
|
||||
return _history->owner().message(_history->channelId(), msgId);
|
||||
};
|
||||
|
||||
const auto now = base::unixtime::now();
|
||||
auto proj = [&](MsgId msgId) {
|
||||
if (const auto item = message(msgId)) {
|
||||
return item->allowsEdit(now);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const auto it = ranges::find_if(_list, std::move(proj));
|
||||
return (it == end(_list))
|
||||
? nullptr
|
||||
: _history->owner().groups().findItemToEdit(message(*it)).get();
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
@@ -31,8 +31,6 @@ public:
|
||||
|
||||
[[nodiscard]] rpl::producer<int> fullCount() const;
|
||||
|
||||
[[nodiscard]] HistoryItem *lastEditableMessage();
|
||||
|
||||
private:
|
||||
struct Viewer;
|
||||
|
||||
|
||||
@@ -546,26 +546,4 @@ int32 ScheduledMessages::countListHash(const List &list) const {
|
||||
return HashFinalize(hash);
|
||||
}
|
||||
|
||||
HistoryItem *ScheduledMessages::lastEditableMessage(
|
||||
not_null<History*> history) {
|
||||
const auto i = _data.find(history);
|
||||
if (i == end(_data)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto &list = i->second;
|
||||
|
||||
sort(list);
|
||||
const auto items = ranges::view::reverse(list.items);
|
||||
|
||||
const auto now = base::unixtime::now();
|
||||
auto proj = [&](const OwnedItem &item) {
|
||||
return item->allowsEdit(now);
|
||||
};
|
||||
|
||||
const auto it = ranges::find_if(items, std::move(proj));
|
||||
return (it == end(items))
|
||||
? nullptr
|
||||
: history->owner().groups().findItemToEdit((*it).get()).get();
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
@@ -32,8 +32,6 @@ public:
|
||||
[[nodiscard]] HistoryItem *lookupItem(PeerId peer, MsgId msg) const;
|
||||
[[nodiscard]] HistoryItem *lookupItem(FullMsgId itemId) const;
|
||||
[[nodiscard]] int count(not_null<History*> history) const;
|
||||
[[nodiscard]] HistoryItem *lastEditableMessage(
|
||||
not_null<History*> history);
|
||||
|
||||
void checkEntitiesAndUpdate(const MTPDmessage &data);
|
||||
void apply(const MTPDupdateNewScheduledMessage &update);
|
||||
|
||||
@@ -168,3 +168,12 @@ TimeId DateFromMessage(const MTPmessage &message) {
|
||||
return message.vdate().v;
|
||||
});
|
||||
}
|
||||
|
||||
bool GoodStickerDimensions(int width, int height) {
|
||||
// Show all .webp (except very large ones) as stickers,
|
||||
// allow to open them in media viewer to see details.
|
||||
constexpr auto kLargetsStickerSide = 2560;
|
||||
return (width > 0)
|
||||
&& (height > 0)
|
||||
&& (width * height <= kLargetsStickerSide * kLargetsStickerSide);
|
||||
}
|
||||
|
||||
@@ -325,12 +325,7 @@ enum DocumentType {
|
||||
};
|
||||
|
||||
inline constexpr auto kStickerSideSize = 512;
|
||||
|
||||
[[nodiscard]] inline bool GoodStickerDimensions(int width, int height) {
|
||||
return (width > 0 && width <= kStickerSideSize)
|
||||
&& (height > 0 && height <= kStickerSideSize)
|
||||
&& (width == kStickerSideSize || height == kStickerSideSize);
|
||||
}
|
||||
[[nodiscard]] bool GoodStickerDimensions(int width, int height);
|
||||
|
||||
using MediaKey = QPair<uint64, uint64>;
|
||||
|
||||
|
||||
@@ -414,7 +414,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto r = e->rect();
|
||||
if (App::wnd()->contentOverlapped(this, r)) {
|
||||
if (_controller->widget()->contentOverlapped(this, r)) {
|
||||
return;
|
||||
}
|
||||
const auto activeEntry = _controller->activeChatEntryCurrent();
|
||||
@@ -2281,7 +2281,7 @@ void InnerWidget::refreshEmptyLabel() {
|
||||
resizeEmptyLabel();
|
||||
_empty->setClickHandlerFilter([=](const auto &...) {
|
||||
if (_emptyState == EmptyState::NoContacts) {
|
||||
App::wnd()->showAddContact();
|
||||
_controller->showAddContact();
|
||||
} else if (_emptyState == EmptyState::EmptyFolder) {
|
||||
editOpenedFilter();
|
||||
}
|
||||
|
||||
@@ -711,7 +711,7 @@ void Widget::animationCallback() {
|
||||
updateControlsVisibility(true);
|
||||
|
||||
if (!_filter->hasFocus()) {
|
||||
if (App::wnd()) App::wnd()->setInnerFocus();
|
||||
controller()->widget()->setInnerFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -957,7 +957,7 @@ void Widget::onChooseByDrag() {
|
||||
}
|
||||
|
||||
void Widget::showMainMenu() {
|
||||
App::wnd()->showMainMenu();
|
||||
controller()->widget()->showMainMenu();
|
||||
}
|
||||
|
||||
void Widget::searchMessages(
|
||||
@@ -1704,7 +1704,9 @@ void Widget::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
|
||||
void Widget::paintEvent(QPaintEvent *e) {
|
||||
if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return;
|
||||
if (controller()->widget()->contentOverlapped(this, e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Painter p(this);
|
||||
QRect r(e->rect());
|
||||
|
||||
@@ -399,7 +399,8 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
|
||||
MTP_int(0),
|
||||
MTP_bytes()));
|
||||
} else if (result.type() == qstr("LOCATION_INVALID")
|
||||
|| result.type() == qstr("VERSION_INVALID")) {
|
||||
|| result.type() == qstr("VERSION_INVALID")
|
||||
|| result.type() == qstr("LOCATION_NOT_AVAILABLE")) {
|
||||
filePartUnavailable();
|
||||
} else if (result.code() == 400
|
||||
&& result.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||
|
||||
@@ -151,6 +151,7 @@ QByteArray SerializeList(const std::vector<QByteArray> &values) {
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray MakeLinks(const QByteArray &value) {
|
||||
const auto domain = QByteArray("https://telegram.org/");
|
||||
auto result = QByteArray();
|
||||
@@ -190,27 +191,6 @@ QByteArray MakeLinks(const QByteArray &value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void SerializeMultiline(
|
||||
QByteArray &appendTo,
|
||||
const QByteArray &value,
|
||||
int newline) {
|
||||
const auto data = value.data();
|
||||
auto offset = 0;
|
||||
do {
|
||||
appendTo.append("> ");
|
||||
const auto win = (newline > 0 && *(data + newline - 1) == '\r');
|
||||
if (win) --newline;
|
||||
appendTo.append(data + offset, newline - offset).append(kLineBreak);
|
||||
if (win) ++newline;
|
||||
offset = newline + 1;
|
||||
newline = value.indexOf('\n', offset);
|
||||
} while (newline > 0);
|
||||
if (const auto size = value.size(); size > offset) {
|
||||
appendTo.append("> ");
|
||||
appendTo.append(data + offset, size - offset).append(kLineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray JoinList(
|
||||
const QByteArray &separator,
|
||||
const std::vector<QByteArray> &list) {
|
||||
@@ -294,31 +274,6 @@ QByteArray FormatText(
|
||||
}) | ranges::to_vector);
|
||||
}
|
||||
|
||||
QByteArray SerializeKeyValue(
|
||||
std::vector<std::pair<QByteArray, QByteArray>> &&values) {
|
||||
auto result = QByteArray();
|
||||
for (const auto &[key, value] : values) {
|
||||
if (value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
result.append(key);
|
||||
if (const auto newline = value.indexOf('\n'); newline >= 0) {
|
||||
result.append(':').append(kLineBreak);
|
||||
SerializeMultiline(result, value, newline);
|
||||
} else {
|
||||
result.append(": ").append(value).append(kLineBreak);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray SerializeBlockquote(
|
||||
std::vector<std::pair<QByteArray, QByteArray>> &&values) {
|
||||
return "<blockquote>"
|
||||
+ SerializeKeyValue(std::move(values))
|
||||
+ "</blockquote>";
|
||||
}
|
||||
|
||||
Data::Utf8String FormatUsername(const Data::Utf8String &username) {
|
||||
return username.isEmpty() ? username : ('@' + username);
|
||||
}
|
||||
@@ -766,7 +721,9 @@ QByteArray HtmlWriter::Wrap::pushUserpic(const UserpicData &userpic) {
|
||||
"line-height: " + size));
|
||||
auto character = [](const QByteArray &from) {
|
||||
const auto utf = QString::fromUtf8(from).trimmed();
|
||||
return utf.isEmpty() ? QByteArray() : utf.mid(0, 1).toUtf8();
|
||||
return utf.isEmpty()
|
||||
? QByteArray()
|
||||
: SerializeString(utf.mid(0, 1).toUtf8());
|
||||
};
|
||||
result.append(character(userpic.firstName));
|
||||
result.append(character(userpic.lastName));
|
||||
@@ -997,16 +954,20 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||
const auto serviceText = v::match(message.action.content, [&](
|
||||
const ActionChatCreate &data) {
|
||||
return serviceFrom
|
||||
+ " created group «" + data.title + "»"
|
||||
+ " created group «"
|
||||
+ SerializeString(data.title)
|
||||
+ "»"
|
||||
+ (data.userIds.empty()
|
||||
? QByteArray()
|
||||
: " with members " + peers.wrapUserNames(data.userIds));
|
||||
}, [&](const ActionChatEditTitle &data) {
|
||||
return isChannel
|
||||
? ("Channel title changed to «" + data.title + "»")
|
||||
? ("Channel title changed to «"
|
||||
+ SerializeString(data.title)
|
||||
+ "»")
|
||||
: (serviceFrom
|
||||
+ " changed group title to «"
|
||||
+ data.title
|
||||
+ " fchanged group title to «"
|
||||
+ SerializeString(data.title)
|
||||
+ "»");
|
||||
}, [&](const ActionChatEditPhoto &data) {
|
||||
return isChannel
|
||||
@@ -1029,14 +990,16 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||
+ " joined group by link from "
|
||||
+ peers.wrapUserName(data.inviterId);
|
||||
}, [&](const ActionChannelCreate &data) {
|
||||
return "Channel «" + data.title + "» created";
|
||||
return "Channel «"
|
||||
+ SerializeString(data.title)
|
||||
+ "» created";
|
||||
}, [&](const ActionChatMigrateTo &data) {
|
||||
return serviceFrom
|
||||
+ " converted this group to a supergroup";
|
||||
}, [&](const ActionChannelMigrateFrom &data) {
|
||||
return serviceFrom
|
||||
+ " converted a basic group to this supergroup "
|
||||
+ "«" + data.title + "»";
|
||||
+ "«" + SerializeString(data.title) + "»";
|
||||
}, [&](const ActionPinMessage &data) {
|
||||
return serviceFrom
|
||||
+ " pinned "
|
||||
@@ -1062,7 +1025,7 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||
return data.message;
|
||||
}, [&](const ActionBotAllowed &data) {
|
||||
return "You allowed this bot to message you when you logged in on "
|
||||
+ data.domain;
|
||||
+ SerializeString(data.domain);
|
||||
}, [&](const ActionSecureValuesSent &data) {
|
||||
auto list = std::vector<QByteArray>();
|
||||
for (const auto type : data.types) {
|
||||
|
||||
@@ -229,12 +229,6 @@ void searchByHashtag(const QString &tag, PeerData *inPeer) {
|
||||
}
|
||||
}
|
||||
|
||||
void showSettings() {
|
||||
if (auto w = App::wnd()) {
|
||||
w->showSettings();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace App
|
||||
|
||||
namespace Ui {
|
||||
|
||||
@@ -48,7 +48,6 @@ void activateBotCommand(
|
||||
int row,
|
||||
int column);
|
||||
void searchByHashtag(const QString &tag, PeerData *inPeer);
|
||||
void showSettings();
|
||||
|
||||
} // namespace App
|
||||
|
||||
|
||||
@@ -1088,12 +1088,15 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
}
|
||||
if (lnkPhoto) {
|
||||
const auto photo = lnkPhoto->photo();
|
||||
_menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
savePhotoToFile(photo);
|
||||
}));
|
||||
_menu->addAction(tr::lng_context_copy_image(tr::now), [=] {
|
||||
copyContextImage(photo);
|
||||
});
|
||||
const auto media = photo->activeMediaView();
|
||||
if (!photo->isNull() && media && media->loaded()) {
|
||||
_menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
savePhotoToFile(photo);
|
||||
}));
|
||||
_menu->addAction(tr::lng_context_copy_image(tr::now), [=] {
|
||||
copyContextImage(photo);
|
||||
});
|
||||
}
|
||||
if (photo->hasAttachedStickers()) {
|
||||
const auto controller = _controller;
|
||||
auto callback = [=] {
|
||||
@@ -1515,7 +1518,7 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but
|
||||
if (_selectedItem && !_pressWasInactive) {
|
||||
if (_selectedText.from == _selectedText.to) {
|
||||
_selectedItem = nullptr;
|
||||
App::wnd()->setInnerFocus();
|
||||
_controller->widget()->setInnerFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1392,7 +1392,7 @@ void HistoryInner::mouseActionFinish(
|
||||
auto sel = _selected.cbegin()->second;
|
||||
if (sel != FullSelection && sel.from == sel.to) {
|
||||
_selected.clear();
|
||||
App::wnd()->setInnerFocus();
|
||||
_controller->widget()->setInnerFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1579,12 +1579,15 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
}
|
||||
};
|
||||
const auto addPhotoActions = [&](not_null<PhotoData*> photo) {
|
||||
_menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
savePhotoToFile(photo);
|
||||
}));
|
||||
_menu->addAction(tr::lng_context_copy_image(tr::now), [=] {
|
||||
copyContextImage(photo);
|
||||
});
|
||||
const auto media = photo->activeMediaView();
|
||||
if (!photo->isNull() && media && media->loaded()) {
|
||||
_menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
savePhotoToFile(photo);
|
||||
}));
|
||||
_menu->addAction(tr::lng_context_copy_image(tr::now), [=] {
|
||||
copyContextImage(photo);
|
||||
});
|
||||
}
|
||||
if (photo->hasAttachedStickers()) {
|
||||
_menu->addAction(tr::lng_context_attached_stickers(tr::now), [=] {
|
||||
session->api().attachedStickers().requestAttachedStickerSets(
|
||||
|
||||
@@ -775,7 +775,7 @@ void HistoryItem::sendFailed() {
|
||||
}
|
||||
|
||||
bool HistoryItem::needCheck() const {
|
||||
return out() || (id < 0 && history()->peer->isSelf());
|
||||
return (out() && !isEmpty()) || (id < 0 && history()->peer->isSelf());
|
||||
}
|
||||
|
||||
bool HistoryItem::unread() const {
|
||||
|
||||
@@ -792,9 +792,7 @@ bool HistoryService::updateDependencyItem() {
|
||||
}
|
||||
|
||||
bool HistoryService::needCheck() const {
|
||||
return out()
|
||||
&& ((GetDependentData() != nullptr)
|
||||
|| Has<HistoryServiceSelfDestruct>());
|
||||
return out() && !isEmpty();
|
||||
}
|
||||
|
||||
QString HistoryService::inDialogsText(DrawInDialog way) const {
|
||||
|
||||
@@ -260,9 +260,11 @@ HistoryWidget::HistoryWidget(
|
||||
connect(_field, &Ui::InputField::changed, [=] {
|
||||
fieldChanged();
|
||||
});
|
||||
connect(App::wnd()->windowHandle(), &QWindow::visibleChanged, this, [=] {
|
||||
windowIsVisibleChanged();
|
||||
});
|
||||
connect(
|
||||
controller->widget()->windowHandle(),
|
||||
&QWindow::visibleChanged,
|
||||
this,
|
||||
[=] { windowIsVisibleChanged(); });
|
||||
|
||||
initTabbedSelector();
|
||||
|
||||
@@ -1630,6 +1632,8 @@ void HistoryWidget::fastShowAtEnd(not_null<History*> history) {
|
||||
|
||||
clearAllLoadRequests();
|
||||
setMsgId(ShowAtUnreadMsgId);
|
||||
_pinnedClickedId = FullMsgId();
|
||||
_minPinnedId = std::nullopt;
|
||||
if (_history->isReadyFor(_showAtMsgId)) {
|
||||
historyLoaded();
|
||||
} else {
|
||||
@@ -2006,7 +2010,7 @@ void HistoryWidget::showHistory(
|
||||
update();
|
||||
controller()->floatPlayerAreaUpdated();
|
||||
|
||||
crl::on_main(App::wnd(), [] { App::wnd()->setInnerFocus(); });
|
||||
crl::on_main(this, [=] { controller()->widget()->setInnerFocus(); });
|
||||
}
|
||||
|
||||
void HistoryWidget::clearDelayedShowAt() {
|
||||
|
||||
@@ -40,4 +40,13 @@ struct SetHistoryArgs {
|
||||
rpl::producer<std::optional<QString>> writeRestriction;
|
||||
};
|
||||
|
||||
struct ReplyNextRequest {
|
||||
enum class Direction {
|
||||
Next,
|
||||
Previous,
|
||||
};
|
||||
const FullMsgId replyId;
|
||||
const Direction direction;
|
||||
};
|
||||
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/controls/history_view_compose_controls.h"
|
||||
|
||||
#include "base/event_filter.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
@@ -49,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/controls/emoji_button.h"
|
||||
#include "ui/controls/send_button.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
@@ -64,6 +66,11 @@ constexpr auto kMouseEvents = {
|
||||
QEvent::MouseButtonRelease
|
||||
};
|
||||
|
||||
constexpr auto kCommonModifiers = 0
|
||||
| Qt::ShiftModifier
|
||||
| Qt::MetaModifier
|
||||
| Qt::ControlModifier;
|
||||
|
||||
using FileChosen = ComposeControls::FileChosen;
|
||||
using PhotoChosen = ComposeControls::PhotoChosen;
|
||||
using MessageToEdit = ComposeControls::MessageToEdit;
|
||||
@@ -657,6 +664,10 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
|
||||
if (!channel->mgInfo->botStatus) {
|
||||
session().api().requestBots(channel);
|
||||
}
|
||||
} else if (hasSilentBroadcastToggle()) {
|
||||
_silent = std::make_unique<Ui::SilentToggle>(
|
||||
_wrap.get(),
|
||||
peer->asChannel());
|
||||
}
|
||||
session().local().readDraftsWithCursors(_history);
|
||||
applyDraft();
|
||||
@@ -712,13 +723,19 @@ rpl::producer<> ComposeControls::cancelRequests() const {
|
||||
return _cancelRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<not_null<QKeyEvent*>> ComposeControls::keyEvents() const {
|
||||
return _wrap->events(
|
||||
) | rpl::map([=](not_null<QEvent*> e) -> not_null<QKeyEvent*> {
|
||||
return static_cast<QKeyEvent*>(e.get());
|
||||
}) | rpl::filter([=](not_null<QEvent*> event) {
|
||||
return (event->type() == QEvent::KeyPress);
|
||||
});
|
||||
auto ComposeControls::scrollKeyEvents() const
|
||||
-> rpl::producer<not_null<QKeyEvent*>> {
|
||||
return _scrollKeyEvents.events();
|
||||
}
|
||||
|
||||
auto ComposeControls::editLastMessageRequests() const
|
||||
-> rpl::producer<not_null<QKeyEvent*>> {
|
||||
return _editLastMessageRequests.events();
|
||||
}
|
||||
|
||||
auto ComposeControls::replyNextRequests() const
|
||||
-> rpl::producer<ReplyNextRequest> {
|
||||
return _replyNextRequests.events();
|
||||
}
|
||||
|
||||
auto ComposeControls::sendContentRequests(SendRequestType requestType) const {
|
||||
@@ -767,7 +784,16 @@ rpl::producer<MessageToEdit> ComposeControls::editRequests() const {
|
||||
}
|
||||
|
||||
rpl::producer<> ComposeControls::attachRequests() const {
|
||||
return _attachToggle->clicks() | rpl::to_empty;
|
||||
return rpl::merge(
|
||||
_attachToggle->clicks() | rpl::to_empty,
|
||||
_attachRequests.events()
|
||||
) | rpl::filter([=] {
|
||||
if (isEditingMessage()) {
|
||||
Ui::show(Box<InformBox>(tr::lng_edit_caption_attach(tr::now)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void ComposeControls::setMimeDataHook(MimeDataHook hook) {
|
||||
@@ -940,6 +966,7 @@ void ComposeControls::init() {
|
||||
initSendButton();
|
||||
initWriteRestriction();
|
||||
initVoiceRecordBar();
|
||||
initKeyHandler();
|
||||
|
||||
_botCommandStart->setClickedCallback([=] { setText({ "/" }); });
|
||||
|
||||
@@ -1041,6 +1068,64 @@ void ComposeControls::drawRestrictedWrite(Painter &p, const QString &error) {
|
||||
style::al_center);
|
||||
}
|
||||
|
||||
void ComposeControls::initKeyHandler() {
|
||||
_wrap->events(
|
||||
) | rpl::filter([=](not_null<QEvent*> event) {
|
||||
return (event->type() == QEvent::KeyPress);
|
||||
}) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
auto keyEvent = static_cast<QKeyEvent*>(e.get());
|
||||
const auto key = keyEvent->key();
|
||||
const auto isCtrl = keyEvent->modifiers() == Qt::ControlModifier;
|
||||
if (key == Qt::Key_O && isCtrl) {
|
||||
_attachRequests.fire({});
|
||||
return;
|
||||
}
|
||||
if (key == Qt::Key_Up) {
|
||||
if (!isEditingMessage()) {
|
||||
_editLastMessageRequests.fire(std::move(keyEvent));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((key == Qt::Key_Up)
|
||||
|| (key == Qt::Key_Down)
|
||||
|| (key == Qt::Key_PageUp)
|
||||
|| (key == Qt::Key_PageDown)) {
|
||||
_scrollKeyEvents.fire(std::move(keyEvent));
|
||||
}
|
||||
}, _wrap->lifetime());
|
||||
|
||||
base::install_event_filter(_wrap.get(), _field, [=](not_null<QEvent*> e) {
|
||||
using Result = base::EventFilterResult;
|
||||
if (e->type() != QEvent::KeyPress) {
|
||||
return Result::Continue;
|
||||
}
|
||||
const auto k = static_cast<QKeyEvent*>(e.get());
|
||||
|
||||
if ((k->modifiers() & kCommonModifiers) == Qt::ControlModifier) {
|
||||
const auto isUp = (k->key() == Qt::Key_Up);
|
||||
const auto isDown = (k->key() == Qt::Key_Down);
|
||||
if (isUp || isDown) {
|
||||
if (Platform::IsMac()) {
|
||||
// Cmd + Up is used instead of Home.
|
||||
if ((isUp && (!_field->textCursor().atStart()))
|
||||
// Cmd + Down is used instead of End.
|
||||
|| (isDown && (!_field->textCursor().atEnd()))) {
|
||||
return Result::Continue;
|
||||
}
|
||||
}
|
||||
_replyNextRequests.fire({
|
||||
.replyId = replyingToMessage(),
|
||||
.direction = (isDown
|
||||
? ReplyNextRequest::Direction::Next
|
||||
: ReplyNextRequest::Direction::Previous)
|
||||
});
|
||||
return Result::Cancel;
|
||||
}
|
||||
}
|
||||
return Result::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void ComposeControls::initField() {
|
||||
_field->setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||
updateSubmitSettings();
|
||||
@@ -1217,6 +1302,17 @@ void ComposeControls::updateFieldPlaceholder() {
|
||||
updateSendButtonType();
|
||||
}
|
||||
|
||||
void ComposeControls::updateSilentBroadcast() {
|
||||
if (!_silent || !_history) {
|
||||
return;
|
||||
}
|
||||
const auto &peer = _history->peer;
|
||||
if (!session().data().notifySilentPostsUnknown(peer)) {
|
||||
_silent->setChecked(session().data().notifySilentPosts(peer));
|
||||
updateFieldPlaceholder();
|
||||
}
|
||||
}
|
||||
|
||||
void ComposeControls::fieldChanged() {
|
||||
if (!_inlineBot
|
||||
&& !_header->isEditingMessage()
|
||||
@@ -1640,14 +1736,15 @@ void ComposeControls::finishAnimating() {
|
||||
|
||||
void ComposeControls::updateControlsGeometry(QSize size) {
|
||||
// _attachToggle -- _inlineResults ------ _tabbedPanel -- _fieldBarCancel
|
||||
// (_attachDocument|_attachPhoto) _field _botCommandStart _tabbedSelectorToggle _send
|
||||
// (_attachDocument|_attachPhoto) _field (_silent|_botCommandStart) _tabbedSelectorToggle _send
|
||||
|
||||
const auto fieldWidth = size.width()
|
||||
- _attachToggle->width()
|
||||
- st::historySendRight
|
||||
- _send->width()
|
||||
- _tabbedSelectorToggle->width()
|
||||
- (_botCommandShown ? _botCommandStart->width() : 0);
|
||||
- (_botCommandShown ? _botCommandStart->width() : 0)
|
||||
- (_silent ? _silent->width() : 0);
|
||||
{
|
||||
const auto oldFieldHeight = _field->height();
|
||||
_field->resizeToWidth(fieldWidth);
|
||||
@@ -1678,6 +1775,9 @@ void ComposeControls::updateControlsGeometry(QSize size) {
|
||||
_tabbedSelectorToggle->moveToRight(right, buttonsTop);
|
||||
right += _tabbedSelectorToggle->width();
|
||||
_botCommandStart->moveToRight(right, buttonsTop);
|
||||
if (_silent) {
|
||||
_silent->moveToRight(right, buttonsTop);
|
||||
}
|
||||
|
||||
_voiceRecordBar->resizeToWidth(size.width());
|
||||
_voiceRecordBar->moveToLeft(
|
||||
@@ -2075,12 +2175,20 @@ void ComposeControls::initWebpageProcess() {
|
||||
|
||||
session().changes().peerUpdates(
|
||||
Data::PeerUpdate::Flag::Rights
|
||||
| Data::PeerUpdate::Flag::Notifications
|
||||
) | rpl::filter([=](const Data::PeerUpdate &update) {
|
||||
return (update.peer.get() == peer);
|
||||
}) | rpl::start_with_next([=] {
|
||||
checkPreview();
|
||||
updateStickersByEmoji();
|
||||
updateFieldPlaceholder();
|
||||
}) | rpl::map([](const Data::PeerUpdate &update) {
|
||||
return update.flags;
|
||||
}) | rpl::start_with_next([=](Data::PeerUpdate::Flags flags) {
|
||||
if (flags & Data::PeerUpdate::Flag::Rights) {
|
||||
checkPreview();
|
||||
updateStickersByEmoji();
|
||||
updateFieldPlaceholder();
|
||||
}
|
||||
if (flags & Data::PeerUpdate::Flag::Notifications) {
|
||||
updateSilentBroadcast();
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
base::ObservableViewer(
|
||||
@@ -2183,6 +2291,18 @@ bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ComposeControls::hasSilentBroadcastToggle() const {
|
||||
if (!_history) {
|
||||
return false;
|
||||
}
|
||||
const auto &peer = _history->peer;
|
||||
return peer
|
||||
&& peer->isChannel()
|
||||
&& !peer->isMegagroup()
|
||||
&& peer->canWrite()
|
||||
&& !session().data().notifySilentPostsUnknown(peer);
|
||||
}
|
||||
|
||||
void ComposeControls::updateInlineBotQuery() {
|
||||
if (!_history) {
|
||||
return;
|
||||
@@ -2253,4 +2373,27 @@ void ComposeControls::applyInlineBotQuery(
|
||||
}
|
||||
}
|
||||
|
||||
Fn<void()> ComposeControls::restoreTextCallback(
|
||||
const QString &insertTextOnCancel) const {
|
||||
const auto cursor = _field->textCursor();
|
||||
const auto position = cursor.position();
|
||||
const auto anchor = cursor.anchor();
|
||||
const auto text = getTextWithAppliedMarkdown();
|
||||
|
||||
_field->setTextWithTags({});
|
||||
|
||||
return crl::guard(_field, [=] {
|
||||
_field->setTextWithTags(text);
|
||||
auto cursor = _field->textCursor();
|
||||
cursor.setPosition(anchor);
|
||||
if (position != anchor) {
|
||||
cursor.setPosition(position, QTextCursor::KeepAnchor);
|
||||
}
|
||||
_field->setTextCursor(cursor);
|
||||
if (!insertTextOnCancel.isEmpty()) {
|
||||
_field->textCursor().insertText(insertTextOnCancel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace Ui {
|
||||
class SendButton;
|
||||
class IconButton;
|
||||
class EmojiButton;
|
||||
class SilentToggle;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
@@ -82,6 +83,7 @@ public:
|
||||
using VoiceToSend = Controls::VoiceToSend;
|
||||
using SendActionUpdate = Controls::SendActionUpdate;
|
||||
using SetHistoryArgs = Controls::SetHistoryArgs;
|
||||
using ReplyNextRequest = Controls::ReplyNextRequest;
|
||||
using FieldHistoryAction = Ui::InputField::HistoryAction;
|
||||
|
||||
enum class Mode {
|
||||
@@ -118,10 +120,15 @@ public:
|
||||
[[nodiscard]] rpl::producer<FileChosen> fileChosen() const;
|
||||
[[nodiscard]] rpl::producer<PhotoChosen> photoChosen() const;
|
||||
[[nodiscard]] rpl::producer<Data::MessagePosition> scrollRequests() const;
|
||||
[[nodiscard]] rpl::producer<not_null<QKeyEvent*>> keyEvents() const;
|
||||
[[nodiscard]] rpl::producer<InlineChosen> inlineResultChosen() const;
|
||||
[[nodiscard]] rpl::producer<SendActionUpdate> sendActionUpdates() const;
|
||||
[[nodiscard]] rpl::producer<not_null<QEvent*>> viewportEvents() const;
|
||||
[[nodiscard]] auto scrollKeyEvents() const
|
||||
-> rpl::producer<not_null<QKeyEvent*>>;
|
||||
[[nodiscard]] auto editLastMessageRequests() const
|
||||
-> rpl::producer<not_null<QKeyEvent*>>;
|
||||
[[nodiscard]] auto replyNextRequests() const
|
||||
-> rpl::producer<ReplyNextRequest>;
|
||||
|
||||
using MimeDataHook = Fn<bool(
|
||||
not_null<const QMimeData*> data,
|
||||
@@ -165,6 +172,8 @@ public:
|
||||
void applyDraft(
|
||||
FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
|
||||
|
||||
Fn<void()> restoreTextCallback(const QString &insertTextOnCancel) const;
|
||||
|
||||
private:
|
||||
enum class TextUpdateEvent {
|
||||
SaveDraft = (1 << 0),
|
||||
@@ -189,6 +198,7 @@ private:
|
||||
void initWriteRestriction();
|
||||
void initVoiceRecordBar();
|
||||
void initAutocomplete();
|
||||
void initKeyHandler();
|
||||
void updateSubmitSettings();
|
||||
void updateSendButtonType();
|
||||
void updateHeight();
|
||||
@@ -211,6 +221,7 @@ private:
|
||||
void checkAutocomplete();
|
||||
void updateStickersByEmoji();
|
||||
void updateFieldPlaceholder();
|
||||
void updateSilentBroadcast();
|
||||
void editMessage(not_null<HistoryItem*> item);
|
||||
|
||||
void escape();
|
||||
@@ -228,6 +239,8 @@ private:
|
||||
void clearInlineBot();
|
||||
void inlineBotChanged();
|
||||
|
||||
bool hasSilentBroadcastToggle() const;
|
||||
|
||||
// Look in the _field for the inline bot and query string.
|
||||
void updateInlineBotQuery();
|
||||
|
||||
@@ -272,6 +285,7 @@ private:
|
||||
const not_null<Ui::EmojiButton*> _tabbedSelectorToggle;
|
||||
const not_null<Ui::InputField*> _field;
|
||||
const not_null<Ui::IconButton*> _botCommandStart;
|
||||
std::unique_ptr<Ui::SilentToggle> _silent;
|
||||
|
||||
std::unique_ptr<InlineBots::Layout::Widget> _inlineResults;
|
||||
std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel;
|
||||
@@ -290,6 +304,10 @@ private:
|
||||
rpl::event_stream<InlineChosen> _inlineResultChosen;
|
||||
rpl::event_stream<SendActionUpdate> _sendActionUpdates;
|
||||
rpl::event_stream<QString> _sendCommandRequests;
|
||||
rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents;
|
||||
rpl::event_stream<not_null<QKeyEvent*>> _editLastMessageRequests;
|
||||
rpl::event_stream<> _attachRequests;
|
||||
rpl::event_stream<ReplyNextRequest> _replyNextRequests;
|
||||
|
||||
TextUpdateEvents _textUpdateEvents = TextUpdateEvents()
|
||||
| TextUpdateEvent::SaveDraft
|
||||
|
||||
@@ -1602,7 +1602,7 @@ void VoiceRecordBar::installListenStateFilter() {
|
||||
_listen->playPause();
|
||||
return Result::Cancel;
|
||||
}
|
||||
if (isEnter) {
|
||||
if (isEnter && !_warningShown) {
|
||||
requestToSendWithOptions({});
|
||||
return Result::Cancel;
|
||||
}
|
||||
@@ -1633,6 +1633,7 @@ void VoiceRecordBar::showDiscardBox(
|
||||
hideAnimated();
|
||||
}
|
||||
close();
|
||||
_warningShown = false;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
@@ -1644,6 +1645,7 @@ void VoiceRecordBar::showDiscardBox(
|
||||
tr::lng_record_lock_discard(tr::now),
|
||||
st::attentionBoxButton,
|
||||
std::move(sure)));
|
||||
_warningShown = true;
|
||||
}
|
||||
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
@@ -143,6 +143,8 @@ private:
|
||||
|
||||
Fn<bool()> _startRecordingFilter;
|
||||
|
||||
bool _warningShown = false;
|
||||
|
||||
rpl::variable<bool> _recording = false;
|
||||
rpl::variable<bool> _inField = false;
|
||||
rpl::variable<bool> _lockShowing = false;
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace {
|
||||
|
||||
// If we can't cloud-export link for such time we export it locally.
|
||||
constexpr auto kExportLocalTimeout = crl::time(1000);
|
||||
constexpr auto kRescheduleLimit = 20;
|
||||
|
||||
//void AddToggleGroupingAction( // #feed
|
||||
// not_null<Ui::PopupMenu*> menu,
|
||||
@@ -443,31 +444,64 @@ bool AddSendNowMessageAction(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddRescheduleMessageAction(
|
||||
bool AddRescheduleAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
const ContextMenuRequest &request,
|
||||
not_null<ListWidget*> list) {
|
||||
if (!HasEditMessageAction(request, list)
|
||||
|| !request.item->isScheduled()) {
|
||||
const auto owner = &request.navigation->session().data();
|
||||
|
||||
const auto goodSingle = !(!HasEditMessageAction(request, list)
|
||||
|| !request.item->isScheduled());
|
||||
const auto goodMany = [&] {
|
||||
if (goodSingle) {
|
||||
return false;
|
||||
}
|
||||
if (!request.overSelection || request.selectedItems.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (request.selectedItems.size() > kRescheduleLimit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
if (!goodSingle && !goodMany) {
|
||||
return false;
|
||||
}
|
||||
const auto owner = &request.item->history()->owner();
|
||||
const auto itemId = request.item->fullId();
|
||||
menu->addAction(tr::lng_context_reschedule(tr::now), [=] {
|
||||
const auto item = owner->message(itemId);
|
||||
if (!item) {
|
||||
auto ids = goodSingle
|
||||
? MessageIdsList{ request.item->fullId() }
|
||||
: ExtractIdsList(request.selectedItems);
|
||||
ranges::sort(ids, [&](const FullMsgId &a, const FullMsgId &b) {
|
||||
const auto itemA = owner->message(a);
|
||||
const auto itemB = owner->message(b);
|
||||
return (itemA && itemB) && (itemA->position() < itemB->position());
|
||||
});
|
||||
|
||||
auto text = ((ids.size() == 1)
|
||||
? tr::lng_context_reschedule
|
||||
: tr::lng_context_reschedule_selected)(tr::now);
|
||||
|
||||
menu->addAction(std::move(text), [=] {
|
||||
const auto firstItem = owner->message(ids.front());
|
||||
if (!firstItem) {
|
||||
return;
|
||||
}
|
||||
list->cancelSelection();
|
||||
const auto callback = [=](Api::SendOptions options) {
|
||||
if (const auto item = owner->message(itemId)) {
|
||||
for (const auto &id : ids) {
|
||||
const auto item = owner->message(id);
|
||||
if (!item && !item->isScheduled()) {
|
||||
continue;
|
||||
}
|
||||
if (!item->media() || !item->media()->webpage()) {
|
||||
options.removeWebPageId = true;
|
||||
}
|
||||
Api::RescheduleMessage(item, options);
|
||||
// Increase the scheduled date by 1s to keep the order.
|
||||
options.scheduled += 1;
|
||||
}
|
||||
};
|
||||
|
||||
const auto peer = item->history()->peer;
|
||||
const auto peer = firstItem->history()->peer;
|
||||
const auto sendMenuType = !peer
|
||||
? SendMenu::Type::Disabled
|
||||
: peer->isSelf()
|
||||
@@ -477,9 +511,10 @@ bool AddRescheduleMessageAction(
|
||||
: SendMenu::Type::Scheduled;
|
||||
|
||||
using S = Data::ScheduledMessages;
|
||||
const auto date = (item->date() == S::kScheduledUntilOnlineTimestamp)
|
||||
const auto itemDate = firstItem->date();
|
||||
const auto date = (itemDate == S::kScheduledUntilOnlineTimestamp)
|
||||
? HistoryView::DefaultScheduleTime()
|
||||
: item->date() + 600;
|
||||
: itemDate + 600;
|
||||
|
||||
const auto box = Ui::show(
|
||||
HistoryView::PrepareScheduleBox(
|
||||
@@ -490,9 +525,10 @@ bool AddRescheduleMessageAction(
|
||||
Ui::LayerOption::KeepOther);
|
||||
|
||||
owner->itemRemoved(
|
||||
itemId
|
||||
) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
if (ranges::contains(ids, item->fullId())) {
|
||||
box->closeBox();
|
||||
}
|
||||
}, box->lifetime());
|
||||
});
|
||||
return true;
|
||||
@@ -825,7 +861,7 @@ void AddMessageActions(
|
||||
AddDeleteAction(menu, request, list);
|
||||
AddReportAction(menu, request, list);
|
||||
AddSelectionAction(menu, request, list);
|
||||
AddRescheduleMessageAction(menu, request, list);
|
||||
AddRescheduleAction(menu, request, list);
|
||||
}
|
||||
|
||||
void AddCopyLinkAction(
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "history/view/history_view_list_widget.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_text.h"
|
||||
@@ -524,6 +525,11 @@ void ListWidget::updateHighlightedMessage() {
|
||||
_highlightedMessageId = FullMsgId();
|
||||
}
|
||||
|
||||
void ListWidget::clearHighlightedMessage() {
|
||||
_highlightedMessageId = FullMsgId();
|
||||
updateHighlightedMessage();
|
||||
}
|
||||
|
||||
void ListWidget::checkUnreadBarCreation() {
|
||||
if (!_bar.element) {
|
||||
if (auto data = _delegate->listMessagesBar(_items); data.bar.element) {
|
||||
@@ -2248,7 +2254,7 @@ void ListWidget::mouseActionFinish(
|
||||
} else if (_selectedTextItem && !_pressWasInactive) {
|
||||
if (_selectedTextRange.from == _selectedTextRange.to) {
|
||||
clearTextSelection();
|
||||
App::wnd()->setInnerFocus();
|
||||
_controller->widget()->setInnerFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2725,10 +2731,27 @@ rpl::producer<FullMsgId> ListWidget::editMessageRequested() const {
|
||||
return _requestedToEditMessage.events();
|
||||
}
|
||||
|
||||
void ListWidget::editMessageRequestNotify(FullMsgId item) {
|
||||
void ListWidget::editMessageRequestNotify(FullMsgId item) const {
|
||||
_requestedToEditMessage.fire(std::move(item));
|
||||
}
|
||||
|
||||
bool ListWidget::lastMessageEditRequestNotify() const {
|
||||
const auto now = base::unixtime::now();
|
||||
auto proj = [&](not_null<Element*> view) {
|
||||
return view->data()->allowsEdit(now);
|
||||
};
|
||||
const auto &list = ranges::view::reverse(_items);
|
||||
const auto it = ranges::find_if(list, std::move(proj));
|
||||
if (it == end(list)) {
|
||||
return false;
|
||||
} else {
|
||||
const auto item =
|
||||
session().data().groups().findItemToEdit((*it)->data()).get();
|
||||
editMessageRequestNotify(item->fullId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<FullMsgId> ListWidget::replyToMessageRequested() const {
|
||||
return _requestedToReplyToMessage.events();
|
||||
}
|
||||
@@ -2741,6 +2764,49 @@ rpl::producer<FullMsgId> ListWidget::readMessageRequested() const {
|
||||
return _requestedToReadMessage.events();
|
||||
}
|
||||
|
||||
rpl::producer<FullMsgId> ListWidget::showMessageRequested() const {
|
||||
return _requestedToShowMessage.events();
|
||||
}
|
||||
|
||||
void ListWidget::replyNextMessage(FullMsgId fullId, bool next) {
|
||||
const auto reply = [&](Element *view) {
|
||||
if (view) {
|
||||
const auto newFullId = view->data()->fullId();
|
||||
replyToMessageRequestNotify(newFullId);
|
||||
_requestedToShowMessage.fire_copy(newFullId);
|
||||
} else {
|
||||
replyToMessageRequestNotify(FullMsgId());
|
||||
clearHighlightedMessage();
|
||||
}
|
||||
};
|
||||
const auto replyFirst = [&] {
|
||||
reply(next ? nullptr : _items.back().get());
|
||||
};
|
||||
if (!fullId) {
|
||||
replyFirst();
|
||||
return;
|
||||
}
|
||||
|
||||
auto proj = [&](not_null<Element*> view) {
|
||||
return view->data()->fullId() == fullId;
|
||||
};
|
||||
const auto &list = ranges::view::reverse(_items);
|
||||
const auto it = ranges::find_if(list, std::move(proj));
|
||||
if (it == end(list)) {
|
||||
replyFirst();
|
||||
return;
|
||||
} else {
|
||||
const auto nextIt = it + (next ? -1 : 1);
|
||||
if (nextIt == end(list)) {
|
||||
return;
|
||||
} else if (next && (it == begin(list))) {
|
||||
reply(nullptr);
|
||||
} else {
|
||||
reply(nextIt->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListWidget::~ListWidget() = default;
|
||||
|
||||
void ConfirmDeleteSelectedItems(not_null<ListWidget*> widget) {
|
||||
|
||||
@@ -206,10 +206,13 @@ public:
|
||||
bool tooltipWindowActive() const override;
|
||||
|
||||
[[nodiscard]] rpl::producer<FullMsgId> editMessageRequested() const;
|
||||
void editMessageRequestNotify(FullMsgId item);
|
||||
void editMessageRequestNotify(FullMsgId item) const;
|
||||
[[nodiscard]] bool lastMessageEditRequestNotify() const;
|
||||
[[nodiscard]] rpl::producer<FullMsgId> replyToMessageRequested() const;
|
||||
void replyToMessageRequestNotify(FullMsgId item);
|
||||
[[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const;
|
||||
[[nodiscard]] rpl::producer<FullMsgId> showMessageRequested() const;
|
||||
void replyNextMessage(FullMsgId fullId, bool next = true);
|
||||
|
||||
// ElementDelegate interface.
|
||||
Context elementContext() override;
|
||||
@@ -447,6 +450,7 @@ private:
|
||||
void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo);
|
||||
|
||||
void updateHighlightedMessage();
|
||||
void clearHighlightedMessage();
|
||||
|
||||
// This function finds all history items that are displayed and calls template method
|
||||
// for each found message (in given direction) in the passed history with passed top offset.
|
||||
@@ -554,6 +558,7 @@ private:
|
||||
rpl::event_stream<FullMsgId> _requestedToEditMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToReplyToMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToReadMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToShowMessage;
|
||||
|
||||
rpl::lifetime _viewerLifetime;
|
||||
|
||||
|
||||
@@ -2113,12 +2113,9 @@ bool Message::displayForwardedFrom() const {
|
||||
}
|
||||
}
|
||||
const auto media = this->media();
|
||||
return item->Has<HistoryMessageVia>()
|
||||
|| !media
|
||||
return !media
|
||||
|| !media->isDisplayed()
|
||||
|| !media->hideForwardedFrom()
|
||||
|| (forwarded->originalSender
|
||||
&& forwarded->originalSender->isChannel());
|
||||
|| !media->hideForwardedFrom();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -221,6 +221,13 @@ RepliesWidget::RepliesWidget(
|
||||
replyToMessage(fullId);
|
||||
}, _inner->lifetime());
|
||||
|
||||
_inner->showMessageRequested(
|
||||
) | rpl::start_with_next([=](auto fullId) {
|
||||
if (const auto item = session().data().message(fullId)) {
|
||||
showAtPosition(item->position());
|
||||
}
|
||||
}, _inner->lifetime());
|
||||
|
||||
_composeControls->sendActionUpdates(
|
||||
) | rpl::start_with_next([=](ComposeControls::SendActionUpdate &&data) {
|
||||
session().sendProgressManager().update(
|
||||
@@ -488,31 +495,26 @@ void RepliesWidget::setupComposeControls() {
|
||||
showAtPosition(pos);
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->keyEvents(
|
||||
_composeControls->scrollKeyEvents(
|
||||
) | rpl::start_with_next([=](not_null<QKeyEvent*> e) {
|
||||
if (e->key() == Qt::Key_Up) {
|
||||
if (!_composeControls->isEditingMessage()) {
|
||||
if (const auto item = _replies->lastEditableMessage()) {
|
||||
_inner->editMessageRequestNotify(item->fullId());
|
||||
} else {
|
||||
_scroll->keyPressEvent(e);
|
||||
}
|
||||
} else {
|
||||
_scroll->keyPressEvent(e);
|
||||
}
|
||||
e->accept();
|
||||
} else if (e->key() == Qt::Key_Down) {
|
||||
_scroll->keyPressEvent(e);
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->editLastMessageRequests(
|
||||
) | rpl::start_with_next([=](not_null<QKeyEvent*> e) {
|
||||
if (!_inner->lastMessageEditRequestNotify()) {
|
||||
_scroll->keyPressEvent(e);
|
||||
e->accept();
|
||||
} else if (e->key() == Qt::Key_PageDown) {
|
||||
_scroll->keyPressEvent(e);
|
||||
e->accept();
|
||||
} else if (e->key() == Qt::Key_PageUp) {
|
||||
_scroll->keyPressEvent(e);
|
||||
e->accept();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->replyNextRequests(
|
||||
) | rpl::start_with_next([=](ComposeControls::ReplyNextRequest &&data) {
|
||||
using Direction = ComposeControls::ReplyNextRequest::Direction;
|
||||
_inner->replyNextMessage(
|
||||
data.replyId,
|
||||
data.direction == Direction::Next);
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->setMimeDataHook([=](
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputField::MimeAction action) {
|
||||
@@ -624,19 +626,14 @@ bool RepliesWidget::confirmSendingFiles(
|
||||
return false;
|
||||
}
|
||||
|
||||
//const auto cursor = _field->textCursor();
|
||||
//const auto position = cursor.position();
|
||||
//const auto anchor = cursor.anchor();
|
||||
const auto text = _composeControls->getTextWithAppliedMarkdown();//_field->getTextWithTags();
|
||||
using SendLimit = SendFilesBox::SendLimit;
|
||||
auto box = Box<SendFilesBox>(
|
||||
controller(),
|
||||
std::move(list),
|
||||
text,
|
||||
_composeControls->getTextWithAppliedMarkdown(),
|
||||
_history->peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many,
|
||||
Api::SendType::Normal,
|
||||
SendMenu::Type::SilentOnly); // #TODO replies schedule
|
||||
_composeControls->setText({});
|
||||
|
||||
const auto replyTo = replyToId();
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
@@ -652,18 +649,8 @@ bool RepliesWidget::confirmSendingFiles(
|
||||
options,
|
||||
ctrlShiftEnter);
|
||||
}));
|
||||
box->setCancelledCallback(crl::guard(this, [=] {
|
||||
_composeControls->setText(text);
|
||||
//auto cursor = _field->textCursor();
|
||||
//cursor.setPosition(anchor);
|
||||
//if (position != anchor) {
|
||||
// cursor.setPosition(position, QTextCursor::KeepAnchor);
|
||||
//}
|
||||
//_field->setTextCursor(cursor);
|
||||
//if (!insertTextOnCancel.isEmpty()) {
|
||||
// _field->textCursor().insertText(insertTextOnCancel);
|
||||
//}
|
||||
}));
|
||||
box->setCancelledCallback(_composeControls->restoreTextCallback(
|
||||
insertTextOnCancel));
|
||||
|
||||
//ActivateWindow(controller());
|
||||
const auto shown = Ui::show(std::move(box));
|
||||
|
||||
@@ -231,24 +231,15 @@ void ScheduledWidget::setupComposeControls() {
|
||||
showAtPosition(pos);
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->keyEvents(
|
||||
_composeControls->scrollKeyEvents(
|
||||
) | rpl::start_with_next([=](not_null<QKeyEvent*> e) {
|
||||
if (e->key() == Qt::Key_Up) {
|
||||
if (!_composeControls->isEditingMessage()) {
|
||||
const auto item = session().data().scheduledMessages()
|
||||
.lastEditableMessage(_history);
|
||||
if (item) {
|
||||
_inner->editMessageRequestNotify(item->fullId());
|
||||
} else {
|
||||
_scroll->keyPressEvent(e);
|
||||
}
|
||||
} else {
|
||||
_scroll->keyPressEvent(e);
|
||||
}
|
||||
e->accept();
|
||||
} else if (e->key() == Qt::Key_Down) {
|
||||
_scroll->keyPressEvent(e);
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->editLastMessageRequests(
|
||||
) | rpl::start_with_next([=](not_null<QKeyEvent*> e) {
|
||||
if (!_inner->lastMessageEditRequestNotify()) {
|
||||
_scroll->keyPressEvent(e);
|
||||
e->accept();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
@@ -359,21 +350,16 @@ bool ScheduledWidget::confirmSendingFiles(
|
||||
return false;
|
||||
}
|
||||
|
||||
//const auto cursor = _field->textCursor();
|
||||
//const auto position = cursor.position();
|
||||
//const auto anchor = cursor.anchor();
|
||||
const auto text = _composeControls->getTextWithAppliedMarkdown();//_field->getTextWithTags();
|
||||
using SendLimit = SendFilesBox::SendLimit;
|
||||
auto box = Box<SendFilesBox>(
|
||||
controller(),
|
||||
std::move(list),
|
||||
text,
|
||||
_composeControls->getTextWithAppliedMarkdown(),
|
||||
_history->peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many,
|
||||
CanScheduleUntilOnline(_history->peer)
|
||||
? Api::SendType::ScheduledToUser
|
||||
: Api::SendType::Scheduled,
|
||||
SendMenu::Type::Disabled);
|
||||
//_field->setTextWithTags({});
|
||||
|
||||
box->setConfirmedCallback(crl::guard(this, [=](
|
||||
Ui::PreparedList &&list,
|
||||
@@ -388,18 +374,8 @@ bool ScheduledWidget::confirmSendingFiles(
|
||||
options,
|
||||
ctrlShiftEnter);
|
||||
}));
|
||||
//box->setCancelledCallback(crl::guard(this, [=] {
|
||||
// _field->setTextWithTags(text);
|
||||
// auto cursor = _field->textCursor();
|
||||
// cursor.setPosition(anchor);
|
||||
// if (position != anchor) {
|
||||
// cursor.setPosition(position, QTextCursor::KeepAnchor);
|
||||
// }
|
||||
// _field->setTextCursor(cursor);
|
||||
// if (!insertTextOnCancel.isEmpty()) {
|
||||
// _field->textCursor().insertText(insertTextOnCancel);
|
||||
// }
|
||||
//}));
|
||||
box->setCancelledCallback(_composeControls->restoreTextCallback(
|
||||
insertTextOnCancel));
|
||||
|
||||
//ActivateWindow(controller());
|
||||
const auto shown = Ui::show(std::move(box));
|
||||
|
||||
@@ -155,11 +155,11 @@ QSize Gif::countOptimalSize() {
|
||||
_thumbh = th;
|
||||
auto maxWidth = qMax(tw, st::minPhotoSize);
|
||||
auto minHeight = qMax(th, st::minPhotoSize);
|
||||
accumulate_max(maxWidth, _parent->minWidthForMedia());
|
||||
if (!activeCurrentStreamed()) {
|
||||
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
}
|
||||
if (_parent->hasBubble()) {
|
||||
accumulate_max(maxWidth, _parent->minWidthForMedia());
|
||||
if (!_caption.isEmpty()) {
|
||||
auto captionw = maxWidth - st::msgPadding.left() - st::msgPadding.right();
|
||||
minHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
@@ -212,11 +212,11 @@ QSize Gif::countCurrentSize(int newWidth) {
|
||||
|
||||
newWidth = qMax(tw, st::minPhotoSize);
|
||||
auto newHeight = qMax(th, st::minPhotoSize);
|
||||
accumulate_max(newWidth, _parent->minWidthForMedia());
|
||||
if (!activeCurrentStreamed()) {
|
||||
accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
}
|
||||
if (_parent->hasBubble()) {
|
||||
accumulate_max(newWidth, _parent->minWidthForMedia());
|
||||
if (!_caption.isEmpty()) {
|
||||
auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right();
|
||||
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
|
||||
@@ -682,6 +682,10 @@ bool GroupedMedia::needsBubble() const {
|
||||
return _needBubble;
|
||||
}
|
||||
|
||||
bool GroupedMedia::hideForwardedFrom() const {
|
||||
return main()->hideForwardedFrom();
|
||||
}
|
||||
|
||||
bool GroupedMedia::computeNeedBubble() const {
|
||||
if (!_caption.isEmpty() || _mode == Mode::Column) {
|
||||
return true;
|
||||
|
||||
@@ -91,6 +91,7 @@ public:
|
||||
bool customHighlight() const override {
|
||||
return true;
|
||||
}
|
||||
bool hideForwardedFrom() const override;
|
||||
|
||||
void stopAnimation() override;
|
||||
void checkAnimation() override;
|
||||
|
||||
@@ -273,6 +273,17 @@ void Sticker::refreshLink() {
|
||||
_link = std::make_shared<LambdaClickHandler>([document = _data] {
|
||||
StickerSetBox::Show(App::wnd()->sessionController(), document);
|
||||
});
|
||||
} else if (sticker
|
||||
&& (_data->dimensions.width() > kStickerSideSize
|
||||
|| _data->dimensions.height() > kStickerSideSize)
|
||||
&& !_parent->data()->isSending()
|
||||
&& !_parent->data()->hasFailed()) {
|
||||
// In case we have a .webp file that is displayed as a sticker, but
|
||||
// that doesn't fit in 512x512, we assume it may be a regular large
|
||||
// .webp image and we allow to open it in media viewer.
|
||||
_link = std::make_shared<DocumentOpenClickHandler>(
|
||||
_data,
|
||||
_parent->data()->fullId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -713,14 +713,14 @@ void Inner::updateSelected() {
|
||||
_pressed = _selected;
|
||||
if (row >= 0 && col >= 0) {
|
||||
auto layout = _rows.at(row).items.at(col);
|
||||
if (const auto w = App::wnd()) {
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
w->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewDocument);
|
||||
} else if (auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
w->showMediaPreview(Data::FileOrigin(), previewPhoto);
|
||||
}
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
_controller->widget()->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewDocument);
|
||||
} else if (auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
_controller->widget()->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewPhoto);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -740,12 +740,14 @@ void Inner::showPreview() {
|
||||
int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift;
|
||||
if (row < _rows.size() && col < _rows.at(row).items.size()) {
|
||||
auto layout = _rows.at(row).items.at(col);
|
||||
if (const auto w = App::wnd()) {
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
_previewShown = w->showMediaPreview(Data::FileOrigin(), previewDocument);
|
||||
} else if (const auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
_previewShown = w->showMediaPreview(Data::FileOrigin(), previewPhoto);
|
||||
}
|
||||
if (const auto previewDocument = layout->getPreviewDocument()) {
|
||||
_previewShown = _controller->widget()->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewDocument);
|
||||
} else if (const auto previewPhoto = layout->getPreviewPhoto()) {
|
||||
_previewShown = _controller->widget()->showMediaPreview(
|
||||
Data::FileOrigin(),
|
||||
previewPhoto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +121,6 @@ Widget::Widget(
|
||||
|
||||
_next->entity()->setClickedCallback([=] { getStep()->submit(); });
|
||||
|
||||
_settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); });
|
||||
|
||||
if (_changeLanguage) {
|
||||
_changeLanguage->finishAnimating();
|
||||
}
|
||||
@@ -153,6 +151,10 @@ Widget::Widget(
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> Widget::showSettingsRequested() const {
|
||||
return _settings->entity()->clicks() | rpl::to_empty;
|
||||
}
|
||||
|
||||
not_null<Media::Player::FloatDelegate*> Widget::floatPlayerDelegate() {
|
||||
return static_cast<Media::Player::FloatDelegate*>(this);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,8 @@ public:
|
||||
|
||||
void setInnerFocus();
|
||||
|
||||
[[nodiscard]] rpl::producer<> showSettingsRequested() const;
|
||||
|
||||
~Widget();
|
||||
|
||||
protected:
|
||||
|
||||
@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_domain.h"
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/connection_box.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "storage/localstorage.h"
|
||||
@@ -171,7 +170,7 @@ void MainWindow::createTrayIconMenu() {
|
||||
void MainWindow::applyInitialWorkMode() {
|
||||
Global::RefWorkMode().setForced(Global::WorkMode().value(), true);
|
||||
|
||||
if (cWindowPos().maximized) {
|
||||
if (Core::App().settings().windowPosition().maximized) {
|
||||
DEBUG_LOG(("Window Pos: First show, setting maximized."));
|
||||
setWindowState(Qt::WindowMaximized);
|
||||
}
|
||||
@@ -290,6 +289,11 @@ void MainWindow::setupIntro(Intro::EnterPoint point) {
|
||||
|
||||
destroyLayer();
|
||||
auto created = object_ptr<Intro::Widget>(bodyWidget(), &account(), point);
|
||||
created->showSettingsRequested(
|
||||
) | rpl::start_with_next([=] {
|
||||
showSettings();
|
||||
}, created->lifetime());
|
||||
|
||||
clearWidgets();
|
||||
_intro = std::move(created);
|
||||
if (_passcodeLock) {
|
||||
@@ -343,9 +347,6 @@ void MainWindow::setupMain() {
|
||||
}
|
||||
|
||||
void MainWindow::showSettings() {
|
||||
if (isHidden()) {
|
||||
showFromTray();
|
||||
}
|
||||
if (_passcodeLock) {
|
||||
return;
|
||||
}
|
||||
@@ -669,75 +670,6 @@ void MainWindow::updateTrayMenu(bool force) {
|
||||
psTrayMenuUpdated();
|
||||
}
|
||||
|
||||
void MainWindow::showAddContact() {
|
||||
if (isHidden()) {
|
||||
showFromTray();
|
||||
}
|
||||
|
||||
if (const auto controller = sessionController()) {
|
||||
Ui::show(
|
||||
Box<AddContactBox>(&controller->session()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showNewGroup() {
|
||||
if (isHidden()) {
|
||||
showFromTray();
|
||||
}
|
||||
|
||||
if (const auto controller = sessionController()) {
|
||||
Ui::show(
|
||||
Box<GroupInfoBox>(controller, GroupInfoBox::Type::Group),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showNewChannel() {
|
||||
if (isHidden()) {
|
||||
showFromTray();
|
||||
}
|
||||
|
||||
if (const auto controller = sessionController()) {
|
||||
Ui::show(
|
||||
Box<GroupInfoBox>(controller, GroupInfoBox::Type::Channel),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showLogoutConfirmation() {
|
||||
if (isHidden()) {
|
||||
showFromTray();
|
||||
}
|
||||
|
||||
const auto account = Core::App().passcodeLocked()
|
||||
? nullptr
|
||||
: sessionController()
|
||||
? &sessionController()->session().account()
|
||||
: nullptr;
|
||||
const auto weak = base::make_weak(account);
|
||||
const auto callback = [=] {
|
||||
if (account && !weak) {
|
||||
return;
|
||||
}
|
||||
if (account
|
||||
&& account->sessionExists()
|
||||
&& Core::App().exportManager().inProgress(&account->session())) {
|
||||
Ui::hideLayer();
|
||||
Core::App().exportManager().stopWithConfirmation([=] {
|
||||
Core::App().logout(account);
|
||||
});
|
||||
} else {
|
||||
Core::App().logout(account);
|
||||
}
|
||||
};
|
||||
Ui::show(Box<ConfirmBox>(
|
||||
tr::lng_sure_logout(tr::now),
|
||||
tr::lng_settings_logout(tr::now),
|
||||
st::attentionBoxButton,
|
||||
callback));
|
||||
}
|
||||
|
||||
bool MainWindow::takeThirdSectionFromLayer() {
|
||||
return _layer ? _layer->takeToThirdSection() : false;
|
||||
}
|
||||
|
||||
@@ -53,9 +53,6 @@ public:
|
||||
void setupMain();
|
||||
|
||||
void showSettings();
|
||||
void showAddContact();
|
||||
void showNewGroup();
|
||||
void showNewChannel();
|
||||
|
||||
void setInnerFocus();
|
||||
|
||||
@@ -106,8 +103,6 @@ public:
|
||||
not_null<PhotoData*> photo);
|
||||
void hideMediaPreview();
|
||||
|
||||
void showLogoutConfirmation();
|
||||
|
||||
void updateControlsGeometry() override;
|
||||
|
||||
protected:
|
||||
|
||||