Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ede4c0f781 | ||
|
|
75337ad1c2 | ||
|
|
e05b2f3b38 | ||
|
|
0f15adb208 | ||
|
|
a7e552ccab | ||
|
|
797433ebe9 | ||
|
|
633532b88d | ||
|
|
a0dcade9d8 | ||
|
|
2a0babe5ab | ||
|
|
dd92f7fb9d | ||
|
|
7ad08b3ef8 | ||
|
|
73917e8a4b | ||
|
|
dc7aef3f86 | ||
|
|
d89597bf64 | ||
|
|
c2b2d0a92a | ||
|
|
b341dddbb9 | ||
|
|
999e4264c5 | ||
|
|
aee11469c4 | ||
|
|
651cfe5b7e | ||
|
|
a34b2a5472 | ||
|
|
5e7e7eaa83 | ||
|
|
a8f05a01ed | ||
|
|
fedd21b0a6 | ||
|
|
7ff7473db6 | ||
|
|
d89aab08bf | ||
|
|
b2fb5424ed | ||
|
|
e7e34d50ba | ||
|
|
19320cc5d8 | ||
|
|
107a87c7ce | ||
|
|
2cb1d2c0bc | ||
|
|
48ab88a9ca | ||
|
|
33b7ac209e | ||
|
|
4a0ffdc9f5 | ||
|
|
1ec2c16d27 | ||
|
|
4b03fd0f23 | ||
|
|
9f117cd680 | ||
|
|
f6d29991d6 | ||
|
|
1a0d430291 | ||
|
|
e3b9927faa | ||
|
|
d199e16a6e | ||
|
|
01c2be3f01 | ||
|
|
6db537d1ec | ||
|
|
e708b2d39c | ||
|
|
ebd9587821 | ||
|
|
9e5117d336 | ||
|
|
1c2ea8d84a | ||
|
|
235484b719 | ||
|
|
b9ea5718a2 | ||
|
|
db0c57a186 | ||
|
|
0fa458737a | ||
|
|
caaeff32c5 | ||
|
|
c4c234f0d3 | ||
|
|
894e7c5828 | ||
|
|
afcebb136c | ||
|
|
8592326a3c |
6
.github/workflows/linux.yml
vendored
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- name: Check.
|
||||
run: |
|
||||
filePath="$REPO_NAME/out/Debug/bin/Telegram"
|
||||
filePath="$REPO_NAME/out/Debug/Telegram"
|
||||
if test -f "$filePath"; then
|
||||
echo "Build successfully done! :)"
|
||||
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: Move artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
run: |
|
||||
cd $REPO_NAME/out/Debug/bin
|
||||
cd $REPO_NAME/out/Debug
|
||||
mkdir artifact
|
||||
mv Telegram artifact/
|
||||
- uses: actions/upload-artifact@master
|
||||
@@ -129,4 +129,4 @@ jobs:
|
||||
name: Upload artifact.
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.REPO_NAME }}/out/Debug/bin/artifact/
|
||||
path: ${{ env.REPO_NAME }}/out/Debug/artifact/
|
||||
|
||||
6
.gitmodules
vendored
@@ -94,3 +94,9 @@
|
||||
[submodule "Telegram/ThirdParty/jemalloc"]
|
||||
path = Telegram/ThirdParty/jemalloc
|
||||
url = https://github.com/jemalloc/jemalloc
|
||||
[submodule "Telegram/ThirdParty/kwayland"]
|
||||
path = Telegram/ThirdParty/kwayland
|
||||
url = https://github.com/KDE/kwayland.git
|
||||
[submodule "Telegram/ThirdParty/dispatch"]
|
||||
path = Telegram/ThirdParty/dispatch
|
||||
url = https://github.com/apple/swift-corelibs-libdispatch
|
||||
|
||||
@@ -1407,7 +1407,7 @@ PRIVATE
|
||||
G_LOG_DOMAIN="Telegram"
|
||||
)
|
||||
|
||||
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode"
|
||||
if (APPLE
|
||||
OR "${CMAKE_GENERATOR}" STREQUAL "Ninja Multi-Config"
|
||||
OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL ""
|
||||
OR NOT "${output_name}" STREQUAL "Telegram")
|
||||
@@ -1526,8 +1526,8 @@ endif()
|
||||
|
||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
include(GNUInstallDirs)
|
||||
configure_file("../lib/xdg/telegramdesktop.appdata.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml" @ONLY)
|
||||
generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml")
|
||||
configure_file("../lib/xdg/telegramdesktop.metainfo.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" @ONLY)
|
||||
generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml")
|
||||
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "telegram.png")
|
||||
@@ -1537,5 +1537,5 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png")
|
||||
install(FILES "../lib/xdg/telegramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.desktop")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.appdata.xml")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.metainfo.xml")
|
||||
endif()
|
||||
|
||||
|
Before Width: | Height: | Size: 107 B |
|
Before Width: | Height: | Size: 126 B |
|
Before Width: | Height: | Size: 174 B |
BIN
Telegram/Resources/icons/contacts_alphabet.png
Normal file
|
After Width: | Height: | Size: 448 B |
BIN
Telegram/Resources/icons/contacts_alphabet@2x.png
Normal file
|
After Width: | Height: | Size: 784 B |
BIN
Telegram/Resources/icons/contacts_alphabet@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/contacts_online.png
Normal file
|
After Width: | Height: | Size: 385 B |
BIN
Telegram/Resources/icons/contacts_online@2x.png
Normal file
|
After Width: | Height: | Size: 649 B |
BIN
Telegram/Resources/icons/contacts_online@3x.png
Normal file
|
After Width: | Height: | Size: 1014 B |
|
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 277 B |
|
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
|
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 715 B |
|
Before Width: | Height: | Size: 173 B After Width: | Height: | Size: 173 B |
|
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 197 B After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 273 B |
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
|
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 253 B |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
|
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 494 B |
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 578 B |
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 285 B |
|
Before Width: | Height: | Size: 444 B After Width: | Height: | Size: 444 B |
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 598 B |
|
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 128 B |
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 247 B |
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
|
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
|
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 170 B |
|
Before Width: | Height: | Size: 362 B After Width: | Height: | Size: 362 B |
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 387 B |
|
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 638 B |
|
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 907 B |
|
Before Width: | Height: | Size: 158 B After Width: | Height: | Size: 158 B |
|
Before Width: | Height: | Size: 309 B After Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 367 B |
|
Before Width: | Height: | Size: 1013 B After Width: | Height: | Size: 1013 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 261 B |
|
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 578 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_verified_check.png
Normal file
|
After Width: | Height: | Size: 233 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_verified_check@2x.png
Normal file
|
After Width: | Height: | Size: 365 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_verified_check@3x.png
Normal file
|
After Width: | Height: | Size: 425 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_verified_star.png
Normal file
|
After Width: | Height: | Size: 371 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_verified_star@2x.png
Normal file
|
After Width: | Height: | Size: 714 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_verified_star@3x.png
Normal file
|
After Width: | Height: | Size: 803 B |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 281 B |
|
Before Width: | Height: | Size: 404 B |
|
Before Width: | Height: | Size: 342 B |
|
Before Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 694 B |
@@ -83,9 +83,6 @@
|
||||
<file alias="settings/devices/device_web_firefox.lottie">../../icons/settings/devices/device_web_firefox.lottie</file>
|
||||
<file alias="settings/devices/device_web_safari.lottie">../../icons/settings/devices/device_web_safari.lottie</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qt-project.org">
|
||||
<file>../qmime/freedesktop.org.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/misc">
|
||||
<file alias="default_shortcuts-custom.json">../../default_shortcuts-custom.json</file>
|
||||
<file alias="telegramdesktop.desktop">../../../../lib/xdg/telegramdesktop.desktop</file>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="3.2.8.0" />
|
||||
Version="3.3.1.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,2,8,0
|
||||
PRODUCTVERSION 3,2,8,0
|
||||
FILEVERSION 3,3,1,0
|
||||
PRODUCTVERSION 3,3,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "3.2.8.0"
|
||||
VALUE "FileVersion", "3.3.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "3.2.8.0"
|
||||
VALUE "ProductVersion", "3.3.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,2,8,0
|
||||
PRODUCTVERSION 3,2,8,0
|
||||
FILEVERSION 3,3,1,0
|
||||
PRODUCTVERSION 3,3,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "3.2.8.0"
|
||||
VALUE "FileVersion", "3.3.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "3.2.8.0"
|
||||
VALUE "ProductVersion", "3.3.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
@@ -98,6 +97,11 @@ bool copyFile(const char *from, const char *to, bool writeprotected) {
|
||||
fclose(ffrom);
|
||||
return false;
|
||||
}
|
||||
static const int BufSize = 65536;
|
||||
char buf[BufSize];
|
||||
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
|
||||
fwrite(buf, 1, size, fto);
|
||||
}
|
||||
|
||||
struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
|
||||
//let's say this wont fail since you already worked OK on that fp
|
||||
@@ -106,33 +110,6 @@ bool copyFile(const char *from, const char *to, bool writeprotected) {
|
||||
fclose(fto);
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t copied = sendfile(
|
||||
fileno(fto),
|
||||
fileno(ffrom),
|
||||
nullptr,
|
||||
fst.st_size);
|
||||
|
||||
if (copied == -1) {
|
||||
writeLog(
|
||||
"Copy by sendfile '%s' to '%s' failed, error: %d, fallback now.",
|
||||
from,
|
||||
to,
|
||||
int(errno));
|
||||
static const int BufSize = 65536;
|
||||
char buf[BufSize];
|
||||
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
|
||||
fwrite(buf, 1, size, fto);
|
||||
}
|
||||
} else {
|
||||
writeLog(
|
||||
"Copy by sendfile '%s' to '%s' done, size: %d, result: %d.",
|
||||
from,
|
||||
to,
|
||||
int(fst.st_size),
|
||||
int(copied));
|
||||
}
|
||||
|
||||
//update to the same uid/gid
|
||||
if (!writeprotected && fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
|
||||
fclose(ffrom);
|
||||
|
||||
@@ -118,7 +118,7 @@ void PeerPhoto::upload(not_null<PeerData*> peer, QImage &&image) {
|
||||
std::move(image));
|
||||
|
||||
const auto fakeId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
peer->id,
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto already = ranges::find(
|
||||
_uploads,
|
||||
|
||||
@@ -74,7 +74,7 @@ void SendExistingMedia(
|
||||
api->sendAction(message.action);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
peer->id,
|
||||
session->data().nextLocalMessageId());
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
|
||||
@@ -255,7 +255,7 @@ bool SendDice(MessageToSend &message) {
|
||||
api->sendAction(message.action);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
peer->id,
|
||||
session->data().nextLocalMessageId());
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
|
||||
@@ -346,10 +346,8 @@ void SendConfirmedFile(
|
||||
const std::shared_ptr<FileLoadResult> &file) {
|
||||
const auto isEditing = (file->type != SendMediaType::Audio)
|
||||
&& (file->to.replaceMediaOf != 0);
|
||||
const auto channelId = peerToChannel(file->to.peer);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
channelId,
|
||||
file->to.peer,
|
||||
isEditing
|
||||
? file->to.replaceMediaOf
|
||||
: session->data().nextLocalMessageId());
|
||||
|
||||
@@ -87,7 +87,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
|
||||
Expects(!_requestKey.empty());
|
||||
|
||||
const auto postId = _requestKey.postId;
|
||||
if (const auto item = _session->data().message(channel, postId)) {
|
||||
if (const auto item = _session->data().message(channel->id, postId)) {
|
||||
_cache.emplace(_requestKey, item->fullId());
|
||||
return item;
|
||||
} else if (!ready) {
|
||||
@@ -112,7 +112,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
|
||||
&& received.messageIds.front() == postId) {
|
||||
_cache.emplace(
|
||||
_requestKey,
|
||||
FullMsgId(peerToChannel(channel->id), postId));
|
||||
FullMsgId(channel->id, postId));
|
||||
ready();
|
||||
} else {
|
||||
fail();
|
||||
|
||||
@@ -730,7 +730,7 @@ void Updates::addActiveChat(rpl::producer<PeerData*> chat) {
|
||||
}
|
||||
|
||||
void Updates::requestChannelRangeDifference(not_null<History*> history) {
|
||||
Expects(history->isChannel());
|
||||
Expects(history->peer->isChannel());
|
||||
|
||||
const auto channel = history->peer->asChannel();
|
||||
if (const auto requestId = _rangeDifferenceRequests.take(channel)) {
|
||||
@@ -1181,7 +1181,7 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
const auto &d = update.c_updateReadMessagesContents();
|
||||
auto possiblyReadMentions = base::flat_set<MsgId>();
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
if (const auto item = _session->data().message(NoChannel, msgId.v)) {
|
||||
if (const auto item = _session->data().nonChannelMessage(msgId.v)) {
|
||||
if (item->isUnreadMedia() || item->isUnreadMention()) {
|
||||
item->markMediaRead();
|
||||
_session->data().requestItemRepaint(item);
|
||||
@@ -1249,7 +1249,7 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateDeleteMessages: {
|
||||
auto &d = update.c_updateDeleteMessages();
|
||||
_session->data().processMessagesDeleted(NoChannel, d.vmessages().v);
|
||||
_session->data().processNonChannelMessagesDeleted(d.vmessages().v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateNewChannelMessage: {
|
||||
@@ -1278,9 +1278,9 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updatePinnedChannelMessages: {
|
||||
const auto &d = update.c_updatePinnedChannelMessages();
|
||||
const auto channelId = d.vchannel_id().v;
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
const auto item = session().data().message(channelId, msgId.v);
|
||||
const auto item = session().data().message(peerId, msgId.v);
|
||||
if (item) {
|
||||
item->setIsPinned(d.is_pinned());
|
||||
}
|
||||
@@ -1299,13 +1299,16 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateDeleteChannelMessages: {
|
||||
auto &d = update.c_updateDeleteChannelMessages();
|
||||
_session->data().processMessagesDeleted(d.vchannel_id().v, d.vmessages().v);
|
||||
_session->data().processMessagesDeleted(
|
||||
peerFromChannel(d.vchannel_id().v),
|
||||
d.vmessages().v);
|
||||
} break;
|
||||
|
||||
case mtpc_updatePinnedMessages: {
|
||||
const auto &d = update.c_updatePinnedMessages();
|
||||
const auto peerId = peerFromMTP(d.vpeer());
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
const auto item = session().data().message(0, msgId.v);
|
||||
const auto item = session().data().message(peerId, msgId.v);
|
||||
if (item) {
|
||||
item->setIsPinned(d.is_pinned());
|
||||
}
|
||||
@@ -1424,7 +1427,7 @@ void Updates::applyUpdates(
|
||||
const auto sent = owner.messageSentData(randomId);
|
||||
const auto lookupMessage = [&] {
|
||||
return sent.peerId
|
||||
? owner.message(peerToChannel(sent.peerId), d.vid().v)
|
||||
? owner.message(sent.peerId, d.vid().v)
|
||||
: nullptr;
|
||||
};
|
||||
if (const auto id = owner.messageIdByRandomId(randomId)) {
|
||||
@@ -1439,9 +1442,9 @@ void Updates::applyUpdates(
|
||||
const auto list = d.ventities();
|
||||
if (list && !MentionUsersLoaded(&session(), *list)) {
|
||||
session().api().requestMessageData(
|
||||
item->history()->peer->asChannel(),
|
||||
item->history()->peer,
|
||||
item->id,
|
||||
ApiWrap::RequestMessageDataCallback());
|
||||
nullptr);
|
||||
}
|
||||
item->applySentMessage(sent.text, d, wasAlready);
|
||||
}
|
||||
@@ -1527,9 +1530,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
if (local->isScheduled()) {
|
||||
session().data().scheduledMessages().apply(d, local);
|
||||
} else {
|
||||
const auto channel = id.channel;
|
||||
const auto existing = session().data().message(
|
||||
channel,
|
||||
id.peer,
|
||||
newId);
|
||||
if (existing && !local->mainView()) {
|
||||
const auto history = local->history();
|
||||
@@ -1566,7 +1568,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
auto possiblyReadMentions = base::flat_set<MsgId>();
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
if (auto item = session().data().message(channel, msgId.v)) {
|
||||
if (auto item = session().data().message(channel->id, msgId.v)) {
|
||||
if (item->isUnreadMedia() || item->isUnreadMention()) {
|
||||
item->markMediaRead();
|
||||
session().data().requestItemRepaint(item);
|
||||
@@ -2146,24 +2148,26 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateChannelMessageViews: {
|
||||
const auto &d = update.c_updateChannelMessageViews();
|
||||
if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
if (const auto item = session().data().message(peerId, d.vid().v)) {
|
||||
item->setViewsCount(d.vviews().v);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelMessageForwards: {
|
||||
const auto &d = update.c_updateChannelMessageForwards();
|
||||
if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
if (const auto item = session().data().message(peerId, d.vid().v)) {
|
||||
item->setForwardsCount(d.vforwards().v);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadChannelDiscussionInbox: {
|
||||
const auto &d = update.c_updateReadChannelDiscussionInbox();
|
||||
const auto channelId = d.vchannel_id().v;
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(channelId, msgId);
|
||||
const auto item = session().data().message(peerId, msgId);
|
||||
const auto unreadCount = item
|
||||
? session().data().countUnreadRepliesLocally(item, readTillId)
|
||||
: std::nullopt;
|
||||
@@ -2175,7 +2179,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
if (const auto broadcastId = d.vbroadcast_id()) {
|
||||
if (const auto post = session().data().message(
|
||||
broadcastId->v,
|
||||
peerFromChannel(*broadcastId),
|
||||
d.vbroadcast_post()->v)) {
|
||||
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
}
|
||||
@@ -2184,10 +2188,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateReadChannelDiscussionOutbox: {
|
||||
const auto &d = update.c_updateReadChannelDiscussionOutbox();
|
||||
const auto channelId = d.vchannel_id().v;
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(channelId, msgId);
|
||||
const auto item = session().data().message(peerId, msgId);
|
||||
if (item) {
|
||||
item->setRepliesOutboxReadTill(readTillId);
|
||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||
|
||||
@@ -95,9 +95,8 @@ void ViewsManager::done(
|
||||
if (id != requestId) {
|
||||
continue;
|
||||
}
|
||||
const auto channel = peerToChannel(peer->id);
|
||||
for (auto j = 0, l = int(ids.size()); j < l; ++j) {
|
||||
if (const auto item = owner.message(channel, ids[j].v)) {
|
||||
if (const auto item = owner.message(peer->id, ids[j].v)) {
|
||||
v[j].match([&](const MTPDmessageViews &data) {
|
||||
if (const auto views = data.vviews()) {
|
||||
item->setViewsCount(views->v);
|
||||
|
||||
@@ -510,14 +510,14 @@ void ApiWrap::sendMessageFail(
|
||||
}
|
||||
|
||||
void ApiWrap::requestMessageData(
|
||||
ChannelData *channel,
|
||||
PeerData *peer,
|
||||
MsgId msgId,
|
||||
RequestMessageDataCallback callback) {
|
||||
auto &requests = channel
|
||||
? _channelMessageDataRequests[channel][msgId]
|
||||
Fn<void()> done) {
|
||||
auto &requests = (peer && peer->isChannel())
|
||||
? _channelMessageDataRequests[peer->asChannel()][msgId]
|
||||
: _messageDataRequests[msgId];
|
||||
if (callback) {
|
||||
requests.callbacks.push_back(callback);
|
||||
if (done) {
|
||||
requests.callbacks.push_back(std::move(done));
|
||||
}
|
||||
if (!requests.requestId) {
|
||||
_messageDataResolveDelayed.call();
|
||||
@@ -539,19 +539,19 @@ QVector<MTPInputMessage> ApiWrap::collectMessageIds(
|
||||
|
||||
auto ApiWrap::messageDataRequests(ChannelData *channel, bool onlyExisting)
|
||||
-> MessageDataRequests* {
|
||||
if (channel) {
|
||||
auto i = _channelMessageDataRequests.find(channel);
|
||||
if (i == end(_channelMessageDataRequests)) {
|
||||
if (onlyExisting) {
|
||||
return nullptr;
|
||||
}
|
||||
i = _channelMessageDataRequests.emplace(
|
||||
channel,
|
||||
MessageDataRequests()).first;
|
||||
}
|
||||
return &i->second;
|
||||
if (!channel) {
|
||||
return &_messageDataRequests;
|
||||
}
|
||||
return &_messageDataRequests;
|
||||
const auto i = _channelMessageDataRequests.find(channel);
|
||||
if (i != end(_channelMessageDataRequests)) {
|
||||
return &i->second;
|
||||
} else if (onlyExisting) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_channelMessageDataRequests.emplace(
|
||||
channel,
|
||||
MessageDataRequests()
|
||||
).first->second;
|
||||
}
|
||||
|
||||
void ApiWrap::resolveMessageDatas() {
|
||||
@@ -614,20 +614,31 @@ void ApiWrap::finalizeMessageDataRequest(
|
||||
ChannelData *channel,
|
||||
mtpRequestId requestId) {
|
||||
auto requests = messageDataRequests(channel, true);
|
||||
if (requests) {
|
||||
for (auto i = requests->begin(); i != requests->cend();) {
|
||||
if (i->second.requestId == requestId) {
|
||||
for (const auto &callback : i->second.callbacks) {
|
||||
callback(channel, i->first);
|
||||
}
|
||||
i = requests->erase(i);
|
||||
if (!requests) {
|
||||
return;
|
||||
}
|
||||
auto callbacks = std::vector<Fn<void()>>();
|
||||
for (auto i = requests->begin(); i != requests->cend();) {
|
||||
if (i->second.requestId == requestId) {
|
||||
auto &list = i->second.callbacks;
|
||||
if (callbacks.empty()) {
|
||||
callbacks = std::move(list);
|
||||
} else {
|
||||
++i;
|
||||
callbacks.insert(
|
||||
end(callbacks),
|
||||
std::make_move_iterator(begin(list)),
|
||||
std::make_move_iterator(end(list)));
|
||||
}
|
||||
i = requests->erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
if (channel && requests->empty()) {
|
||||
_channelMessageDataRequests.remove(channel);
|
||||
}
|
||||
}
|
||||
if (channel && requests->empty()) {
|
||||
_channelMessageDataRequests.remove(channel);
|
||||
}
|
||||
for (const auto &callback : callbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,7 +657,7 @@ QString ApiWrap::exportDirectMessageLink(
|
||||
if (inRepliesContext) {
|
||||
if (const auto rootId = item->replyToTop()) {
|
||||
const auto root = item->history()->owner().message(
|
||||
peerToChannel(channel->id),
|
||||
channel->id,
|
||||
rootId);
|
||||
const auto sender = root
|
||||
? root->discussionPostOriginalSender()
|
||||
@@ -1388,9 +1399,8 @@ void ApiWrap::deleteAllFromParticipant(
|
||||
const auto ids = history
|
||||
? history->collectMessagesFromParticipantToDelete(from)
|
||||
: std::vector<MsgId>();
|
||||
const auto channelId = peerToChannel(channel->id);
|
||||
for (const auto &msgId : ids) {
|
||||
if (const auto item = _session->data().message(channelId, msgId)) {
|
||||
if (const auto item = _session->data().message(channel->id, msgId)) {
|
||||
item->destroy();
|
||||
}
|
||||
}
|
||||
@@ -2225,11 +2235,7 @@ void ApiWrap::resolveWebPages() {
|
||||
if (i.key()->pendingTill <= t) {
|
||||
const auto item = _session->data().findWebPageItem(i.key());
|
||||
if (item) {
|
||||
if (item->channelId() == NoChannel) {
|
||||
ids.push_back(MTP_inputMessageID(MTP_int(item->id)));
|
||||
i.value() = -1;
|
||||
} else {
|
||||
auto channel = item->history()->peer->asChannel();
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
auto channelMap = idsByChannel.find(channel);
|
||||
if (channelMap == idsByChannel.cend()) {
|
||||
channelMap = idsByChannel.emplace(
|
||||
@@ -2244,6 +2250,9 @@ void ApiWrap::resolveWebPages() {
|
||||
MTP_inputMessageID(MTP_int(item->id)));
|
||||
}
|
||||
i.value() = -channelMap->second.first - 2;
|
||||
} else {
|
||||
ids.push_back(MTP_inputMessageID(MTP_int(item->id)));
|
||||
i.value() = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2927,14 +2936,13 @@ void ApiWrap::preloadEnoughUnreadMentions(not_null<History*> history) {
|
||||
void ApiWrap::checkForUnreadMentions(
|
||||
const base::flat_set<MsgId> &possiblyReadMentions,
|
||||
ChannelData *channel) {
|
||||
for (auto msgId : possiblyReadMentions) {
|
||||
requestMessageData(channel, msgId, [=](
|
||||
ChannelData *channel,
|
||||
MsgId msgId) {
|
||||
if (const auto item = _session->data().message(channel, msgId)) {
|
||||
if (item->mentionsMe()) {
|
||||
item->markMediaRead();
|
||||
}
|
||||
for (const auto msgId : possiblyReadMentions) {
|
||||
requestMessageData(channel, msgId, [=] {
|
||||
const auto item = channel
|
||||
? _session->data().message(channel->id, msgId)
|
||||
: _session->data().nonChannelMessage(msgId);
|
||||
if (item && item->mentionsMe()) {
|
||||
item->markMediaRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3206,7 +3214,7 @@ void ApiWrap::forwardMessages(
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
if (genClientSideMessage) {
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
peer->id,
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto self = _session->user();
|
||||
const auto messageFromId = sendAs
|
||||
@@ -3279,7 +3287,7 @@ void ApiWrap::sendSharedContact(
|
||||
const auto peer = history->peer;
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
history->channelId(),
|
||||
peer->id,
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
|
||||
@@ -3505,7 +3513,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
|
||||
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
|
||||
auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
peer->id,
|
||||
_session->data().nextLocalMessageId());
|
||||
auto randomId = base::RandomValue<uint64>();
|
||||
|
||||
@@ -3663,7 +3671,7 @@ void ApiWrap::sendInlineResult(
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
peer->id,
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
|
||||
|
||||
@@ -147,11 +147,7 @@ public:
|
||||
bool archived,
|
||||
Fn<void()> callback);
|
||||
|
||||
using RequestMessageDataCallback = Fn<void(ChannelData*, MsgId)>;
|
||||
void requestMessageData(
|
||||
ChannelData *channel,
|
||||
MsgId msgId,
|
||||
RequestMessageDataCallback callback);
|
||||
void requestMessageData(PeerData *peer, MsgId msgId, Fn<void()> done);
|
||||
QString exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext);
|
||||
@@ -365,7 +361,7 @@ public:
|
||||
|
||||
private:
|
||||
struct MessageDataRequest {
|
||||
using Callbacks = std::vector<RequestMessageDataCallback>;
|
||||
using Callbacks = std::vector<Fn<void()>>;
|
||||
|
||||
mtpRequestId requestId = 0;
|
||||
Callbacks callbacks;
|
||||
@@ -520,7 +516,7 @@ private:
|
||||
|
||||
MessageDataRequests _messageDataRequests;
|
||||
base::flat_map<
|
||||
ChannelData*,
|
||||
not_null<ChannelData*>,
|
||||
MessageDataRequests> _channelMessageDataRequests;
|
||||
SingleQueuedInvokation _messageDataResolveDelayed;
|
||||
|
||||
|
||||
@@ -80,9 +80,15 @@ void AboutBox::prepare() {
|
||||
void AboutBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
|
||||
const auto available = width()
|
||||
- st::boxPadding.left()
|
||||
- st::boxPadding.right();
|
||||
_version->moveToLeft(st::boxPadding.left(), st::aboutVersionTop);
|
||||
_text1->resizeToWidth(available);
|
||||
_text1->moveToLeft(st::boxPadding.left(), st::aboutTextTop);
|
||||
_text2->resizeToWidth(available);
|
||||
_text2->moveToLeft(st::boxPadding.left(), _text1->y() + _text1->height() + st::aboutSkip);
|
||||
_text3->resizeToWidth(available);
|
||||
_text3->moveToLeft(st::boxPadding.left(), _text2->y() + _text2->height() + st::aboutSkip);
|
||||
}
|
||||
|
||||
|
||||
@@ -418,32 +418,34 @@ void AddContactBox::save() {
|
||||
MTP_string(lastName)))
|
||||
)).done(crl::guard(this, [=](
|
||||
const MTPcontacts_ImportedContacts &result) {
|
||||
result.match([&](const MTPDcontacts_importedContacts &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
|
||||
const auto extractUser = [&](const MTPImportedContact &data) {
|
||||
return data.match([&](const MTPDimportedContact &data) {
|
||||
return (data.vclient_id().v == _contactId)
|
||||
? _session->data().userLoaded(data.vuser_id())
|
||||
: nullptr;
|
||||
});
|
||||
};
|
||||
const auto &list = data.vimported().v;
|
||||
const auto user = list.isEmpty()
|
||||
? nullptr
|
||||
: extractUser(list.front());
|
||||
if (user) {
|
||||
if (user->isContact() || user->session().supportMode()) {
|
||||
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
|
||||
}
|
||||
Ui::hideLayer();
|
||||
} else if (isBoxShown()) {
|
||||
hideChildren();
|
||||
_retrying = true;
|
||||
updateButtons();
|
||||
update();
|
||||
}
|
||||
const auto &data = result.match([](
|
||||
const auto &data) -> const MTPDcontacts_importedContacts& {
|
||||
return data;
|
||||
});
|
||||
_session->data().processUsers(data.vusers());
|
||||
|
||||
const auto extractUser = [&](const MTPImportedContact &data) {
|
||||
return data.match([&](const MTPDimportedContact &data) {
|
||||
return (data.vclient_id().v == _contactId)
|
||||
? _session->data().userLoaded(data.vuser_id())
|
||||
: nullptr;
|
||||
});
|
||||
};
|
||||
const auto &list = data.vimported().v;
|
||||
const auto user = list.isEmpty()
|
||||
? nullptr
|
||||
: extractUser(list.front());
|
||||
if (user) {
|
||||
if (user->isContact() || user->session().supportMode()) {
|
||||
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
|
||||
}
|
||||
Ui::hideLayer();
|
||||
} else if (isBoxShown()) {
|
||||
hideChildren();
|
||||
_retrying = true;
|
||||
updateButtons();
|
||||
update();
|
||||
}
|
||||
})).send();
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,21 @@ contactsAboutFg: windowSubTextFgOver;
|
||||
contactsAboutTop: 60px;
|
||||
contactsAboutBottom: 19px;
|
||||
|
||||
contactsSortButton: IconButton(defaultIconButton) {
|
||||
width: 48px;
|
||||
height: 54px;
|
||||
icon: icon{{ "contacts_alphabet", boxTitleCloseFg }};
|
||||
iconOver: icon{{ "contacts_alphabet", boxTitleCloseFgOver }};
|
||||
iconPosition: point(10px, -1px);
|
||||
rippleAreaPosition: point(1px, 6px);
|
||||
rippleAreaSize: 42px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
contactsSortOnlineIcon: icon{{ "contacts_online", boxTitleCloseFg }};
|
||||
contactsSortOnlineIconOver: icon{{ "contacts_online", boxTitleCloseFgOver }};
|
||||
|
||||
contactsMarginTop: 4px;
|
||||
contactsMarginBottom: 4px;
|
||||
membersMarginTop: 10px;
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
@@ -26,12 +27,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "window/window_session_controller.h" // showAddContact()
|
||||
#include "base/unixtime.h"
|
||||
#include "facades.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_profile.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000);
|
||||
|
||||
void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
|
||||
const auto history = chat->owner().history(chat);
|
||||
auto &histories = history->owner().histories();
|
||||
@@ -110,17 +114,31 @@ void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) {
|
||||
|
||||
object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||
not_null<Window::SessionController*> sessionController) {
|
||||
const auto controller = sessionController;
|
||||
auto delegate = [=](not_null<PeerListBox*> box) {
|
||||
using Mode = ContactsBoxController::SortMode;
|
||||
auto controller = std::make_unique<ContactsBoxController>(
|
||||
&sessionController->session());
|
||||
const auto raw = controller.get();
|
||||
auto init = [=](not_null<PeerListBox*> box) {
|
||||
struct State {
|
||||
QPointer<Ui::IconButton> toggleSort;
|
||||
Mode mode = ContactsBoxController::SortMode::Online;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
|
||||
box->addLeftButton(
|
||||
tr::lng_profile_add_contact(),
|
||||
[=] { controller->showAddContact(); });
|
||||
[=] { sessionController->showAddContact(); });
|
||||
state->toggleSort = box->addTopButton(st::contactsSortButton, [=] {
|
||||
const auto online = (state->mode == Mode::Online);
|
||||
state->mode = online ? Mode::Alphabet : Mode::Online;
|
||||
raw->setSortMode(state->mode);
|
||||
state->toggleSort->setIconOverride(
|
||||
online ? &st::contactsSortOnlineIcon : nullptr,
|
||||
online ? &st::contactsSortOnlineIconOver : nullptr);
|
||||
});
|
||||
raw->setSortMode(Mode::Online);
|
||||
};
|
||||
return Box<PeerListBox>(
|
||||
std::make_unique<ContactsBoxController>(
|
||||
&sessionController->session()),
|
||||
std::move(delegate));
|
||||
return Box<PeerListBox>(std::move(controller), std::move(init));
|
||||
}
|
||||
|
||||
void PeerListRowWithLink::setActionLink(const QString &action) {
|
||||
@@ -368,7 +386,8 @@ ContactsBoxController::ContactsBoxController(
|
||||
not_null<Main::Session*> session,
|
||||
std::unique_ptr<PeerListSearchController> searchController)
|
||||
: PeerListController(std::move(searchController))
|
||||
, _session(session) {
|
||||
, _session(session)
|
||||
, _sortByOnlineTimer([=] { sort(); }) {
|
||||
}
|
||||
|
||||
Main::Session &ContactsBoxController::session() const {
|
||||
@@ -404,6 +423,7 @@ void ContactsBoxController::rebuildRows() {
|
||||
};
|
||||
appendList(session().data().contactsList());
|
||||
checkForEmptyRows();
|
||||
sort();
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
@@ -427,6 +447,66 @@ void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
|
||||
}
|
||||
|
||||
void ContactsBoxController::setSortMode(SortMode mode) {
|
||||
if (_sortMode == mode) {
|
||||
return;
|
||||
}
|
||||
_sortMode = mode;
|
||||
sort();
|
||||
if (_sortMode == SortMode::Online) {
|
||||
session().changes().peerUpdates(
|
||||
Data::PeerUpdate::Flag::OnlineStatus
|
||||
) | rpl::filter([=](const Data::PeerUpdate &update) {
|
||||
return !_sortByOnlineTimer.isActive()
|
||||
&& delegate()->peerListFindRow(update.peer->id.value);
|
||||
}) | rpl::start_with_next([=] {
|
||||
_sortByOnlineTimer.callOnce(kSortByOnlineThrottle);
|
||||
}, _sortByOnlineLifetime);
|
||||
} else {
|
||||
_sortByOnlineTimer.cancel();
|
||||
_sortByOnlineLifetime.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsBoxController::sort() {
|
||||
switch (_sortMode) {
|
||||
case SortMode::Alphabet: sortByName(); break;
|
||||
case SortMode::Online: sortByOnline(); break;
|
||||
default: Unexpected("SortMode in ContactsBoxController.");
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsBoxController::sortByName() {
|
||||
auto keys = base::flat_map<PeerListRowId, QString>();
|
||||
keys.reserve(delegate()->peerListFullRowsCount());
|
||||
const auto key = [&](const PeerListRow &row) {
|
||||
const auto id = row.id();
|
||||
const auto i = keys.find(id);
|
||||
if (i != end(keys)) {
|
||||
return i->second;
|
||||
}
|
||||
const auto peer = row.peer();
|
||||
const auto history = peer->owner().history(peer);
|
||||
return keys.emplace(id, history->chatListNameSortKey()).first->second;
|
||||
};
|
||||
const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) {
|
||||
return (key(a).compare(key(b)) < 0);
|
||||
};
|
||||
delegate()->peerListSortRows(predicate);
|
||||
}
|
||||
|
||||
void ContactsBoxController::sortByOnline() {
|
||||
const auto now = base::unixtime::now();
|
||||
const auto key = [&](const PeerListRow &row) {
|
||||
const auto user = row.peer()->asUser();
|
||||
return user ? (std::min(user->onlineTill, now) + 1) : TimeId();
|
||||
};
|
||||
const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) {
|
||||
return key(a) > key(b);
|
||||
};
|
||||
delegate()->peerListSortRows(predicate);
|
||||
}
|
||||
|
||||
bool ContactsBoxController::appendRow(not_null<UserData*> user) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id.value)) {
|
||||
updateRowHook(row);
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
// Not used for now.
|
||||
//
|
||||
@@ -136,6 +137,12 @@ public:
|
||||
not_null<PeerData*> peer) override final;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
enum class SortMode {
|
||||
Alphabet,
|
||||
Online,
|
||||
};
|
||||
void setSortMode(SortMode mode);
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user);
|
||||
virtual void prepareViewHook() {
|
||||
@@ -144,11 +151,17 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
void sort();
|
||||
void sortByName();
|
||||
void sortByOnline();
|
||||
void rebuildRows();
|
||||
void checkForEmptyRows();
|
||||
bool appendRow(not_null<UserData*> user);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
SortMode _sortMode = SortMode::Alphabet;
|
||||
base::Timer _sortByOnlineTimer;
|
||||
rpl::lifetime _sortByOnlineLifetime;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -662,7 +662,7 @@ UserData *ParticipantsAdditionalData::applyCreator(
|
||||
} else {
|
||||
_adminCanEdit.erase(user);
|
||||
}
|
||||
if (data.rank().isEmpty()) {
|
||||
if (!data.rank().isEmpty()) {
|
||||
_adminRanks[user] = data.rank();
|
||||
} else {
|
||||
_adminRanks.remove(user);
|
||||
@@ -693,7 +693,7 @@ UserData *ParticipantsAdditionalData::applyAdmin(
|
||||
} else {
|
||||
_adminCanEdit.erase(user);
|
||||
}
|
||||
if (data.rank().isEmpty()) {
|
||||
if (!data.rank().isEmpty()) {
|
||||
_adminRanks[user] = data.rank();
|
||||
} else {
|
||||
_adminRanks.remove(user);
|
||||
|
||||
@@ -78,8 +78,8 @@ public:
|
||||
: tr::lng_manage_peer_channel_type();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isAllowSave() {
|
||||
return _isAllowSave;
|
||||
[[nodiscard]] bool goodUsername() const {
|
||||
return _goodUsername;
|
||||
}
|
||||
|
||||
[[nodiscard]] Privacy getPrivacy() const {
|
||||
@@ -144,7 +144,7 @@ private:
|
||||
|
||||
bool _useLocationPhrases = false;
|
||||
bool _isGroup = false;
|
||||
bool _isAllowSave = false;
|
||||
bool _goodUsername = false;
|
||||
|
||||
base::unique_qptr<Ui::VerticalLayout> _wrap;
|
||||
base::Timer _checkUsernameTimer;
|
||||
@@ -171,7 +171,8 @@ Controller::Controller(
|
||||
, _noForwardsSavedValue(noForwardsSavedValue)
|
||||
, _useLocationPhrases(useLocationPhrases)
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup())
|
||||
, _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty())
|
||||
, _goodUsername(!_usernameSavedValue.value_or(
|
||||
_peer->isChannel() ? _peer->asChannel()->username : QString()).isEmpty())
|
||||
, _wrap(container)
|
||||
, _checkUsernameTimer([=] { checkUsernameAvailability(); }) {
|
||||
_peer->updateFull();
|
||||
@@ -236,7 +237,8 @@ void Controller::createContent() {
|
||||
if (_controls.privacy->value() == Privacy::NoUsername) {
|
||||
checkUsernameAvailability();
|
||||
}
|
||||
const auto forShowing = _privacySavedValue.value_or(Privacy::NoUsername);
|
||||
const auto forShowing = _privacySavedValue.value_or(
|
||||
Privacy::NoUsername);
|
||||
_controls.inviteLinkWrap->toggle(
|
||||
(forShowing != Privacy::HasUsername),
|
||||
anim::type::instant);
|
||||
@@ -333,8 +335,8 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||
Expects(_wrap != nullptr);
|
||||
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto username =
|
||||
_usernameSavedValue.value_or(channel ? channel->username : QString());
|
||||
const auto username = _usernameSavedValue.value_or(
|
||||
channel ? channel->username : QString());
|
||||
|
||||
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_wrap,
|
||||
@@ -505,7 +507,7 @@ void Controller::askUsernameRevoke() {
|
||||
}
|
||||
|
||||
void Controller::usernameChanged() {
|
||||
_isAllowSave = false;
|
||||
_goodUsername = false;
|
||||
const auto username = getUsernameInput();
|
||||
if (username.isEmpty()) {
|
||||
_controls.usernameResult = nullptr;
|
||||
@@ -529,12 +531,12 @@ void Controller::usernameChanged() {
|
||||
}
|
||||
|
||||
void Controller::showUsernameError(rpl::producer<QString> &&error) {
|
||||
_isAllowSave = false;
|
||||
_goodUsername = false;
|
||||
showUsernameResult(std::move(error), &st::editPeerUsernameError);
|
||||
}
|
||||
|
||||
void Controller::showUsernameGood() {
|
||||
_isAllowSave = true;
|
||||
_goodUsername = true;
|
||||
showUsernameResult(
|
||||
tr::lng_create_channel_link_available(),
|
||||
&st::editPeerUsernameGood);
|
||||
@@ -655,7 +657,7 @@ void EditPeerTypeBox::prepare() {
|
||||
if (_savedCallback.has_value()) {
|
||||
addButton(tr::lng_settings_save(), [=] {
|
||||
const auto v = controller->getPrivacy();
|
||||
if (!controller->isAllowSave() && (v == Privacy::HasUsername)) {
|
||||
if ((v == Privacy::HasUsername) && !controller->goodUsername()) {
|
||||
controller->setFocusUsername();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ namespace {
|
||||
if (!top) {
|
||||
return false;
|
||||
} else if (peer == migrated) {
|
||||
return top.channel || (id < top.msg);
|
||||
return peerIsChannel(top.peer) || (id < top.msg);
|
||||
} else if (migrated) {
|
||||
return top.channel && (id < top.msg);
|
||||
return peerIsChannel(top.peer) && (id < top.msg);
|
||||
} else {
|
||||
return (id < top.msg);
|
||||
}
|
||||
|
||||
@@ -550,11 +550,6 @@ QRect Row::elementGeometry(int element, int outerWidth) const {
|
||||
const auto size = QSize(
|
||||
st::sessionTerminate.width,
|
||||
st::sessionTerminate.height);
|
||||
const auto margins = QMargins(
|
||||
0,
|
||||
(st::sessionListItem.height - size.height()) / 2,
|
||||
st::sessionListThreeDotsSkip,
|
||||
0);
|
||||
const auto right = st::sessionTerminateSkip;
|
||||
const auto top = st::sessionTerminateTop;
|
||||
const auto left = outerWidth - right - size.width();
|
||||
|
||||
@@ -1105,12 +1105,14 @@ QString AppendShareGameScoreUrl(
|
||||
const FullMsgId &fullId) {
|
||||
auto shareHashData = QByteArray(0x20, Qt::Uninitialized);
|
||||
auto shareHashDataInts = reinterpret_cast<uint64*>(shareHashData.data());
|
||||
auto channel = fullId.channel
|
||||
? session->data().channelLoaded(fullId.channel)
|
||||
: static_cast<ChannelData*>(nullptr);
|
||||
auto channelAccessHash = uint64(channel ? channel->access : 0);
|
||||
const auto peer = fullId.peer
|
||||
? session->data().peerLoaded(fullId.peer)
|
||||
: static_cast<PeerData*>(nullptr);
|
||||
const auto channelAccessHash = uint64((peer && peer->isChannel())
|
||||
? peer->asChannel()->access
|
||||
: 0);
|
||||
shareHashDataInts[0] = session->userId().bare;
|
||||
shareHashDataInts[1] = fullId.channel.bare;
|
||||
shareHashDataInts[1] = fullId.peer.value;
|
||||
shareHashDataInts[2] = uint64(fullId.msg.bare);
|
||||
shareHashDataInts[3] = channelAccessHash;
|
||||
|
||||
@@ -1190,31 +1192,22 @@ void ShareGameScoreByHash(
|
||||
return;
|
||||
}
|
||||
|
||||
// Check first 32 bits of channel access hash.
|
||||
auto channelAccessHash = hashDataInts[3];
|
||||
//auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
|
||||
//if (channelAccessHashInts[0] != hashDataInts[3]) {
|
||||
// Ui::show(Box<Ui::InformBox>(tr::lng_share_wrong_user(tr::now)));
|
||||
// return;
|
||||
//}
|
||||
|
||||
if (((hashDataInts[1] >> 40) != 0)
|
||||
|| (!hashDataInts[1] && channelAccessHash)) {
|
||||
const auto peerId = PeerId(hashDataInts[1]);
|
||||
const auto channelAccessHash = hashDataInts[3];
|
||||
if (!peerIsChannel(peerId) && channelAccessHash) {
|
||||
// If there is no channel id, there should be no channel access_hash.
|
||||
Ui::show(Box<Ui::InformBox>(tr::lng_share_wrong_user(tr::now)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto channelId = ChannelId(hashDataInts[1]);
|
||||
auto msgId = MsgId(int64(hashDataInts[2]));
|
||||
if (const auto item = session->data().message(channelId, msgId)) {
|
||||
const auto msgId = MsgId(int64(hashDataInts[2]));
|
||||
if (const auto item = session->data().message(peerId, msgId)) {
|
||||
FastShareMessage(item);
|
||||
} else {
|
||||
auto resolveMessageAndShareScore = [=](ChannelData *channel) {
|
||||
session->api().requestMessageData(channel, msgId, [=](
|
||||
ChannelData *channel,
|
||||
MsgId msgId) {
|
||||
if (const auto item = session->data().message(channel, msgId)) {
|
||||
auto resolveMessageAndShareScore = [=](PeerData *peer) {
|
||||
session->api().requestMessageData(peer, msgId, [=] {
|
||||
const auto item = session->data().message(peerId, msgId);
|
||||
if (item) {
|
||||
FastShareMessage(item);
|
||||
} else {
|
||||
Ui::show(Box<Ui::InformBox>(
|
||||
@@ -1223,24 +1216,24 @@ void ShareGameScoreByHash(
|
||||
});
|
||||
};
|
||||
|
||||
const auto channel = channelId
|
||||
? session->data().channelLoaded(channelId)
|
||||
const auto peer = peerIsChannel(peerId)
|
||||
? session->data().peerLoaded(peerId)
|
||||
: nullptr;
|
||||
if (channel || !channelId) {
|
||||
resolveMessageAndShareScore(channel);
|
||||
if (peer || !peerIsChannel(peerId)) {
|
||||
resolveMessageAndShareScore(peer);
|
||||
} else {
|
||||
session->api().request(MTPchannels_GetChannels(
|
||||
MTP_vector<MTPInputChannel>(
|
||||
1,
|
||||
MTP_inputChannel(
|
||||
MTP_long(channelId.bare),
|
||||
MTP_long(peerToChannel(peerId).bare),
|
||||
MTP_long(channelAccessHash)))
|
||||
)).done([=](const MTPmessages_Chats &result) {
|
||||
result.match([&](const auto &data) {
|
||||
session->data().processChats(data.vchats());
|
||||
});
|
||||
if (const auto channel = session->data().channelLoaded(channelId)) {
|
||||
resolveMessageAndShareScore(channel);
|
||||
if (const auto peer = session->data().peerLoaded(peerId)) {
|
||||
resolveMessageAndShareScore(peer);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
@@ -245,11 +245,12 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_wasForMsgId == FullMsgId(to->channelId(), to->id) && !force) {
|
||||
const auto peerId = to->history()->peer->id;
|
||||
if (_wasForMsgId == FullMsgId(peerId, to->id) && !force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_wasForMsgId = FullMsgId(to->channelId(), to->id);
|
||||
_wasForMsgId = FullMsgId(peerId, to->id);
|
||||
|
||||
auto markupFlags = to->replyKeyboardFlags();
|
||||
_forceReply = markupFlags & ReplyMarkupFlag::ForceReply;
|
||||
|
||||
@@ -164,7 +164,7 @@ void EmojiInteractions::startIncoming(
|
||||
if (!peer->isUser() || bunch.interactions.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto item = _session->data().message(nullptr, messageId);
|
||||
const auto item = _session->data().message(peer->id, messageId);
|
||||
if (!item || !item->isRegular()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,109 +22,6 @@ namespace {
|
||||
|
||||
std::map<int, const char*> BetaLogs() {
|
||||
return {
|
||||
{
|
||||
2009004,
|
||||
"- Choose one from dozens of new gorgeous animated backgrounds"
|
||||
" in Chat Settings > Chat background.\n"
|
||||
},
|
||||
{
|
||||
2009005,
|
||||
"- Tile chat background patterns horizontally.\n"
|
||||
|
||||
"- Fix a rare crash in spellchecker on Windows.\n"
|
||||
|
||||
"- Fix animated chat backgrounds in Saved Messages.\n"
|
||||
|
||||
"- Fix \"Sorry, group is inaccessible\" message "
|
||||
"in scheduled voice chats.\n",
|
||||
},
|
||||
{
|
||||
2009013,
|
||||
"- See unread comments count when scrolling discussions in channels."
|
||||
},
|
||||
{
|
||||
3000002,
|
||||
"- Check who've seen your message in small groups "
|
||||
"from the context menu.\n"
|
||||
|
||||
"- Enable recording with video in live streams and video chats."
|
||||
},
|
||||
{
|
||||
3000004,
|
||||
"- Fix a crash when joining video chat or live broadcast.\n"
|
||||
|
||||
"- Add a \"Close to Taskbar\" option when tray icon is disabled "
|
||||
"(Windows and Linux)."
|
||||
},
|
||||
{
|
||||
3000005,
|
||||
"- Add support for Emoji 13.1."
|
||||
},
|
||||
{
|
||||
3001002,
|
||||
"- Control video in fullscreen mode using arrows and numbers.\n"
|
||||
|
||||
"- Open locations in browser if default Bing Maps is not installed.\n"
|
||||
|
||||
"- Reconnect without timeout when network availability changes.\n"
|
||||
|
||||
"- Crash fixes."
|
||||
},
|
||||
{
|
||||
3001005,
|
||||
"- Choose one of 8 new preset themes for any individual private chat.\n"
|
||||
|
||||
"- Click on '...' menu > 'Change Colors' to pick a theme.\n"
|
||||
|
||||
"- Both chat participants will see the same theme in that chat "
|
||||
"– on all their devices.\n"
|
||||
|
||||
"- Each new theme features colorful gradient message bubbles, "
|
||||
"beautifully animated backgrounds and unique background patterns.\n"
|
||||
|
||||
"- All chat themes have day and night versions and will follow "
|
||||
"your overall dark mode settings.\n"
|
||||
|
||||
"- Implement main window rounded corners on Windows 11.\n"
|
||||
|
||||
"- Fix audio capture from AirPods on macOS.\n"
|
||||
},
|
||||
{
|
||||
3001006,
|
||||
"- Show small media previews in chats list.\n"
|
||||
|
||||
"- Show media album previews and caption text in chats list.\n"
|
||||
|
||||
"- Add \"Quick Reply\" and \"Mark as Read\" "
|
||||
"to native Windows notifications.\n"
|
||||
},
|
||||
{
|
||||
3001012,
|
||||
"- Create special invite links that require admins "
|
||||
"to approve users before they become members.\n"
|
||||
|
||||
"- Admins can view the applicants' profiles and bios "
|
||||
"by tapping the Join Requests bar at the top of the chat.\n"
|
||||
|
||||
"- Add internal labels to your chat's Invite Links "
|
||||
"to keep them organized.\n"
|
||||
|
||||
"- Run natively on Apple Silicon (macOS only).\n"
|
||||
},
|
||||
{
|
||||
3001013,
|
||||
"- Fix requests to groups / channels processing.\n"
|
||||
|
||||
"- Fix internal link previews with View Content button layout.\n"
|
||||
|
||||
"- Fix crash in messages search with imported messages results.\n"
|
||||
|
||||
"- Don't use fractional system UI scaling on Linux.\n"
|
||||
|
||||
"- Fix invite link icons on macOS.\n"
|
||||
|
||||
"- Several crash fixes.\n"
|
||||
},
|
||||
{
|
||||
3002006,
|
||||
"- Try out the new audio player with playlist shuffle and repeat.\n"
|
||||
@@ -141,6 +38,24 @@ std::map<int, const char*> BetaLogs() {
|
||||
"- Fix a crash in archived stickers loading.\n"
|
||||
|
||||
"- Fix a crash in calls to old Telegram versions.\n"
|
||||
},
|
||||
{
|
||||
3003001,
|
||||
"- Switch between contacts list sorting modes.\n"
|
||||
|
||||
"- Sort contacts list by last seen time by default.\n"
|
||||
|
||||
"- Fix disappearing Send As Channel button after message editing.\n"
|
||||
|
||||
"- Fix file upload cancelling.\n"
|
||||
|
||||
"- Fix crash in video capture on macOS.\n"
|
||||
|
||||
"- Fix labels in the About box.\n"
|
||||
|
||||
"- Use Qt 6.2.2 for macOS and Linux builds.\n"
|
||||
|
||||
"- Allow installing x64 Windows version on Windows ARM.\n"
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -43,9 +43,7 @@ PreLaunchWindow::PreLaunchWindow(QString title) {
|
||||
p.setColor(QPalette::Window, QColor(255, 255, 255));
|
||||
setPalette(p);
|
||||
|
||||
QLabel tmp(this);
|
||||
tmp.setText(qsl("Tmp"));
|
||||
_size = tmp.sizeHint().height();
|
||||
_size = QFontMetrics(QGuiApplication::font()).height();
|
||||
|
||||
int paddingVertical = (_size / 2);
|
||||
int paddingHorizontal = _size;
|
||||
|
||||
@@ -460,7 +460,6 @@ void Launcher::processArguments() {
|
||||
auto parseMap = std::map<QByteArray, KeyFormat> {
|
||||
{ "-debug" , KeyFormat::NoValues },
|
||||
{ "-freetype" , KeyFormat::NoValues },
|
||||
{ "-many" , KeyFormat::NoValues },
|
||||
{ "-key" , KeyFormat::OneValue },
|
||||
{ "-autostart" , KeyFormat::NoValues },
|
||||
{ "-fixprevious" , KeyFormat::NoValues },
|
||||
@@ -499,7 +498,6 @@ void Launcher::processArguments() {
|
||||
|
||||
gUseFreeType = parseResult.contains("-freetype");
|
||||
gDebugMode = parseResult.contains("-debug");
|
||||
gManyInstance = parseResult.contains("-many");
|
||||
gKeyFile = parseResult.value("-key", {}).join(QString()).toLower();
|
||||
gKeyFile = gKeyFile.replace(QRegularExpression("[^a-z0-9\\-_]"), {});
|
||||
gLaunchMode = parseResult.contains("-autostart") ? LaunchModeAutoStart
|
||||
|
||||
@@ -439,8 +439,8 @@ bool OpenMediaTimestamp(
|
||||
const auto parts = base.mid(3).split('_');
|
||||
const auto documentId = parts.value(0).toULongLong();
|
||||
const auto itemId = FullMsgId(
|
||||
parts.value(1).toInt(),
|
||||
parts.value(2).toInt());
|
||||
PeerId(parts.value(1).toULongLong()),
|
||||
MsgId(parts.value(2).toLongLong()));
|
||||
const auto session = &controller->session();
|
||||
const auto document = session->data().document(documentId);
|
||||
session->settings().setMediaLastPlaybackPosition(
|
||||
|
||||
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/animations.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QLockFile>
|
||||
#include <QtGui/QSessionManager>
|
||||
#include <QtGui/QScreen>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
@@ -99,10 +100,35 @@ int Sandbox::start() {
|
||||
if (!Core::UpdaterDisabled()) {
|
||||
_updateChecker = std::make_unique<Core::UpdateChecker>();
|
||||
}
|
||||
const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
|
||||
char h[33] = { 0 };
|
||||
hashMd5Hex(d.constData(), d.size(), h);
|
||||
_localServerName = Platform::SingleInstanceLocalServerName(h);
|
||||
|
||||
{
|
||||
const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
|
||||
char h[33] = { 0 };
|
||||
hashMd5Hex(d.constData(), d.size(), h);
|
||||
_localServerName = Platform::SingleInstanceLocalServerName(h);
|
||||
}
|
||||
|
||||
{
|
||||
const auto d = QFile::encodeName(cExeDir() + cExeName());
|
||||
QByteArray h;
|
||||
h.resize(32);
|
||||
hashMd5Hex(d.constData(), d.size(), h.data());
|
||||
_lockFile = std::make_unique<QLockFile>(QDir::tempPath() + '/' + h + '-' + cGUIDStr());
|
||||
_lockFile->setStaleLockTime(0);
|
||||
if (!_lockFile->tryLock() && _launcher->customWorkingDir()) {
|
||||
// On Windows, QLockFile has problems detecting a stale lock
|
||||
// if the machine's hostname contains characters outside the US-ASCII character set.
|
||||
if constexpr (Platform::IsWindows()) {
|
||||
// QLockFile::removeStaleLockFile returns false on Windows,
|
||||
// when the application owning the lock is still running.
|
||||
if (!_lockFile->removeStaleLockFile()) {
|
||||
gManyInstance = true;
|
||||
}
|
||||
} else {
|
||||
gManyInstance = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect(
|
||||
&_localSocket,
|
||||
@@ -149,13 +175,8 @@ int Sandbox::start() {
|
||||
restartHint,
|
||||
Qt::DirectConnection);
|
||||
|
||||
if (cManyInstance()) {
|
||||
LOG(("Many instance allowed, starting..."));
|
||||
singleInstanceChecked();
|
||||
} else {
|
||||
LOG(("Connecting local socket to %1...").arg(_localServerName));
|
||||
_localSocket.connectToServer(_localServerName);
|
||||
}
|
||||
LOG(("Connecting local socket to %1...").arg(_localServerName));
|
||||
_localSocket.connectToServer(_localServerName);
|
||||
|
||||
if (QuitOnStartRequested) {
|
||||
closeApplication();
|
||||
@@ -339,12 +360,12 @@ void Sandbox::socketError(QLocalSocket::LocalSocketError e) {
|
||||
|
||||
void Sandbox::singleInstanceChecked() {
|
||||
if (cManyInstance()) {
|
||||
Logs::multipleInstances();
|
||||
LOG(("App Info: Detected another instance"));
|
||||
}
|
||||
|
||||
Ui::DisableCustomScaling();
|
||||
refreshGlobalProxy();
|
||||
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
|
||||
if (!Logs::started() || !Logs::instanceChecked()) {
|
||||
new NotStartedWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QtCore/QAbstractNativeEventFilter>
|
||||
|
||||
class QLockFile;
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Launcher;
|
||||
@@ -119,6 +121,7 @@ private:
|
||||
QLocalServer _localServer;
|
||||
QLocalSocket _localSocket;
|
||||
LocalClients _localClients;
|
||||
std::unique_ptr<QLockFile> _lockFile;
|
||||
bool _secondInstance = false;
|
||||
bool _started = false;
|
||||
static bool QuitOnStartRequested;
|
||||
|
||||