Compare commits

...

83 Commits

Author SHA1 Message Date
John Preston
b3660f1ed8 Version 2.5.9: Move window position to Core::Settings. 2021-02-17 19:20:05 +04:00
John Preston
70570e0987 Always make sure that settings are saved. 2021-02-17 19:19:00 +04:00
John Preston
baccec623d Allow opening large (> 512px) stickers in media viewer. 2021-02-17 19:15:49 +04:00
John Preston
093d89db83 Fix display of stickers in media viewer. 2021-02-17 19:13:37 +04:00
John Preston
48fea47d16 Version 2.5.9: Fix build for Microsoft Store. 2021-02-17 17:33:24 +04:00
John Preston
8500bf6073 Version 2.5.9: Fix reading settings from old apps. 2021-02-17 15:58:57 +04:00
John Preston
69b41fadef Version 2.5.9.
- Add 'Invite via Link' button to Add Members box.
- Fix window size in Windows 10 Tablet Mode.
- Fix layout of round video messages in channels.
2021-02-17 11:19:45 +04:00
Ilya Fedin
d44f076f0b Take in account device pixel ratio when setting window extents 2021-02-17 11:16:00 +04:00
Ilya Fedin
3637fec397 Ensure controls aren't duplictated 2021-02-17 11:16:00 +04:00
Ilya Fedin
e109da037e Add a private method to get control widget by enum to TitleWidgetQt 2021-02-17 11:16:00 +04:00
Nicholas Guriev
3a659b4b54 Add -mxgot flag to td_scheme on MIPS64
This fixes "relocation truncated to fit ..." error on final linking. It should
not introduce a lot of overhead since the code is used only in network
communications.

This errors appears sometimes and I do not know exact conditions, I think it is
related to large size of an object file with the schema.

More docs see at https://gcc.gnu.org/onlinedocs/gcc/MIPS-Options.html#index-mxgot-1
2021-02-17 11:15:31 +04:00
Ilya Fedin
7e4dff25e9 Log media viewer geometry 2021-02-17 11:15:16 +04:00
Ilya Fedin
da74fe4248 Call moveToScreen right before showFullScreen for Wayland 2021-02-17 11:15:16 +04:00
Ilya Fedin
294f849775 Init last path with gtk dialog 2021-02-17 11:14:41 +04:00
Ilya Fedin
88951e9e5c Fix saving last path in confined environments 2021-02-17 11:14:41 +04:00
Il'ya
153b91248d set nproc for number of jobs to build webrtc in docker 2021-02-17 11:14:13 +04:00
Ilya Fedin
7977331d8b Read DESKTOPINTEGRATION variable instead of TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
Since it's widely used (by AppImages, for instance)
2021-02-17 11:12:06 +04:00
John Preston
8ec60e0321 Show all .webp as stickers.
Allow opening stickers not from stickerpacks in media viewer.
2021-02-17 10:46:36 +04:00
John Preston
dcebefe2bb Fix layout of round video messages. 2021-02-17 10:46:36 +04:00
John Preston
93a88b54ad Fix sending images from clipboard.
Regression was introduced in ce1b94eb1.
2021-02-17 10:46:36 +04:00
John Preston
07f94cc184 Fix skin-colored animated emoji refresh. 2021-02-17 10:46:36 +04:00
John Preston
9a0edbd0c5 Update skin-colored animated emoji. 2021-02-17 10:46:36 +04:00
John Preston
c935f1bb16 Always show check marks on outgoing service messages. 2021-02-17 10:46:36 +04:00
John Preston
3cd05a34d9 In legacy groups admins can't remove admins. 2021-02-17 10:46:36 +04:00
John Preston
223681d2da Fix default chat rights for creator. 2021-02-17 10:46:36 +04:00
23rd
c3c1759f3c Fixed updating number of sessions after terminating any of them. 2021-02-17 01:27:44 +03:00
23rd
bff3291631 Fixed clearing of text field in sections when sending files. 2021-02-12 13:05:50 +03:00
John Preston
e85394b520 Fix build with updated scheme. 2021-02-12 14:01:29 +04:00
John Preston
5cea5fc4e6 Fix window size in Tablet Mode on Windows 10. 2021-02-09 19:26:56 +04:00
John Preston
39742d22d9 Apply volume_by_admin flag in voice chats. 2021-02-09 19:26:56 +04:00
John Preston
d60a89f354 Update tgcalls. 2021-02-09 19:26:56 +04:00
23rd
90f90a4ca3 Fixed accepting of Enter key in box of voice message discarding. 2021-02-09 19:26:56 +04:00
23rd
776c099a25 Limited maximum number of selected messages for rescheduling to 20. 2021-02-09 19:26:56 +04:00
23rd
f2b434d82b Fixed text color in playback speed item. 2021-02-09 19:26:56 +04:00
Ilya Fedin
03e8d28456 Check for null manager type 2021-02-09 16:51:25 +04:00
Ilya Fedin
9b70f24e91 Adjust some tabs in gtk file dialog 2021-02-05 20:23:00 +04:00
Ilya Fedin
8fd1d16db6 Fix accept/reject lifetime in gtk file dialog 2021-02-05 20:23:00 +04:00
Ilya Fedin
36acf60f7e Add XDG Desktop Portal based file dialog implementation from Qt
This allows to use portal dialogs more flexibly (e.g. fallback mechanism)
This also allows to have any changes we want for portal dialogs without patchig Qt

No more need to override QT_QPA_PLATFORM to use portal dialogs
2021-02-05 20:23:00 +04:00
John Preston
ce1b94eb16 Send PDFs only as files.
Fixes #10294.
2021-02-05 13:18:11 +04:00
John Preston
e8affa85b0 Try to open localized changelog. 2021-02-04 20:42:32 +04:00
John Preston
d782ea63f8 Fix audio file forward inconsistencies. 2021-02-04 19:58:57 +04:00
John Preston
11b965e82e Skip image context actions for not-loaded photos. 2021-02-04 19:26:49 +04:00
John Preston
a2187a1d2b Fix pinned message reset on message being sent.
Fixes #10304
2021-02-04 19:12:31 +04:00
John Preston
7cd626c9fd Fix build, update lib_ui. 2021-02-04 18:46:04 +04:00
John Preston
37b8551760 Ignore 400: LOCATION_NOT_AVAILABLE in export.
Fix #6807.
2021-02-04 18:13:04 +04:00
John Preston
0cd8453b00 Support different invite link syntax. 2021-02-04 18:13:04 +04:00
23rd
0b4d0b83c2 Removed App::wnd from classes that have pointer to Window::Controller. 2021-02-04 18:13:04 +04:00
23rd
0783a682dc Removed App:wnd from Platform::MainWindow for macOS. 2021-02-04 18:13:04 +04:00
23rd
fb9a34a069 Removed App::wnd for opening about box. 2021-02-04 18:13:04 +04:00
23rd
b4af805521 Moved showLogoutConfirmation from MainWindow to Window::Controller. 2021-02-04 18:13:03 +04:00
23rd
1f80c297ec Removed App:wnd for opening settings.
Removed unused App::showSettings from facades.
2021-02-04 18:13:03 +04:00
23rd
019e691fbb Moved some session dependent methods to SessionController.
MainWindow::showAddContact(),
MainWindow::showNewGroup(),
MainWindow::showNewChannel().
2021-02-04 18:13:03 +04:00
23rd
683d78c64a Added missed passing of resize event to parent of OverlayWidget. 2021-02-04 18:13:03 +04:00
23rd
0aaa88cb79 Added animation to toggle silent broadcast button. 2021-02-04 18:13:03 +04:00
23rd
0cb8f2cc85 Added ability to toggle silent broadcast from sections.
Fixed #8655.
2021-02-04 18:13:03 +04:00
23rd
03a5619d61 Added ability to reschedule multiple messages.
Fixed #9851.
2021-02-04 18:13:03 +04:00
23rd
f1236edf5b Added ability to select message for reply with Ctrl+Up/Down in sections. 2021-02-04 18:13:03 +04:00
23rd
0b98cfbfec Added ability to attach file with shortcut in sections. 2021-02-04 18:13:03 +04:00
23rd
062c451c27 Refactored handle of last editable message on Up arrow in sections. 2021-02-04 18:13:03 +04:00
23rd
b13e5ddce9 Moved scroll keys from sections to compose controls. 2021-02-04 18:13:03 +04:00
23rd
4ad0837661 Fixed ability to attach file while editing message in sections. 2021-02-04 18:13:03 +04:00
23rd
4695ccfdb8 Fixed mouse hiding in OverlayWidget when menu of controls is displayed. 2021-02-04 18:13:03 +04:00
23rd
813470ff25 Replaced submenu for playback speed with slider. 2021-02-04 18:13:03 +04:00
23rd
2d50c61703 Added ability to set dividers to MediaSlider. 2021-02-04 18:13:03 +04:00
23rd
2dd99a535c United enum classes of arc directions into single enum class. 2021-02-04 18:13:03 +04:00
Ilya Fedin
57ca6e23b9 Port Qt-based title widget to lib_ui 2021-02-04 18:11:44 +04:00
Nicholas Guriev
153c949a88 Clean up DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION 2021-02-04 17:39:33 +04:00
Ilya Fedin
76457c1e52 Restore QGuiApplication::setOverrideCursor usage
QWindow::setCursor doesn't work as expected...
2021-02-03 10:27:54 +04:00
Ilya Fedin
bcc333c2e1 Use QWindow::setCursor instead of QGuiApplication::setCursorOverride 2021-02-02 20:12:24 +04:00
Ilya Fedin
fe8bc30645 Use GCancellable to prevent crash in notificationShown 2021-02-02 20:11:09 +04:00
John Preston
f7b72bffe2 Fix build. 2021-02-02 17:39:51 +04:00
John Preston
5538c5eace Add 'Invite via Link' button to Add Members box. 2021-02-01 22:36:40 +04:00
John Preston
34ec1c371c Return invite-link-only box. 2021-02-01 19:10:59 +04:00
John Preston
cb2d77d386 Implement SingleChoiceBox using Ui::GenericBox. 2021-02-01 17:51:01 +04:00
John Preston
eb11185de7 Improve escaping of some data in HTML export. 2021-02-01 16:57:42 +04:00
John Preston
4e5c81dac2 Correctly handle 'min' group call participant updates. 2021-02-01 16:25:38 +04:00
John Preston
5feb381cb2 Allow showing images from cache in media viewer.
Fixes #10237.
2021-02-01 15:44:24 +04:00
John Preston
2a1096d83c Don't reset interface scale to auto on Settings open. 2021-02-01 12:32:08 +04:00
GitHub Action
d202a0cd06 Update User-Agent for DNS to Chrome 88.0.4324.96. 2021-02-01 11:29:58 +04:00
Ilya Fedin
3251b8bf6e Don't set geometry for media viewer only on Wayland 2021-01-31 12:39:13 +04:00
Ilya Fedin
f51055d606 Ensure the window is not out of available geometry on geometry restoring 2021-01-31 12:38:41 +04:00
Ilya Fedin
9c86755546 Take custom scale in account when saving window geometry 2021-01-31 12:38:41 +04:00
Ilya Fedin
0b5213a9cb Use SNAPCRAFT_PARALLEL_BUILD_COUNT instead of nproc in snap 2021-01-30 10:01:39 +04:00
156 changed files with 2737 additions and 1858 deletions

View File

@@ -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
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -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";

View File

@@ -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;

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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();
};

View File

@@ -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());
}
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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([&](

View File

@@ -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>(

View File

@@ -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(

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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(

View File

@@ -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));

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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]);
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -19,6 +19,7 @@ struct MuteRequest {
bool mute = false;
bool locallyOnly = false;
};
struct VolumeRequest {
not_null<UserData*> user;
int volume = kDefaultVolume;

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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());
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)) {

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 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;

View File

@@ -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 &) {
});

View File

@@ -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;

View File

@@ -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);
}
}
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -31,8 +31,6 @@ public:
[[nodiscard]] rpl::producer<int> fullCount() const;
[[nodiscard]] HistoryItem *lastEditableMessage();
private:
struct Viewer;

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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>;

View File

@@ -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();
}

View File

@@ -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());

View File

@@ -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_"))) {

View File

@@ -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 &laquo;" + data.title + "&raquo;"
+ " created group &laquo;"
+ SerializeString(data.title)
+ "&raquo;"
+ (data.userIds.empty()
? QByteArray()
: " with members " + peers.wrapUserNames(data.userIds));
}, [&](const ActionChatEditTitle &data) {
return isChannel
? ("Channel title changed to &laquo;" + data.title + "&raquo;")
? ("Channel title changed to &laquo;"
+ SerializeString(data.title)
+ "&raquo;")
: (serviceFrom
+ " changed group title to &laquo;"
+ data.title
+ " fchanged group title to &laquo;"
+ SerializeString(data.title)
+ "&raquo;");
}, [&](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 &laquo;" + data.title + "&raquo; created";
return "Channel &laquo;"
+ SerializeString(data.title)
+ "&raquo; created";
}, [&](const ActionChatMigrateTo &data) {
return serviceFrom
+ " converted this group to a supergroup";
}, [&](const ActionChannelMigrateFrom &data) {
return serviceFrom
+ " converted a basic group to this supergroup "
+ "&laquo;" + data.title + "&raquo;";
+ "&laquo;" + SerializeString(data.title) + "&raquo;";
}, [&](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) {

View File

@@ -229,12 +229,6 @@ void searchByHashtag(const QString &tag, PeerData *inPeer) {
}
}
void showSettings() {
if (auto w = App::wnd()) {
w->showSettings();
}
}
} // namespace App
namespace Ui {

View File

@@ -48,7 +48,6 @@ void activateBotCommand(
int row,
int column);
void searchByHashtag(const QString &tag, PeerData *inPeer);
void showSettings();
} // namespace App

View File

@@ -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();
}
}
}

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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(

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -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));

View File

@@ -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);

View File

@@ -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;

View File

@@ -91,6 +91,7 @@ public:
bool customHighlight() const override {
return true;
}
bool hideForwardedFrom() const override;
void stopAnimation() override;
void checkAnimation() override;

View File

@@ -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());
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -99,6 +99,8 @@ public:
void setInnerFocus();
[[nodiscard]] rpl::producer<> showSettingsRequested() const;
~Widget();
protected:

View File

@@ -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;
}

View File

@@ -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:

Some files were not shown because too many files have changed in this diff Show More