Compare commits

..

60 Commits

Author SHA1 Message Date
John Preston
9717a8b5fa Version 2.4.4.
- Fix application quit on call end with main window hidden in tray.
- Update OpenAL library on Windows.
- Several crash fixes.
2020-10-23 19:40:48 +03:00
John Preston
aff4f69b64 Don't quit on call end with window hidden in tray.
Fixes #8585.
2020-10-23 19:37:58 +03:00
John Preston
9de4c42555 Keep sending typings up to 30s after offline. 2020-10-23 18:25:55 +03:00
John Preston
1de144a48d Show transfer ownership button for non-anonymous admins. 2020-10-23 17:37:27 +03:00
John Preston
0690d14f1b Don't send typings to bots and offline users. 2020-10-23 17:28:11 +03:00
John Preston
53ac4c00ad Track deleted messages carefully.
Fixes #8855.
2020-10-23 16:35:43 +03:00
John Preston
f064692e57 Close media viewer when photo message is deleted. 2020-10-23 15:28:20 +03:00
John Preston
3d54a263b8 Stop playing documents when items are deleted. 2020-10-23 15:22:38 +03:00
23rd
47bb8ec687 Added Github Action that updates user-agent for DNS. 2020-10-23 15:13:20 +03:00
23rd
3a2b772a5d Added spellchecker to Replies / Scheduled messages sections.
Fixed #8793.
2020-10-23 13:32:44 +03:00
23rd
bc8f8bc68c Added auto-closing to some boxes which depend on certain message. 2020-10-23 13:32:44 +03:00
23rd
bc7975ece7 Fixed crash when user reschedules already sent message.
Fixed #8867.
2020-10-23 13:32:43 +03:00
23rd
52cca98144 Fixed replies button display in section of scheduled messages. 2020-10-23 13:32:43 +03:00
23rd
5540b0bb8b Fixed glitch for scheduled messages with elapsed date in channels. 2020-10-23 13:32:32 +03:00
23rd
7de9bcad03 Added ability to open contacts with shortcut.
Fixed #8775.
2020-10-23 13:31:24 +03:00
23rd
367b028094 Moved contacts box preparing to single place. 2020-10-23 13:31:24 +03:00
John Preston
8b27aa5331 Update cmake_helpers submodule. 2020-10-23 13:11:42 +03:00
John Preston
9697567b8d Add some more open file warnings. 2020-10-23 13:10:43 +03:00
Ilya Fedin
1fdfa94497 Remove explicit Opus clone step from macos action
Since no longer needed
2020-10-23 12:51:52 +03:00
John Preston
1373bd0af1 Use OpenAL 1.20.1 with bugfix backport on Windows. 2020-10-23 12:47:41 +03:00
Ilya Fedin
4f2b0531f8 Replace GDBusProxy with GDBusConnection in NotificationData 2020-10-23 11:29:35 +03:00
Ilya Fedin
ca67ac913f Check for KDE portal backend when using portals on KDE 2020-10-23 11:28:18 +03:00
Ilya Fedin
4033a091b5 Hide mark as read button in notifications when app is pass-code locked 2020-10-23 11:25:18 +03:00
Ilya Fedin
0179a2ca10 Rename InstallMainDesktopFile to InstallLauncher 2020-10-23 11:25:18 +03:00
Ilya Fedin
f58874572d Check actual socket path length rather than InSnap/InFlatpak 2020-10-23 11:25:18 +03:00
Ilya Fedin
143b9682a4 Get rid of lxqt-qtplugin
It is stil impossible to build it statically and it seems that reading icon theme from gtk is pretty enough
2020-10-23 11:24:37 +03:00
John Preston
00c962e557 Bump cmake_helpers submodule. 2020-10-23 11:24:02 +03:00
John Preston
1cabfaa6a4 Fix cancel / crash in sending album to scheduled messages.
Fixes #8788
2020-10-23 11:22:38 +03:00
Ilya Fedin
b788ae0ae4 Add stale bot configuration 2020-10-23 11:18:14 +03:00
Ilya Fedin
3f6399f13d Log getting GTK settings 2020-10-21 10:57:40 +03:00
Ilya Fedin
b6fc418d32 01org/libva -> intel/libva 2020-10-20 09:37:56 +03:00
Ilya Fedin
245d644cd7 Add always on top hint for media view window
To avoid overlapping by panels in KDE
2020-10-20 09:37:31 +03:00
Ilya Fedin
2aa0b674cd Use new XCB methods from lib_base 2020-10-16 16:12:38 +03:00
Ilya Fedin
654784ce9f Use external_xcb and external_glib 2020-10-16 16:12:38 +03:00
John Preston
744eccc51e Version 2.4.3: Fix build for OS X 10.10-10.11. 2020-10-07 18:18:58 +03:00
Ilya Fedin
ce49714533 Use FindALSA instead of pkg-config 2020-10-07 17:10:27 +03:00
John Preston
ae2c858dc9 Version 2.4.3: Update lib_ui submodule. 2020-10-07 15:10:31 +03:00
John Preston
f0b5dc42f9 Version 2.4.3.
- Fix sending voice messages in scheduled messages section.
- Fix deleting profile / group / channel photos.
- Several crash fixes.
2020-10-07 15:08:52 +03:00
John Preston
9c213bf1c0 Warn when launching .sh on Windows.
Fixes #8753.
2020-10-07 14:41:21 +03:00
John Preston
0c1175f9cd Fix broadcast field placeholder update. 2020-10-07 14:41:21 +03:00
23rd
0c1312419a Fixed crash when user schedules message with elapsed date.
Fixed #8764.
2020-10-07 14:30:29 +03:00
23rd
7e9695b213 Added missed *.mov extension to dialog files filter for album items. 2020-10-07 13:09:15 +03:00
Ilya Fedin
093fcc3821 Subscribe to StatusNotifierHostRegistered signal 2020-10-07 13:06:06 +03:00
Ilya Fedin
6f89598a7b Clean old attempts to register url scheme on scheme registration 2020-10-07 12:55:34 +03:00
John Preston
6ccd53689d Fix crash in shared media search message delete.
Fixes #8237.
2020-10-06 14:20:49 +03:00
John Preston
cd506dfff5 Fix reply_to_top_id in local sent stickers.
Fixes #8758.
2020-10-06 14:01:19 +03:00
John Preston
5a3733b5b6 Fix selecting stickers in Replies section. 2020-10-06 13:46:19 +03:00
John Preston
22a85016e3 Show rank in anonymous outgoing messages. 2020-10-06 10:23:58 +03:00
John Preston
26c7a95a9f Use network-manager-observe in snap version. 2020-10-06 10:15:22 +03:00
John Preston
9acf617c9f Show full cached song as downloaded. 2020-10-06 10:10:22 +03:00
John Preston
72af170484 Force call panel to be a separate window.
I hope this fixes #8715.
2020-10-05 18:30:08 +03:00
John Preston
4db2505f5d Fix deleting profile photos.
Fixes #8720.
2020-10-05 18:26:29 +03:00
23rd
4d40336be0 Fixed voice recording cancel. 2020-10-05 17:08:52 +03:00
John Preston
616531b0d0 Fix author signature in discussion posts. 2020-10-05 16:21:34 +03:00
John Preston
473803edb8 Fix comments button getState / remove. 2020-10-05 16:21:34 +03:00
Ilya Fedin
a33ca97298 Find taskbar window on the same monitor 2020-10-05 13:50:03 +03:00
John Preston
a711c89409 Fix crash on wrong server response.
Fixes #8724.
2020-10-05 12:58:12 +03:00
John Preston
24ec0e0866 Fix recording stop in voice messages. 2020-10-05 12:58:12 +03:00
John Preston
e6df927e30 Correctly use alcGetIntegerv. 2020-10-05 12:58:12 +03:00
Ilya Fedin
638ea3111f Fallback to D-Bus methods if XCB-based LastUserInputTime failed 2020-10-05 10:10:40 +03:00
75 changed files with 1045 additions and 634 deletions

21
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels: []
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: |
Hey there!
This issue will be automatically closed in 7 days if there would be no activity. We therefore assume that the user has lost interest or resolved the problem on their own.
Don't worry though; if this is an error, let us know with a comment and we'll be happy to reopen the issue.
Thanks!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
# Process only issues
only: issues

View File

@@ -182,7 +182,7 @@ jobs:
run: |
cd $LibrariesPath
git clone $GIT/01org/libva.git
git clone $GIT/intel/libva.git
cd libva
./autogen.sh --enable-static
make -j$(nproc)

View File

@@ -169,17 +169,14 @@ jobs:
with:
path: ${{ env.LibrariesPath }}/opus-cache
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
- name: Opus clone.
run: |
cd $LibrariesPath
git clone -b v1.3 --depth=1 $GIT/xiph/opus
- name: Opus build.
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone $GIT/xiph/opus
cd opus
git checkout v1.3
./autogen.sh
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX
make -j$(nproc)

View File

@@ -0,0 +1,90 @@
name: User-agent updater.
on:
repository_dispatch:
types: ["Restart user_agent_updater workflow."]
schedule:
# At 00:00 on day-of-month 1.
- cron: '0 0 1 * *'
jobs:
User-agent:
runs-on: ubuntu-latest
env:
code_file: "Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp"
skip: "0"
steps:
- name: Clone.
if: env.skip == '0'
uses: actions/checkout@v2
- name: Write a new version of Google Chrome to the user-agent for DNS.
if: env.skip == '0'
shell: python
run: |
import subprocess, os, re;
regExpVersion = "[0-9]+.[0-9]+.[0-9]+.[0-9]+";
chrome = "Chrome/";
def newVersion():
output = subprocess.check_output(["google-chrome", "--version"]);
version = re.search(regExpVersion, output);
if not version:
print("Can't find a Chrome version.");
exit();
return version.group(0);
newChromeVersion = newVersion();
print(newChromeVersion);
def writeUserAgent():
p = os.environ['code_file'];
w = open(p, "r");
content = w.read();
w.close();
regExpChrome = chrome + regExpVersion;
version = re.search(regExpChrome, content);
if not version:
print("Can't find an user-agent in the code.");
exit();
content = re.sub(regExpChrome, chrome + newChromeVersion, content);
w = open(p, "w");
w.write(content);
print("::set-env name=ChromeVersion::" + newChromeVersion);
writeUserAgent();
- name: Push to the current branch.
if: env.skip == '0' && env.ChromeVersion != ''
run: |
token=${{ secrets.TOKEN_FOR_MASTER_UPDATER }}
if [ -z "${token}" ]; then
echo "Token is unset. Nothing to do."
exit 0
fi
url=https://x-access-token:$token@github.com/$GITHUB_REPOSITORY
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git diff > git_diff.txt
if [[ ! -s git_diff.txt ]]; then
echo "Nothing to commit."
exit 0
fi
git add $code_file
git commit -m "Update User-Agent for DNS to Chrome $ChromeVersion."
git remote set-url origin $url
git push origin HEAD:$GITHUB_REF
echo "Done!"

6
.gitmodules vendored
View File

@@ -82,12 +82,6 @@
[submodule "Telegram/ThirdParty/qt5ct"]
path = Telegram/ThirdParty/qt5ct
url = https://github.com/desktop-app/qt5ct.git
[submodule "Telegram/ThirdParty/lxqt-qtplugin"]
path = Telegram/ThirdParty/lxqt-qtplugin
url = https://github.com/lxqt/lxqt-qtplugin.git
[submodule "Telegram/ThirdParty/libqtxdg"]
path = Telegram/ThirdParty/libqtxdg
url = https://github.com/lxqt/libqtxdg.git
[submodule "Telegram/ThirdParty/fcitx5-qt"]
path = Telegram/ThirdParty/fcitx5-qt
url = https://github.com/fcitx/fcitx5-qt.git

View File

@@ -106,17 +106,12 @@ if (LINUX)
desktop-app::external_materialdecoration
desktop-app::external_nimf_qt5
desktop-app::external_qt5ct_support
desktop-app::external_xcb_screensaver
desktop-app::external_xcb
desktop-app::external_glib
)
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
# conflicts with Qt static link
if (DESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_lxqt_qtplugin
)
endif()
target_link_libraries(Telegram
PRIVATE
desktop-app::external_statusnotifieritem
@@ -137,35 +132,6 @@ if (LINUX)
)
endif()
if (DESKTOP_APP_USE_PACKAGED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(XCB_SCREENSAVER REQUIRED IMPORTED_TARGET xcb-screensaver)
pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb)
target_link_libraries(Telegram
PRIVATE
PkgConfig::XCB_SCREENSAVER
PkgConfig::XCB
)
else()
target_link_static_libraries(Telegram PRIVATE xcb-screensaver)
target_link_libraries(Telegram PRIVATE xcb)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB2 REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
target_link_libraries(Telegram
PRIVATE
PkgConfig::GLIB2
PkgConfig::GOBJECT
PkgConfig::GIO
)
target_compile_definitions(Telegram PRIVATE G_LOG_DOMAIN="Telegram")
if (NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
find_package(PkgConfig REQUIRED)
@@ -1290,6 +1256,7 @@ target_compile_definitions(Telegram
PRIVATE
TDESKTOP_API_ID=${TDESKTOP_API_ID}
TDESKTOP_API_HASH=${TDESKTOP_API_HASH}
G_LOG_DOMAIN="Telegram"
)
if (APPLE OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL "" OR NOT "${output_name}" STREQUAL "Telegram")

View File

@@ -2271,6 +2271,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_language_not_ready_link" = "translations platform";
"lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?";
"lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?";
"lng_launch_exe_sure" = "Run";
"lng_launch_exe_dont_ask" = "Don't ask me again";

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="2.4.2.0" />
Version="2.4.4.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,4,2,0
PRODUCTVERSION 2,4,2,0
FILEVERSION 2,4,4,0
PRODUCTVERSION 2,4,4,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.4.2.0"
VALUE "FileVersion", "2.4.4.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.4.2.0"
VALUE "ProductVersion", "2.4.4.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,4,2,0
PRODUCTVERSION 2,4,2,0
FILEVERSION 2,4,4,0
PRODUCTVERSION 2,4,4,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.4.2.0"
VALUE "FileVersion", "2.4.4.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.4.2.0"
VALUE "ProductVersion", "2.4.4.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -10,6 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "history/history.h"
#include "data/data_peer.h"
#include "data/data_user.h"
#include "base/unixtime.h"
#include "data/data_peer_values.h"
#include "apiwrap.h"
namespace Api {
@@ -17,6 +20,7 @@ namespace {
constexpr auto kCancelTypingActionTimeout = crl::time(5000);
constexpr auto kSetMyActionForMs = 10 * crl::time(1000);
constexpr auto kSendTypingsToOfflineFor = TimeId(30);
} // namespace
@@ -98,6 +102,9 @@ bool SendProgressManager::updated(const Key &key, bool doing) {
}
void SendProgressManager::send(const Key &key, int progress) {
if (skipRequest(key)) {
return;
}
using Type = SendProgressType;
const auto action = [&]() -> MTPsendMessageAction {
const auto p = MTP_int(progress);
@@ -135,6 +142,19 @@ void SendProgressManager::send(const Key &key, int progress) {
}
}
bool SendProgressManager::skipRequest(const Key &key) const {
const auto user = key.history->peer->asUser();
if (!user) {
return false;
} else if (user->isSelf()) {
return true;
} else if (user->isBot() && !user->isSupport()) {
return true;
}
const auto recently = base::unixtime::now() - kSendTypingsToOfflineFor;
return !Data::OnlineTextActive(user->onlineTill, recently);
}
void SendProgressManager::done(
const MTPBool &result,
mtpRequestId requestId) {

View File

@@ -90,6 +90,8 @@ private:
void send(const Key &key, int progress);
void done(const MTPBool &result, mtpRequestId requestId);
[[nodiscard]] bool skipRequest(const Key &key) const;
const not_null<Main::Session*> _session;
base::flat_map<Key, mtpRequestId> _requests;
base::flat_map<Key, crl::time> _updated;

View File

@@ -63,6 +63,22 @@ enum class DataIsLoadedResult {
Ok = 3,
};
void ProcessScheduledMessageWithElapsedTime(
not_null<Main::Session*> session,
bool needToAdd,
const MTPDmessage &data) {
if (needToAdd && !data.is_from_scheduled()) {
// If we still need to add a new message,
// we should first check if this message is in
// the list of scheduled messages.
// This is necessary to correctly update the file reference.
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
session->data().scheduledMessages().checkEntitiesAndUpdate(data);
}
}
bool IsForceLogoutNotification(const MTPDupdateServiceNotification &data) {
return qs(data.vtype()).startsWith(qstr("AUTH_KEY_DROP_"));
}
@@ -958,17 +974,7 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
if (needToAdd && !data.is_from_scheduled()) {
// If we still need to add a new message,
// we should first check if this message is in
// the list of scheduled messages.
// This is necessary to correctly update the file reference.
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
_session->data().scheduledMessages().checkEntitiesAndUpdate(
data);
}
ProcessScheduledMessageWithElapsedTime(_session, needToAdd, data);
}
if (needToAdd) {
_session->data().addNewMessage(
@@ -1057,10 +1063,12 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
auto &d = update.c_updateNewChannelMessage();
auto needToAdd = true;
if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
if (_session->data().checkEntitiesAndViewsUpdate(d.vmessage().c_message())) { // already in blocks
const auto &data = d.vmessage().c_message();
if (_session->data().checkEntitiesAndViewsUpdate(data)) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
ProcessScheduledMessageWithElapsedTime(_session, needToAdd, data);
}
if (needToAdd) {
_session->data().addNewMessage(

View File

@@ -363,6 +363,12 @@ EditCaptionBox::EditCaptionBox(
) | rpl::start_with_next([&](bool checked) {
_asFile = checked;
}, _wayWrap->lifetime());
_controller->session().data().itemRemoved(
_msgId
) | rpl::start_with_next([=] {
closeBox();
}, lifetime());
}
EditCaptionBox::~EditCaptionBox() = default;

View File

@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h"
#include "apiwrap.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "lang/lang_keys.h"
#include "history/history.h"
#include "dialogs/dialogs_main_list.h"
@@ -104,6 +105,20 @@ void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) {
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
//}
object_ptr<Ui::BoxContent> PrepareContactsBox(
not_null<Window::SessionController*> sessionController) {
const auto controller = sessionController;
auto delegate = [=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
box->addLeftButton(
tr::lng_profile_add_contact(),
[=] { controller->widget()->onShowAddContact(); });
};
return Box<PeerListBox>(
std::make_unique<ContactsBoxController>(controller),
std::move(delegate));
}
void PeerListRowWithLink::setActionLink(const QString &action) {
_action = action;
refreshActionLink();

View File

@@ -31,9 +31,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History;
namespace Window {
class SessionController;
class SessionNavigation;
} // namespace Window
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareContactsBox(
not_null<Window::SessionController*> sessionController);
class PeerListRowWithLink : public PeerListRow {
public:
using PeerListRow::PeerListRow;

View File

@@ -308,7 +308,7 @@ void EditAdminBox::prepare() {
}, lifetime());
if (canTransferOwnership()) {
const auto allFlags = FullAdminRights(isGroup);
const auto allFlags = AdminRightsForOwnershipTransfer(isGroup);
setupTransferButton(
isGroup
)->toggleOn(rpl::duplicate(

View File

@@ -298,10 +298,12 @@ ChatRestrictions FixDependentRestrictions(ChatRestrictions restrictions) {
return restrictions;
}
ChatAdminRights FullAdminRights(bool isGroup) {
ChatAdminRights AdminRightsForOwnershipTransfer(bool isGroup) {
auto result = ChatAdminRights();
for (const auto &[flag, label] : AdminRightLabels(isGroup, true)) {
result |= flag;
if (!(flag & ChatAdminRight::f_anonymous)) {
result |= flag;
}
}
return result;
}

View File

@@ -71,4 +71,4 @@ EditFlagsControl<MTPDchatAdminRights::Flags> CreateEditAdminRights(
ChatAdminRights DisabledByDefaultRestrictions(not_null<PeerData*> peer);
ChatRestrictions FixDependentRestrictions(ChatRestrictions restrictions);
ChatAdminRights FullAdminRights(bool isGroup);
ChatAdminRights AdminRightsForOwnershipTransfer(bool isGroup);

View File

@@ -109,7 +109,8 @@ QByteArray Settings::serialize() const {
<< qint32(_notifyFromAll ? 1 : 0)
<< qint32(_nativeWindowFrame.current() ? 1 : 0)
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
<< _callVideoInputDeviceId;
<< _callVideoInputDeviceId
<< qint32(_ipRevealWarning ? 1 : 0);
}
return result;
}
@@ -176,6 +177,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 notifyFromAll = _notifyFromAll ? 1 : 0;
qint32 nativeWindowFrame = _nativeWindowFrame.current() ? 1 : 0;
qint32 systemDarkModeEnabled = _systemDarkModeEnabled.current() ? 1 : 0;
qint32 ipRevealWarning = _ipRevealWarning ? 1 : 0;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -259,6 +261,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> callVideoInputDeviceId;
}
if (!stream.atEnd()) {
stream >> ipRevealWarning;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -318,6 +323,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_includeMutedCounter = (includeMutedCounter == 1);
_countUnreadMessages = (countUnreadMessages == 1);
_exeLaunchWarning = (exeLaunchWarning == 1);
_ipRevealWarning = (ipRevealWarning == 1);
_notifyAboutPinned = (notifyAboutPinned == 1);
_loopAnimatedStickers = (loopAnimatedStickers == 1);
_largeEmoji = (largeEmoji == 1);
@@ -468,6 +474,7 @@ void Settings::resetOnLastLogout() {
_soundOverrides = {};
_exeLaunchWarning = true;
_ipRevealWarning = true;
_loopAnimatedStickers = true;
_largeEmoji = true;
_replaceEmoji = true;

View File

@@ -255,6 +255,12 @@ public:
void setExeLaunchWarning(bool warning) {
_exeLaunchWarning = warning;
}
[[nodiscard]] bool ipRevealWarning() const {
return _ipRevealWarning;
}
void setIpRevealWarning(bool warning) {
_ipRevealWarning = warning;
}
[[nodiscard]] bool loopAnimatedStickers() const {
return _loopAnimatedStickers;
}
@@ -513,6 +519,7 @@ private:
Ui::InputSubmitSettings _sendSubmitWay;
base::flat_map<QString, QString> _soundOverrides;
bool _exeLaunchWarning = true;
bool _ipRevealWarning = true;
bool _loopAnimatedStickers = true;
rpl::variable<bool> _largeEmoji = true;
rpl::variable<bool> _replaceEmoji = true;

View File

@@ -310,7 +310,7 @@ QString AllFilesFilter() {
}
QString AlbumFilesFilter() {
return qsl("Image and Video Files (*.png *.jpg *.mp4 *.jpeg)");
return qsl("Image and Video Files (*.png *.jpg *.jpeg *.mp4 *.mov)");
}
namespace internal {

View File

@@ -90,6 +90,7 @@ Sandbox::Sandbox(
}
})
, _launcher(launcher) {
setQuitOnLastWindowClosed(false);
}
int Sandbox::start() {

View File

@@ -88,6 +88,7 @@ const auto CommandByName = base::flat_map<QString, Command>{
{ qsl("last_folder") , Command::ShowFolderLast },
{ qsl("show_archive") , Command::ShowArchive },
{ qsl("show_contacts") , Command::ShowContacts },
{ qsl("read_chat") , Command::ReadChat },
@@ -132,6 +133,7 @@ const auto CommandNames = base::flat_map<Command, QString>{
{ Command::ShowFolderLast , qsl("last_folder") },
{ Command::ShowArchive , qsl("show_archive") },
{ Command::ShowContacts , qsl("show_contacts") },
{ Command::ReadChat , qsl("read_chat") },
};
@@ -383,6 +385,7 @@ void Manager::fillDefaults() {
set(qsl("ctrl+0"), Command::ChatSelf);
set(qsl("ctrl+9"), Command::ShowArchive);
set(qsl("ctrl+shift+n"), Command::ShowContacts);
set(qsl("ctrl+r"), Command::ReadChat);
}

View File

@@ -48,6 +48,7 @@ enum class Command {
FolderPrevious,
ShowArchive,
ShowContacts,
JustSendMessage,
SendSilentMessage,

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 = 2004002;
constexpr auto AppVersionStr = "2.4.2";
constexpr auto AppVersion = 2004004;
constexpr auto AppVersionStr = "2.4.4";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -74,15 +74,15 @@ void LaunchWithWarning(
not_null<Main::Session*> session,
const QString &name,
HistoryItem *item) {
const auto isExecutable = Data::IsExecutableName(name);
const auto isIpReveal = Data::IsIpRevealingName(name);
auto &app = Core::App();
const auto warn = [&] {
if (!Data::IsExecutableName(name)) {
return false;
} else if (!Core::App().settings().exeLaunchWarning()) {
return false;
} else if (item && item->history()->peer->isVerified()) {
if (item && item->history()->peer->isVerified()) {
return false;
}
return true;
return (isExecutable && app.settings().exeLaunchWarning())
|| (isIpReveal && app.settings().ipRevealWarning());
}();
const auto extension = '.' + Data::FileExtension(name);
if (Platform::IsWindows() && extension == u"."_q) {
@@ -99,20 +99,27 @@ void LaunchWithWarning(
File::Launch(name);
return;
}
const auto callback = [=](bool checked) {
const auto callback = [=, &app](bool checked) {
if (checked) {
Core::App().settings().setExeLaunchWarning(false);
Core::App().saveSettingsDelayed();
if (isExecutable) {
app.settings().setExeLaunchWarning(false);
} else if (isIpReveal) {
app.settings().setIpRevealWarning(false);
}
app.saveSettingsDelayed();
}
File::Launch(name);
};
Ui::show(Box<ConfirmDontWarnBox>(
tr::lng_launch_exe_warning(
auto text = isExecutable
? tr::lng_launch_exe_warning(
lt_extension,
rpl::single(Ui::Text::Bold(extension)),
Ui::Text::WithEntities),
Ui::Text::WithEntities)
: tr::lng_launch_svg_warning(Ui::Text::WithEntities);
Ui::show(Box<ConfirmDontWarnBox>(
std::move(text),
tr::lng_launch_exe_dont_ask(tr::now),
tr::lng_launch_exe_sure(),
(isExecutable ? tr::lng_launch_exe_sure : tr::lng_continue)(),
callback));
}
@@ -1668,17 +1675,17 @@ mpkg pkg scpt scptd xhtm webarchive");
slp zsh");
#else // Q_OS_MAC || Q_OS_UNIX
qsl("\
ad ade adp app application appref-ms asp asx bas bat bin cdxml cer cfg chi \
chm cmd cnt com cpl crt csh der diagcab dll drv eml exe fon fxp gadget grp \
hlp hpj hta htt inf ini ins inx isp isu its jar jnlp job js jse ksh lnk \
local lua mad maf mag mam manifest maq mar mas mat mau mav maw mcf mda mdb \
mde mdt mdw mdz mht mhtml mjs mmc mof msc msg msh msh1 msh2 msh1xml msh2xml \
mshxml msi msp mst ops osd paf pcd phar php php3 php4 php5 php7 phps php-s \
pht phtml pif pl plg pm pod prf prg ps1 ps2 ps1xml ps2xml psc1 psc2 psd1 \
psm1 pssc pst py py3 pyc pyd pyi pyo pyw pywz pyz rb reg rgs scf scr sct \
search-ms settingcontent-ms shb shs slk sys t tmp u3p url vb vbe vbp vbs \
vbscript vdx vsmacros vsd vsdm vsdx vss vssm vssx vst vstm vstx vsw vsx vtx \
website ws wsc wsf wsh xbap xll xnk xs");
ad ade adp app application appref-ms asp asx bas bat bin cab cdxml cer cfg \
chi chm cmd cnt com cpl crt csh der diagcab dll drv eml exe fon fxp gadget \
grp hlp hpj hta htt inf ini ins inx isp isu its jar jnlp job js jse key ksh \
lnk local lua mad maf mag mam manifest maq mar mas mat mau mav maw mcf mda \
mdb mde mdt mdw mdz mht mhtml mjs mmc mof msc msg msh msh1 msh2 msh1xml \
msh2xml mshxml msi msp mst ops osd paf pcd phar php php3 php4 php5 php7 phps \
php-s pht phtml pif pl plg pm pod prf prg ps1 ps2 ps1xml ps2xml psc1 psc2 \
psd1 psm1 pssc pst py py3 pyc pyd pyi pyo pyw pywz pyz rb reg rgs scf scr \
sct search-ms settingcontent-ms sh shb shs slk sys t tmp u3p url vb vbe vbp \
vbs vbscript vdx vsmacros vsd vsdm vsdx vss vssm vssx vst vstm vstx vsw vsx \
vtx website ws wsc wsf wsh xbap xll xnk xs");
#endif // !Q_OS_MAC && !Q_OS_UNIX
const auto list = joined.split(' ');
return base::flat_set<QString>(list.begin(), list.end());
@@ -1689,6 +1696,18 @@ website ws wsc wsf wsh xbap xll xnk xs");
FileExtension(filepath).toLower());
}
bool IsIpRevealingName(const QString &filepath) {
static const auto kExtensions = [] {
const auto joined = u"htm html svg"_q;
const auto list = joined.split(' ');
return base::flat_set<QString>(list.begin(), list.end());
}();
return ranges::binary_search(
kExtensions,
FileExtension(filepath).toLower());
}
base::binary_guard ReadImageAsync(
not_null<Data::DocumentMedia*> media,
FnMut<QImage(QImage)> postprocess,

View File

@@ -443,9 +443,10 @@ QString DocumentFileNameForSave(
namespace Data {
QString FileExtension(const QString &filepath);
bool IsValidMediaFile(const QString &filepath);
bool IsExecutableName(const QString &filepath);
[[nodiscard]] QString FileExtension(const QString &filepath);
[[nodiscard]] bool IsValidMediaFile(const QString &filepath);
[[nodiscard]] bool IsExecutableName(const QString &filepath);
[[nodiscard]] bool IsIpRevealingName(const QString &filepath);
base::binary_guard ReadImageAsync(
not_null<Data::DocumentMedia*> media,
FnMut<QImage(QImage)> postprocess,

View File

@@ -42,7 +42,10 @@ struct RepliesList::Viewer {
MsgId around = 0;
int limitBefore = 0;
int limitAfter = 0;
int injectedForRoot = 0;
base::has_weak_ptr guard;
bool stale = true;
bool scheduled = false;
};
RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
@@ -67,7 +70,9 @@ rpl::producer<MessagesSlice> RepliesList::source(
_history->session().changes().historyFlagsValue(
_history,
Data::HistoryUpdate::Flag::LocalMessages)
) | rpl::map([=](MessagesSlice &&server, const auto &) {
) | rpl::filter([=](const MessagesSlice &data, const auto &) {
return (data.fullCount.value_or(0) >= 0);
}) | rpl::map([=](MessagesSlice &&server, const auto &) {
appendLocalMessages(server);
return std::move(server);
});
@@ -82,10 +87,22 @@ rpl::producer<MessagesSlice> RepliesList::sourceFromServer(
auto lifetime = rpl::lifetime();
const auto viewer = lifetime.make_state<Viewer>();
const auto push = [=] {
viewer->scheduled = false;
if (buildFromData(viewer)) {
viewer->stale = false;
consumer.put_next_copy(viewer->slice);
}
};
const auto pushDelayed = [=] {
if (!viewer->stale) {
viewer->stale = true;
consumer.put_next_copy(MessagesSlice{ .fullCount = -1 });
}
if (!viewer->scheduled) {
viewer->scheduled = true;
crl::on_main(&viewer->guard, push);
}
};
viewer->around = around;
viewer->limitBefore = limitBefore;
viewer->limitAfter = limitAfter;
@@ -96,14 +113,10 @@ rpl::producer<MessagesSlice> RepliesList::sourceFromServer(
| MessageUpdate::Flag::Destroyed
) | rpl::filter([=](const MessageUpdate &update) {
return applyUpdate(viewer, update);
}) | rpl::start_with_next([=] {
crl::on_main(&viewer->guard, push);
}, lifetime);
}) | rpl::start_with_next(pushDelayed, lifetime);
_partLoaded.events(
) | rpl::start_with_next([=] {
crl::on_main(&viewer->guard, push);
}, lifetime);
) | rpl::start_with_next(pushDelayed, lifetime);
push();
return lifetime;
@@ -173,32 +186,37 @@ rpl::producer<int> RepliesList::fullCount() const {
return _fullCount.value() | rpl::filter_optional();
}
void RepliesList::injectRootMessageAndReverse(
not_null<MessagesSlice*> slice) {
injectRootMessage(slice);
ranges::reverse(slice->ids);
void RepliesList::injectRootMessageAndReverse(not_null<Viewer*> viewer) {
injectRootMessage(viewer);
ranges::reverse(viewer->slice.ids);
}
void RepliesList::injectRootMessage(not_null<MessagesSlice*> slice) {
void RepliesList::injectRootMessage(not_null<Viewer*> viewer) {
const auto slice = &viewer->slice;
viewer->injectedForRoot = 0;
if (slice->skippedBefore != 0) {
return;
}
if (const auto root = lookupRoot()) {
injectRootDivider(root, slice);
const auto root = lookupRoot();
if (!root) {
return;
}
injectRootDivider(root, slice);
if (const auto group = _history->owner().groups().find(root)) {
for (const auto item : ranges::view::reverse(group->items)) {
slice->ids.push_back(item->fullId());
}
if (slice->fullCount) {
*slice->fullCount += group->items.size();
}
} else {
slice->ids.push_back(root->fullId());
if (slice->fullCount) {
++*slice->fullCount;
}
if (const auto group = _history->owner().groups().find(root)) {
for (const auto item : ranges::view::reverse(group->items)) {
slice->ids.push_back(item->fullId());
}
viewer->injectedForRoot = group->items.size();
if (slice->fullCount) {
*slice->fullCount += group->items.size();
}
} else {
slice->ids.push_back(root->fullId());
viewer->injectedForRoot = 1;
}
if (slice->fullCount) {
*slice->fullCount += viewer->injectedForRoot;
}
}
@@ -232,7 +250,8 @@ bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
= viewer->slice.skippedBefore
= viewer->slice.skippedAfter
= 0;
injectRootMessageAndReverse(&viewer->slice);
viewer->injectedForRoot = 0;
injectRootMessageAndReverse(viewer);
return true;
}
const auto around = [&] {
@@ -285,7 +304,7 @@ bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
slice->ids.empty() ? 0 : slice->ids.back().msg));
slice->fullCount = _fullCount.current();
injectRootMessageAndReverse(slice);
injectRootMessageAndReverse(viewer);
if (_skippedBefore != 0 && useBefore < viewer->limitBefore + 1) {
loadBefore();
@@ -301,10 +320,20 @@ bool RepliesList::applyUpdate(
not_null<Viewer*> viewer,
const MessageUpdate &update) {
if (update.item->history() != _history
|| update.item->replyToTop() != _rootId
|| !IsServerMsgId(update.item->id)) {
return false;
}
if (update.flags & MessageUpdate::Flag::Destroyed) {
const auto id = update.item->fullId();
for (auto i = 0; i != viewer->injectedForRoot; ++i) {
if (viewer->slice.ids[i] == id) {
return true;
}
}
}
if (update.item->replyToTop() != _rootId) {
return false;
}
const auto id = update.item->id;
const auto i = ranges::lower_bound(_list, id, std::greater<>());
if (update.flags & MessageUpdate::Flag::Destroyed) {
@@ -541,6 +570,8 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) {
} else {
_list.push_back(item->id);
}
} else {
++skipped;
}
} else {
++skipped;

View File

@@ -47,8 +47,8 @@ private:
[[nodiscard]] bool applyUpdate(
not_null<Viewer*> viewer,
const MessageUpdate &update);
void injectRootMessageAndReverse(not_null<MessagesSlice*> slice);
void injectRootMessage(not_null<MessagesSlice*> slice);
void injectRootMessageAndReverse(not_null<Viewer*> viewer);
void injectRootMessage(not_null<Viewer*> viewer);
void injectRootDivider(
not_null<HistoryItem*> root,
not_null<MessagesSlice*> slice);

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_scheduled_messages.h"
#include "base/unixtime.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "api/api_hash.h"
@@ -26,6 +27,11 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
return (received > 0) && (received + kRequestTimeLimit > crl::now());
}
[[nodiscard]] bool HasScheduledDate(not_null<HistoryItem*> item) {
return (item->date() != ScheduledMessages::kScheduledUntilOnlineTimestamp)
&& (item->date() > base::unixtime::now());
}
MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) {
return message.match([&](const MTPDmessageEmpty &) {
return MTP_messageEmpty(MTP_int(id));
@@ -147,7 +153,11 @@ void ScheduledMessages::sendNowSimpleMessage(
not_null<HistoryItem*> local) {
Expects(local->isSending());
Expects(local->isScheduled());
Expects(local->date() == kScheduledUntilOnlineTimestamp);
if (HasScheduledDate(local)) {
LOG(("Error: trying to put to history a new local message, "
"that has scheduled date."));
return;
}
// When the user sends a text message scheduled until online
// while the recipient is already online, the server sends
@@ -218,14 +228,12 @@ void ScheduledMessages::apply(const MTPDupdateNewScheduledMessage &update) {
void ScheduledMessages::checkEntitiesAndUpdate(const MTPDmessage &data) {
// When the user sends a message with a media scheduled until online
// while the recipient is already online, the server sends
// updateNewMessage to the client and the client calls this method.
// while the recipient is already online, or scheduled message
// is already due and is sent immediately, the server sends
// updateNewMessage or updateNewChannelMessage to the client
// and the client calls this method.
const auto peer = peerFromMTP(data.vpeer_id());
if (!peerIsUser(peer)) {
return;
}
const auto history = _session->data().historyLoaded(peer);
if (!history) {
return;
@@ -243,16 +251,18 @@ void ScheduledMessages::checkEntitiesAndUpdate(const MTPDmessage &data) {
}
const auto existing = j->second;
Assert(existing->date() == kScheduledUntilOnlineTimestamp);
existing->updateSentContent({
qs(data.vmessage()),
Api::EntitiesFromMTP(_session, data.ventities().value_or_empty())
}, data.vmedia());
existing->updateReplyMarkup(data.vreply_markup());
existing->updateForwardedInfo(data.vfwd_from());
_session->data().requestItemTextRefresh(existing);
if (!HasScheduledDate(existing)) {
// Destroy a local message, that should be in history.
existing->updateSentContent({
qs(data.vmessage()),
Api::EntitiesFromMTP(_session, data.ventities().value_or_empty())
}, data.vmedia());
existing->updateReplyMarkup(data.vreply_markup());
existing->updateForwardedInfo(data.vfwd_from());
_session->data().requestItemTextRefresh(existing);
existing->destroy();
existing->destroy();
}
}
void ScheduledMessages::apply(

View File

@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_account.h"
#include "storage/storage_encrypted_file.h"
#include "media/player/media_player_instance.h" // instance()->play()
#include "media/audio/media_audio.h"
#include "boxes/abstract_box.h"
#include "passport/passport_form_controller.h"
#include "window/themes/window_theme.h"
@@ -1428,6 +1429,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemRemoved() const {
return _itemRemoved.events();
}
rpl::producer<not_null<const HistoryItem*>> Session::itemRemoved(
FullMsgId itemId) const {
return itemRemoved(
) | rpl::filter([=](not_null<const HistoryItem*> item) {
return (itemId == item->fullId());
});
}
void Session::notifyViewRemoved(not_null<const ViewElement*> view) {
_viewRemoved.fire_copy(view);
}
@@ -1775,6 +1784,7 @@ bool Session::checkEntitiesAndViewsUpdate(const MTPDmessage &data) {
data.vreply_to_msg_id().v));
});
}
existing->setPostAuthor(data.vpost_author().value_or_empty());
existing->indexAsNewItem();
existing->contributeToSlowmode(data.vdate().v);
requestItemTextRefresh(existing);
@@ -3324,6 +3334,15 @@ void Session::unregisterContactItem(
}
}
void Session::documentMessageRemoved(not_null<DocumentData*> document) {
if (_documentItems.find(document) != _documentItems.end()) {
return;
}
if (document->loading()) {
document->cancel();
}
}
void Session::checkPlayingAnimations() {
auto check = base::flat_set<not_null<ViewElement*>>();
for (const auto view : _heavyViewParts) {

View File

@@ -231,6 +231,8 @@ public:
[[nodiscard]] rpl::producer<not_null<const History*>> historyUnloaded() const;
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemRemoved(
FullMsgId itemId) const;
void notifyViewRemoved(not_null<const ViewElement*> view);
[[nodiscard]] rpl::producer<not_null<const ViewElement*>> viewRemoved() const;
void notifyHistoryCleared(not_null<const History*> history);
@@ -545,6 +547,8 @@ public:
UserId contactId,
not_null<HistoryItem*> item);
void documentMessageRemoved(not_null<DocumentData*> document);
void checkPlayingAnimations();
HistoryItem *findWebPageItem(not_null<WebPageData*> page) const;

View File

@@ -3178,6 +3178,11 @@ void InnerWidget::setupShortcuts() {
return (history != nullptr);
});
request->check(Command::ShowContacts) && request->handle([=] {
Ui::show(PrepareContactsBox(_controller));
return true;
});
if (session().supportMode() && row.key.history()) {
request->check(
Command::SupportScrollToCurrent

View File

@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_document.h"
#include "data/data_histories.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
@@ -416,11 +417,17 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
types,
item->id));
}
} else {
session().api().cancelLocalItem(item);
}
itemRemoved(item);
}
if (item->isSending()) {
session().api().cancelLocalItem(item);
}
const auto document = [&] {
const auto media = item->media();
return media ? media->document() : nullptr;
}();
owner().unregisterMessage(item);
Core::App().notifications().clearFromItem(item);
@@ -431,6 +438,10 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
Assert(i != end(_messages));
_messages.erase(i);
if (document) {
session().data().documentMessageRemoved(document);
}
}
not_null<HistoryItem*> History::addNewItem(

View File

@@ -191,8 +191,7 @@ TimeId HistoryItem::date() const {
}
TimeId HistoryItem::NewMessageDate(TimeId scheduled) {
const auto now = base::unixtime::now();
return scheduled ? std::max(scheduled, now + 60) : now;
return scheduled ? scheduled : base::unixtime::now();
}
void HistoryItem::finishEdition(int oldKeyboardTop) {

View File

@@ -293,6 +293,8 @@ public:
}
virtual void setReplyToTop(MsgId replyToTop) {
}
virtual void setPostAuthor(const QString &author) {
}
virtual void setRealId(MsgId newId);
virtual void incrementReplyToTopCounter() {
}

View File

@@ -374,18 +374,22 @@ MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer) {
return result;
}
MsgId LookupReplyToTop(not_null<History*> history, MsgId replyToId) {
const auto &owner = history->owner();
if (const auto item = owner.message(history->channelId(), replyToId)) {
return item->replyToTop();
}
return 0;
}
MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
if (const auto id = action.replyTo) {
const auto history = action.history;
const auto &owner = history->owner();
if (const auto item = owner.message(history->channelId(), id)) {
if (item->replyToId()) {
return MTP_messageReplyHeader(
MTP_flags(MTPDmessageReplyHeader::Flag::f_reply_to_top_id),
MTP_int(id),
MTPPeer(),
MTP_int(item->replyToTop()));
}
if (const auto replyToTop = LookupReplyToTop(action.history, id)) {
return MTP_messageReplyHeader(
MTP_flags(MTPDmessageReplyHeader::Flag::f_reply_to_top_id),
MTP_int(id),
MTPPeer(),
MTP_int(replyToTop));
}
return MTP_messageReplyHeader(
MTP_flags(0),
@@ -483,7 +487,7 @@ HistoryMessage::HistoryMessage(
}
config.viaBotId = data.vvia_bot_id().value_or_empty();
config.viewsCount = data.vviews().value_or(-1);
config.mtpReplies = data.vreplies();
config.mtpReplies = isScheduled() ? nullptr : data.vreplies();
config.mtpMarkup = data.vreply_markup();
config.editDate = data.vedit_date().value_or_empty();
config.author = qs(data.vpost_author().value_or_empty());
@@ -741,7 +745,9 @@ void HistoryMessage::createComponentsHelper(
if (flags & MTPDmessage::Flag::f_via_bot_id) config.viaBotId = viaBotId;
if (flags & MTPDmessage::Flag::f_reply_to) {
config.replyToTop = config.replyTo = replyTo;
config.replyTo = replyTo;
const auto replyToTop = LookupReplyToTop(history(), replyTo);
config.replyToTop = replyToTop ? replyToTop : replyTo;
}
if (flags & MTPDmessage::Flag::f_reply_markup) config.mtpMarkup = &markup;
if (flags & MTPDmessage::Flag::f_post_author) config.author = postAuthor;
@@ -988,6 +994,14 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
}
if (!config.author.isEmpty()) {
mask |= HistoryMessageSigned::Bit();
} else if (_history->peer->isMegagroup() // Discussion posts signatures.
&& config.savedFromPeer
&& !config.authorOriginal.isEmpty()) {
const auto savedFrom = _history->owner().peerLoaded(
config.savedFromPeer);
if (savedFrom && savedFrom->isChannel()) {
mask |= HistoryMessageSigned::Bit();
}
}
if (config.editDate != TimeId(0)) {
mask |= HistoryMessageEdited::Bit();
@@ -1048,8 +1062,11 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
edited->date = config.editDate;
}
if (const auto msgsigned = Get<HistoryMessageSigned>()) {
msgsigned->author = config.author;
msgsigned->isAnonymousRank = author()->isMegagroup();
msgsigned->author = config.author.isEmpty()
? config.authorOriginal
: config.author;
msgsigned->isAnonymousRank = !isDiscussionPost()
&& author()->isMegagroup();
}
setupForwardedComponent(config);
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
@@ -1299,6 +1316,11 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
setViewsCount(message.vviews().value_or(-1));
setForwardsCount(message.vforwards().value_or(-1));
setText(_media ? textWithEntities : EnsureNonEmpty(textWithEntities));
if (const auto replies = message.vreplies()) {
setReplies(*replies);
} else {
clearReplies();
}
finishEdition(keyboardTop);
}
@@ -1585,6 +1607,28 @@ void HistoryMessage::setViewsCount(int count) {
void HistoryMessage::setForwardsCount(int count) {
}
void HistoryMessage::setPostAuthor(const QString &author) {
auto msgsigned = Get<HistoryMessageSigned>();
if (author.isEmpty()) {
if (!msgsigned) {
return;
}
RemoveComponents(HistoryMessageSigned::Bit());
history()->owner().requestItemResize(this);
return;
}
if (!msgsigned) {
AddComponents(HistoryMessageSigned::Bit());
msgsigned = Get<HistoryMessageSigned>();
} else if (msgsigned->author == author) {
return;
}
msgsigned->author = author;
msgsigned->isAnonymousRank = !isDiscussionPost()
&& this->author()->isMegagroup();
history()->owner().requestItemResize(this);
}
void HistoryMessage::setReplies(const MTPMessageReplies &data) {
data.match([&](const MTPDmessageReplies &data) {
auto views = Get<HistoryMessageViews>();

View File

@@ -21,16 +21,20 @@ struct HistoryMessageEdited;
struct HistoryMessageReply;
struct HistoryMessageViews;
Fn<void(ChannelData*, MsgId)> HistoryDependentItemCallback(
[[nodiscard]] Fn<void(ChannelData*, MsgId)> HistoryDependentItemCallback(
not_null<HistoryItem*> item);
MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer);
MTPDmessage_ClientFlags NewMessageClientFlags();
MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action);
QString GetErrorTextForSending(
[[nodiscard]] MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer);
[[nodiscard]] MTPDmessage_ClientFlags NewMessageClientFlags();
[[nodiscard]] MsgId LookupReplyToTop(
not_null<History*> history,
MsgId replyToId);
[[nodiscard]] MTPMessageReplyHeader NewMessageReplyHeader(
const Api::SendAction &action);
[[nodiscard]] QString GetErrorTextForSending(
not_null<PeerData*> peer,
const HistoryItemsList &items,
bool ignoreSlowmodeCountdown = false);
QString GetErrorTextForSending(
[[nodiscard]] QString GetErrorTextForSending(
not_null<PeerData*> peer,
const HistoryItemsList &items,
const TextWithTags &comment,
@@ -129,6 +133,7 @@ public:
void clearReplies() override;
void changeRepliesCount(int delta, PeerId replier) override;
void setReplyToTop(MsgId replyToTop) override;
void setPostAuthor(const QString &author) override;
void setRealId(MsgId newId) override;
void incrementReplyToTopCounter() override;

View File

@@ -1942,6 +1942,7 @@ void HistoryWidget::updateNotifyControls() {
if (!session().data().notifySilentPostsUnknown(_peer)) {
if (_silent) {
_silent->setChecked(session().data().notifySilentPosts(_peer));
updateFieldPlaceholder();
} else if (hasSilentToggle()) {
refreshSilentToggle();
updateControlsVisibility();
@@ -3132,10 +3133,6 @@ void HistoryWidget::toggleMuteUnmute() {
session().data().updateNotifySettings(_peer, muteForSeconds);
}
void HistoryWidget::onBroadcastSilentChange() {
updateFieldPlaceholder();
}
History *HistoryWidget::history() const {
return _history;
}

View File

@@ -294,8 +294,6 @@ signals:
public slots:
void onScroll();
void onBroadcastSilentChange();
void activate();
void onTextChange();

View File

@@ -1005,6 +1005,7 @@ void ComposeControls::initField() {
_field,
&_window->session());
_raiseEmojiSuggestions = [=] { suggestions->raise(); };
InitSpellchecker(_window, _field);
}
void ComposeControls::fieldChanged() {

View File

@@ -69,7 +69,7 @@ constexpr auto kExportLocalTimeout = crl::time(1000);
//}
MsgId ItemIdAcrossData(not_null<HistoryItem*> item) {
if (!item->isScheduled()) {
if (!item->isScheduled() || item->isSending() || item->hasFailed()) {
return item->id;
}
const auto session = &item->history()->session();
@@ -420,23 +420,23 @@ bool AddSendNowMessageAction(
bool AddRescheduleMessageAction(
not_null<Ui::PopupMenu*> menu,
const ContextMenuRequest &request) {
if (!HasEditMessageAction(request)
|| !request.item->isScheduled()) {
if (!HasEditMessageAction(request) || !request.item->isScheduled()) {
return false;
}
const auto item = request.item;
const auto owner = &item->history()->owner();
const auto itemId = item->fullId();
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) {
return;
}
const auto callback = [=](Api::SendOptions options) {
if (!item->media() || !item->media()->webpage()) {
options.removeWebPageId = true;
if (const auto item = owner->message(itemId)) {
if (!item->media() || !item->media()->webpage()) {
options.removeWebPageId = true;
}
Api::RescheduleMessage(item, options);
}
Api::RescheduleMessage(item, options);
};
const auto peer = item->history()->peer;
@@ -453,13 +453,19 @@ bool AddRescheduleMessageAction(
? HistoryView::DefaultScheduleTime()
: item->date() + 600;
Ui::show(
const auto box = Ui::show(
HistoryView::PrepareScheduleBox(
&request.navigation->session(),
sendMenuType,
callback,
date),
Ui::LayerOption::KeepOther);
owner->itemRemoved(
itemId
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
});
return true;
}

View File

@@ -292,9 +292,10 @@ ListWidget::ListWidget(
}, lifetime());
session().data().itemRemoved(
) | rpl::start_with_next(
[this](auto item) { itemRemoved(item); },
lifetime());
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
itemRemoved(item);
}, lifetime());
subscribe(session().data().queryItemVisibility(), [this](const Data::Session::ItemVisibilityQuery &query) {
if (const auto view = viewForItem(query.item)) {
const auto top = itemTop(view);
@@ -2094,7 +2095,8 @@ void ListWidget::mouseActionStart(
if (isPressInSelectedText(dragState)) {
_mouseAction = MouseAction::PrepareDrag; // start text drag
} else if (!_pressWasInactive) {
if (requiredToStartDragging(pressElement)) {
if (requiredToStartDragging(pressElement)
&& _pressState.pointState != PointState::Outside) {
_mouseAction = MouseAction::PrepareDrag;
} else {
if (dragState.afterSymbol) ++_mouseTextSymbol;

View File

@@ -228,7 +228,6 @@ Message::Message(
: Element(delegate, data, replacing) {
initLogEntryOriginal();
initPsa();
refreshRightBadge();
}
Message::~Message() {
@@ -287,7 +286,6 @@ void Message::applyGroupAdminChanges(
const base::flat_set<UserId> &changes) {
if (!data()->out()
&& changes.contains(peerToUser(data()->author()->id))) {
refreshRightBadge();
history()->owner().requestViewResize(this);
}
}
@@ -301,6 +299,7 @@ QSize Message::performCountOptimalSize() {
updateMediaInBubbleState();
refreshEditedBadge();
refreshRightBadge();
auto mediaOnBottom = (logEntryOriginal() != nullptr)
|| (media && media->isDisplayed() && media->isBubbleBottom());
@@ -543,14 +542,14 @@ void Message::draw(
auto displayTail = skipTail ? RectPart::None : (outbg && !Core::App().settings().chatWide()) ? RectPart::Right : RectPart::Left;
PaintBubble(p, g, width(), selected, outbg, displayTail);
const auto gBubble = g;
paintCommentsButton(p, g, selected);
auto inner = g;
paintCommentsButton(p, inner, selected);
// Entry page is always a bubble bottom.
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
auto trect = g.marginsRemoved(st::msgPadding);
auto trect = inner.marginsRemoved(st::msgPadding);
if (mediaOnBottom) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
}
@@ -568,7 +567,7 @@ void Message::draw(
paintText(p, trect, selection);
if (mediaDisplayed) {
auto mediaHeight = media->height();
auto mediaLeft = g.left();
auto mediaLeft = inner.left();
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
p.translate(mediaLeft, mediaTop);
@@ -576,7 +575,7 @@ void Message::draw(
p.translate(-mediaLeft, -mediaTop);
}
if (entry) {
auto entryLeft = g.left();
auto entryLeft = inner.left();
auto entryTop = trect.y() + trect.height();
p.translate(entryLeft, entryTop);
auto entrySelection = skipTextSelection(selection);
@@ -592,29 +591,29 @@ void Message::draw(
? !media->customInfoLayout()
: true);
if (needDrawInfo) {
drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayType::Default);
if (g != gBubble) {
drawInfo(p, inner.left() + inner.width(), inner.top() + inner.height(), 2 * inner.left() + inner.width(), selected, InfoDisplayType::Default);
if (g != inner) {
const auto o = p.opacity();
p.setOpacity(0.3);
const auto color = selected
? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected)
: (outbg ? st::msgOutDateFg : st::msgInDateFg);
p.fillRect(g.left(), g.top() + g.height() - st::lineWidth, g.width(), st::lineWidth, color);
p.fillRect(inner.left(), inner.top() + inner.height() - st::lineWidth, inner.width(), st::lineWidth, color);
p.setOpacity(o);
}
}
if (const auto size = rightActionSize()) {
const auto fastShareSkip = std::clamp(
(gBubble.height() - size->height()) / 2,
(g.height() - size->height()) / 2,
0,
st::historyFastShareBottom);
const auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft;
const auto fastShareTop = g.top() + gBubble.height() - fastShareSkip - size->height();
const auto fastShareTop = g.top() + g.height() - fastShareSkip - size->height();
drawRightAction(p, fastShareLeft, fastShareTop, width());
}
if (media) {
media->paintBubbleFireworks(p, gBubble, ms);
media->paintBubbleFireworks(p, g, ms);
}
} else if (media && media->isDisplayed()) {
p.translate(g.topLeft());
@@ -783,74 +782,77 @@ void Message::paintFromName(
QRect &trect,
bool selected) const {
const auto item = message();
if (displayFromName()) {
const auto badgeWidth = _rightBadge.isEmpty() ? 0 : _rightBadge.maxWidth();
const auto replyWidth = [&] {
if (isUnderCursor() && displayFastReply()) {
return st::msgFont->width(FastReplyText());
}
return 0;
}();
const auto rightWidth = replyWidth ? replyWidth : badgeWidth;
auto availableLeft = trect.left();
auto availableWidth = trect.width();
if (rightWidth) {
availableWidth -= st::msgPadding.right() + rightWidth;
if (!displayFromName()) {
return;
}
const auto badgeWidth = _rightBadge.isEmpty() ? 0 : _rightBadge.maxWidth();
const auto replyWidth = [&] {
if (isUnderCursor() && displayFastReply()) {
return st::msgFont->width(FastReplyText());
}
return 0;
}();
const auto rightWidth = replyWidth ? replyWidth : badgeWidth;
auto availableLeft = trect.left();
auto availableWidth = trect.width();
if (rightWidth) {
availableWidth -= st::msgPadding.right() + rightWidth;
}
p.setFont(st::msgNameFont);
const auto nameText = [&]() -> const Ui::Text::String * {
const auto from = item->displayFrom();
if (hasOutLayout()) {
p.setPen(selected ? st::msgOutServiceFgSelected : st::msgOutServiceFg);
return &from->nameText();
} else if (item->isPost()) {
p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg);
return &from->nameText();
} else if (from) {
p.setPen(FromNameFg(from->id, selected));
return &from->nameText();
} else if (const auto info = item->hiddenForwardedInfo()) {
p.setPen(FromNameFg(info->colorPeerId, selected));
return &info->nameText;
} else {
Unexpected("Corrupt forwarded information in message.");
}
}();
nameText->drawElided(p, availableLeft, trect.top(), availableWidth);
const auto skipWidth = nameText->maxWidth() + st::msgServiceFont->spacew;
p.setFont(st::msgNameFont);
const auto outbg = hasOutLayout();
const auto nameText = [&]() -> const Ui::Text::String * {
const auto from = item->displayFrom();
if (outbg) {
p.setPen(selected ? st::msgOutServiceFgSelected : st::msgOutServiceFg);
return &from->nameText();
} else if (item->isPost()) {
p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg);
return &from->nameText();
} else if (from) {
p.setPen(FromNameFg(from->id, selected));
return &from->nameText();
} else if (const auto info = item->hiddenForwardedInfo()) {
p.setPen(FromNameFg(info->colorPeerId, selected));
return &info->nameText;
} else {
Unexpected("Corrupt forwarded information in message.");
}
}();
nameText->drawElided(p, availableLeft, trect.top(), availableWidth);
const auto skipWidth = nameText->maxWidth() + st::msgServiceFont->spacew;
availableLeft += skipWidth;
availableWidth -= skipWidth;
auto via = item->Get<HistoryMessageVia>();
if (via && !displayForwardedFrom() && availableWidth > 0) {
p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
p.drawText(availableLeft, trect.top() + st::msgServiceFont->ascent, via->text);
auto skipWidth = via->width + st::msgServiceFont->spacew;
availableLeft += skipWidth;
availableWidth -= skipWidth;
auto via = item->Get<HistoryMessageVia>();
if (via && !displayForwardedFrom() && availableWidth > 0) {
const auto outbg = hasOutLayout();
p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
p.drawText(availableLeft, trect.top() + st::msgServiceFont->ascent, via->text);
auto skipWidth = via->width + st::msgServiceFont->spacew;
availableLeft += skipWidth;
availableWidth -= skipWidth;
}
if (rightWidth) {
p.setPen(selected ? st::msgInDateFgSelected : st::msgInDateFg);
p.setFont(ClickHandler::showAsActive(_fastReplyLink)
? st::msgFont->underline()
: st::msgFont);
if (replyWidth) {
p.drawText(
trect.left() + trect.width() - rightWidth,
trect.top() + st::msgFont->ascent,
FastReplyText());
} else {
_rightBadge.draw(
p,
trect.left() + trect.width() - rightWidth,
trect.top(),
rightWidth);
}
}
trect.setY(trect.y() + st::msgNameFont->height);
}
if (rightWidth) {
p.setPen(outbg
? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg)
: (selected ? st::msgInDateFgSelected : st::msgInDateFg));
p.setFont(ClickHandler::showAsActive(_fastReplyLink)
? st::msgFont->underline()
: st::msgFont);
if (replyWidth) {
p.drawText(
trect.left() + trect.width() - rightWidth,
trect.top() + st::msgFont->ascent,
FastReplyText());
} else {
_rightBadge.draw(
p,
trect.left() + trect.width() - rightWidth,
trect.top(),
rightWidth);
}
}
trect.setY(trect.y() + st::msgNameFont->height);
}
void Message::paintForwardedInfo(Painter &p, QRect &trect, bool selected) const {
@@ -1149,12 +1151,12 @@ TextState Message::textState(
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
const auto gBubble = g;
if (getStateCommentsButton(point, g, &result)) {
auto bubble = g;
if (getStateCommentsButton(point, bubble, &result)) {
return result;
}
auto trect = g.marginsRemoved(st::msgPadding);
auto trect = bubble.marginsRemoved(st::msgPadding);
if (mediaOnBottom) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
}
@@ -1177,7 +1179,7 @@ TextState Message::textState(
if (entry) {
auto entryHeight = entry->height();
trect.setHeight(trect.height() - entryHeight);
auto entryLeft = g.left();
auto entryLeft = bubble.left();
auto entryTop = trect.y() + trect.height();
if (point.y() >= entryTop && point.y() < entryTop + entryHeight) {
result = entry->textState(
@@ -1192,8 +1194,8 @@ TextState Message::textState(
return;
}
const auto inDate = pointInTime(
g.left() + g.width(),
g.top() + g.height(),
bubble.left() + bubble.width(),
bubble.top() + bubble.height(),
point,
InfoDisplayType::Default);
if (inDate) {
@@ -1225,11 +1227,11 @@ TextState Message::textState(
checkForPointInTime();
if (const auto size = rightActionSize()) {
const auto fastShareSkip = snap(
(gBubble.height() - size->height()) / 2,
(g.height() - size->height()) / 2,
0,
st::historyFastShareBottom);
const auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft;
const auto fastShareTop = g.top() + gBubble.height() - fastShareSkip - size->height();
const auto fastShareTop = g.top() + g.height() - fastShareSkip - size->height();
if (QRect(
fastShareLeft,
fastShareTop,

View File

@@ -216,6 +216,7 @@ RepliesWidget::RepliesWidget(
if (update.item == _root) {
_root = nullptr;
updatePinnedVisibility();
controller->showBackFromStack();
}
while (update.item == _replyReturn) {
calculateNextReplyReturn();
@@ -1758,7 +1759,7 @@ MessagesBarData RepliesWidget::listMessagesBar(
for (auto i = 0, count = int(elements.size()); i != count; ++i) {
const auto item = elements[i]->data();
if (IsServerMsgId(item->id) && item->id > till) {
if (item->out()) {
if (item->out() || !item->replyToId()) {
readTill(item);
} else {
return MessagesBarData{

View File

@@ -384,9 +384,7 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
_animation->radial.draw(p, rinner, st::msgFileRadialLine, fg);
}
if (!loaded) {
drawCornerDownload(p, selected);
}
drawCornerDownload(p, selected);
}
auto namewidth = width() - nameleft - nameright;
auto statuswidth = namewidth;
@@ -530,7 +528,9 @@ bool Document::downloadInCorner() const {
}
void Document::drawCornerDownload(Painter &p, bool selected) const {
if (!downloadInCorner()) {
if (dataLoaded()
|| _data->loadedInMediaCache()
|| !downloadInCorner()) {
return;
}
auto outbg = _parent->hasOutLayout();
@@ -570,7 +570,9 @@ TextState Document::cornerDownloadTextState(
QPoint point,
StateRequest request) const {
auto result = TextState(_parent);
if (!downloadInCorner()) {
if (dataLoaded()
|| _data->loadedInMediaCache()
|| !downloadInCorner()) {
return result;
}
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
@@ -627,10 +629,8 @@ TextState Document::textState(QPoint point, StateRequest request) const {
nametop = st::msgFileNameTop - topMinus;
bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() - topMinus;
if (!loaded) {
if (const auto state = cornerDownloadTextState(point, request); state.link) {
return state;
}
if (const auto state = cornerDownloadTextState(point, request); state.link) {
return state;
}
QRect inner(style::rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, st::msgFileSize, st::msgFileSize, width()));
if ((_data->loading() || _data->uploading()) && inner.contains(point) && !downloadInCorner()) {

View File

@@ -649,36 +649,41 @@ void ListWidget::restart() {
}
void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
if (isMyItem(item)) {
auto id = GetUniversalId(item);
auto sectionIt = findSectionByItem(id);
if (sectionIt != _sections.end()) {
if (sectionIt->removeItem(id)) {
auto top = sectionIt->top();
if (sectionIt->empty()) {
_sections.erase(sectionIt);
}
refreshHeight();
}
}
if (isItemLayout(item, _overLayout)) {
_overLayout = nullptr;
}
if (const auto i = _layouts.find(id); i != _layouts.end()) {
_heavyLayouts.remove(i->second.item.get());
_layouts.erase(i);
}
_dragSelected.remove(id);
if (const auto i = _selected.find(id); i != _selected.cend()) {
removeItemSelection(i);
}
mouseActionUpdate(_mousePosition);
if (!isMyItem(item)) {
return;
}
auto id = GetUniversalId(item);
auto needHeightRefresh = false;
auto sectionIt = findSectionByItem(id);
if (sectionIt != _sections.end()) {
if (sectionIt->removeItem(id)) {
auto top = sectionIt->top();
if (sectionIt->empty()) {
_sections.erase(sectionIt);
}
needHeightRefresh = true;
}
}
if (isItemLayout(item, _overLayout)) {
_overLayout = nullptr;
}
if (const auto i = _layouts.find(id); i != _layouts.end()) {
_heavyLayouts.remove(i->second.item.get());
_layouts.erase(i);
}
_dragSelected.remove(id);
if (const auto i = _selected.find(id); i != _selected.cend()) {
removeItemSelection(i);
}
if (needHeightRefresh) {
refreshHeight();
}
mouseActionUpdate(_mousePosition);
}
FullMsgId ListWidget::computeFullId(

View File

@@ -349,6 +349,11 @@ MainWidget::MainWidget(
if (!songState.id || IsStoppedOrStopping(songState.state)) {
closeBothPlayers();
}
} else if (type == AudioMsgId::Type::Song) {
const auto songState = Media::Player::instance()->getState(AudioMsgId::Type::Song);
if (!songState.id) {
closeBothPlayers();
}
}
});

View File

@@ -100,7 +100,15 @@ void Instance::start() {
void Instance::stop(Fn<void(Result&&)> callback) {
InvokeQueued(_inner.get(), [=] {
_inner->stop(callback);
if (!callback) {
_inner->stop();
return;
}
_inner->stop([=](Result &&result) {
crl::on_main([=, result = std::move(result)]() mutable {
callback(std::move(result));
});
});
});
}
@@ -524,7 +532,7 @@ void Instance::Inner::timeout() {
return;
}
ALint samples;
alcGetIntegerv(d->device, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
alcGetIntegerv(d->device, ALC_CAPTURE_SAMPLES, 1, &samples);
if (ErrorHappened(d->device)) {
fail();
return;

View File

@@ -203,10 +203,14 @@ void Instance::setSession(not_null<Data*> data, Main::Session *session) {
) | rpl::start_with_next([=] {
setSession(data, nullptr);
}, data->sessionLifetime);
session->data().itemRemoved(
) | rpl::filter([=](not_null<const HistoryItem*> item) {
return (data->current.contextId() == item->fullId());
}) | rpl::start_with_next([=] {
stopAndClear(data);
}, data->sessionLifetime);
} else {
stop(data->type);
_tracksFinishedNotifier.notify(data->type);
*data = Data(data->type, data->overview);
stopAndClear(data);
}
}
@@ -527,6 +531,12 @@ void Instance::stop(AudioMsgId::Type type) {
}
}
void Instance::stopAndClear(not_null<Data*> data) {
stop(data->type);
_tracksFinishedNotifier.notify(data->type);
*data = Data(data->type, data->overview);
}
void Instance::playPause(AudioMsgId::Type type) {
if (const auto data = getData(type)) {
if (!data->streamed) {

View File

@@ -217,6 +217,7 @@ private:
void playlistUpdated(not_null<Data*> data);
bool moveInPlaylist(not_null<Data*> data, int delta, bool autonext);
HistoryItem *itemByIndex(not_null<Data*> data, int index);
void stopAndClear(not_null<Data*> data);
void handleStreamingUpdate(
not_null<Data*> data,

View File

@@ -345,11 +345,13 @@ OverlayWidget::OverlayWidget()
connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(onScreenResized(int)));
#if defined Q_OS_UNIX && !defined Q_OS_MAC
setWindowFlags(Qt::FramelessWindowHint | Qt::MaximizeUsingFullscreenGeometryHint);
#else // Q_OS_UNIX && !Q_OS_MAC
setWindowFlags(Qt::FramelessWindowHint);
#endif // Q_OS_UNIX && !Q_OS_MAC
if (Platform::IsLinux()) {
setWindowFlags(Qt::FramelessWindowHint
| Qt::WindowStaysOnTopHint
| Qt::MaximizeUsingFullscreenGeometryHint);
} else {
setWindowFlags(Qt::FramelessWindowHint);
}
moveToScreen();
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
@@ -1489,32 +1491,33 @@ void OverlayWidget::onForward() {
}
void OverlayWidget::onDelete() {
const auto session = _session;
if (!session) {
if (!_session) {
return;
}
close();
const auto deletingPeerPhoto = [this] {
const auto session = _session;
const auto photo = _photo;
const auto msgid = _msgid;
const auto deletingPeerPhoto = [&] {
if (!_msgid) {
return true;
}
if (_photo && _history) {
} else if (_photo && _history) {
if (_history->peer->userpicPhotoId() == _photo->id) {
return _firstOpenedPeerPhoto;
}
}
return false;
};
}();
close();
Core::App().domain().activate(&_session->account());
const auto &active = _session->windows();
Core::App().domain().activate(&session->account());
const auto &active = session->windows();
if (active.empty()) {
return;
}
if (deletingPeerPhoto()) {
active.front()->content()->deletePhotoLayer(_photo);
} else if (const auto item = session->data().message(_msgid)) {
if (deletingPeerPhoto) {
active.front()->content()->deletePhotoLayer(photo);
} else if (const auto item = session->data().message(msgid)) {
const auto suggestModerateActions = true;
Ui::show(Box<DeleteMessagesBox>(item, suggestModerateActions));
}
@@ -2279,11 +2282,11 @@ void OverlayWidget::displayFinished() {
updateControls();
if (isHidden()) {
Ui::Platform::UpdateOverlayed(this);
#if defined Q_OS_UNIX && !defined Q_OS_MAC
showFullScreen();
#else // Q_OS_UNIX && !Q_OS_MAC
show();
#endif // Q_OS_UNIX && !Q_OS_MAC
if (Platform::IsLinux()) {
showFullScreen();
} else {
show();
}
Ui::Platform::ShowOverAll(this);
activateWindow();
QApplication::setActiveWindow(this);
@@ -3671,6 +3674,14 @@ void OverlayWidget::setSession(not_null<Main::Session*> session) {
changingMsgId(change.item, change.oldId);
}, _sessionLifetime);
session->data().itemRemoved(
) | rpl::filter([=](not_null<const HistoryItem*> item) {
return (_document != nullptr || _photo != nullptr)
&& (item->fullId() == _msgid);
}) | rpl::start_with_next([=] {
close();
}, _sessionLifetime);
session->account().sessionChanges(
) | rpl::start_with_next([=] {
clearSession();

View File

@@ -277,6 +277,7 @@ inline QString GtkSetting(const gchar *propertyName) {
gchararray value = GtkSetting<gchararray>(propertyName);
QString str = QString::fromUtf8(value);
g_free(value);
DEBUG_LOG(("Getting GTK setting, %1: '%2'").arg(propertyName).arg(str));
return str;
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION

View File

@@ -39,6 +39,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtDBus/QDBusReply>
#include <QtDBus/QDBusError>
#include <QtDBus/QDBusMetaType>
extern "C" {
#undef signals
#include <gio/gio.h>
#define signals public
} // extern "C"
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include <glib.h>
@@ -51,10 +57,13 @@ constexpr auto kForcePanelIcon = "TDESKTOP_FORCE_PANEL_ICON"_cs;
constexpr auto kPanelTrayIconName = "telegram-panel"_cs;
constexpr auto kMutePanelTrayIconName = "telegram-mute-panel"_cs;
constexpr auto kAttentionPanelTrayIconName = "telegram-attention-panel"_cs;
constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
constexpr auto kTrayIconFilename = "tdesktop-trayicon-XXXXXX.png"_cs;
constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
constexpr auto kSNIWatcherObjectPath = "/StatusNotifierWatcher"_cs;
constexpr auto kSNIWatcherInterface = kSNIWatcherService;
constexpr auto kAppMenuService = "com.canonical.AppMenu.Registrar"_cs;
constexpr auto kAppMenuObjectPath = "/com/canonical/AppMenu/Registrar"_cs;
constexpr auto kAppMenuInterface = kAppMenuService;
@@ -65,9 +74,6 @@ base::flat_map<int, QImage> TrayIconImageBack;
QIcon TrayIcon;
QString TrayIconThemeName, TrayIconName;
bool SNIAvailable = false;
bool AppMenuSupported = false;
QString GetPanelIconName(int counter, bool muted) {
return (counter > 0)
? (muted
@@ -222,7 +228,13 @@ QIcon TrayIconGen(int counter, bool muted) {
iconImage.height() - layer.height() - 1,
layer);
} else {
App::wnd()->placeSmallCounter(iconImage, 16, counter, bg, QPoint(), fg);
App::wnd()->placeSmallCounter(
iconImage,
16,
counter,
bg,
QPoint(),
fg);
}
}
@@ -312,7 +324,7 @@ bool IsSNIAvailable() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
auto message = QDBusMessage::createMethodCall(
kSNIWatcherService.utf16(),
qsl("/StatusNotifierWatcher"),
kSNIWatcherObjectPath.utf16(),
kPropertiesInterface.utf16(),
qsl("Get"));
@@ -416,15 +428,26 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller)
}
void MainWindow::initHook() {
SNIAvailable = IsSNIAvailable();
const auto trayAvailable = SNIAvailable
|| QSystemTrayIcon::isSystemTrayAvailable();
LOG(("System tray available: %1").arg(Logs::b(trayAvailable)));
Platform::SetTrayIconSupported(trayAvailable);
_sniAvailable = IsSNIAvailable();
LOG(("System tray available: %1").arg(Logs::b(trayAvailable())));
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
_sniDBusProxy = g_dbus_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
nullptr,
kSNIWatcherService.utf8(),
kSNIWatcherObjectPath.utf8(),
kSNIWatcherInterface.utf8(),
nullptr,
nullptr);
g_signal_connect(
_sniDBusProxy,
"g-signal",
G_CALLBACK(sniSignalEmitted),
this);
auto sniWatcher = new QDBusServiceWatcher(
kSNIWatcherService.utf16(),
QDBusConnection::sessionBus(),
@@ -442,7 +465,7 @@ void MainWindow::initHook() {
handleSNIOwnerChanged(service, oldOwner, newOwner);
});
AppMenuSupported = IsAppMenuSupported();
_appMenuSupported = IsAppMenuSupported();
auto appMenuWatcher = new QDBusServiceWatcher(
kAppMenuService.utf16(),
@@ -461,7 +484,7 @@ void MainWindow::initHook() {
handleAppMenuOwnerChanged(service, oldOwner, newOwner);
});
if (AppMenuSupported) {
if (_appMenuSupported) {
LOG(("Using D-Bus global menu."));
} else {
LOG(("Not using D-Bus global menu."));
@@ -484,7 +507,7 @@ void MainWindow::initHook() {
bool MainWindow::hasTrayIcon() const {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
return trayIcon || (SNIAvailable && _sniTrayIcon);
return trayIcon || (_sniAvailable && _sniTrayIcon);
#else
return trayIcon;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -564,22 +587,50 @@ void MainWindow::attachToSNITrayIcon() {
updateTrayMenu();
}
void MainWindow::handleSNIOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner) {
SNIAvailable = !newOwner.isEmpty();
void MainWindow::sniSignalEmitted(
GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
MainWindow *window) {
if(signal_name == qstr("StatusNotifierHostRegistered")) {
window->handleSNIHostRegistered();
}
}
const auto trayAvailable = SNIAvailable
|| QSystemTrayIcon::isSystemTrayAvailable();
void MainWindow::handleSNIHostRegistered() {
if (_sniAvailable) {
return;
}
Platform::SetTrayIconSupported(trayAvailable);
_sniAvailable = true;
if (Global::WorkMode().value() == dbiwmWindowOnly) {
return;
}
if (oldOwner.isEmpty() && !newOwner.isEmpty()) {
LOG(("Switching to SNI tray icon..."));
if (trayIcon) {
trayIcon->setContextMenu(0);
trayIcon->deleteLater();
}
trayIcon = nullptr;
psSetupTrayIcon();
}
void MainWindow::handleSNIOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner) {
_sniAvailable = IsSNIAvailable();
if (Global::WorkMode().value() == dbiwmWindowOnly) {
return;
}
if (oldOwner.isEmpty() && !newOwner.isEmpty() && _sniAvailable) {
LOG(("Switching to SNI tray icon..."));
} else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
LOG(("Switching to Qt tray icon..."));
@@ -593,7 +644,7 @@ void MainWindow::handleSNIOwnerChanged(
}
trayIcon = nullptr;
if (trayAvailable) {
if (trayAvailable()) {
psSetupTrayIcon();
} else {
LOG(("System tray is not available."));
@@ -605,14 +656,14 @@ void MainWindow::handleAppMenuOwnerChanged(
const QString &oldOwner,
const QString &newOwner) {
if (oldOwner.isEmpty() && !newOwner.isEmpty()) {
AppMenuSupported = true;
_appMenuSupported = true;
LOG(("Using D-Bus global menu."));
} else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
AppMenuSupported = false;
_appMenuSupported = false;
LOG(("Not using D-Bus global menu."));
}
if (AppMenuSupported && !_mainMenuPath.path().isEmpty()) {
if (_appMenuSupported && !_mainMenuPath.path().isEmpty()) {
RegisterAppMenu(winId(), _mainMenuPath);
} else {
UnregisterAppMenu(winId());
@@ -624,7 +675,7 @@ void MainWindow::psSetupTrayIcon() {
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
if (SNIAvailable) {
if (_sniAvailable) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
LOG(("Using SNI tray icon."));
if (!_sniTrayIcon) {
@@ -654,7 +705,7 @@ void MainWindow::psSetupTrayIcon() {
}
void MainWindow::workmodeUpdated(DBIWorkMode mode) {
if (!Platform::TrayIconSupported()) {
if (!trayAvailable()) {
return;
} else if (mode == dbiwmWindowOnly) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -899,18 +950,7 @@ void MainWindow::createGlobalMenu() {
return;
}
Ui::show(
Box<PeerListBox>(std::make_unique<ContactsBoxController>(
sessionController()),
[](not_null<PeerListBox*> box) {
box->addButton(tr::lng_close(), [box] {
box->closeBox();
});
box->addLeftButton(tr::lng_profile_add_contact(), [] {
App::wnd()->onShowAddContact();
});
}));
Ui::show(PrepareContactsBox(sessionController()));
}));
psAddContact = tools->addAction(
@@ -953,7 +993,7 @@ void MainWindow::createGlobalMenu() {
_mainMenuPath.path(),
psMainMenu);
if (AppMenuSupported) {
if (_appMenuSupported) {
RegisterAppMenu(winId(), _mainMenuPath);
}
@@ -1074,7 +1114,7 @@ void MainWindow::updateGlobalMenuHook() {
}
void MainWindow::handleVisibleChangedHook(bool visible) {
if (AppMenuSupported && !_mainMenuPath.path().isEmpty()) {
if (_appMenuSupported && !_mainMenuPath.path().isEmpty()) {
if (visible) {
RegisterAppMenu(winId(), _mainMenuPath);
} else {
@@ -1089,12 +1129,14 @@ MainWindow::~MainWindow() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
delete _sniTrayIcon;
if (AppMenuSupported) {
if (_appMenuSupported) {
UnregisterAppMenu(winId());
}
delete _mainMenuExporter;
delete psMainMenu;
g_object_unref(_sniDBusProxy);
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
delete _trayIconMenuXEmbed;

View File

@@ -16,7 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QTemporaryFile>
#include <QtDBus/QDBusObjectPath>
#include <dbusmenuexporter.h>
#endif
typedef char gchar;
typedef struct _GVariant GVariant;
typedef struct _GDBusProxy GDBusProxy;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
namespace Platform {
@@ -33,6 +37,14 @@ public:
void psShowTrayMenu();
bool trayAvailable() {
return _sniAvailable || QSystemTrayIcon::isSystemTrayAvailable();
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void handleSNIHostRegistered();
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
static void LibsLoaded();
~MainWindow();
@@ -64,6 +76,7 @@ protected:
style::color color) = 0;
private:
bool _sniAvailable = false;
Ui::PopupMenu *_trayIconMenuXEmbed = nullptr;
void updateIconCounters();
@@ -71,8 +84,10 @@ private:
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
StatusNotifierItem *_sniTrayIcon = nullptr;
GDBusProxy *_sniDBusProxy = nullptr;
std::unique_ptr<QTemporaryFile> _trayIconFile = nullptr;
bool _appMenuSupported = false;
DBusMenuExporter *_mainMenuExporter = nullptr;
QDBusObjectPath _mainMenuPath;
@@ -124,6 +139,13 @@ private:
void psLinuxStrikeOut();
void psLinuxMonospace();
void psLinuxClearFormat();
static void sniSignalEmitted(
GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
MainWindow *window);
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
};

View File

@@ -244,7 +244,7 @@ public:
void notificationReplied(uint id, const QString &text);
private:
GDBusProxy *_dbusProxy;
GDBusConnection *_dbusConnection = nullptr;
base::weak_ptr<Manager> _manager;
QString _title;
@@ -255,14 +255,19 @@ private:
QImage _image;
uint _notificationId = 0;
guint _actionInvokedSignalId = 0;
guint _notificationRepliedSignalId = 0;
guint _notificationClosedSignalId = 0;
NotificationId _id;
static void signalEmitted(
GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
NotificationData *notificationData);
gpointer user_data);
};
@@ -275,20 +280,24 @@ NotificationData::NotificationData(
const QString &msg,
NotificationId id,
bool hideReplyButton)
: _dbusProxy(g_dbus_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
nullptr,
kService.utf8(),
kObjectPath.utf8(),
kInterface.utf8(),
nullptr,
nullptr))
, _manager(manager)
: _manager(manager)
, _title(title)
, _imageKey(GetImageKey(ParseSpecificationVersion(
GetServerInformation())))
, _id(id) {
GError *error = nullptr;
_dbusConnection = g_bus_get_sync(
G_BUS_TYPE_SESSION,
nullptr,
&error);
if (error) {
LOG(("Native notification error: %1").arg(error->message));
g_error_free(error);
return;
}
const auto capabilities = GetCapabilities();
if (capabilities.contains(qsl("body-markup"))) {
@@ -307,17 +316,43 @@ NotificationData::NotificationData(
_actions.push_back(qsl("default"));
_actions.push_back(QString());
_actions.push_back(qsl("mail-mark-read"));
_actions.push_back(tr::lng_context_mark_read(tr::now));
if (!hideReplyButton) {
_actions.push_back(qsl("mail-mark-read"));
_actions.push_back(tr::lng_context_mark_read(tr::now));
}
if (capabilities.contains(qsl("inline-reply")) && !hideReplyButton) {
_actions.push_back(qsl("inline-reply"));
_actions.push_back(tr::lng_notification_reply(tr::now));
_notificationRepliedSignalId = g_dbus_connection_signal_subscribe(
_dbusConnection,
kService.utf8(),
kInterface.utf8(),
"NotificationReplied",
kObjectPath.utf8(),
nullptr,
G_DBUS_SIGNAL_FLAGS_NONE,
signalEmitted,
this,
nullptr);
} else {
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
_actions.push_back(qsl("mail-reply-sender"));
_actions.push_back(tr::lng_notification_reply(tr::now));
}
_actionInvokedSignalId = g_dbus_connection_signal_subscribe(
_dbusConnection,
kService.utf8(),
kInterface.utf8(),
"ActionInvoked",
kObjectPath.utf8(),
nullptr,
G_DBUS_SIGNAL_FLAGS_NONE,
signalEmitted,
this,
nullptr);
}
if (capabilities.contains(qsl("action-icons"))) {
@@ -351,11 +386,35 @@ NotificationData::NotificationData(
qsl("desktop-entry"),
g_variant_new_string(GetLauncherBasename().toUtf8()));
g_signal_connect(_dbusProxy, "g-signal", G_CALLBACK(signalEmitted), this);
_notificationClosedSignalId = g_dbus_connection_signal_subscribe(
_dbusConnection,
kService.utf8(),
kInterface.utf8(),
"NotificationClosed",
kObjectPath.utf8(),
nullptr,
G_DBUS_SIGNAL_FLAGS_NONE,
signalEmitted,
this,
nullptr);
}
NotificationData::~NotificationData() {
g_object_unref(_dbusProxy);
if (_dbusConnection) {
if (_actionInvokedSignalId != 0) {
g_dbus_connection_signal_unsubscribe(_dbusConnection, _actionInvokedSignalId);
}
if (_notificationRepliedSignalId != 0) {
g_dbus_connection_signal_unsubscribe(_dbusConnection, _notificationRepliedSignalId);
}
if (_notificationClosedSignalId != 0) {
g_dbus_connection_signal_unsubscribe(_dbusConnection, _notificationClosedSignalId);
}
g_object_unref(_dbusConnection);
}
for (const auto &[key, value] : _hints) {
if (value) {
@@ -391,8 +450,11 @@ bool NotificationData::show() {
? GetIconName()
: QString();
auto reply = g_dbus_proxy_call_sync(
_dbusProxy,
auto reply = g_dbus_connection_call_sync(
_dbusConnection,
kService.utf8(),
kObjectPath.utf8(),
kInterface.utf8(),
"Notify",
g_variant_new(
kNotifyArgsType.utf8(),
@@ -404,6 +466,7 @@ bool NotificationData::show() {
&actionsBuilder,
&hintsBuilder,
-1),
nullptr,
G_DBUS_CALL_FLAGS_NONE,
kDBusTimeout,
nullptr,
@@ -423,10 +486,14 @@ bool NotificationData::show() {
}
void NotificationData::close() {
g_dbus_proxy_call(
_dbusProxy,
g_dbus_connection_call(
_dbusConnection,
kService.utf8(),
kObjectPath.utf8(),
kInterface.utf8(),
"CloseNotification",
g_variant_new("(u)", _notificationId),
nullptr,
G_DBUS_CALL_FLAGS_NONE,
-1,
nullptr,
@@ -463,11 +530,20 @@ void NotificationData::setImage(const QString &imagePath) {
}
void NotificationData::signalEmitted(
GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
NotificationData *notificationData) {
gpointer user_data) {
const auto notificationData = reinterpret_cast<NotificationData*>(
user_data);
if (!notificationData) {
return;
}
if(signal_name == qstr("ActionInvoked")) {
guint32 id;
gchar *actionName;

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_libs.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_xcb_utilities_linux.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -25,7 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QProcess>
#include <QtCore/QVersionNumber>
#include <QtGui/QWindow>
#include <qpa/qplatformnativeinterface.h>
#include <private/qwaylanddisplay_p.h>
#include <private/qwaylandwindow_p.h>
@@ -85,8 +85,6 @@ constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs;
QStringList PlatformThemes;
bool IsTrayIconSupported = false;
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void PortalAutostart(bool autostart, bool silent = false) {
if (cExeName().isEmpty()) {
@@ -125,6 +123,14 @@ void PortalAutostart(bool autostart, bool silent = false) {
}
}
bool IsXDGDesktopPortalKDEPresent() {
static const auto Result = QDBusInterface(
qsl("org.freedesktop.impl.portal.desktop.kde"),
kXDGDesktopPortalObjectPath.utf16()).isValid();
return Result;
}
uint FileChooserPortalVersion() {
static const auto Result = [&]() -> uint {
auto message = QDBusMessage::createMethodCall(
@@ -303,128 +309,26 @@ bool GetImageFromClipboardSupported() {
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
std::optional<xcb_atom_t> GetXCBAtom(
xcb_connection_t *connection,
const QString &name) {
const auto cookie = xcb_intern_atom(
connection,
0,
name.size(),
name.toUtf8());
auto reply = xcb_intern_atom_reply(
connection,
cookie,
nullptr);
if (!reply) {
return std::nullopt;
}
const auto atom = reply->atom;
free(reply);
return atom;
}
bool IsXCBExtensionPresent(
xcb_connection_t *connection,
xcb_extension_t *ext) {
const auto reply = xcb_get_extension_data(
connection,
ext);
if (!reply) {
return false;
}
return reply->present;
}
std::vector<xcb_atom_t> GetXCBWMSupported(xcb_connection_t *connection) {
auto netWmAtoms = std::vector<xcb_atom_t>{};
const auto native = QGuiApplication::platformNativeInterface();
if (!native) {
return netWmAtoms;
}
const auto root = static_cast<xcb_window_t>(reinterpret_cast<quintptr>(
native->nativeResourceForIntegration(QByteArray("rootwindow"))));
const auto supportedAtom = GetXCBAtom(connection, "_NET_SUPPORTED");
if (!supportedAtom.has_value()) {
return netWmAtoms;
}
auto offset = 0;
auto remaining = 0;
do {
const auto cookie = xcb_get_property(
connection,
false,
root,
*supportedAtom,
XCB_ATOM_ATOM,
offset,
1024);
auto reply = xcb_get_property_reply(
connection,
cookie,
nullptr);
if (!reply) {
break;
}
remaining = 0;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32) {
const auto len = xcb_get_property_value_length(reply)
/ sizeof(xcb_atom_t);
const auto atoms = reinterpret_cast<xcb_atom_t*>(
xcb_get_property_value(reply));
const auto s = netWmAtoms.size();
netWmAtoms.resize(s + len);
memcpy(netWmAtoms.data() + s, atoms, len * sizeof(xcb_atom_t));
remaining = reply->bytes_after;
offset += len;
}
free(reply);
} while (remaining > 0);
return netWmAtoms;
}
std::optional<crl::time> XCBLastUserInputTime() {
const auto native = QGuiApplication::platformNativeInterface();
if (!native) {
return std::nullopt;
}
const auto connection = reinterpret_cast<xcb_connection_t*>(
native->nativeResourceForIntegration(QByteArray("connection")));
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return std::nullopt;
}
if (!IsXCBExtensionPresent(connection, &xcb_screensaver_id)) {
if (!base::Platform::XCB::IsExtensionPresent(
connection,
&xcb_screensaver_id)) {
return std::nullopt;
}
const auto root = static_cast<xcb_window_t>(reinterpret_cast<quintptr>(
native->nativeResourceForIntegration(QByteArray("rootwindow"))));
const auto root = base::Platform::XCB::GetRootWindowFromQt();
if (!root.has_value()) {
return std::nullopt;
}
const auto cookie = xcb_screensaver_query_info(
connection,
root);
*root);
auto reply = xcb_screensaver_query_info_reply(
connection,
@@ -575,27 +479,21 @@ enum wl_shell_surface_resize WlResizeFromEdges(Qt::Edges edges) {
#endif // Qt < 5.13 && !DESKTOP_APP_QT_PATCHED
bool StartXCBMoveResize(QWindow *window, int edges) {
const auto native = QGuiApplication::platformNativeInterface();
if (!native) {
return false;
}
const auto connection = reinterpret_cast<xcb_connection_t*>(
native->nativeResourceForIntegration(QByteArray("connection")));
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return false;
}
const auto screen = xcb_setup_roots_iterator(
xcb_get_setup(connection)).data;
if (!screen) {
const auto root = base::Platform::XCB::GetRootWindowFromQt();
if (!root.has_value()) {
return false;
}
const auto moveResize = GetXCBAtom(connection, "_NET_WM_MOVERESIZE");
if (!moveResize.has_value()) {
const auto moveResizeAtom = base::Platform::XCB::GetAtom(
connection,
"_NET_WM_MOVERESIZE");
if (!moveResizeAtom.has_value()) {
return false;
}
@@ -603,7 +501,7 @@ bool StartXCBMoveResize(QWindow *window, int edges) {
xcb_client_message_event_t xev;
xev.response_type = XCB_CLIENT_MESSAGE;
xev.type = *moveResize;
xev.type = *moveResizeAtom;
xev.sequence = 0;
xev.window = window->winId();
xev.format = 32;
@@ -619,7 +517,7 @@ bool StartXCBMoveResize(QWindow *window, int edges) {
xcb_send_event(
connection,
false,
screen->root,
*root,
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
| XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
reinterpret_cast<const char*>(&xev));
@@ -681,19 +579,12 @@ bool ShowWaylandWindowMenu(QWindow *window) {
}
bool XCBFrameExtentsSupported() {
const auto native = QGuiApplication::platformNativeInterface();
if (!native) {
return false;
}
const auto connection = reinterpret_cast<xcb_connection_t*>(
native->nativeResourceForIntegration(QByteArray("connection")));
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return false;
}
const auto frameExtentsAtom = GetXCBAtom(
const auto frameExtentsAtom = base::Platform::XCB::GetAtom(
connection,
kXCBFrameExtentsAtomName.utf16());
@@ -701,23 +592,18 @@ bool XCBFrameExtentsSupported() {
return false;
}
return ranges::contains(GetXCBWMSupported(connection), *frameExtentsAtom);
return ranges::contains(
base::Platform::XCB::GetWMSupported(connection),
*frameExtentsAtom);
}
bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) {
const auto native = QGuiApplication::platformNativeInterface();
if (!native) {
return false;
}
const auto connection = reinterpret_cast<xcb_connection_t*>(
native->nativeResourceForIntegration(QByteArray("connection")));
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return false;
}
const auto frameExtentsAtom = GetXCBAtom(
const auto frameExtentsAtom = base::Platform::XCB::GetAtom(
connection,
kXCBFrameExtentsAtomName.utf16());
@@ -746,19 +632,12 @@ bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) {
}
bool UnsetXCBFrameExtents(QWindow *window) {
const auto native = QGuiApplication::platformNativeInterface();
if (!native) {
return false;
}
const auto connection = reinterpret_cast<xcb_connection_t*>(
native->nativeResourceForIntegration(QByteArray("connection")));
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return false;
}
const auto frameExtentsAtom = GetXCBAtom(
const auto frameExtentsAtom = base::Platform::XCB::GetAtom(
connection,
kXCBFrameExtentsAtomName.utf16());
@@ -860,17 +739,20 @@ bool IsXDGDesktopPortalPresent() {
}
bool UseXDGDesktopPortal() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
static const auto Result = [&] {
const auto envVar = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL");
const auto portalPresent = IsXDGDesktopPortalPresent();
const auto neededForKde = DesktopEnvironment::IsKDE()
&& IsXDGDesktopPortalKDEPresent();
return (
DesktopEnvironment::IsKDE()
|| envVar
) && portalPresent;
return (neededForKde || envVar) && portalPresent;
}();
return Result;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
return false;
}
bool CanOpenDirectoryWithPortal() {
@@ -928,10 +810,15 @@ QString AppRuntimeDirectory() {
}
QString SingleInstanceLocalServerName(const QString &hash) {
if (InFlatpak() || InSnap()) {
const auto idealSocketPath = AppRuntimeDirectory()
+ hash
+ '-'
+ cGUIDStr();
if (idealSocketPath.size() > 108) {
return AppRuntimeDirectory() + hash;
} else {
return AppRuntimeDirectory() + hash + '-' + cGUIDStr();
return idealSocketPath;
}
}
@@ -1013,7 +900,10 @@ QImage GetImageFromClipboard() {
std::optional<crl::time> LastUserInputTime() {
if (!IsWayland()) {
return XCBLastUserInputTime();
const auto xcbResult = XCBLastUserInputTime();
if (xcbResult.has_value()) {
return xcbResult;
}
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -1063,11 +953,9 @@ bool AutostartSupported() {
}
bool TrayIconSupported() {
return IsTrayIconSupported;
}
void SetTrayIconSupported(bool supported) {
IsTrayIconSupported = supported;
return App::wnd()
? App::wnd()->trayAvailable()
: false;
}
bool StartSystemMove(QWindow *window) {
@@ -1376,7 +1264,7 @@ void start() {
void finish() {
}
void InstallMainDesktopFile() {
void InstallLauncher() {
static const auto DisabledByEnv = qEnvironmentVariableIsSet(
"TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION");
@@ -1420,13 +1308,13 @@ void RegisterCustomScheme(bool force) {
GError *error = nullptr;
const auto actualCommandlineBuilder = qsl("%1 --")
const auto neededCommandlineBuilder = qsl("%1 --")
.arg((IsStaticBinary() || InAppImage())
? cExeDir() + cExeName()
: cExeName());
const auto actualCommandline = qsl("%1 %u")
.arg(actualCommandlineBuilder);
const auto neededCommandline = qsl("%1 %u")
.arg(neededCommandlineBuilder);
auto currentAppInfo = g_app_info_get_default_for_type(
kHandlerTypeName.utf8(),
@@ -1438,13 +1326,36 @@ void RegisterCustomScheme(bool force) {
g_object_unref(currentAppInfo);
if (currentCommandline == actualCommandline) {
if (currentCommandline == neededCommandline) {
return;
}
}
auto registeredAppInfoList = g_app_info_get_recommended_for_type(
kHandlerTypeName.utf8());
for (auto l = registeredAppInfoList; l != nullptr; l = l->next) {
const auto currentRegisteredAppInfo = reinterpret_cast<GAppInfo*>(
l->data);
const auto currentAppInfoId = QString(
g_app_info_get_id(currentRegisteredAppInfo));
const auto currentCommandline = QString(
g_app_info_get_commandline(currentRegisteredAppInfo));
if (currentCommandline == neededCommandline
&& currentAppInfoId.startsWith(qsl("userapp-"))) {
g_app_info_delete(currentRegisteredAppInfo);
}
}
if (registeredAppInfoList) {
g_list_free_full(registeredAppInfoList, g_object_unref);
}
auto newAppInfo = g_app_info_create_from_commandline(
actualCommandlineBuilder.toUtf8(),
neededCommandlineBuilder.toUtf8(),
AppName.utf8(),
G_APP_INFO_CREATE_SUPPORTS_URIS,
&error);
@@ -1521,7 +1432,7 @@ void finish() {
} // namespace Platform
void psNewVersion() {
Platform::InstallMainDesktopFile();
Platform::InstallLauncher();
Platform::RegisterCustomScheme();
}

View File

@@ -42,8 +42,7 @@ QString GetIconName();
inline void IgnoreApplicationActivationRightNow() {
}
void SetTrayIconSupported(bool supported);
void InstallMainDesktopFile();
void InstallLauncher();
} // namespace Platform

View File

@@ -721,10 +721,7 @@ void MainWindow::createGlobalMenu() {
if (!sessionController()) {
return;
}
Ui::show(Box<PeerListBox>(std::make_unique<ContactsBoxController>(sessionController()), [](not_null<PeerListBox*> box) {
box->addButton(tr::lng_close(), [box] { box->closeBox(); });
box->addLeftButton(tr::lng_profile_add_contact(), [] { App::wnd()->onShowAddContact(); });
}));
Ui::show(PrepareContactsBox(sessionController()));
}));
psAddContact = window->addAction(tr::lng_mac_menu_add_contact(tr::now), App::wnd(), SLOT(onShowAddContact()));
window->addSeparator();

View File

@@ -35,8 +35,29 @@ bool IsCompositionEnabled() {
return success && result;
}
bool IsTaskbarAutoHidden(PUINT pEdge = nullptr) {
HWND hTaskbar = FindWindowW(L"Shell_TrayWnd", nullptr);
HWND FindTaskbarWindow(LPRECT rcMon = nullptr) {
HWND hTaskbar = nullptr;
RECT rcTaskbar, rcMatch;
while ((hTaskbar = FindWindowEx(
nullptr,
hTaskbar,
L"Shell_TrayWnd",
nullptr)) != nullptr) {
if (!rcMon) {
break; // OK, return first found
}
if (GetWindowRect(hTaskbar, &rcTaskbar)
&& IntersectRect(&rcMatch, &rcTaskbar, rcMon)) {
break; // OK, taskbar match monitor
}
}
return hTaskbar;
}
bool IsTaskbarAutoHidden(LPRECT rcMon = nullptr, PUINT pEdge = nullptr) {
HWND hTaskbar = FindTaskbarWindow(rcMon);
if (!hTaskbar) {
if (pEdge) {
*pEdge = (UINT)-1;
@@ -134,7 +155,7 @@ bool EventFilter::customWindowFrameEvent(
if (GetMonitorInfo(hMonitor, &mi)) {
*r = mi.rcWork;
UINT uEdge = (UINT)-1;
if (IsTaskbarAutoHidden(&uEdge)) {
if (IsTaskbarAutoHidden(&mi.rcMonitor, &uEdge)) {
switch (uEdge) {
case ABE_LEFT: r->left += 1; break;
case ABE_RIGHT: r->right -= 1; break;

View File

@@ -62,11 +62,4 @@ Q_IMPORT_PLUGIN(QHimePlatformInputContextPlugin)
Q_IMPORT_PLUGIN(Qt5CTPlatformThemePlugin)
Q_IMPORT_PLUGIN(Qt5CTStylePlugin)
#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES
// conflicts with Qt static link
#ifdef DESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
Q_IMPORT_PLUGIN(LXQtPlatformThemePlugin)
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#endif // DESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES
#endif // Q_OS_UNIX && !Q_OS_MAC

View File

@@ -861,10 +861,7 @@ void MainMenu::refreshMenu() {
App::wnd()->onShowNewChannel();
}, &st::mainMenuNewChannel, &st::mainMenuNewChannelOver);
_menu->addAction(tr::lng_menu_contacts(tr::now), [=] {
Ui::show(Box<PeerListBox>(std::make_unique<ContactsBoxController>(controller), [](not_null<PeerListBox*> box) {
box->addButton(tr::lng_close(), [box] { box->closeBox(); });
box->addLeftButton(tr::lng_profile_add_contact(), [] { App::wnd()->onShowAddContact(); });
}));
Ui::show(PrepareContactsBox(controller));
}, &st::mainMenuContacts, &st::mainMenuContactsOver);
if (_controller->session().serverConfig().phoneCallsEnabled.current()) {
_menu->addAction(tr::lng_menu_calls(tr::now), [=] {

View File

@@ -1,7 +1,7 @@
AppVersion 2004002
AppVersion 2004004
AppVersionStrMajor 2.4
AppVersionStrSmall 2.4.2
AppVersionStr 2.4.2
AppVersionStrSmall 2.4.4
AppVersionStr 2.4.4
BetaChannel 0
AlphaVersion 0
AppVersionOriginal 2.4.2
AppVersionOriginal 2.4.4

View File

@@ -827,7 +827,7 @@ if (NOT TGVOIP_FOUND)
if (LINUX)
find_package(PkgConfig REQUIRED)
pkg_check_modules(ALSA REQUIRED alsa)
find_package(ALSA REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
target_include_directories(lib_tgvoip_bundled

View File

@@ -1,3 +1,15 @@
2.4.4 (23.10.20)
- Fix application quit on call end with main window hidden in tray.
- Update OpenAL library on Windows.
- Several crash fixes.
2.4.3 (07.10.20)
- Fix sending voice messages in scheduled messages section.
- Fix deleting profile / group / channel photos.
- Several crash fixes.
2.4.2 (02.10.20)
- Allow block, report and delete all message from user from "user joined" service message context menu.

2
cmake

Submodule cmake updated: 5d6f8ebee3...cfc6051fb6

View File

@@ -67,7 +67,7 @@ Go to ***BuildPath*** and run
sudo make install
cd ..
git clone https://github.com/01org/libva.git
git clone https://github.com/intel/libva.git
cd libva
CFLAGS=-fPIC CPPFLAGS=-fPIC LDFLAGS=-fPIC ./autogen.sh --enable-static
make $MAKE_THREADS_CNT

View File

@@ -102,14 +102,13 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
git clone https://github.com/telegramdesktop/openal-soft.git
cd openal-soft
git checkout fix_capture
git checkout fix_mono
cd build
cmake .. ^
-G "Visual Studio 16 2019" ^
-A Win32 ^
-D LIBTYPE:STRING=STATIC ^
-D FORCE_STATIC_VCRT=ON ^
-D ALSOFT_BACKEND_WASAPI=OFF
-D FORCE_STATIC_VCRT=ON
msbuild OpenAL.vcxproj /property:Configuration=Debug
msbuild OpenAL.vcxproj /property:Configuration=Release
cd ..\..

View File

@@ -29,7 +29,7 @@ apps:
- desktop-legacy
- home
- network
- network-manager
- network-manager-observe
- opengl
- pulseaudio
- removable-media