Compare commits

..

59 Commits

Author SHA1 Message Date
John Preston
ac397e6e19 Beta version 2.6.7.
- Improve voice chat participants list updating.
2021-03-18 18:05:43 +04:00
John Preston
38e15c9bdb Fix saving legacy chat admins without migration.
Fixes #10558.
2021-03-18 17:27:33 +04:00
John Preston
00d65fa978 Request one participants slice on voice chat reload. 2021-03-18 16:58:05 +04:00
John Preston
3fea9cca08 Subscribe to channel updates in voice chat. 2021-03-18 15:54:28 +04:00
John Preston
b390e0766b Apply all queued updates on reload. 2021-03-18 15:30:58 +04:00
John Preston
2f75e6bbe2 Add some logging for voice chat updates. 2021-03-18 15:22:55 +04:00
Ilya Fedin
decbbb9a73 Check for openal fork updates in Dockerfile 2021-03-18 07:51:57 +03:00
Ilya Fedin
b4b80822c8 Set glib's application name and prgname 2021-03-18 07:51:57 +03:00
John Preston
bc82cdc3b3 Call dump_syms and strip outside of docker. 2021-03-18 02:33:29 +04:00
John Preston
ebc67d25f0 Migrate docker build to GCC 9. 2021-03-18 00:30:12 +04:00
John Preston
348b4d54ba Revert "Workaround build issues on GCC."
This reverts commit 3defb06783.
2021-03-18 00:30:12 +04:00
John Preston
6f86ce595b Beta version 2.6.6.
- Fix joining popular voice chats.
2021-03-18 00:26:53 +04:00
John Preston
8c53a3c19e Don't skip updateGroupCallParticipants while in getDifference. 2021-03-18 00:25:53 +04:00
John Preston
67623072d6 Fix joining a voice chat. 2021-03-18 00:24:36 +04:00
John Preston
1291f1c80d Beta version 2.6.5.
- Improvements and fixes in new voice chat features.
2021-03-17 21:16:55 +04:00
John Preston
0684db9bd8 Improve participants sorting in voice chats. 2021-03-17 20:37:55 +04:00
John Preston
db7b61a77b Rewrite voice chat members list management. 2021-03-17 20:37:55 +04:00
John Preston
d392633b90 Send speaking typings in channels. 2021-03-17 20:37:55 +04:00
John Preston
76e08af26a Apply updateGroupCallParticipants before updateGroupCall. 2021-03-17 20:37:55 +04:00
John Preston
b23f16e6e4 Don't show 'allowed to speak' on first join. 2021-03-17 20:37:54 +04:00
23rd
23156d523c Fixed Github CI Windows build. 2021-03-17 18:59:20 +03:00
Ilya Fedin
04b0e2e9e6 Update submodules 2021-03-17 18:58:02 +03:00
Ilya Fedin
ace5740125 Use QProcess::startDetached for xdg-open
Since it may running continously
2021-03-17 15:39:26 +03:00
John Preston
bc67b79023 Beta version 2.6.4: 110% UI scale on macOS Retina. 2021-03-17 00:07:08 +04:00
John Preston
528c98af67 Beta version 2.6.4.
- Fix freeze in voice chats.
2021-03-17 00:02:05 +04:00
John Preston
311a2f2753 Fix freeze in voice chats. 2021-03-16 23:54:58 +04:00
John Preston
3defb06783 Workaround build issues on GCC. 2021-03-16 22:26:58 +04:00
John Preston
5708b5e849 Fix confirmation when joining by link. 2021-03-16 21:06:36 +04:00
John Preston
1db1328a91 Beta version 2.6.3.
- Fix audio device selection in voice chats.
- Fix blinking self profile photo
in case the profile photo privacy is used.
- Fix voice chat admin menu on macOS.
2021-03-16 19:13:15 +04:00
John Preston
2e9d6d73c3 Fix invalid 'You can speak' notification. 2021-03-16 19:09:23 +04:00
John Preston
38dd5ab837 Fix 'join as' userpic button display. 2021-03-16 18:58:10 +04:00
John Preston
83ab670c50 Remove 'wants to speak' status in three seconds. 2021-03-16 18:51:38 +04:00
John Preston
5621e41529 Limit voice chat title to 40 characters. 2021-03-16 18:51:11 +04:00
John Preston
056cab6268 Don't apply private userpic 'min' updates. 2021-03-16 18:15:19 +04:00
John Preston
61d0d240aa Add 'rejoin as' and 'change title' toast notifications. 2021-03-16 18:13:51 +04:00
John Preston
33ae4c2802 Improve layout of voice chat channel choosing. 2021-03-16 17:16:56 +04:00
John Preston
2c806b11d7 Always join voice chats by link with confirmation. 2021-03-16 17:16:01 +04:00
John Preston
199434c7a2 Add recording mark animation. 2021-03-16 14:48:12 +04:00
John Preston
c65c554d88 Don't ask joinAs for voice chats each time. 2021-03-16 14:33:11 +04:00
John Preston
5d16359a5a Fix voice chat three-dot menu on macOS. 2021-03-16 13:50:40 +04:00
John Preston
fd9ad04d15 Vazir Without-Latin instead of Without-Latin-UI.
Fixes #10536.
2021-03-16 13:29:46 +04:00
Ilya Fedin
0c8febce9c Avoid allocations for case-insensetive compare 2021-03-16 12:20:48 +03:00
John Preston
4659cc50f2 Allow inviting members in channel voice chats. 2021-03-15 22:40:59 +04:00
John Preston
2fddeb478b Fix long texts in invite link error toasts. 2021-03-15 20:49:38 +04:00
Ilya Fedin
4ffe1d3acc Use base::flat_map for pointer keys 2021-03-15 19:49:06 +03:00
Ilya Fedin
cdf0512515 Remove unused variable in mainwidget 2021-03-15 19:49:06 +03:00
Ilya Fedin
971e188063 Replace remaining QFileInfo::exists 2021-03-15 19:49:06 +03:00
John Preston
a909c1a813 Make default UI scale 120% on macOS Retina. 2021-03-15 20:33:41 +04:00
John Preston
4fc2b1f1a3 Append x64 to app_version on Windows. 2021-03-15 20:14:39 +04:00
John Preston
fb04f33ae8 Fix long voice chat title elision on macOS. 2021-03-15 19:34:58 +04:00
John Preston
b2c87e7a73 Fix three-dot toggle in voice chats. 2021-03-15 19:19:29 +04:00
John Preston
86a33ceea1 Fix audio device selection in voice chats. 2021-03-15 19:00:58 +04:00
John Preston
a5d8d7a550 Add some validation for GroupThumbs. 2021-03-15 19:00:32 +04:00
Ilya Fedin
11723aedff Get rid of empty QStringLiterals 2021-03-15 17:19:59 +03:00
Ilya Fedin
fe5de8f009 Fix non-working UniqueConnection in gtk file dialog 2021-03-15 17:19:44 +03:00
Ilya Fedin
6b68d001ae Get rid of deprecated ranges::action 2021-03-15 11:41:29 +03:00
Ilya Fedin
ae0b9141dd Trying to get rid of unused variables... 2021-03-15 11:40:44 +03:00
Ilya Fedin
12e306dd7b Replace remaining multi-args 2021-03-15 11:21:05 +03:00
Ilya Fedin
508762cd2c Use static QFileInfo::exists 2021-03-15 11:21:05 +03:00
88 changed files with 1406 additions and 683 deletions

View File

@@ -128,7 +128,7 @@ jobs:
shell: bash
run: |
echo "Find any version of Python 2."
p=`ls /c/hostedtoolcache/windows/python | grep 2 | tail -1`
p=`ls /c/hostedtoolcache/windows/python | grep "^2" | tail -1`
if [ -z "$p" ]; then
echo "Python 2 is not found."
exit 1
@@ -409,6 +409,7 @@ jobs:
-D TDESKTOP_API_TEST=ON ^
-D DESKTOP_APP_USE_PACKAGED=OFF ^
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
-D DESKTOP_APP_NO_PDB=ON ^
%TDESKTOP_BUILD_DEFINE% ^
-DCMAKE_SYSTEM_VERSION=%SDK%

View File

@@ -1966,6 +1966,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_copy_listener_link" = "Copy Listener Link";
"lng_group_call_end" = "End Voice Chat";
"lng_group_call_join" = "Join";
"lng_group_call_join_confirm" = "Do you want to join the voice chat {chat}?";
"lng_group_call_invite_done_user" = "You invited {user} to the voice chat.";
"lng_group_call_invite_done_many#one" = "You invited **{count} member** to the voice chat.";
"lng_group_call_invite_done_many#other" = "You invited **{count} members** to the voice chat.";
@@ -2011,7 +2012,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_recording_start_button" = "Start";
"lng_group_call_is_recorded" = "Voice chat is being recorded.";
"lng_group_call_can_speak_here" = "You can now speak.";
"lng_group_call_can_speak" = "You can now speak in **{chat}**.";
"lng_group_call_can_speak" = "You can now speak in {chat}.";
"lng_group_call_title_changed" = "Voice chat title changed to {title}";
"lng_group_call_join_as_changed" = "Members of this voice chat will now see you as {name}";
"lng_no_mic_permission" = "Telegram needs access to your microphone so that you can make calls and record voice messages.";

View File

@@ -1203,7 +1203,7 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#c0c2052e flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int version:int = GroupCall;
groupCall#c0c2052e flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int version:int = GroupCall;
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
@@ -1572,7 +1572,7 @@ channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> =
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true for_groupcall:flags.2?true = messages.Chats;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats;
channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;

View File

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

View File

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

View File

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

View File

@@ -65,7 +65,10 @@ void SendProgressManager::update(
SendProgressType type,
int progress) {
const auto peer = history->peer;
if (peer->isSelf() || (peer->isChannel() && !peer->isMegagroup())) {
if (peer->isSelf()
|| (peer->isChannel()
&& !peer->isMegagroup()
&& type != SendProgressType::Speaking)) {
return;
}

View File

@@ -277,12 +277,32 @@ void Updates::checkLastUpdate(bool afterSleep) {
void Updates::feedUpdateVector(
const MTPVector<MTPUpdate> &updates,
bool skipMessageIds) {
for (const auto &update : updates.v) {
if (skipMessageIds && update.type() == mtpc_updateMessageID) {
SkipUpdatePolicy policy) {
auto list = updates.v;
const auto hasGroupCallParticipantUpdates = ranges::contains(
list,
mtpc_updateGroupCallParticipants,
&MTPUpdate::type);
if (hasGroupCallParticipantUpdates) {
ranges::stable_sort(list, std::less<>(), [](const MTPUpdate &entry) {
if (entry.type() == mtpc_updateGroupCallParticipants) {
return 0;
} else {
return 1;
}
});
} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
return;
}
for (const auto &entry : std::as_const(list)) {
const auto type = entry.type();
if ((policy == SkipUpdatePolicy::SkipMessageIds
&& type == mtpc_updateMessageID)
|| (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants
&& type != mtpc_updateGroupCallParticipants)) {
continue;
}
feedUpdate(update);
feedUpdate(entry);
}
session().data().sendHistoryChangeNotifications();
}
@@ -392,7 +412,9 @@ void Updates::feedChannelDifference(
session().data().processMessages(
data.vnew_messages(),
NewMessageType::Unread);
feedUpdateVector(data.vother_updates(), true);
feedUpdateVector(
data.vother_updates(),
SkipUpdatePolicy::SkipMessageIds);
_handlingChannelDifference = false;
}
@@ -553,14 +575,14 @@ void Updates::feedDifference(
session().data().processChats(chats);
feedMessageIds(other);
session().data().processMessages(msgs, NewMessageType::Unread);
feedUpdateVector(other, true);
feedUpdateVector(other, SkipUpdatePolicy::SkipMessageIds);
}
void Updates::differenceFail(const MTP::Error &error) {
LOG(("RPC Error in getDifference: %1 %2: %3"
).arg(error.code()
).arg(error.type()
).arg(error.description()));
LOG(("RPC Error in getDifference: %1 %2: %3").arg(
QString::number(error.code()),
error.type(),
error.description()));
failDifferenceStartTimerFor(nullptr);
}
@@ -810,9 +832,32 @@ void Updates::mtpUpdateReceived(const MTPUpdates &updates) {
if (!requestingDifference()
|| HasForceLogoutNotification(updates)) {
applyUpdates(updates);
} else {
applyGroupCallParticipantUpdates(updates);
}
}
void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
updates.match([&](const MTPDupdates &data) {
session().data().processUsers(data.vusers());
session().data().processChats(data.vchats());
feedUpdateVector(
data.vupdates(),
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
}, [&](const MTPDupdatesCombined &data) {
session().data().processUsers(data.vusers());
session().data().processChats(data.vchats());
feedUpdateVector(
data.vupdates(),
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
}, [&](const MTPDupdateShort &data) {
if (data.vupdate().type() == mtpc_updateGroupCallParticipants) {
feedUpdate(data.vupdate());
}
}, [](const auto &) {
});
}
int32 Updates::pts() const {
return _ptsWaiter.current();
}

View File

@@ -66,6 +66,12 @@ private:
AfterFail,
};
enum class SkipUpdatePolicy {
SkipNone,
SkipMessageIds,
SkipExceptGroupCallParticipants,
};
struct ActiveChatTracker {
PeerData *peer = nullptr;
rpl::lifetime lifetime;
@@ -113,12 +119,14 @@ private:
void mtpNewSessionCreated();
void feedUpdateVector(
const MTPVector<MTPUpdate> &updates,
bool skipMessageIds = false);
SkipUpdatePolicy policy = SkipUpdatePolicy::SkipNone);
// Doesn't call sendHistoryChangeNotifications itself.
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
// Doesn't call sendHistoryChangeNotifications itself.
void feedUpdate(const MTPUpdate &update);
void applyGroupCallParticipantUpdates(const MTPUpdates &updates);
bool whenGetDiffChanged(
ChannelData *channel,
int32 ms,

View File

@@ -2600,7 +2600,7 @@ void ApiWrap::clearWebPageRequests() {
void ApiWrap::resolveWebPages() {
auto ids = QVector<MTPInputMessage>(); // temp_req_id = -1
using IndexAndMessageIds = QPair<int32, QVector<MTPInputMessage>>;
using MessageIdsByChannel = QMap<ChannelData*, IndexAndMessageIds>;
using MessageIdsByChannel = base::flat_map<ChannelData*, IndexAndMessageIds>;
MessageIdsByChannel idsByChannel; // temp_req_id = -index - 2
ids.reserve(_webPagesPending.size());
@@ -2617,18 +2617,18 @@ void ApiWrap::resolveWebPages() {
auto channel = item->history()->peer->asChannel();
auto channelMap = idsByChannel.find(channel);
if (channelMap == idsByChannel.cend()) {
channelMap = idsByChannel.insert(
channelMap = idsByChannel.emplace(
channel,
IndexAndMessageIds(
idsByChannel.size(),
QVector<MTPInputMessage>(
1,
MTP_inputMessageID(MTP_int(item->id)))));
MTP_inputMessageID(MTP_int(item->id))))).first;
} else {
channelMap.value().second.push_back(
channelMap->second.second.push_back(
MTP_inputMessageID(MTP_int(item->id)));
}
i.value() = -channelMap.value().first - 2;
i.value() = -channelMap->second.first - 2;
}
}
} else {
@@ -2648,10 +2648,10 @@ void ApiWrap::resolveWebPages() {
}
QVector<mtpRequestId> reqsByIndex(idsByChannel.size(), 0);
for (auto i = idsByChannel.cbegin(), e = idsByChannel.cend(); i != e; ++i) {
reqsByIndex[i.value().first] = request(MTPchannels_GetMessages(
i.key()->inputChannel,
MTP_vector<MTPInputMessage>(i.value().second)
)).done([=, channel = i.key()](
reqsByIndex[i->second.first] = request(MTPchannels_GetMessages(
i->first->inputChannel,
MTP_vector<MTPInputMessage>(i->second.second)
)).done([=, channel = i->first](
const MTPmessages_Messages &result,
mtpRequestId requestId) {
gotWebPages(channel, result, requestId);

View File

@@ -111,7 +111,7 @@ confirmInviteUserName: FlatLabel(defaultFlatLabel) {
confirmInviteUserNameTop: 227px;
confirmPhoneAboutLabel: FlatLabel(defaultFlatLabel) {
minWidth: 282px;
minWidth: 272px;
}
confirmPhoneCodeField: InputField(defaultInputField) {
}

View File

@@ -464,7 +464,6 @@ void Rows::showMenu(int index) {
Fn<void()> callback) {
return _menu->addAction(text, std::move(callback));
};
const auto id = row->data.id;
if (canShare(row)) {
addAction(tr::lng_proxy_edit_share(tr::now), [=] { share(row); });
}

View File

@@ -695,8 +695,6 @@ bool PasscodeBox::handleCustomCheckError(const MTP::Error &error) {
void PasscodeBox::sendClearCloudPassword(
const Core::CloudPasswordResult &check) {
const auto newPasswordData = QByteArray();
const auto newPasswordHash = QByteArray();
const auto hint = QString();
const auto email = QString();
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo

View File

@@ -566,7 +566,6 @@ int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
}
void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
if (_elements.empty()) {

View File

@@ -792,9 +792,10 @@ groupCallBoxLabel: FlatLabel(boxLabel) {
textFg: groupCallMembersFg;
}
groupCallJoinAsLabel: FlatLabel(defaultFlatLabel) {
minWidth: 282px;
minWidth: 272px;
textFg: groupCallMembersFg;
}
groupCallJoinAsWidth: 330px;
groupCallJoinAsTextTop: 4px;
groupCallJoinAsNameTop: 23px;
groupCallJoinAsPadding: margins(12px, 8px, 12px, 7px);

View File

@@ -142,7 +142,7 @@ uint64 ComputeFingerprint(bytes::const_span authKey) {
}
[[nodiscard]] QVector<MTPstring> CollectVersionsForApi() {
return WrapVersions(tgcalls::Meta::Versions() | ranges::action::reverse);
return WrapVersions(tgcalls::Meta::Versions() | ranges::actions::reverse);
}
[[nodiscard]] Webrtc::VideoState StartVideoState(bool enabled) {

View File

@@ -12,12 +12,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "data/data_group_call.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "boxes/peer_list_box.h"
#include "boxes/confirm_box.h"
#include "styles/style_boxes.h"
#include "styles/style_calls.h"
@@ -111,10 +114,12 @@ void ChooseJoinAsBox(
Context context,
JoinInfo info,
Fn<void(JoinInfo)> done) {
box->setWidth(st::groupCallJoinAsWidth);
box->setTitle([&] {
switch (context) {
case Context::Create: return tr::lng_group_call_start_as_header();
case Context::Join: return tr::lng_group_call_join_as_header();
case Context::Join:
case Context::JoinWithConfirm: return tr::lng_group_call_join_as_header();
case Context::Switch: return tr::lng_group_call_display_as_header();
}
Unexpected("Context in ChooseJoinAsBox.");
@@ -172,7 +177,7 @@ void ChooseJoinAsProcess::start(
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
Fn<void(QString)> showToast,
Fn<void(JoinInfo)> done,
PeerData *currentJoinAs) {
PeerData *changingJoinAsFrom) {
Expects(done != nullptr);
const auto session = &peer->session();
@@ -231,26 +236,18 @@ void ChooseJoinAsProcess::start(
}
return list;
});
const auto selectedId = peer->groupCallDefaultJoinAs();
if (list.empty()) {
_request->showToast(Lang::Hard::ServerError());
return;
} else if (list.size() == 1
&& list.front() == self
&& (!peer->isChannel()
|| !peer->asChannel()->amAnonymous()
|| (peer->isBroadcast() && !peer->canWrite()))) {
info.possibleJoinAs = std::move(list);
finish(info);
return;
}
info.joinAs = [&]() -> not_null<PeerData*> {
const auto selectedId = peer->groupCallDefaultJoinAs();
if (!selectedId) {
return self;
}
const auto loaded = session->data().peerLoaded(selectedId);
return (currentJoinAs && ranges::contains(list, not_null{ currentJoinAs }))
? not_null(currentJoinAs)
const auto loaded = selectedId
? session->data().peerLoaded(selectedId)
: nullptr;
return (changingJoinAsFrom
&& ranges::contains(list, not_null{ changingJoinAsFrom }))
? not_null(changingJoinAsFrom)
: (loaded && ranges::contains(list, not_null{ loaded }))
? not_null(loaded)
: ranges::contains(list, self)
@@ -259,6 +256,42 @@ void ChooseJoinAsProcess::start(
}();
info.possibleJoinAs = std::move(list);
const auto onlyByMe = (info.possibleJoinAs.size() == 1)
&& (info.possibleJoinAs.front() == self)
&& (!peer->isChannel()
|| !peer->asChannel()->amAnonymous()
|| (peer->isBroadcast() && !peer->canWrite()));
// We already joined this voice chat, just rejoin with the same.
const auto byAlreadyUsed = selectedId
&& (info.joinAs->id == selectedId);
if (!changingJoinAsFrom && (onlyByMe || byAlreadyUsed)) {
if (context != Context::JoinWithConfirm) {
finish(info);
return;
}
const auto real = peer->groupCall();
const auto name = (real && !real->title().isEmpty())
? real->title()
: peer->name;
auto box = Box<::ConfirmBox>(
tr::lng_group_call_join_confirm(
tr::now,
lt_chat,
Ui::Text::Bold(name),
Ui::Text::WithEntities),
tr::lng_group_call_join(tr::now),
crl::guard(&_request->guard, [=] { finish(info); }));
box->boxClosing(
) | rpl::start_with_next([=] {
_request = nullptr;
}, _request->lifetime);
_request->box = box.data();
_request->showBox(std::move(box));
return;
}
auto box = Box(
ChooseJoinAsBox,
context,

View File

@@ -28,6 +28,7 @@ public:
enum class Context {
Create,
Join,
JoinWithConfirm,
Switch,
};
@@ -37,7 +38,7 @@ public:
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
Fn<void(QString)> showToast,
Fn<void(JoinInfo)> done,
PeerData *currentJoinAs = nullptr);
PeerData *changingJoinAsFrom = nullptr);
private:
struct ChannelsListRequest {

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_group_common.h"
#include "main/main_session.h"
#include "api/api_send_progress.h"
#include "api/api_updates.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "lang/lang_hardcoded.h"
@@ -272,11 +273,6 @@ void GroupCall::setState(State state) {
if (state == State::Joined) {
stopConnectingSound();
if (!_hadJoinedState) {
_hadJoinedState = true;
applyGlobalShortcutChanges();
_delegate->groupCallPlaySound(Delegate::GroupCallSound::Started);
}
if (const auto call = _peer->groupCall(); call && call->id() == _id) {
call->setInCall();
}
@@ -392,8 +388,11 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
addParticipantsToInstance();
_peer->session().updates().addActiveChat(
_peerStream.events_starting_with_copy(_peer));
SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
_peer = group;
_peerStream.fire_copy(group);
});
}
@@ -426,6 +425,11 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
LOG(("Call Info: Requesting join payload."));
_joinAs = as;
if (const auto chat = _peer->asChat()) {
chat->setGroupCallDefaultJoinAs(_joinAs->id);
} else if (const auto channel = _peer->asChannel()) {
channel->setGroupCallDefaultJoinAs(_joinAs->id);
}
const auto weak = base::make_weak(this);
_instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
@@ -474,6 +478,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
applyMeInCallLocally();
maybeSendMutedUpdate(wasMuteState);
_peer->session().api().applyUpdates(updates);
checkFirstTimeJoined();
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
LOG(("Call Error: Could not join, error: %1").arg(type));
@@ -542,7 +547,7 @@ void GroupCall::applyMeInCallLocally() {
| Flag::f_volume_by_admin // Self volume can only be set by admin.
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0))
| (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0));
call->applyUpdateChecked(
call->applyLocalUpdate(
MTP_updateGroupCallParticipants(
inputCall(),
MTP_vector<MTPGroupCallParticipant>(
@@ -587,7 +592,7 @@ void GroupCall::applyParticipantLocally(
| (participant->raisedHandRating
? Flag::f_raise_hand_rating
: Flag(0));
_peer->groupCall()->applyUpdateChecked(
_peer->groupCall()->applyLocalUpdate(
MTP_updateGroupCallParticipants(
inputCall(),
MTP_vector<MTPGroupCallParticipant>(
@@ -712,98 +717,102 @@ void GroupCall::setMutedAndUpdate(MuteState mute) {
}
}
void GroupCall::handleUpdate(const MTPGroupCall &call) {
return call.match([&](const MTPDgroupCall &data) {
if (_acceptFields) {
if (!_instance && !_id) {
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
}
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDupdateGroupCall &data) {
data.vcall().match([&](const MTPDgroupCall &data) {
handlePossibleCreateOrJoinResponse(data);
}, [](const MTPDgroupCallDiscarded &data) {
});
}
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDgroupCall &data) {
if (_acceptFields) {
if (!_instance && !_id) {
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
}
return;
} else if (_id != data.vid().v
|| _accessHash != data.vaccess_hash().v
|| !_instance) {
return;
}
const auto streamDcId = MTP::BareDcId(
data.vstream_dc_id().value_or_empty());
const auto params = data.vparams();
if (!params) {
return;
}
params->match([&](const MTPDdataJSON &data) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vdata().v,
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: "
"Failed to parse group call params, error: %1."
).arg(error.errorString()));
return;
} else if (_id != data.vid().v
|| _accessHash != data.vaccess_hash().v
|| !_instance) {
} else if (!document.isObject()) {
LOG(("API Error: "
"Not an object received in group call params."));
return;
}
const auto streamDcId = MTP::BareDcId(
data.vstream_dc_id().value_or_empty());
if (const auto params = data.vparams()) {
params->match([&](const MTPDdataJSON &data) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vdata().v,
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: "
"Failed to parse group call params, error: %1."
).arg(error.errorString()));
return;
} else if (!document.isObject()) {
LOG(("API Error: "
"Not an object received in group call params."));
return;
}
const auto guard = gsl::finally([&] {
addParticipantsToInstance();
});
const auto guard = gsl::finally([&] {
addParticipantsToInstance();
});
if (document.object().value("stream").toBool()) {
if (!streamDcId) {
LOG(("Api Error: Empty stream_dc_id in groupCall."));
}
_broadcastDcId = streamDcId
? streamDcId
: _peer->session().mtp().mainDcId();
setInstanceMode(InstanceMode::Stream);
return;
}
if (document.object().value("stream").toBool()) {
if (!streamDcId) {
LOG(("Api Error: Empty stream_dc_id in groupCall."));
}
_broadcastDcId = streamDcId
? streamDcId
: _peer->session().mtp().mainDcId();
setInstanceMode(InstanceMode::Stream);
return;
}
const auto readString = [](
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto root = document.object().value("transport").toObject();
auto payload = tgcalls::GroupJoinResponsePayload();
payload.ufrag = readString(root, "ufrag");
payload.pwd = readString(root, "pwd");
const auto prints = root.value("fingerprints").toArray();
const auto candidates = root.value("candidates").toArray();
for (const auto &print : prints) {
const auto object = print.toObject();
payload.fingerprints.push_back(tgcalls::GroupJoinPayloadFingerprint{
.hash = readString(object, "hash"),
.setup = readString(object, "setup"),
.fingerprint = readString(object, "fingerprint"),
});
}
for (const auto &candidate : candidates) {
const auto object = candidate.toObject();
payload.candidates.push_back(tgcalls::GroupJoinResponseCandidate{
.port = readString(object, "port"),
.protocol = readString(object, "protocol"),
.network = readString(object, "network"),
.generation = readString(object, "generation"),
.id = readString(object, "id"),
.component = readString(object, "component"),
.foundation = readString(object, "foundation"),
.priority = readString(object, "priority"),
.ip = readString(object, "ip"),
.type = readString(object, "type"),
.tcpType = readString(object, "tcpType"),
.relAddr = readString(object, "relAddr"),
.relPort = readString(object, "relPort"),
});
}
setInstanceMode(InstanceMode::Rtc);
_instance->setJoinResponsePayload(payload, {});
const auto readString = [](
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto root = document.object().value("transport").toObject();
auto payload = tgcalls::GroupJoinResponsePayload();
payload.ufrag = readString(root, "ufrag");
payload.pwd = readString(root, "pwd");
const auto prints = root.value("fingerprints").toArray();
const auto candidates = root.value("candidates").toArray();
for (const auto &print : prints) {
const auto object = print.toObject();
payload.fingerprints.push_back(tgcalls::GroupJoinPayloadFingerprint{
.hash = readString(object, "hash"),
.setup = readString(object, "setup"),
.fingerprint = readString(object, "fingerprint"),
});
}
}, [&](const MTPDgroupCallDiscarded &data) {
if (data.vid().v == _id) {
_mySsrc = 0;
hangup();
for (const auto &candidate : candidates) {
const auto object = candidate.toObject();
payload.candidates.push_back(tgcalls::GroupJoinResponseCandidate{
.port = readString(object, "port"),
.protocol = readString(object, "protocol"),
.network = readString(object, "network"),
.generation = readString(object, "generation"),
.id = readString(object, "id"),
.component = readString(object, "component"),
.foundation = readString(object, "foundation"),
.priority = readString(object, "priority"),
.ip = readString(object, "ip"),
.type = readString(object, "type"),
.tcpType = readString(object, "tcpType"),
.relAddr = readString(object, "relAddr"),
.relPort = readString(object, "relPort"),
});
}
setInstanceMode(InstanceMode::Rtc);
_instance->setJoinResponsePayload(payload, {});
});
}
@@ -848,7 +857,33 @@ void GroupCall::addPreparedParticipantsDelayed() {
crl::on_main(this, [=] { addPreparedParticipants(); });
}
void GroupCall::handleUpdate(const MTPUpdate &update) {
update.match([&](const MTPDupdateGroupCall &data) {
handleUpdate(data);
}, [&](const MTPDupdateGroupCallParticipants &data) {
handleUpdate(data);
}, [](const auto &) {
Unexpected("Type in Instance::applyGroupCallUpdateChecked.");
});
}
void GroupCall::handleUpdate(const MTPDupdateGroupCall &data) {
data.vcall().match([](const MTPDgroupCall &) {
}, [&](const MTPDgroupCallDiscarded &data) {
if (data.vid().v == _id) {
_mySsrc = 0;
hangup();
}
});
}
void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
const auto callId = data.vcall().match([](const auto &data) {
return data.vid().v;
});
if (_id != callId) {
return;
}
const auto state = _state.current();
if (state != State::Joined && state != State::Connecting) {
return;
@@ -885,18 +920,22 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
handleOtherParticipants(data);
return;
}
if (data.is_left() && data.vsource().v == _mySsrc) {
// I was removed from the call, rejoin.
LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
setState(State::Joining);
rejoin();
} else if (!data.is_left() && data.vsource().v != _mySsrc) {
if (data.is_left()) {
if (data.vsource().v == _mySsrc) {
// I was removed from the call, rejoin.
LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
setState(State::Joining);
rejoin();
}
return;
} else if (data.vsource().v != _mySsrc) {
// I joined from another device, hangup.
LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
_mySsrc = 0;
hangup();
return;
}
if (data.is_muted() && !data.is_can_self_unmute()) {
setMuted(data.vraise_hand_rating().value_or_empty()
@@ -925,12 +964,12 @@ void GroupCall::changeTitle(const QString &title) {
return;
}
real->setTitle(title);
_api.request(MTPphone_EditGroupCallTitle(
inputCall(),
MTP_string(title)
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
_titleChanged.fire({});
}).fail([=](const MTP::Error &error) {
}).send();
}
@@ -1303,9 +1342,24 @@ void GroupCall::setInstanceConnected(
if (nowCanSpeak) {
notifyAboutAllowedToSpeak();
}
if (!_hadJoinedState && state() == State::Joined) {
checkFirstTimeJoined();
}
}
void GroupCall::checkFirstTimeJoined() {
if (_hadJoinedState || state() != State::Joined) {
return;
}
_hadJoinedState = true;
applyGlobalShortcutChanges();
_delegate->groupCallPlaySound(Delegate::GroupCallSound::Started);
}
void GroupCall::notifyAboutAllowedToSpeak() {
if (!_hadJoinedState) {
return;
}
_delegate->groupCallPlaySound(
Delegate::GroupCallSound::AllowedToSpeak);
_allowedToSpeakNotifications.fire({});
@@ -1369,11 +1423,6 @@ void GroupCall::sendSelfUpdate(SendUpdateType type) {
}).send();
}
auto GroupCall::instanceStateValue() const -> rpl::producer<InstanceState> {
using namespace rpl::mappers;
return _instanceState.value();
}
void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
if (input) {
_mediaDevices->switchToAudioInput(deviceId);

View File

@@ -116,8 +116,8 @@ public:
void rejoinAs(Group::JoinInfo info);
void rejoinWithHash(const QString &hash);
void join(const MTPInputGroupCall &inputCall);
void handleUpdate(const MTPGroupCall &call);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
void handleUpdate(const MTPUpdate &update);
void handlePossibleCreateOrJoinResponse(const MTPDupdateGroupCall &data);
void changeTitle(const QString &title);
void toggleRecording(bool enabled, const QString &title);
[[nodiscard]] bool recordingStoppedByMe() const {
@@ -158,7 +158,12 @@ public:
TransitionToRtc,
Connected,
};
[[nodiscard]] rpl::producer<InstanceState> instanceStateValue() const;
[[nodiscard]] InstanceState instanceState() const {
return _instanceState.current();
}
[[nodiscard]] rpl::producer<InstanceState> instanceStateValue() const {
return _instanceState.value();
}
[[nodiscard]] rpl::producer<LevelUpdate> levelUpdates() const {
return _levelUpdates.events();
@@ -169,6 +174,9 @@ public:
[[nodiscard]] rpl::producer<> allowedToSpeakNotifications() const {
return _allowedToSpeakNotifications.events();
}
[[nodiscard]] rpl::producer<> titleChanged() const {
return _titleChanged.events();
}
static constexpr auto kSpeakLevelThreshold = 0.2;
void setCurrentAudioDevice(bool input, const QString &deviceId);
@@ -219,6 +227,9 @@ private:
RaiseHand,
};
void handlePossibleCreateOrJoinResponse(const MTPDgroupCall &data);
void handleUpdate(const MTPDupdateGroupCall &data);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
void handleRequestError(const MTP::Error &error);
void handleControllerError(const QString &error);
void ensureControllerCreated();
@@ -242,6 +253,7 @@ private:
void checkGlobalShortcutAvailability();
void checkJoined();
void checkFirstTimeJoined();
void notifyAboutAllowedToSpeak();
void playConnectingSound();
@@ -268,6 +280,7 @@ private:
const not_null<Delegate*> _delegate;
not_null<PeerData*> _peer; // Can change in legacy group migration.
rpl::event_stream<PeerData*> _peerStream;
not_null<History*> _history; // Can change in legacy group migration.
MTP::Sender _api;
rpl::variable<State> _state = State::Creating;
@@ -304,6 +317,7 @@ private:
base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke;
rpl::event_stream<Group::RejoinEvent> _rejoinEvents;
rpl::event_stream<> _allowedToSpeakNotifications;
rpl::event_stream<> _titleChanged;
base::Timer _lastSpokeCheckTimer;
base::Timer _checkJoinedTimer;

View File

@@ -37,7 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "styles/style_calls.h"
namespace Calls {
namespace Calls::Group {
namespace {
constexpr auto kBlobsEnterDuration = crl::time(250);
@@ -47,6 +47,7 @@ constexpr auto kMinorBlobFactor = 0.9f;
constexpr auto kUserpicMinScale = 0.8;
constexpr auto kMaxLevel = 1.;
constexpr auto kWideScale = 5;
constexpr auto kKeepRaisedHandStatusDuration = 3 * crl::time(1000);
const auto kSpeakerThreshold = std::vector<float>{
Group::kDefaultVolume * 0.1f / Group::kMaxVolume,
@@ -89,6 +90,7 @@ public:
virtual bool rowIsMe(not_null<PeerData*> participantPeer) = 0;
virtual bool rowCanMuteMembers() = 0;
virtual void rowUpdateRow(not_null<Row*> row) = 0;
virtual void rowScheduleRaisedHandStatusRemove(not_null<Row*> row) = 0;
virtual void rowPaintIcon(
Painter &p,
QRect rect,
@@ -115,6 +117,7 @@ public:
void updateState(const Data::GroupCall::Participant *participant);
void updateLevel(float level);
void updateBlobAnimation(crl::time now);
void clearRaisedHandStatus();
[[nodiscard]] State state() const {
return _state;
}
@@ -254,6 +257,7 @@ private:
int _volume = Group::kDefaultVolume;
bool _sounding = false;
bool _speaking = false;
bool _raisedHandStatus = false;
bool _skipLevelUpdate = false;
};
@@ -291,6 +295,7 @@ public:
bool rowIsMe(not_null<PeerData*> participantPeer) override;
bool rowCanMuteMembers() override;
void rowUpdateRow(not_null<Row*> row) override;
void rowScheduleRaisedHandStatusRemove(not_null<Row*> row) override;
void rowPaintIcon(
Painter &p,
QRect rect,
@@ -335,6 +340,7 @@ private:
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
void appendInvitedUsers();
void scheduleRaisedHandStatusRemove();
const base::weak_ptr<GroupCall> _call;
not_null<PeerData*> _peer;
@@ -348,13 +354,14 @@ private:
rpl::event_stream<VolumeRequest> _changeVolumeRequests;
rpl::event_stream<not_null<PeerData*>> _kickParticipantRequests;
rpl::variable<int> _fullCount = 1;
rpl::variable<int> _fullCountMin = 0;
rpl::variable<int> _fullCountMax = std::numeric_limits<int>::max();
not_null<QWidget*> _menuParent;
base::unique_qptr<Ui::PopupMenu> _menu;
base::flat_set<not_null<PeerData*>> _menuCheckRowsAfterHidden;
base::flat_map<PeerListRowId, crl::time> _raisedHandStatusRemoveAt;
base::Timer _raisedHandStatusRemoveTimer;
base::flat_map<uint32, not_null<Row*>> _soundingRowBySsrc;
Ui::Animations::Basic _soundingAnimation;
@@ -479,6 +486,15 @@ void Row::setSounding(bool sounding) {
}
}
void Row::clearRaisedHandStatus() {
if (!_raisedHandStatus) {
return;
}
_raisedHandStatus = false;
refreshStatus();
_delegate->rowUpdateRow(this);
}
void Row::setState(State state) {
if (_state == state) {
return;
@@ -486,10 +502,16 @@ void Row::setState(State state) {
const auto wasActive = (_state == State::Active);
const auto wasMuted = (_state == State::Muted)
|| (_state == State::RaisedHand);
const auto wasRaisedHand = (_state == State::RaisedHand);
_state = state;
const auto nowActive = (_state == State::Active);
const auto nowMuted = (_state == State::Muted)
|| (_state == State::RaisedHand);
const auto nowRaisedHand = (_state == State::RaisedHand);
if (!wasRaisedHand && nowRaisedHand) {
_raisedHandStatus = true;
_delegate->rowScheduleRaisedHandStatusRemove(this);
}
if (nowActive != wasActive) {
_activeAnimation.start(
[=] { _delegate->rowUpdateRow(this); },
@@ -710,10 +732,10 @@ void Row::paintStatusText(
const auto &font = st::normalFont;
const auto about = (_state == State::Inactive
|| _state == State::Muted
|| _state == State::RaisedHand)
|| (_state == State::RaisedHand && !_raisedHandStatus))
? _aboutText
: QString();
if (_aboutText.isEmpty()
if (about.isEmpty()
&& _state != State::Invited
&& _state != State::MutedByMe) {
p.save();
@@ -743,8 +765,8 @@ void Row::paintStatusText(
outerWidth,
(_state == State::MutedByMe
? tr::lng_group_call_muted_by_me_status(tr::now)
: !_aboutText.isEmpty()
? font->m.elidedText(_aboutText, Qt::ElideRight, availableWidth)
: !about.isEmpty()
? font->m.elidedText(about, Qt::ElideRight, availableWidth)
: _delegate->rowIsMe(peer())
? tr::lng_status_connecting(tr::now)
: tr::lng_group_call_invited_status(tr::now)));
@@ -802,7 +824,7 @@ void Row::refreshStatus() {
? u"%1% %2"_q
.arg(std::round(_volume / 100.))
.arg(tr::lng_group_call_active(tr::now))
: (_state == State::RaisedHand)
: _raisedHandStatus
? tr::lng_group_call_raised_hand_status(tr::now)
: tr::lng_group_call_inactive(tr::now)),
_speaking);
@@ -833,6 +855,7 @@ MembersController::MembersController(
: _call(call)
, _peer(call->peer())
, _menuParent(menuParent)
, _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); })
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
setupListChangeViewers(call);
@@ -929,19 +952,11 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
delegate()->peerListRefreshRows();
});
if (const auto row = findRow(event.wasJoinAs)) {
if (row->state() != Row::State::Invited) {
if (const auto min = _fullCountMin.current()) {
_fullCountMin = min - 1;
}
}
removeRow(row);
}
if (findRow(event.nowJoinAs)) {
return;
} else if (auto row = createRowForMe()) {
if (row->state() != Row::State::Invited) {
_fullCountMin = _fullCountMin.current() + 1;
}
delegate()->peerListAppendRow(std::move(row));
}
}, _lifetime);
@@ -951,13 +966,7 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
_realCallRawValue = real;
_realId = real->id();
_fullCount = rpl::combine(
real->fullCountValue(),
_fullCountMin.value(),
_fullCountMax.value()
) | rpl::map([](int value, int min, int max) {
return std::max(std::clamp(value, min, max), 1);
});
_fullCount = real->fullCountValue();
real->participantsSliceAdded(
) | rpl::start_with_next([=] {
@@ -978,9 +987,6 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
if (isMe(participantPeer)) {
updateRow(row, nullptr);
} else {
if (const auto min = _fullCountMin.current()) {
_fullCountMin = min - 1;
}
removeRow(row);
delegate()->peerListRefreshRows();
}
@@ -1018,37 +1024,41 @@ void MembersController::appendInvitedUsers() {
void MembersController::updateRow(
const std::optional<Data::GroupCall::Participant> &was,
const Data::GroupCall::Participant &now) {
auto reorderIfInvitedBeforeIndex = 0;
auto countChange = 0;
auto reorderIfInvitedBefore = 0;
auto checkPosition = (Row*)nullptr;
auto addedToBottom = (Row*)nullptr;
if (const auto row = findRow(now.peer)) {
if (row->state() == Row::State::Invited) {
reorderIfInvitedBeforeIndex = row->absoluteIndex();
countChange = 1;
reorderIfInvitedBefore = row->absoluteIndex();
}
updateRow(row, &now);
if ((now.speaking && (!was || !was->speaking))
|| (now.raisedHandRating != (was ? was->raisedHandRating : 0))
|| (!now.canSelfUnmute && was && was->canSelfUnmute)) {
checkRowPosition(row);
checkPosition = row;
}
} else if (auto row = createRow(now)) {
if (row->speaking()) {
delegate()->peerListPrependRow(std::move(row));
} else {
reorderIfInvitedBeforeIndex = delegate()->peerListFullRowsCount();
reorderIfInvitedBefore = delegate()->peerListFullRowsCount();
if (now.raisedHandRating != 0) {
checkPosition = row.get();
} else {
addedToBottom = row.get();
}
delegate()->peerListAppendRow(std::move(row));
}
delegate()->peerListRefreshRows();
countChange = 1;
}
static constexpr auto kInvited = Row::State::Invited;
const auto reorder = [&] {
const auto count = reorderIfInvitedBeforeIndex;
const auto count = reorderIfInvitedBefore;
if (count <= 0) {
return false;
}
const auto row = delegate()->peerListRowAt(
reorderIfInvitedBeforeIndex - 1).get();
reorderIfInvitedBefore - 1).get();
return (static_cast<Row*>(row)->state() == kInvited);
}();
if (reorder) {
@@ -1056,12 +1066,25 @@ void MembersController::updateRow(
return static_cast<const Row&>(row).state() != kInvited;
});
}
if (countChange) {
const auto fullCountMin = _fullCountMin.current() + countChange;
if (_fullCountMax.current() < fullCountMin) {
_fullCountMax = fullCountMin;
if (checkPosition) {
checkRowPosition(checkPosition);
} else if (addedToBottom) {
const auto real = resolvedRealCall();
if (real && real->joinedToTop()) {
const auto proj = [&](const PeerListRow &other) {
const auto &real = static_cast<const Row&>(other);
return real.speaking()
? 2
: (&real == addedToBottom)
? 1
: 0;
};
delegate()->peerListSortRows([&](
const PeerListRow &a,
const PeerListRow &b) {
return proj(a) > proj(b);
});
}
_fullCountMin = fullCountMin;
}
}
@@ -1163,7 +1186,7 @@ void MembersController::checkRowPosition(not_null<Row*> row) {
// All force muted at the bottom, but 'row' still above others.
? (&real == row.get() ? 1ULL : 0ULL)
// All not force-muted lie between raised hands and speaking.
: (std::numeric_limits<uint64>::max() - 2);
: (kTop - 2);
};
const auto projForOther = [&](const PeerListRow &other) {
const auto &real = static_cast<const Row&>(other);
@@ -1218,11 +1241,6 @@ void MembersController::updateRow(
_soundingAnimation.stop();
}
if (!participant && wasInChat) {
if (const auto min = _fullCountMin.current()) {
_fullCountMin = min - 1;
}
}
delegate()->peerListUpdateRow(row);
}
@@ -1268,7 +1286,6 @@ void MembersController::prepare() {
; real && call && real->id() == call->id()) {
prepareRows(real);
} else if (auto row = createRowForMe()) {
_fullCountMin = (row->state() == Row::State::Invited) ? 0 : 1;
delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows();
}
@@ -1289,7 +1306,6 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
auto foundMe = false;
auto changed = false;
const auto &participants = real->participants();
auto fullCountMin = 0;
auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count;) {
auto row = delegate()->peerListRowAt(i);
@@ -1304,7 +1320,6 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
participantPeer,
&Data::GroupCall::Participant::peer);
if (contains) {
++fullCountMin;
++i;
} else {
changed = true;
@@ -1323,9 +1338,6 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
? createRow(*i)
: createRowForMe();
if (row) {
if (row->state() != Row::State::Invited) {
++fullCountMin;
}
changed = true;
delegate()->peerListAppendRow(std::move(row));
}
@@ -1333,20 +1345,12 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
}
for (const auto &participant : participants) {
if (auto row = createRow(participant)) {
++fullCountMin;
changed = true;
delegate()->peerListAppendRow(std::move(row));
}
}
if (changed) {
delegate()->peerListRefreshRows();
if (_fullCountMax.current() < fullCountMin) {
_fullCountMax = fullCountMin;
}
_fullCountMin = fullCountMin;
if (real->participantsLoaded()) {
_fullCountMax = fullCountMin;
}
}
}
@@ -1378,6 +1382,44 @@ void MembersController::rowUpdateRow(not_null<Row*> row) {
delegate()->peerListUpdateRow(row);
}
void MembersController::rowScheduleRaisedHandStatusRemove(
not_null<Row*> row) {
const auto id = row->peer()->id;
const auto when = crl::now() + kKeepRaisedHandStatusDuration;
const auto i = _raisedHandStatusRemoveAt.find(id);
if (i != _raisedHandStatusRemoveAt.end()) {
i->second = when;
} else {
_raisedHandStatusRemoveAt.emplace(id, when);
}
scheduleRaisedHandStatusRemove();
}
void MembersController::scheduleRaisedHandStatusRemove() {
auto waiting = crl::time(0);
const auto now = crl::now();
for (auto i = begin(_raisedHandStatusRemoveAt)
; i != end(_raisedHandStatusRemoveAt);) {
if (i->second <= now) {
if (const auto row = delegate()->peerListFindRow(i->first)) {
static_cast<Row*>(row)->clearRaisedHandStatus();
}
i = _raisedHandStatusRemoveAt.erase(i);
} else {
if (!waiting || waiting > (i->second - now)) {
waiting = i->second - now;
}
++i;
}
}
if (waiting > 0) {
if (!_raisedHandStatusRemoveTimer.isActive()
|| _raisedHandStatusRemoveTimer.remainingTime() > waiting) {
_raisedHandStatusRemoveTimer.callOnce(waiting);
}
}
}
void MembersController::rowPaintIcon(
Painter &p,
QRect rect,
@@ -1555,7 +1597,11 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
}
} else {
result->addAction(
tr::lng_context_view_profile(tr::now),
(participantPeer->isUser()
? tr::lng_context_view_profile(tr::now)
: participantPeer->isBroadcast()
? tr::lng_context_view_channel(tr::now)
: tr::lng_context_view_group(tr::now)),
showProfile);
if (participantPeer->isUser()) {
result->addAction(
@@ -1747,8 +1793,7 @@ std::unique_ptr<Row> MembersController::createInvitedRow(
} // namespace
GroupMembers::GroupMembers(
Members::Members(
not_null<QWidget*> parent,
not_null<GroupCall*> call)
: RpWidget(parent)
@@ -1762,25 +1807,25 @@ GroupMembers::GroupMembers(
_listController->setDelegate(static_cast<PeerListDelegate*>(this));
}
auto GroupMembers::toggleMuteRequests() const
auto Members::toggleMuteRequests() const
-> rpl::producer<Group::MuteRequest> {
return static_cast<MembersController*>(
_listController.get())->toggleMuteRequests();
}
auto GroupMembers::changeVolumeRequests() const
auto Members::changeVolumeRequests() const
-> rpl::producer<Group::VolumeRequest> {
return static_cast<MembersController*>(
_listController.get())->changeVolumeRequests();
}
auto GroupMembers::kickParticipantRequests() const
auto Members::kickParticipantRequests() const
-> rpl::producer<not_null<PeerData*>> {
return static_cast<MembersController*>(
_listController.get())->kickParticipantRequests();
}
int GroupMembers::desiredHeight() const {
int Members::desiredHeight() const {
const auto top = _addMember ? _addMember->height() : 0;
auto count = [&] {
if (const auto call = _call.get()) {
@@ -1798,7 +1843,7 @@ int GroupMembers::desiredHeight() const {
+ (use ? st::lineWidth : 0);
}
rpl::producer<int> GroupMembers::desiredHeightValue() const {
rpl::producer<int> Members::desiredHeightValue() const {
const auto controller = static_cast<MembersController*>(
_listController.get());
return rpl::combine(
@@ -1810,12 +1855,28 @@ rpl::producer<int> GroupMembers::desiredHeightValue() const {
});
}
void GroupMembers::setupAddMember(not_null<GroupCall*> call) {
void Members::setupAddMember(not_null<GroupCall*> call) {
using namespace rpl::mappers;
const auto peer = call->peer();
if (peer->isBroadcast()) {
_canAddMembers = false;
if (const auto channel = peer->asBroadcast()) {
_canAddMembers = rpl::single(
false
) | rpl::then(peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
return peer->groupCall();
}) | rpl::filter([=](Data::GroupCall *real) {
const auto call = _call.get();
return call && real && (real->id() == call->id());
}) | rpl::take(
1
) | rpl::map([=] {
return Data::PeerFlagValue(
channel,
MTPDchannel::Flag::f_username);
}) | rpl::flatten_latest());
} else {
_canAddMembers = Data::CanWriteValue(peer.get());
SubscribeToMigration(
@@ -1851,12 +1912,12 @@ void GroupMembers::setupAddMember(not_null<GroupCall*> call) {
}, lifetime());
}
rpl::producer<int> GroupMembers::fullCountValue() const {
rpl::producer<int> Members::fullCountValue() const {
return static_cast<MembersController*>(
_listController.get())->fullCountValue();
}
void GroupMembers::setupList() {
void Members::setupList() {
_listController->setStyleOverrides(&st::groupCallMembersList);
_list = _scroll->setOwnedWidget(object_ptr<ListWidget>(
this,
@@ -1877,11 +1938,11 @@ void GroupMembers::setupList() {
updateControlsGeometry();
}
void GroupMembers::resizeEvent(QResizeEvent *e) {
void Members::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
void GroupMembers::resizeToList() {
void Members::resizeToList() {
if (!_list) {
return;
}
@@ -1898,7 +1959,7 @@ void GroupMembers::resizeToList() {
}
}
void GroupMembers::updateControlsGeometry() {
void Members::updateControlsGeometry() {
if (!_list) {
return;
}
@@ -1912,7 +1973,7 @@ void GroupMembers::updateControlsGeometry() {
_list->resizeToWidth(width());
}
void GroupMembers::setupFakeRoundCorners() {
void Members::setupFakeRoundCorners() {
const auto size = st::roundRadiusLarge;
const auto full = 3 * size;
const auto imagePartSize = size * cIntRetinaFactor();
@@ -1975,40 +2036,40 @@ void GroupMembers::setupFakeRoundCorners() {
}, lifetime());
}
void GroupMembers::peerListSetTitle(rpl::producer<QString> title) {
void Members::peerListSetTitle(rpl::producer<QString> title) {
}
void GroupMembers::peerListSetAdditionalTitle(rpl::producer<QString> title) {
void Members::peerListSetAdditionalTitle(rpl::producer<QString> title) {
}
void GroupMembers::peerListSetHideEmpty(bool hide) {
void Members::peerListSetHideEmpty(bool hide) {
}
bool GroupMembers::peerListIsRowChecked(not_null<PeerListRow*> row) {
bool Members::peerListIsRowChecked(not_null<PeerListRow*> row) {
return false;
}
void GroupMembers::peerListScrollToTop() {
void Members::peerListScrollToTop() {
}
int GroupMembers::peerListSelectedRowsCount() {
int Members::peerListSelectedRowsCount() {
return 0;
}
void GroupMembers::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
Unexpected("Item selection in Calls::GroupMembers.");
void Members::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
Unexpected("Item selection in Calls::Members.");
}
void GroupMembers::peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) {
Unexpected("Item selection in Calls::GroupMembers.");
void Members::peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) {
Unexpected("Item selection in Calls::Members.");
}
void GroupMembers::peerListFinishSelectedRowsBunch() {
void Members::peerListFinishSelectedRowsBunch() {
}
void GroupMembers::peerListSetDescription(
void Members::peerListSetDescription(
object_ptr<Ui::FlatLabel> description) {
description.destroy();
}
} // namespace Calls
} // namespace Calls::Group

View File

@@ -19,19 +19,19 @@ class GroupCall;
} // namespace Data
namespace Calls {
class GroupCall;
} // namespace Calls
namespace Calls::Group {
namespace Group {
struct VolumeRequest;
struct MuteRequest;
} // namespace Group
class GroupCall;
class GroupMembers final
class Members final
: public Ui::RpWidget
, private PeerListContentDelegate {
public:
GroupMembers(
Members(
not_null<QWidget*> parent,
not_null<GroupCall*> call);

View File

@@ -31,6 +31,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Calls::Group {
namespace {
constexpr auto kMaxGroupCallLength = 40;
void EditGroupCallTitleBox(
not_null<Ui::GenericBox*> box,
const QString &placeholder,
@@ -42,6 +44,7 @@ void EditGroupCallTitleBox(
st::groupCallField,
rpl::single(placeholder),
title));
input->setMaxLength(kMaxGroupCallLength);
box->setFocusCallback([=] {
input->setFocusFast();
});

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_group_settings.h"
#include "calls/calls_group_menu.h"
#include "ui/platform/ui_platform_window_title.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/controls/call_mute_button.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/window.h"
@@ -23,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "ui/special_buttons.h"
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
#include "core/application.h"
@@ -48,10 +50,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QApplication>
#include <QtGui/QWindow>
namespace Calls {
namespace Calls::Group {
namespace {
constexpr auto kSpacePushToTalkDelay = crl::time(250);
constexpr auto kRecordingAnimationDuration = crl::time(1200);
constexpr auto kRecordingOpacity = 0.6;
class InviteController final : public ParticipantsBoxController {
public:
@@ -246,7 +250,7 @@ std::unique_ptr<PeerListRow> InviteContactsController::createRow(
} // namespace
GroupPanel::GroupPanel(not_null<GroupCall*> call)
Panel::Panel(not_null<GroupCall*> call)
: _call(call)
, _peer(call->peer())
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
@@ -281,6 +285,8 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
initControls();
initLayout();
showAndActivate();
setupJoinAsChangedToasts();
setupTitleChangedToasts();
call->allowedToSpeakNotifications(
) | rpl::start_with_next([=] {
@@ -299,21 +305,21 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
.text = tr::lng_group_call_can_speak(
tr::now,
lt_chat,
Ui::Text::WithEntities(name),
Ui::Text::RichLangValue),
Ui::Text::Bold(name),
Ui::Text::WithEntities),
.st = &st::defaultToast,
});
}
}, widget()->lifetime());
}
GroupPanel::~GroupPanel() {
Panel::~Panel() {
if (_menu) {
_menu.destroy();
}
}
void GroupPanel::setupRealCallViewers(not_null<GroupCall*> call) {
void Panel::setupRealCallViewers(not_null<GroupCall*> call) {
const auto peer = call->peer();
peer->session().changes().peerFlagsValue(
peer,
@@ -329,21 +335,21 @@ void GroupPanel::setupRealCallViewers(not_null<GroupCall*> call) {
}, _window->lifetime());
}
bool GroupPanel::isActive() const {
bool Panel::isActive() const {
return _window->isActiveWindow()
&& _window->isVisible()
&& !(_window->windowState() & Qt::WindowMinimized);
}
void GroupPanel::minimize() {
void Panel::minimize() {
_window->setWindowState(_window->windowState() | Qt::WindowMinimized);
}
void GroupPanel::close() {
void Panel::close() {
_window->close();
}
void GroupPanel::showAndActivate() {
void Panel::showAndActivate() {
if (_window->isHidden()) {
_window->show();
}
@@ -356,7 +362,7 @@ void GroupPanel::showAndActivate() {
_window->setFocus();
}
void GroupPanel::migrate(not_null<ChannelData*> channel) {
void Panel::migrate(not_null<ChannelData*> channel) {
_peer = channel;
_peerLifetime.destroy();
subscribeToPeerChanges();
@@ -364,7 +370,7 @@ void GroupPanel::migrate(not_null<ChannelData*> channel) {
refreshTitle();
}
void GroupPanel::subscribeToPeerChanges() {
void Panel::subscribeToPeerChanges() {
Info::Profile::NameValue(
_peer
) | rpl::start_with_next([=](const TextWithEntities &name) {
@@ -372,7 +378,7 @@ void GroupPanel::subscribeToPeerChanges() {
}, _peerLifetime);
}
void GroupPanel::initWindow() {
void Panel::initWindow() {
_window->setAttribute(Qt::WA_OpaquePaintEvent);
_window->setAttribute(Qt::WA_NoSystemBackground);
_window->setWindowIcon(
@@ -405,13 +411,17 @@ void GroupPanel::initWindow() {
0,
widget()->width(),
st::groupCallMembersTop);
return titleRect.contains(widgetPoint)
return (titleRect.contains(widgetPoint)
&& (!_menuToggle || !_menuToggle->geometry().contains(widgetPoint))
&& (!_menu || !_menu->geometry().contains(widgetPoint))
&& (!_recordingMark || !_recordingMark->geometry().contains(widgetPoint))
&& (!_joinAsToggle || !_joinAsToggle->geometry().contains(widgetPoint)))
? (Flag::Move | Flag::Maximize)
: Flag::None;
});
}
void GroupPanel::initWidget() {
void Panel::initWidget() {
widget()->setMouseTracking(true);
widget()->paintRequest(
@@ -429,7 +439,7 @@ void GroupPanel::initWidget() {
}, widget()->lifetime());
}
void GroupPanel::endCall() {
void Panel::endCall() {
if (!_call) {
return;
} else if (!_call->peer()->canManageGroupCall()) {
@@ -437,13 +447,13 @@ void GroupPanel::endCall() {
return;
}
_layerBg->showBox(Box(
Group::LeaveBox,
LeaveBox,
_call,
false,
Group::BoxContext::GroupCallPanel));
BoxContext::GroupCallPanel));
}
void GroupPanel::initControls() {
void Panel::initControls() {
_mute->clicks(
) | rpl::filter([=](Qt::MouseButton button) {
return (button == Qt::LeftButton) && (_call != nullptr);
@@ -462,11 +472,11 @@ void GroupPanel::initControls() {
_hangup->setClickedCallback([=] { endCall(); });
_settings->setClickedCallback([=] {
if (_call) {
_layerBg->showBox(Box(Group::SettingsBox, _call));
_layerBg->showBox(Box(SettingsBox, _call));
}
});
_settings->setText(tr::lng_menu_settings());
_settings->setText(tr::lng_group_call_settings());
_hangup->setText(tr::lng_group_call_leave());
_members->desiredHeightValue(
@@ -477,7 +487,7 @@ void GroupPanel::initControls() {
initWithCall(_call);
}
void GroupPanel::initWithCall(GroupCall *call) {
void Panel::initWithCall(GroupCall *call) {
_callLifetime.destroy();
_call = call;
if (!_call) {
@@ -504,14 +514,14 @@ void GroupPanel::initWithCall(GroupCall *call) {
}, _callLifetime);
_members->toggleMuteRequests(
) | rpl::start_with_next([=](Group::MuteRequest request) {
) | rpl::start_with_next([=](MuteRequest request) {
if (_call) {
_call->toggleMute(request);
}
}, _callLifetime);
_members->changeVolumeRequests(
) | rpl::start_with_next([=](Group::VolumeRequest request) {
) | rpl::start_with_next([=](VolumeRequest request) {
if (_call) {
_call->changeVolume(request);
}
@@ -524,10 +534,27 @@ void GroupPanel::initWithCall(GroupCall *call) {
}
}, _callLifetime);
const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
_layerBg->showBox(std::move(next));
};
const auto showToast = [=](QString text) {
Ui::Toast::Show(widget(), text);
};
auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction(
_peer,
showBox,
showToast);
auto shareLink = std::move(shareLinkCallback);
_members->lifetime().add(std::move(shareLinkLifetime));
_members->addMembersRequests(
) | rpl::start_with_next([=] {
if (_call) {
addMembers();
if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) {
shareLink();
} else {
addMembers();
}
}
}, _callLifetime);
@@ -573,15 +600,61 @@ void GroupPanel::initWithCall(GroupCall *call) {
}, _callLifetime);
}
void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
void Panel::setupJoinAsChangedToasts() {
_call->rejoinEvents(
) | rpl::filter([](RejoinEvent event) {
return (event.wasJoinAs != event.nowJoinAs);
}) | rpl::map([=] {
return _call->stateValue() | rpl::filter([](State state) {
return (state == State::Joined);
}) | rpl::take(1);
}) | rpl::flatten_latest() | rpl::start_with_next([=] {
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = tr::lng_group_call_join_as_changed(
tr::now,
lt_name,
Ui::Text::Bold(_call->joinAs()->name),
Ui::Text::WithEntities),
});
}, widget()->lifetime());
}
void Panel::setupTitleChangedToasts() {
_call->titleChanged(
) | rpl::filter([=] {
return _peer->groupCall() && _peer->groupCall()->id() == _call->id();
}) | rpl::map([=] {
return _peer->groupCall()->title().isEmpty()
? _peer->name
: _peer->groupCall()->title();
}) | rpl::start_with_next([=](const QString &title) {
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = tr::lng_group_call_title_changed(
tr::now,
lt_title,
Ui::Text::Bold(title),
Ui::Text::WithEntities),
});
}, widget()->lifetime());
}
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
_titleText = real->titleValue();
const auto validateRecordingMark = [=](bool recording) {
if (!recording && _recordingMark) {
_recordingMark.destroy();
} else if (recording && !_recordingMark) {
struct State {
Ui::Animations::Simple animation;
base::Timer timer;
bool opaque = true;
};
_recordingMark.create(widget());
_recordingMark->show();
const auto state = _recordingMark->lifetime().make_state<State>();
const auto size = st::groupCallRecordingMark;
const auto skip = st::groupCallRecordingMarkSkip;
_recordingMark->resize(size + 2 * skip, size + 2 * skip);
@@ -590,12 +663,27 @@ void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
widget(),
tr::lng_group_call_is_recorded(tr::now));
});
const auto animate = [=] {
const auto opaque = state->opaque;
state->opaque = !opaque;
state->animation.start(
[=] { _recordingMark->update(); },
opaque ? 1. : kRecordingOpacity,
opaque ? kRecordingOpacity : 1.,
kRecordingAnimationDuration);
};
state->timer.setCallback(animate);
state->timer.callEach(kRecordingAnimationDuration);
animate();
_recordingMark->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(_recordingMark.data());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::groupCallMemberMutedIcon);
p.setOpacity(state->animation.value(
state->opaque ? 1. : kRecordingOpacity));
p.drawEllipse(skip, skip, size, size);
}, _recordingMark->lifetime());
}
@@ -633,7 +721,7 @@ void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
rpl::single(
_call->joinAs()
) | rpl::then(_call->rejoinEvents(
) | rpl::map([](const Group::RejoinEvent &event) {
) | rpl::map([](const RejoinEvent &event) {
return event.nowJoinAs;
})) | rpl::start_with_next([=](not_null<PeerData*> joinAs) {
auto joinAsToggle = object_ptr<Ui::UserpicButton>(
@@ -647,6 +735,7 @@ void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
_joinAsToggle->setClickedCallback([=] {
chooseJoinAs();
});
updateControlsGeometry();
}, widget()->lifetime());
} else {
_menuToggle.destroy();
@@ -655,9 +744,9 @@ void GroupPanel::subscribeToChanges(not_null<Data::GroupCall*> real) {
updateControlsGeometry();
}
void GroupPanel::chooseJoinAs() {
const auto context = Group::ChooseJoinAsProcess::Context::Switch;
const auto callback = [=](Group::JoinInfo info) {
void Panel::chooseJoinAs() {
const auto context = ChooseJoinAsProcess::Context::Switch;
const auto callback = [=](JoinInfo info) {
if (_call) {
_call->rejoinAs(info);
}
@@ -677,12 +766,12 @@ void GroupPanel::chooseJoinAs() {
_call->joinAs());
}
void GroupPanel::showMainMenu() {
void Panel::showMainMenu() {
if (_menu || !_call) {
return;
}
_menu.create(widget(), st::groupCallDropdownMenu);
Group::FillMenu(
FillMenu(
_menu.data(),
_peer,
_call,
@@ -724,7 +813,7 @@ void GroupPanel::showMainMenu() {
}
}
void GroupPanel::addMembers() {
void Panel::addMembers() {
const auto real = _peer->groupCall();
if (!_call || !real || real->id() != _call->id()) {
return;
@@ -824,7 +913,7 @@ void GroupPanel::addMembers() {
finish();
};
auto box = Box(
Group::ConfirmBox,
ConfirmBox,
TextWithEntities{ text },
tr::lng_participant_invite(),
[=] { inviteWithAdd(users, nonMembers, finishWithConfirm); });
@@ -865,7 +954,7 @@ void GroupPanel::addMembers() {
_layerBg->showBox(Box<PeerListsBox>(std::move(controllers), initBox));
}
void GroupPanel::kickMember(not_null<UserData*> user) {
void Panel::kickMember(not_null<UserData*> user) {
_layerBg->showBox(Box([=](not_null<Ui::GenericBox*> box) {
box->addRow(
object_ptr<Ui::FlatLabel>(
@@ -888,7 +977,7 @@ void GroupPanel::kickMember(not_null<UserData*> user) {
}));
}
void GroupPanel::kickMemberSure(not_null<UserData*> user) {
void Panel::kickMemberSure(not_null<UserData*> user) {
if (const auto chat = _peer->asChat()) {
chat->session().api().kickParticipant(chat, user);
} else if (const auto channel = _peer->asChannel()) {
@@ -906,26 +995,33 @@ void GroupPanel::kickMemberSure(not_null<UserData*> user) {
}
}
void GroupPanel::initLayout() {
void Panel::initLayout() {
initGeometry();
#ifndef Q_OS_MAC
_controls->raise();
Ui::Platform::TitleControlsLayoutChanged(
) | rpl::start_with_next([=] {
// _menuToggle geometry depends on _controls arrangement.
crl::on_main(widget(), [=] { updateControlsGeometry(); });
}, widget()->lifetime());
#endif // !Q_OS_MAC
}
void GroupPanel::showControls() {
void Panel::showControls() {
Expects(_call != nullptr);
widget()->showChildren();
}
void GroupPanel::closeBeforeDestroy() {
void Panel::closeBeforeDestroy() {
_window->close();
initWithCall(nullptr);
}
void GroupPanel::initGeometry() {
void Panel::initGeometry() {
const auto center = Core::App().getPointForCallPanelCenter();
const auto rect = QRect(0, 0, st::groupCallWidth, st::groupCallHeight);
_window->setGeometry(rect.translated(center - rect.center()));
@@ -934,7 +1030,7 @@ void GroupPanel::initGeometry() {
updateControlsGeometry();
}
QRect GroupPanel::computeTitleRect() const {
QRect Panel::computeTitleRect() const {
const auto skip = st::groupCallTitleTop;
const auto remove = skip + (_menuToggle
? (_menuToggle->width() + st::groupCallMenuTogglePosition.x())
@@ -943,7 +1039,7 @@ QRect GroupPanel::computeTitleRect() const {
: 0);
const auto width = widget()->width();
#ifdef Q_OS_MAC
return QRect(70, 0, width - skip - 70, 28);
return QRect(70, 0, width - remove - 70, 28);
#else // Q_OS_MAC
const auto controls = _controls->geometry();
const auto right = controls.x() + controls.width() + skip;
@@ -953,7 +1049,7 @@ QRect GroupPanel::computeTitleRect() const {
#endif // !Q_OS_MAC
}
void GroupPanel::updateControlsGeometry() {
void Panel::updateControlsGeometry() {
if (widget()->size().isEmpty()) {
return;
}
@@ -1011,7 +1107,7 @@ void GroupPanel::updateControlsGeometry() {
}
}
void GroupPanel::refreshTitle() {
void Panel::refreshTitle() {
if (!_title) {
auto text = rpl::combine(
Info::Profile::NameValue(_peer),
@@ -1036,7 +1132,9 @@ void GroupPanel::refreshTitle() {
widget(),
tr::lng_group_call_members(
lt_count_decimal,
_members->fullCountValue() | tr::to_count()),
_members->fullCountValue() | rpl::map([](int value) {
return (value > 0) ? float64(value) : 1.;
})),
st::groupCallSubtitleLabel);
_subtitle->show();
_subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
@@ -1052,7 +1150,7 @@ void GroupPanel::refreshTitle() {
top);
}
void GroupPanel::refreshTitleGeometry() {
void Panel::refreshTitleGeometry() {
if (!_title) {
return;
}
@@ -1091,7 +1189,7 @@ void GroupPanel::refreshTitleGeometry() {
}
}
void GroupPanel::paint(QRect clip) {
void Panel::paint(QRect clip) {
Painter p(widget());
auto region = QRegion(clip);
@@ -1100,7 +1198,7 @@ void GroupPanel::paint(QRect clip) {
}
}
bool GroupPanel::handleClose() {
bool Panel::handleClose() {
if (_call) {
_window->hide();
return true;
@@ -1108,8 +1206,8 @@ bool GroupPanel::handleClose() {
return false;
}
not_null<Ui::RpWidget*> GroupPanel::widget() const {
not_null<Ui::RpWidget*> Panel::widget() const {
return _window->body();
}
} // namespace Calls
} // namespace Calls::Group

View File

@@ -48,17 +48,14 @@ struct CallSignalBars;
struct CallBodyLayout;
} // namespace style
namespace Calls {
namespace Calls::Group {
class Userpic;
class SignalBars;
class Members;
class GroupMembers;
class GroupPanel final {
class Panel final {
public:
GroupPanel(not_null<GroupCall*> call);
~GroupPanel();
Panel(not_null<GroupCall*> call);
~Panel();
[[nodiscard]] bool isActive() const;
void minimize();
@@ -79,6 +76,8 @@ private:
void initWithCall(GroupCall *call);
void initLayout();
void initGeometry();
void setupJoinAsChangedToasts();
void setupTitleChangedToasts();
bool handleClose();
@@ -119,9 +118,9 @@ private:
object_ptr<Ui::IconButton> _menuToggle = { nullptr };
object_ptr<Ui::DropdownMenu> _menu = { nullptr };
object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr };
object_ptr<GroupMembers> _members;
object_ptr<Members> _members;
rpl::variable<QString> _titleText;
Group::ChooseJoinAsProcess _joinAsProcess;
ChooseJoinAsProcess _joinAsProcess;
object_ptr<Ui::CallButton> _settings;
std::unique_ptr<Ui::CallMuteButton> _mute;
@@ -131,4 +130,4 @@ private:
};
} // namespace Calls
} // namespace Calls::Group

View File

@@ -216,14 +216,6 @@ void SettingsBox(
const auto weakBox = Ui::MakeWeak(box);
struct State {
State(not_null<Main::Session*> session) : session(session) {
}
~State() {
session->api().request(linkListenerRequestId).cancel();
session->api().request(linkSpeakerRequestId).cancel();
}
not_null<Main::Session*> session;
rpl::event_stream<QString> outputNameStream;
rpl::event_stream<QString> inputNameStream;
std::unique_ptr<Webrtc::AudioInputTester> micTester;
@@ -231,14 +223,10 @@ void SettingsBox(
float micLevel = 0.;
Ui::Animations::Simple micLevelAnimation;
base::Timer levelUpdateTimer;
std::optional<QString> linkSpeaker;
QString linkListener;
bool generatingLink = false;
mtpRequestId linkListenerRequestId = 0;
mtpRequestId linkSpeakerRequestId = 0;
};
const auto peer = call->peer();
const auto state = box->lifetime().make_state<State>(&peer->session());
const auto state = box->lifetime().make_state<State>();
const auto real = peer->groupCall();
const auto id = call->id();
const auto goodReal = (real && real->id() == id);
@@ -538,74 +526,25 @@ void SettingsBox(
//AddDivider(layout);
//AddSkip(layout);
if (!peer->canManageGroupCall()) {
state->linkSpeaker = QString();
}
auto shareLink = Fn<void()>();
if (peer->isChannel()
&& peer->asChannel()->hasUsername()
&& goodReal) {
const auto input = real->input();
const auto shareReady = [=] {
if (!state->linkSpeaker.has_value()
|| state->linkListener.isEmpty()) {
return false;
}
const auto showToast = crl::guard(box, [=](QString text) {
Ui::Toast::Show(
box->getDelegate()->outerContainer(),
text);
});
box->getDelegate()->show(ShareInviteLinkBox(
peer,
*state->linkSpeaker,
state->linkListener,
showToast));
return true;
};
shareLink = [=] {
if (shareReady() || state->generatingLink) {
return;
}
state->generatingLink = true;
state->linkListenerRequestId = peer->session().api().request(
MTPphone_ExportGroupCallInvite(
MTP_flags(0),
input
)
).done(crl::guard(box, [=](
const MTPphone_ExportedGroupCallInvite &result) {
state->linkListenerRequestId = 0;
result.match([&](
const MTPDphone_exportedGroupCallInvite &data) {
state->linkListener = qs(data.vlink());
shareReady();
});
})).send();
if (!state->linkSpeaker.has_value()) {
using Flag = MTPphone_ExportGroupCallInvite::Flag;
state->linkSpeakerRequestId = peer->session().api().request(
MTPphone_ExportGroupCallInvite(
MTP_flags(Flag::f_can_self_unmute),
input
)).done(crl::guard(box, [=](
const MTPphone_ExportedGroupCallInvite &result) {
state->linkSpeakerRequestId = 0;
result.match([&](
const MTPDphone_exportedGroupCallInvite &data) {
state->linkSpeaker = qs(data.vlink());
shareReady();
});
})).fail([=] {
state->linkSpeakerRequestId = 0;
state->linkSpeaker = QString();
shareReady();
}).send();
}
};
const auto showBox = crl::guard(box, [=](
object_ptr<Ui::BoxContent> next) {
box->getDelegate()->show(std::move(next));
});
const auto showToast = crl::guard(box, [=](QString text) {
Ui::Toast::Show(
box->getDelegate()->outerContainer(),
text);
});
auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction(
peer,
showBox,
showToast);
shareLink = std::move(shareLinkCallback);
box->lifetime().add(std::move(shareLinkLifetime));
} else {
const auto lookupLink = [=] {
if (const auto group = peer->asMegagroup()) {
@@ -698,4 +637,85 @@ void SettingsBox(
});
}
std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
not_null<PeerData*> peer,
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
Fn<void(QString)> showToast) {
auto lifetime = rpl::lifetime();
struct State {
State(not_null<Main::Session*> session) : session(session) {
}
~State() {
session->api().request(linkListenerRequestId).cancel();
session->api().request(linkSpeakerRequestId).cancel();
}
not_null<Main::Session*> session;
std::optional<QString> linkSpeaker;
QString linkListener;
mtpRequestId linkListenerRequestId = 0;
mtpRequestId linkSpeakerRequestId = 0;
bool generatingLink = false;
};
const auto state = lifetime.make_state<State>(&peer->session());
if (!peer->canManageGroupCall()) {
state->linkSpeaker = QString();
}
const auto shareReady = [=] {
if (!state->linkSpeaker.has_value()
|| state->linkListener.isEmpty()) {
return false;
}
showBox(ShareInviteLinkBox(
peer,
*state->linkSpeaker,
state->linkListener,
showToast));
return true;
};
auto callback = [=] {
const auto real = peer->groupCall();
if (shareReady() || state->generatingLink || !real) {
return;
}
state->generatingLink = true;
state->linkListenerRequestId = peer->session().api().request(
MTPphone_ExportGroupCallInvite(
MTP_flags(0),
real->input()
)
).done([=](const MTPphone_ExportedGroupCallInvite &result) {
state->linkListenerRequestId = 0;
result.match([&](
const MTPDphone_exportedGroupCallInvite &data) {
state->linkListener = qs(data.vlink());
shareReady();
});
}).send();
if (!state->linkSpeaker.has_value()) {
using Flag = MTPphone_ExportGroupCallInvite::Flag;
state->linkSpeakerRequestId = peer->session().api().request(
MTPphone_ExportGroupCallInvite(
MTP_flags(Flag::f_can_self_unmute),
real->input()
)).done([=](const MTPphone_ExportedGroupCallInvite &result) {
state->linkSpeakerRequestId = 0;
result.match([&](
const MTPDphone_exportedGroupCallInvite &data) {
state->linkSpeaker = qs(data.vlink());
shareReady();
});
}).fail([=] {
state->linkSpeakerRequestId = 0;
state->linkSpeaker = QString();
shareReady();
}).send();
}
};
return { std::move(callback), std::move(lifetime) };
}
} // namespace Calls::Group

View File

@@ -19,4 +19,9 @@ void SettingsBox(
not_null<Ui::GenericBox*> box,
not_null<GroupCall*> call);
[[nodiscard]] std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
not_null<PeerData*> peer,
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
Fn<void(QString)> showToast);
} // namespace Calls::Group

View File

@@ -65,8 +65,11 @@ void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
void Instance::startOrJoinGroupCall(
not_null<PeerData*> peer,
const QString &joinHash) {
const auto context = peer->groupCall()
const QString &joinHash,
bool confirmNeeded) {
const auto context = confirmNeeded
? Group::ChooseJoinAsProcess::Context::JoinWithConfirm
: peer->groupCall()
? Group::ChooseJoinAsProcess::Context::Join
: Group::ChooseJoinAsProcess::Context::Create;
_chooseJoinAs.start(peer, context, [=](object_ptr<Ui::BoxContent> box) {
@@ -224,7 +227,7 @@ void Instance::createGroupCall(
destroyGroupCall(raw);
}, raw->lifetime());
_currentGroupCallPanel = std::make_unique<GroupPanel>(raw);
_currentGroupCallPanel = std::make_unique<Group::Panel>(raw);
_currentGroupCall = std::move(call);
_currentGroupCallChanges.fire_copy(raw);
}
@@ -320,9 +323,9 @@ void Instance::handleUpdate(
}, [&](const MTPDupdatePhoneCallSignalingData &data) {
handleSignalingData(session, data);
}, [&](const MTPDupdateGroupCall &data) {
handleGroupCallUpdate(session, data.vcall());
handleGroupCallUpdate(session, update);
}, [&](const MTPDupdateGroupCallParticipants &data) {
handleGroupCallUpdate(session, data);
handleGroupCallUpdate(session, update);
}, [](const auto &) {
Unexpected("Update type in Calls::Instance::handleUpdate.");
});
@@ -407,31 +410,38 @@ void Instance::handleCallUpdate(
void Instance::handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPGroupCall &call) {
const auto callId = call.match([](const auto &data) {
return data.vid().v;
const MTPUpdate &update) {
const auto callId = update.match([](const MTPDupdateGroupCall &data) {
return data.vcall().match([](const auto &data) {
return data.vid().v;
});
}, [](const MTPDupdateGroupCallParticipants &data) {
return data.vcall().match([&](const MTPDinputGroupCall &data) {
return data.vid().v;
});
}, [](const auto &) -> uint64 {
Unexpected("Type in Instance::handleGroupCallUpdate.");
});
if (const auto existing = session->data().groupCall(callId)) {
existing->applyUpdate(call);
existing->enqueueUpdate(update);
} else {
applyGroupCallUpdateChecked(session, update);
}
if (_currentGroupCall
&& (&_currentGroupCall->peer()->session() == session)) {
_currentGroupCall->handleUpdate(call);
update.match([&](const MTPDupdateGroupCall &data) {
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
}, [](const auto &) {
});
}
}
void Instance::handleGroupCallUpdate(
void Instance::applyGroupCallUpdateChecked(
not_null<Main::Session*> session,
const MTPDupdateGroupCallParticipants &update) {
const auto callId = update.vcall().match([](const auto &data) {
return data.vid().v;
});
if (const auto existing = session->data().groupCall(callId)) {
existing->applyUpdate(update);
}
const MTPUpdate &update) {
if (_currentGroupCall
&& (&_currentGroupCall->peer()->session() == session)
&& (_currentGroupCall->id() == callId)) {
&& (&_currentGroupCall->peer()->session() == session)) {
_currentGroupCall->handleUpdate(update);
}
}

View File

@@ -26,12 +26,12 @@ class Session;
namespace Calls::Group {
struct JoinInfo;
class Panel;
} // namespace Calls::Group
namespace Calls {
class Panel;
class GroupPanel;
class Instance
: private Call::Delegate
@@ -45,10 +45,17 @@ public:
void startOutgoingCall(not_null<UserData*> user, bool video);
void startOrJoinGroupCall(
not_null<PeerData*> peer,
const QString &joinHash = QString());
const QString &joinHash = QString(),
bool confirmNeeded = false);
void handleUpdate(
not_null<Main::Session*> session,
const MTPUpdate &update);
// Called by Data::GroupCall when it is appropriate by the 'version'.
void applyGroupCallUpdateChecked(
not_null<Main::Session*> session,
const MTPUpdate &update);
void showInfoPanel(not_null<Call*> call);
void showInfoPanel(not_null<GroupCall*> call);
[[nodiscard]] Call *currentCall() const;
@@ -129,10 +136,7 @@ private:
const MTPDupdatePhoneCallSignalingData &data);
void handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPGroupCall &call);
void handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPDupdateGroupCallParticipants &update);
const MTPUpdate &update);
DhConfig _dhConfig;
@@ -146,7 +150,7 @@ private:
std::unique_ptr<GroupCall> _currentGroupCall;
rpl::event_stream<GroupCall*> _currentGroupCallChanges;
std::unique_ptr<GroupPanel> _currentGroupCallPanel;
std::unique_ptr<Group::Panel> _currentGroupCallPanel;
base::flat_map<QString, std::unique_ptr<Media::Audio::Track>> _tracks;

View File

@@ -406,7 +406,6 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
mrows.push_back({ i->second });
}
} else if (_channel && _channel->isMegagroup()) {
QMultiMap<int32, UserData*> ordered;
if (_channel->lastParticipantsRequestNeeded()) {
_channel->session().api().requestLastParticipants(_channel);
} else {
@@ -437,7 +436,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
} else if (_type == Type::BotCommands) {
bool listAllSuggestions = _filter.isEmpty();
bool hasUsername = _filter.indexOf('@') > 0;
QMap<UserData*, bool> bots;
base::flat_map<UserData*, bool> bots;
int32 cnt = 0;
if (_chat) {
if (_chat->noParticipantInfo()) {
@@ -452,7 +451,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (user->botInfo->commands.isEmpty()) {
continue;
}
bots.insert(user, true);
bots.emplace(user, true);
cnt += user->botInfo->commands.size();
}
}
@@ -461,7 +460,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
_user->session().api().requestFullPeer(_user);
}
cnt = _user->botInfo->commands.size();
bots.insert(_user, true);
bots.emplace(_user, true);
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->bots.empty()) {
if (!_channel->mgInfo->botStatus) {
@@ -477,7 +476,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (user->botInfo->commands.isEmpty()) {
continue;
}
bots.insert(user, true);
bots.emplace(user, true);
cnt += user->botInfo->commands.size();
}
}
@@ -511,9 +510,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
}
}
}
if (!bots.isEmpty()) {
for (QMap<UserData*, bool>::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
UserData *user = i.key();
if (!bots.empty()) {
for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
UserData *user = i->first;
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (!listAllSuggestions) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;

View File

@@ -348,7 +348,7 @@ QImage TabbedPanel::grabForAnimation() {
_a_show = base::take(showAnimation);
_showAnimation = base::take(showAnimationData);
_a_opacity = base::take(opacityAnimation);
_cache = base::take(_cache);
_cache = base::take(cache);
return result;
}

View File

@@ -1022,7 +1022,7 @@ void Application::unregisterLeaveSubscription(not_null<QWidget*> widget) {
#ifdef Q_OS_MAC
_leaveSubscriptions = std::move(
_leaveSubscriptions
) | ranges::action::remove_if([&](const LeaveSubscription &subscription) {
) | ranges::actions::remove_if([&](const LeaveSubscription &subscription) {
auto pointer = subscription.pointer.data();
return !pointer || (pointer == widget);
});

View File

@@ -117,7 +117,30 @@ std::map<int, const char*> BetaLogs() {
{
2006002,
"- Fix text disappearing because of cloud drafts sync.\n"
}
},
{
2006003,
"- Fix audio device selection in voice chats.\n"
"- Fix blinking self profile photo "
"in case the profile photo privacy is used.\n"
"- Fix voice chat admin menu on macOS.\n"
},
{
2006004,
"- Fix freeze in voice chats.\n"
"- Make default interface scale 110% on macOS Retina screens.\n"
},
{
2006005,
"- Improvements and fixes in new voice chat features.\n"
},
{
2006007,
"- Improve voice chat participants list updating.\n"
},
};
};

View File

@@ -786,7 +786,6 @@ void LastCrashedWindow::updateControls() {
h += _networkSettings.height() + padding;
}
QRect scr(QApplication::primaryScreen()->availableGeometry());
QSize s(2 * padding + QFontMetrics(_label.font()).horizontalAdvance(qsl("Last time Telegram Desktop was not closed properly.")) + padding + _networkSettings.width(), h);
if (s == size()) {
resizeEvent(0);

View File

@@ -94,7 +94,7 @@ QString filedialogDefaultName(
const auto nameBase = (dir.endsWith('/') ? dir : (dir + '/'))
+ base;
name = nameBase + extension;
for (int i = 0; QFileInfo(name).exists(); ++i) {
for (int i = 0; QFileInfo::exists(name); ++i) {
name = nameBase + qsl(" (%1)").arg(i + 2) + extension;
}
}
@@ -115,7 +115,7 @@ QString filedialogNextFilename(
const auto dir = directory.absolutePath();
const auto nameBase = (dir.endsWith('/') ? dir : (dir + '/')) + prefix;
auto result = nameBase + extension;
for (int i = 0; result.toLower() != cur.toLower() && QFileInfo(result).exists(); ++i) {
for (int i = 0; result.toLower() != cur.toLower() && QFileInfo::exists(result); ++i) {
result = nameBase + qsl(" (%1)").arg(i + 2) + extension;
}
return result;

View File

@@ -209,7 +209,11 @@ void Sandbox::setupScreenScale() {
LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(qEnvironmentVariable("QT_SCREEN_SCALE_FACTORS")));
}
style::SetDevicePixelRatio(int(ratio));
cSetScreenScale(style::kScaleDefault);
if (Platform::IsMac() && ratio == 2.) {
cSetScreenScale(110); // 110% for Retina screens by default.
} else {
cSetScreenScale(style::kScaleDefault);
}
}
}

View File

@@ -250,7 +250,6 @@ QString ExtractFilename(const QString &url) {
bool UnpackUpdate(const QString &filepath) {
QFile input(filepath);
QByteArray packed;
if (!input.open(QIODevice::ReadOnly)) {
LOG(("Update Error: cant read updates file!"));
return false;

View File

@@ -517,7 +517,7 @@ QString translitLetterRusEng(QChar letter, QChar next, int32 &toSkip) {
fastLetterRusEng.insert(QString::fromUtf8("ч").at(0), qsl("ch"));
fastLetterRusEng.insert(QString::fromUtf8("ш").at(0), qsl("sh"));
fastLetterRusEng.insert(QString::fromUtf8("щ").at(0), qsl("sch"));
fastLetterRusEng.insert(QString::fromUtf8("ъ").at(0), qsl(""));
fastLetterRusEng.insert(QString::fromUtf8("ъ").at(0), QString());
fastLetterRusEng.insert(QString::fromUtf8("э").at(0), qsl("e"));
fastLetterRusEng.insert(QString::fromUtf8("ю").at(0), qsl("yu"));
fastLetterRusEng.insert(QString::fromUtf8("я").at(0), qsl("ya"));
@@ -526,7 +526,7 @@ QString translitLetterRusEng(QChar letter, QChar next, int32 &toSkip) {
fastLetterRusEng.insert(QString::fromUtf8("и").at(0), qsl("i"));
fastLetterRusEng.insert(QString::fromUtf8("к").at(0), qsl("k"));
fastLetterRusEng.insert(QString::fromUtf8("ы").at(0), qsl("y"));
fastLetterRusEng.insert(QString::fromUtf8("ь").at(0), qsl(""));
fastLetterRusEng.insert(QString::fromUtf8("ь").at(0), QString());
}
QHash<QChar, QString>::const_iterator j = fastLetterRusEng.constFind(letter);
if (j != fastLetterRusEng.cend()) {

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 = 2006002;
constexpr auto AppVersionStr = "2.6.2";
constexpr auto AppVersion = 2006007;
constexpr auto AppVersionStr = "2.6.7";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -52,7 +52,8 @@ auto ChatData::defaultAdminRights(not_null<UserData*> user) -> AdminRights {
const auto isCreator = (creator == user->bareId())
|| (user->isSelf() && amCreator());
using Flag = AdminRight;
return Flag::f_change_info
return Flag::f_other
| Flag::f_change_info
| Flag::f_delete_messages
| Flag::f_ban_users
| Flag::f_invite_users

View File

@@ -213,7 +213,7 @@ QString FileNameUnsafe(
}
QString nameBase = path + nameStart;
name = nameBase + extension;
for (int i = 0; QFileInfo(name).exists(); ++i) {
for (int i = 0; QFileInfo::exists(name); ++i) {
name = nameBase + QString(" (%1)").arg(i + 2) + extension;
}

View File

@@ -22,9 +22,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
namespace {
constexpr auto kRequestPerPage = 30;
constexpr auto kRequestPerPage = 50;
constexpr auto kSpeakingAfterActive = crl::time(6000);
constexpr auto kActiveAfterJoined = crl::time(1000);
constexpr auto kWaitForUpdatesTimeout = 3 * crl::time(1000);
[[nodiscard]] QString ExtractNextOffset(const MTPphone_GroupCall &call) {
return call.match([&](const MTPDphone_groupCall &data) {
return qs(data.vparticipants_next_offset());
});
}
} // namespace
@@ -35,6 +42,7 @@ GroupCall::GroupCall(
: _id(id)
, _accessHash(accessHash)
, _peer(peer)
, _reloadByQueuedUpdatesTimer([=] { reload(); })
, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); }) {
}
@@ -48,6 +56,10 @@ uint64 GroupCall::id() const {
return _id;
}
bool GroupCall::loaded() const {
return _version > 0;
}
not_null<PeerData*> GroupCall::peer() const {
return _peer;
}
@@ -69,21 +81,24 @@ auto GroupCall::participants() const
}
void GroupCall::requestParticipants() {
if (_participantsRequestId || _reloadRequestId) {
return;
} else if (_participants.size() >= _fullCount.current() && _allReceived) {
return;
} else if (_allReceived) {
reload();
return;
if (!_savedFull) {
if (_participantsRequestId || _reloadRequestId) {
return;
} else if (_allParticipantsLoaded) {
return;
}
}
_participantsRequestId = api().request(MTPphone_GetGroupParticipants(
input(),
MTP_vector<MTPInputPeer>(), // ids
MTP_vector<MTPint>(), // ssrcs
MTP_string(_nextOffset),
MTP_string(_savedFull
? ExtractNextOffset(*_savedFull)
: _nextOffset),
MTP_int(kRequestPerPage)
)).done([=](const MTPphone_GroupParticipants &result) {
_participantsRequestId = 0;
processSavedFullCall();
result.match([&](const MTPDphone_groupParticipants &data) {
_nextOffset = qs(data.vnext_offset());
_peer->owner().processUsers(data.vusers());
@@ -91,26 +106,40 @@ void GroupCall::requestParticipants() {
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::SliceLoaded);
_fullCount = data.vcount().v;
if (!_allReceived
&& (data.vparticipants().v.size() < kRequestPerPage)) {
_allReceived = true;
}
if (_allReceived) {
_fullCount = _participants.size();
setServerParticipantsCount(data.vcount().v);
if (data.vparticipants().v.isEmpty()) {
_allParticipantsLoaded = true;
}
finishParticipantsSliceRequest();
});
_participantsSliceAdded.fire({});
_participantsRequestId = 0;
changePeerEmptyCallFlag();
}).fail([=](const MTP::Error &error) {
_fullCount = _participants.size();
_allReceived = true;
_participantsRequestId = 0;
changePeerEmptyCallFlag();
processSavedFullCall();
setServerParticipantsCount(_participants.size());
_allParticipantsLoaded = true;
finishParticipantsSliceRequest();
}).send();
}
void GroupCall::processSavedFullCall() {
if (!_savedFull) {
return;
}
_reloadRequestId = 0;
processFullCallFields(*base::take(_savedFull));
}
void GroupCall::finishParticipantsSliceRequest() {
computeParticipantsCount();
processQueuedUpdates();
_participantsSliceAdded.fire({});
}
void GroupCall::setServerParticipantsCount(int count) {
_serverParticipantsCount = count;
changePeerEmptyCallFlag();
}
void GroupCall::changePeerEmptyCallFlag() {
const auto chat = _peer->asChat();
const auto channel = _peer->asChannel();
@@ -118,7 +147,7 @@ void GroupCall::changePeerEmptyCallFlag() {
constexpr auto channelFlag = MTPDchannel::Flag::f_call_not_empty;
if (_peer->groupCall() != this) {
return;
} else if (fullCount() > 0) {
} else if (_serverParticipantsCount > 0) {
if (chat && !(chat->flags() & chatFlag)) {
chat->addFlags(chatFlag);
chat->session().changes().peerUpdated(
@@ -152,7 +181,7 @@ rpl::producer<int> GroupCall::fullCountValue() const {
}
bool GroupCall::participantsLoaded() const {
return _allReceived;
return _allParticipantsLoaded;
}
PeerData *GroupCall::participantPeerBySsrc(uint32 ssrc) const {
@@ -169,49 +198,202 @@ auto GroupCall::participantUpdated() const
return _participantUpdates.events();
}
void GroupCall::applyUpdate(const MTPGroupCall &update) {
applyCall(update, false);
void GroupCall::enqueueUpdate(const MTPUpdate &update) {
update.match([&](const MTPDupdateGroupCall &updateData) {
updateData.vcall().match([&](const MTPDgroupCall &data) {
const auto version = data.vversion().v;
if (!_version || _version == version) {
DEBUG_LOG(("Group Call Participants: "
"Apply updateGroupCall %1 -> %2"
).arg(_version
).arg(version));
applyUpdate(update);
} else if (_version < version) {
DEBUG_LOG(("Group Call Participants: "
"Queue updateGroupCall %1 -> %2"
).arg(_version
).arg(version));
_queuedUpdates.emplace(std::pair{ version, false }, update);
}
}, [&](const MTPDgroupCallDiscarded &data) {
applyUpdate(update);
});
}, [&](const MTPDupdateGroupCallParticipants &updateData) {
const auto version = updateData.vversion().v;
const auto proj = [](const MTPGroupCallParticipant &data) {
return data.match([&](const MTPDgroupCallParticipant &data) {
return data.is_versioned();
});
};
const auto increment = ranges::contains(
updateData.vparticipants().v,
true,
proj);
const auto required = increment ? (version - 1) : version;
if (_version == required) {
DEBUG_LOG(("Group Call Participants: "
"Apply updateGroupCallParticipant %1 (%2)"
).arg(_version
).arg(Logs::b(increment)));
applyUpdate(update);
} else if (_version < required) {
DEBUG_LOG(("Group Call Participants: "
"Queue updateGroupCallParticipant %1 -> %2 (%3)"
).arg(_version
).arg(version
).arg(Logs::b(increment)));
_queuedUpdates.emplace(std::pair{ version, increment }, update);
}
}, [](const auto &) {
Unexpected("Type in GroupCall::enqueueUpdate.");
});
processQueuedUpdates();
}
void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
call.match([&](const MTPDgroupCall &data) {
if (!_version) {
_version = data.vversion().v;
}
const auto title = qs(data.vtitle().value_or_empty());
const auto recordDate = data.vrecord_start_date().value_or_empty();
const auto changed = (_joinMuted != data.is_join_muted())
|| (_fullCount.current() != data.vparticipants_count().v)
|| (_canChangeJoinMuted != data.is_can_change_join_muted())
|| (_title.current() != title)
|| (_recordStartDate.current() != recordDate);
if (!force && !changed) {
return;
} else if (!force && _version > data.vversion().v) {
reload();
return;
}
_joinMuted = data.is_join_muted();
_canChangeJoinMuted = data.is_can_change_join_muted();
_fullCount = data.vparticipants_count().v;
_title = title;
_recordStartDate = recordDate;
changePeerEmptyCallFlag();
}, [&](const MTPDgroupCallDiscarded &data) {
const auto id = _id;
const auto peer = _peer;
crl::on_main(&peer->session(), [=] {
if (peer->groupCall() && peer->groupCall()->id() == id) {
if (const auto chat = peer->asChat()) {
chat->clearGroupCall();
} else if (const auto channel = peer->asChannel()) {
channel->clearGroupCall();
}
void GroupCall::discard() {
const auto id = _id;
const auto peer = _peer;
crl::on_main(&peer->session(), [=] {
if (peer->groupCall() && peer->groupCall()->id() == id) {
if (const auto chat = peer->asChat()) {
chat->clearGroupCall();
} else if (const auto channel = peer->asChannel()) {
channel->clearGroupCall();
}
}
});
}
void GroupCall::processFullCallUsersChats(const MTPphone_GroupCall &call) {
call.match([&](const MTPDphone_groupCall &data) {
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
});
}
void GroupCall::processFullCallFields(const MTPphone_GroupCall &call) {
call.match([&](const MTPDphone_groupCall &data) {
const auto &participants = data.vparticipants().v;
const auto nextOffset = qs(data.vparticipants_next_offset());
data.vcall().match([&](const MTPDgroupCall &data) {
_participants.clear();
_speakingByActiveFinishes.clear();
_participantPeerBySsrc.clear();
_allParticipantsLoaded = false;
applyParticipantsSlice(
participants,
ApplySliceSource::SliceLoaded);
_nextOffset = nextOffset;
applyCallFields(data);
}, [&](const MTPDgroupCallDiscarded &data) {
discard();
});
});
}
void GroupCall::processFullCall(const MTPphone_GroupCall &call) {
processFullCallUsersChats(call);
processFullCallFields(call);
finishParticipantsSliceRequest();
}
void GroupCall::applyCallFields(const MTPDgroupCall &data) {
DEBUG_LOG(("Group Call Participants: "
"Set from groupCall %1 -> %2"
).arg(_version
).arg(data.vversion().v));
_version = data.vversion().v;
if (!_version) {
LOG(("API Error: Got zero version in groupCall."));
_version = 1;
}
_joinMuted = data.is_join_muted();
_canChangeJoinMuted = data.is_can_change_join_muted();
_joinedToTop = !data.is_join_date_asc();
setServerParticipantsCount(data.vparticipants_count().v);
changePeerEmptyCallFlag();
_title = qs(data.vtitle().value_or_empty());
_recordStartDate = data.vrecord_start_date().value_or_empty();
_allParticipantsLoaded
= (_serverParticipantsCount == _participants.size());
}
void GroupCall::applyLocalUpdate(
const MTPDupdateGroupCallParticipants &update) {
applyParticipantsSlice(
update.vparticipants().v,
ApplySliceSource::UpdateReceived);
}
void GroupCall::applyUpdate(const MTPUpdate &update) {
update.match([&](const MTPDupdateGroupCall &data) {
data.vcall().match([&](const MTPDgroupCall &data) {
applyCallFields(data);
computeParticipantsCount();
processQueuedUpdates();
}, [&](const MTPDgroupCallDiscarded &data) {
discard();
});
}, [&](const MTPDupdateGroupCallParticipants &data) {
DEBUG_LOG(("Group Call Participants: "
"Set from updateGroupCallParticipants %1 -> %2"
).arg(_version
).arg(data.vversion().v));
_version = data.vversion().v;
if (!_version) {
LOG(("API Error: "
"Got zero version in updateGroupCallParticipants."));
_version = 1;
}
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::UpdateReceived);
}, [](const auto &) {
Unexpected("Type in GroupCall::processQueuedUpdates.");
});
Core::App().calls().applyGroupCallUpdateChecked(
&_peer->session(),
update);
}
void GroupCall::processQueuedUpdates() {
if (!_version) {
return;
}
const auto size = _queuedUpdates.size();
while (!_queuedUpdates.empty()) {
const auto &entry = _queuedUpdates.front();
const auto version = entry.first.first;
const auto versionIncremented = entry.first.second;
if ((version < _version)
|| (version == _version && versionIncremented)) {
_queuedUpdates.erase(_queuedUpdates.begin());
} else if (version == _version
|| (version == _version + 1 && versionIncremented)) {
const auto update = entry.second;
_queuedUpdates.erase(_queuedUpdates.begin());
applyUpdate(update);
} else {
break;
}
}
if (_queuedUpdates.empty()) {
_reloadByQueuedUpdatesTimer.cancel();
} else if (_queuedUpdates.size() != size
|| !_reloadByQueuedUpdatesTimer.isActive()) {
_reloadByQueuedUpdatesTimer.callOnce(kWaitForUpdatesTimeout);
}
}
void GroupCall::computeParticipantsCount() {
_fullCount = _allParticipantsLoaded
? int(_participants.size())
: std::max(int(_participants.size()), _serverParticipantsCount);
}
void GroupCall::reload() {
if (_reloadRequestId) {
return;
@@ -219,28 +401,48 @@ void GroupCall::reload() {
api().request(_participantsRequestId).cancel();
_participantsRequestId = 0;
}
DEBUG_LOG(("Group Call Participants: "
"Reloading with queued: %1"
).arg(_queuedUpdates.size()));
while (!_queuedUpdates.empty()) {
const auto &entry = _queuedUpdates.front();
const auto update = entry.second;
_queuedUpdates.erase(_queuedUpdates.begin());
applyUpdate(update);
}
_reloadByQueuedUpdatesTimer.cancel();
_reloadRequestId = api().request(
MTPphone_GetGroupCall(input())
).done([=](const MTPphone_GroupCall &result) {
result.match([&](const MTPDphone_groupCall &data) {
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
_participants.clear();
_speakingByActiveFinishes.clear();
_participantPeerBySsrc.clear();
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::SliceLoaded);
applyCall(data.vcall(), true);
_allReceived = (_fullCount.current() == _participants.size());
_participantsSliceAdded.fire({});
});
if (requestParticipantsAfterReload(result)) {
_savedFull = result;
processFullCallUsersChats(result);
requestParticipants();
return;
}
_reloadRequestId = 0;
processFullCall(result);
}).fail([=](const MTP::Error &error) {
_reloadRequestId = 0;
}).send();
}
bool GroupCall::requestParticipantsAfterReload(
const MTPphone_GroupCall &call) const {
return call.match([&](const MTPDphone_groupCall &data) {
const auto received = data.vparticipants().v.size();
const auto size = data.vcall().match([&](const MTPDgroupCall &data) {
return data.vparticipants_count().v;
}, [](const auto &) {
return 0;
});
return (received < size) && (received < _participants.size());
});
}
void GroupCall::applyParticipantsSlice(
const QVector<MTPGroupCallParticipant> &list,
ApplySliceSource sliceSource) {
@@ -248,7 +450,6 @@ void GroupCall::applyParticipantsSlice(
const auto now = base::unixtime::now();
const auto speakingAfterActive = TimeId(kSpeakingAfterActive / 1000);
auto changedCount = _fullCount.current();
for (const auto &participant : list) {
participant.match([&](const MTPDgroupCallParticipant &data) {
const auto participantPeerId = peerFromMTP(data.vpeer());
@@ -270,8 +471,8 @@ void GroupCall::applyParticipantsSlice(
_participantUpdates.fire(std::move(update));
}
}
if (changedCount > _participants.size()) {
--changedCount;
if (_serverParticipantsCount > 0) {
--_serverParticipantsCount;
}
return;
}
@@ -334,7 +535,7 @@ void GroupCall::applyParticipantsSlice(
*i = value;
}
if (data.is_just_joined()) {
++changedCount;
++_serverParticipantsCount;
}
if (sliceSource != ApplySliceSource::SliceLoaded) {
_participantUpdates.fire({
@@ -345,8 +546,8 @@ void GroupCall::applyParticipantsSlice(
});
}
if (sliceSource == ApplySliceSource::UpdateReceived) {
_fullCount = changedCount;
changePeerEmptyCallFlag();
computeParticipantsCount();
}
}
@@ -538,6 +739,7 @@ void GroupCall::requestUnknownParticipants() {
).done([=](const MTPphone_GroupParticipants &result) {
result.match([&](const MTPDphone_groupParticipants &data) {
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::UnknownLoaded);
@@ -601,41 +803,6 @@ bool GroupCall::inCall() const {
&& (current->state() == Calls::GroupCall::State::Joined);
}
void GroupCall::applyUpdate(const MTPDupdateGroupCallParticipants &update) {
const auto version = update.vversion().v;
const auto applyUpdate = [&] {
if (version < _version) {
return false;
}
auto versionShouldIncrement = false;
for (const auto &participant : update.vparticipants().v) {
const auto versioned = participant.match([&](
const MTPDgroupCallParticipant &data) {
return data.is_versioned();
});
if (versioned) {
versionShouldIncrement = true;
break;
}
}
return versionShouldIncrement
? (version == _version + 1)
: (version == _version);
}();
if (!applyUpdate) {
return;
}
_version = version;
applyUpdateChecked(update);
}
void GroupCall::applyUpdateChecked(
const MTPDupdateGroupCallParticipants &update) {
applyParticipantsSlice(
update.vparticipants().v,
ApplySliceSource::UpdateReceived);
}
void GroupCall::setJoinMutedLocally(bool muted) {
_joinMuted = muted;
}
@@ -648,6 +815,10 @@ bool GroupCall::canChangeJoinMuted() const {
return _canChangeJoinMuted;
}
bool GroupCall::joinedToTop() const {
return _joinedToTop;
}
ApiWrap &GroupCall::api() const {
return _peer->session().api();
}

View File

@@ -42,6 +42,7 @@ public:
~GroupCall();
[[nodiscard]] uint64 id() const;
[[nodiscard]] bool loaded() const;
[[nodiscard]] not_null<PeerData*> peer() const;
[[nodiscard]] MTPInputGroupCall input() const;
[[nodiscard]] QString title() const {
@@ -82,10 +83,10 @@ public:
[[nodiscard]] rpl::producer<> participantsSliceAdded();
[[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const;
void applyUpdate(const MTPGroupCall &update);
void applyUpdate(const MTPDupdateGroupCallParticipants &update);
void applyUpdateChecked(
void enqueueUpdate(const MTPUpdate &update);
void applyLocalUpdate(
const MTPDupdateGroupCallParticipants &update);
void applyLastSpoke(uint32 ssrc, LastSpokeTimes when, crl::time now);
void applyActiveUpdate(
PeerId participantPeerId,
@@ -99,10 +100,12 @@ public:
void setInCall();
void reload();
void processFullCall(const MTPphone_GroupCall &call);
void setJoinMutedLocally(bool muted);
[[nodiscard]] bool joinMuted() const;
[[nodiscard]] bool canChangeJoinMuted() const;
[[nodiscard]] bool joinedToTop() const;
private:
enum class ApplySliceSource {
@@ -112,14 +115,25 @@ private:
};
[[nodiscard]] ApiWrap &api() const;
void discard();
[[nodiscard]] bool inCall() const;
void applyCall(const MTPGroupCall &call, bool force);
void applyParticipantsSlice(
const QVector<MTPGroupCallParticipant> &list,
ApplySliceSource sliceSource);
void requestUnknownParticipants();
void changePeerEmptyCallFlag();
void checkFinishSpeakingByActive();
void applyCallFields(const MTPDgroupCall &data);
void applyUpdate(const MTPUpdate &update);
void setServerParticipantsCount(int count);
void computeParticipantsCount();
void processQueuedUpdates();
void processFullCallUsersChats(const MTPphone_GroupCall &call);
void processFullCallFields(const MTPphone_GroupCall &call);
[[nodiscard]] bool requestParticipantsAfterReload(
const MTPphone_GroupCall &call) const;
void processSavedFullCall();
void finishParticipantsSliceRequest();
const uint64 _id = 0;
const uint64 _accessHash = 0;
@@ -130,11 +144,16 @@ private:
mtpRequestId _reloadRequestId = 0;
rpl::variable<QString> _title;
base::flat_multi_map<std::pair<int,bool>, MTPUpdate> _queuedUpdates;
base::Timer _reloadByQueuedUpdatesTimer;
std::optional<MTPphone_GroupCall> _savedFull;
std::vector<Participant> _participants;
base::flat_map<uint32, not_null<PeerData*>> _participantPeerBySsrc;
base::flat_map<not_null<PeerData*>, crl::time> _speakingByActiveFinishes;
base::Timer _speakingByActiveFinishTimer;
QString _nextOffset;
int _serverParticipantsCount = 0;
rpl::variable<int> _fullCount = 0;
rpl::variable<TimeId> _recordStartDate = 0;
@@ -147,7 +166,8 @@ private:
bool _joinMuted = false;
bool _canChangeJoinMuted = true;
bool _allReceived = false;
bool _allParticipantsLoaded = false;
bool _joinedToTop = false;
};

View File

@@ -488,10 +488,12 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
: result->nameOrPhone;
result->setName(fname, lname, pname, uname);
if (const auto photo = data.vphoto()) {
result->setPhoto(*photo);
} else {
result->setPhoto(MTP_userProfilePhotoEmpty());
if (!minimal || data.is_apply_min_photo()) {
if (const auto photo = data.vphoto()) {
result->setPhoto(*photo);
} else {
result->setPhoto(MTP_userProfilePhotoEmpty());
}
}
if (const auto accessHash = data.vaccess_hash()) {
result->setAccessHash(accessHash->v);

View File

@@ -1093,7 +1093,7 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
}
}
ranges::action::sort(
ranges::actions::sort(
result,
std::greater<>(),
&StickerWithDate::date);

View File

@@ -782,7 +782,7 @@ void InnerWidget::paintPeerSearchResult(
QRect tr(nameleft, st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip, namewidth, st::dialogsTextFont->height);
p.setFont(st::dialogsTextFont);
QString username = peer->userName();
if (!active && username.toLower().startsWith(_peerSearchQuery)) {
if (!active && username.startsWith(_peerSearchQuery, Qt::CaseInsensitive)) {
auto first = '@' + username.mid(0, _peerSearchQuery.size());
auto second = username.mid(_peerSearchQuery.size());
auto w = st::dialogsTextFont->width(first);

View File

@@ -2628,14 +2628,14 @@ MessageIdsList HistoryInner::getSelectedItems() const {
auto result = ranges::make_subrange(
_selected.begin(),
_selected.end()
) | view::filter([](const auto &selected) {
) | views::filter([](const auto &selected) {
const auto item = selected.first;
return item && item->toHistoryMessage() && (item->id > 0);
}) | view::transform([](const auto &selected) {
}) | views::transform([](const auto &selected) {
return selected.first->fullId();
}) | to_vector;
result |= action::sort(ordered_less{}, [](const FullMsgId &msgId) {
result |= actions::sort(ordered_less{}, [](const FullMsgId &msgId) {
return msgId.channel ? msgId.msg : (msgId.msg - ServerMaxMsgId);
});
return result;

View File

@@ -1183,8 +1183,6 @@ void ComposeControls::initAutocomplete() {
}
};
const auto insertMention = [=](not_null<UserData*> user) {
auto replacement = QString();
auto entityTag = QString();
if (user->username.isEmpty()) {
_field->insertTag(
user->firstName.isEmpty() ? user->name : user->firstName,

View File

@@ -2004,7 +2004,7 @@ void ListWidget::performDrag() {
}
TextWithEntities sel;
QList<QUrl> urls;
//QList<QUrl> urls;
if (uponSelected) {
// sel = getSelectedText();
} else if (pressedHandler) {

View File

@@ -317,14 +317,14 @@ void ListController::collapse() {
return;
}
const auto remove = count - (kFirstPage - kLeavePreloaded);
ranges::action::reverse(_preloaded);
ranges::actions::reverse(_preloaded);
_preloaded.reserve(_preloaded.size() + remove);
for (auto i = 0; i != remove; ++i) {
const auto row = delegate()->peerListRowAt(count - i - 1);
_preloaded.push_back(row->peer()->asUser());
delegate()->peerListRemoveRow(row);
}
ranges::action::reverse(_preloaded);
ranges::actions::reverse(_preloaded);
delegate()->peerListRefreshRows();
const auto now = count - remove;

View File

@@ -213,7 +213,7 @@ void Widget::startShowAnimation() {
showChildren();
auto image = grabForPanelAnimation();
_a_opacity = base::take(opacityAnimation);
_cache = base::take(_cache);
_cache = base::take(cache);
_showAnimation = std::make_unique<Ui::PanelAnimation>(st::emojiPanAnimation, Ui::PanelAnimation::Origin::BottomLeft);
auto inner = rect().marginsRemoved(st::emojiPanMargins);

View File

@@ -153,7 +153,7 @@ bool FileParser::readKeyValue(const char *&from, const char *end) {
}
QByteArray FileParser::ReadFile(const QString &absolutePath, const QString &relativePath) {
QFile file(QFileInfo(relativePath).exists() ? relativePath : absolutePath);
QFile file(QFileInfo::exists(relativePath) ? relativePath : absolutePath);
if (!file.open(QIODevice::ReadOnly)) {
Ui::Integration::Instance().writeLogEntry(u"Lang Error: Could not open file at '%1' ('%2')"_q.arg(relativePath, absolutePath));
return QByteArray();

View File

@@ -151,7 +151,7 @@ private:
for (QStringList::const_iterator i = oldlogs.cbegin(), e = oldlogs.cend(); i != e; ++i) {
QString oldlog = cWorkingDir() + *i, oldlogend = i->mid(qstr("log_start").size());
if (oldlogend.size() == 1 + qstr(".txt").size() && oldlogend.at(0).isDigit() && oldlogend.midRef(1) == qstr(".txt")) {
bool removed = QFile(*i).remove();
bool removed = QFile(oldlog).remove();
LOG(("Old start log '%1' found, deleted: %2").arg(*i, Logs::b(removed)));
}
}
@@ -265,7 +265,7 @@ bool DebugModeEnabled = false;
void MoveOldDataFiles(const QString &wasDir) {
QFile data(wasDir + "data"), dataConfig(wasDir + "data_config"), tdataConfig(wasDir + "tdata/config");
if (data.exists() && dataConfig.exists() && !QFileInfo(cWorkingDir() + "data").exists() && !QFileInfo(cWorkingDir() + "data_config").exists()) { // move to home dir
if (data.exists() && dataConfig.exists() && !QFileInfo::exists(cWorkingDir() + "data") && !QFileInfo::exists(cWorkingDir() + "data_config")) { // move to home dir
LOG(("Copying data to home dir '%1' from '%2'").arg(cWorkingDir(), wasDir));
if (data.copy(cWorkingDir() + "data")) {
LOG(("Copied 'data' to home dir"));

View File

@@ -1795,8 +1795,6 @@ void MainWidget::showNewSection(
Ui::hideSettingsAndLayer();
}
QPixmap animCache;
_controller->dialogsListFocused().set(false, true);
_a_dialogsWidth.stop();

View File

@@ -194,7 +194,7 @@ void Float::paintEvent(QPaintEvent *e) {
const auto progress = playback ? playback->value() : 1.;
if (progress > 0.) {
auto pen = st::historyVideoMessageProgressFg->p;
auto was = p.pen();
//auto was = p.pen();
pen.setWidth(st::radialLine);
pen.setCapStyle(Qt::RoundCap);
p.setPen(pen);

View File

@@ -476,7 +476,6 @@ void Widget::handleSongUpdate(const TrackState &state) {
}
void Widget::updateTimeText(const TrackState &state) {
QString time;
qint64 position = 0, length = 0, display = 0;
const auto frequency = state.frequency;
const auto document = state.id.audio();

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_media.h"
#include "ui/image/image.h"
#include "main/main_session.h"
#include "core/crash_reports.h"
#include "app.h"
#include "styles/style_media_view.h"
@@ -37,6 +38,42 @@ int Round(float64 value) {
using Context = GroupThumbs::Context;
using Key = GroupThumbs::Key;
[[nodiscard]] QString DebugSerializeMsgId(FullMsgId itemId) {
return QString("msg%1_%2").arg(itemId.channel).arg(itemId.msg);
}
[[nodiscard]] QString DebugSerializePeer(PeerId peerId) {
return peerIsUser(peerId)
? QString("user%1").arg(peerToUser(peerId))
: peerIsChat(peerId)
? QString("chat%1").arg(peerToChat(peerId))
: QString("channel%1").arg(peerToChannel(peerId));
}
[[nodiscard]] QString DebugSerializeKey(const Key &key) {
return v::match(key, [&](PhotoId photoId) {
return QString("photo%1").arg(photoId);
}, [](FullMsgId itemId) {
return DebugSerializeMsgId(itemId);
}, [&](GroupThumbs::CollageKey key) {
return QString("collage%1").arg(key.index);
});
}
[[nodiscard]] QString DebugSerializeContext(const Context &context) {
return v::match(context, [](PeerId peerId) {
return DebugSerializePeer(peerId);
}, [](MessageGroupId groupId) {
return QString("group_%1_%2"
).arg(DebugSerializePeer(groupId.peer)
).arg(groupId.value);
}, [](FullMsgId item) {
return DebugSerializeMsgId(item);
}, [](v::null_t) -> QString {
return "null";
});
}
Data::FileOrigin ComputeFileOrigin(const Key &key, const Context &context) {
return v::match(key, [&](PhotoId photoId) {
return v::match(context, [&](PeerId peerId) {
@@ -474,6 +511,40 @@ void GroupThumbs::RefreshFromSlice(
}
}
template <typename Slice>
void ValidateSlice(
const Slice &slice,
const Context &context,
int from,
int index,
int till) {
auto keys = base::flat_set<Key>();
for (auto i = from; i != till; ++i) {
const auto key = ComputeKey(slice, i);
if (keys.contains(key)) {
// All items should be unique!
auto strings = QStringList();
strings.reserve(till - from);
for (auto i = from; i != till; ++i) {
strings.push_back(DebugSerializeKey(ComputeKey(slice, i)));
}
CrashReports::SetAnnotation(
"keys",
QString("%1:%2-(%3)-%4:"
).arg(DebugSerializeContext(context)
).arg(from
).arg(index
).arg(till) + strings.join(","));
if (Logs::DebugEnabled()) {
Unexpected("Bad slice in GroupThumbs.");
}
break;
} else {
keys.emplace(key);
}
}
}
template <typename Slice>
void GroupThumbs::fillItems(
const Slice &slice,
@@ -487,6 +558,8 @@ void GroupThumbs::fillItems(
const auto current = (index - from);
const auto old = base::take(_items);
ValidateSlice(slice, _context, from, index, till);
markCacheStale();
_items.reserve(till - from);
for (auto i = from; i != till; ++i) {
@@ -514,12 +587,16 @@ void GroupThumbs::animateAliveItems(int current) {
}
void GroupThumbs::fillDyingItems(const std::vector<not_null<Thumb*>> &old) {
Expects(_cache.size() >= _items.size());
_dying.reserve(_cache.size() - _items.size());
animatePreviouslyAlive(old);
markRestAsDying();
}
void GroupThumbs::markRestAsDying() {
Expects(_cache.size() >= _items.size());
_dying.reserve(_cache.size() - _items.size());
for (const auto &cacheItem : _cache) {
const auto &thumb = cacheItem.second;

View File

@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/openssl_help.h"
#include "base/qthelp_url.h"
#include "base/unixtime.h"
#include "base/platform/base_platform_info.h"
#include "zlib.h"
namespace MTP {
@@ -81,6 +82,25 @@ using namespace details;
return idsStr + "]";
}
[[nodiscard]] QString ComputeAppVersion() {
return QString::fromLatin1(AppVersionStr) + ([] {
#if defined OS_MAC_STORE
return u" Mac App Store"_q;
#elif defined OS_WIN_STORE // OS_MAC_STORE
return (Platform::IsWindows64Bit() ? u" x64"_q : QString())
+ u" Microsoft Store"_q;
#elif defined Q_OS_UNIX && !defined Q_OS_MAC // OS_MAC_STORE || OS_WIN_STORE
return Platform::InFlatpak()
? u" Flatpak"_q
: Platform::InSnap()
? u" Snap"_q
: QString();
#else // OS_MAC_STORE || OS_WIN_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
return Platform::IsWindows64Bit() ? u" x64"_q : QString();
#endif // OS_MAC_STORE || OS_WIN_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
})();
}
void WrapInvokeAfter(
SerializedRequest &to,
const SerializedRequest &from,
@@ -632,26 +652,7 @@ void SessionPrivate::tryToSend() {
const auto systemVersion = (_currentDcType == DcType::Cdn)
? "n/a"
: _instance->systemVersion();
#if defined OS_MAC_STORE
const auto appVersion = QString::fromLatin1(AppVersionStr)
+ " Mac App Store";
#elif defined OS_WIN_STORE // OS_MAC_STORE
const auto appVersion = QString::fromLatin1(AppVersionStr)
+ " Microsoft Store";
#elif defined Q_OS_UNIX && !defined Q_OS_MAC // OS_MAC_STORE || OS_WIN_STORE
const auto appVersion = [] {
if (Platform::InFlatpak()) {
return QString::fromLatin1(AppVersionStr)
+ " Flatpak";
} else if (Platform::InSnap()) {
return QString::fromLatin1(AppVersionStr)
+ " Snap";
}
return QString::fromLatin1(AppVersionStr);
}();
#else // OS_MAC_STORE || OS_WIN_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
const auto appVersion = QString::fromLatin1(AppVersionStr);
#endif // OS_MAC_STORE || OS_WIN_STORE || (defined Q_OS_UNIX && !defined Q_OS_MAC)
const auto appVersion = ComputeAppVersion();
const auto proxyType = _options->proxy.type;
const auto mtprotoProxy = (proxyType == ProxyData::Type::Mtproto);
const auto clientProxyFields = mtprotoProxy
@@ -1669,7 +1670,6 @@ SessionPrivate::HandleResult SessionPrivate::handleOneReceived(
auto rFrom = originalRequest->constData() + 8;
const auto rEnd = originalRequest->constData() + originalRequest->size();
auto toAck = QVector<MTPlong>();
if (mtpTypeId(*rFrom) == mtpc_msgs_state_req) {
MTPMsgsStateReq request;
if (!request.read(rFrom, rEnd)) {

View File

@@ -39,7 +39,7 @@ void UnsafeOpenUrl(const QString &url) {
return;
}
QProcess::execute(qsl("xdg-open"), { url });
QProcess::startDetached(qsl("xdg-open"), { url });
}
void UnsafeOpenEmailLink(const QString &email) {
@@ -83,7 +83,7 @@ void UnsafeLaunch(const QString &filepath) {
return;
}
QProcess::execute(qsl("xdg-open"), { qUrlPath.toEncoded() });
QProcess::startDetached(qsl("xdg-open"), { qUrlPath.toEncoded() });
}
} // namespace File

View File

@@ -61,9 +61,9 @@ void Launcher::initHook() {
appimagePath.size(),
md5Hash);
return qsl("appimagekit_%1-%2.desktop")
.arg(md5Hash)
.arg(AppName.utf16().replace(' ', '_'));
return qsl("appimagekit_%1-%2.desktop").arg(
md5Hash,
AppName.utf16().replace(' ', '_'));
}
return qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) ".desktop");

View File

@@ -147,6 +147,8 @@ private:
rpl::event_stream<> _accept;
rpl::event_stream<> _reject;
bool _destroyedConnected = false;
};
class GtkFileDialog : public QDialog {
@@ -261,8 +263,9 @@ void QGtkDialog::exec() {
}
void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
connect(parent, &QWindow::destroyed, this, [=] { onParentWindowDestroyed(); },
Qt::UniqueConnection);
if (!std::exchange(_destroyedConnected, true)) {
connect(parent, &QWindow::destroyed, this, [=] { onParentWindowDestroyed(); });
}
setParent(parent);
setFlags(flags);
setModality(modality);

View File

@@ -465,7 +465,7 @@ std::optional<bool> IsDarkMode() {
if (!themeName.has_value()) {
return std::nullopt;
} else if (themeName->toLower().contains(qsl("-dark"))) {
} else if (themeName->contains(qsl("-dark"), Qt::CaseInsensitive)) {
return true;
}
@@ -593,6 +593,9 @@ void start() {
Glib::init();
Gio::init();
Glib::set_prgname(cExeName().toStdString());
Glib::set_application_name(std::string(AppName));
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->prepareEnvironment();
} else {

View File

@@ -140,7 +140,7 @@ void GroupMembersWidget::refreshUserOnline(UserData *user) {
_now = base::unixtime::now();
auto member = getMember(it.value());
auto member = getMember(it->second);
member->statusHasOnlineColor = !user->isBot()
&& Data::OnlineTextActive(user->onlineTill, _now);
member->onlineTill = user->onlineTill;
@@ -420,16 +420,16 @@ void GroupMembersWidget::setItemFlags(
auto GroupMembersWidget::computeMember(not_null<UserData*> user)
-> not_null<Member*> {
auto it = _membersByUser.constFind(user);
auto it = _membersByUser.find(user);
if (it == _membersByUser.cend()) {
auto member = new Member(user);
it = _membersByUser.insert(user, member);
it = _membersByUser.emplace(user, member).first;
member->statusHasOnlineColor = !user->isBot()
&& Data::OnlineTextActive(user->onlineTill, _now);
member->onlineTill = user->onlineTill;
member->onlineForSort = Data::SortByOnlineValue(user, _now);
}
return it.value();
return it->second;
}
void GroupMembersWidget::onUpdateOnlineDisplay() {
@@ -461,7 +461,7 @@ void GroupMembersWidget::onUpdateOnlineDisplay() {
GroupMembersWidget::~GroupMembersWidget() {
auto members = base::take(_membersByUser);
for_const (auto member, members) {
for (const auto &[_, member] : members) {
delete member;
}
}

View File

@@ -79,7 +79,7 @@ private:
not_null<ChannelData*> megagroup);
bool addUsersToEnd(not_null<ChannelData*> megagroup);
QMap<UserData*, Member*> _membersByUser;
base::flat_map<UserData*, Member*> _membersByUser;
bool _sortByOnline = false;
TimeId _now = 0;

View File

@@ -38,15 +38,15 @@ QString ToFilePart(FileKey val) {
bool KeyAlreadyUsed(QString &name) {
name += '0';
if (QFileInfo(name).exists()) {
if (QFileInfo::exists(name)) {
return true;
}
name[name.size() - 1] = '1';
if (QFileInfo(name).exists()) {
if (QFileInfo::exists(name)) {
return true;
}
name[name.size() - 1] = 's';
if (QFileInfo(name).exists()) {
if (QFileInfo::exists(name)) {
return true;
}
return false;
@@ -319,7 +319,7 @@ bool ReadFile(
// detect order of read attempts
QString toTry[2];
const auto modern = base + 's';
if (QFileInfo(modern).exists()) {
if (QFileInfo::exists(modern)) {
toTry[0] = modern;
} else {
// Legacy way.

View File

@@ -387,9 +387,9 @@ QString FormatUpdateNotification(const QString &path, const Delta &delta) {
if (!delta.changed.empty()) {
result += qstr("-------- Modified --------\n\n");
for (const auto question : delta.changed) {
result += qsl("Q: %1\nA: %2\n\n"
).arg(question->question
).arg(question->value.trimmed());
result += qsl("Q: %1\nA: %2\n\n").arg(
question->question,
question->value.trimmed());
}
}
if (!delta.removed.empty()) {
@@ -734,7 +734,7 @@ auto Templates::query(const QString &text) const -> std::vector<Question> {
pairById
) | ranges::views::filter([](const Pair &pair) {
return pair.second > 0;
}) | ranges::to_vector | ranges::action::stable_sort(sorter);
}) | ranges::to_vector | ranges::actions::stable_sort(sorter);
return good | ranges::views::transform([&](const Pair &pair) {
return questionById(pair.first);
}) | ranges::views::take(kQueryLimit) | ranges::to_vector;

View File

@@ -31,7 +31,7 @@ CountryInput::CountryInput(QWidget *parent, const style::InputField &st) : TWidg
auto availableWidth = width() - _st.textMargins.left() - _st.textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
auto placeholderFont = _st.placeholderFont->f;
placeholderFont.setStyleStrategy(QFont::PreferMatch);
auto metrics = QFontMetrics(placeholderFont);
//auto metrics = QFontMetrics(placeholderFont);
auto placeholder = QString();// metrics.elidedText(tr::lng_country_fake_ph(tr::now), Qt::ElideRight, availableWidth);
if (!placeholder.isNull()) {
_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, placeholder);

View File

@@ -389,7 +389,7 @@ QImage FilterIconPanel::grabForAnimation() {
_a_show = base::take(showAnimation);
_showAnimation = base::take(showAnimationData);
_a_opacity = base::take(opacityAnimation);
_cache = base::take(_cache);
_cache = base::take(cache);
return result;
}

View File

@@ -223,10 +223,12 @@ UserpicButton::UserpicButton(
, _peer(peer)
, _cropTitle(CropTitle(_peer))
, _role(role) {
Expects(_role != Role::OpenProfile);
Expects(_role != Role::OpenProfile && _role != Role::OpenPhoto);
_waiting = false;
processPeerPhoto();
prepare();
setupPeerViewers();
}
void UserpicButton::prepare() {
@@ -432,8 +434,10 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) {
if (_streamed
&& _streamed->player().ready()
&& !_streamed->player().videoSize().isEmpty()) {
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::RoundPlaying);
const auto paused = _controller
? _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::RoundPlaying)
: false;
auto request = Media::Streaming::FrameRequest();
auto size = QSize{ _st.photoSize, _st.photoSize };
request.outer = size * cIntRetinaFactor();

View File

@@ -13,14 +13,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
void ShowMultilineToast(MultilineToastArgs &&args) {
Ui::Toast::Show(Ui::Toast::Config{
auto config = Ui::Toast::Config{
.text = std::move(args.text),
.st = &st::defaultMultilineToast,
.durationMs = (args.duration
? args.duration
: Ui::Toast::kDefaultDuration),
.multiline = true,
});
};
if (args.parentOverride) {
Ui::Toast::Show(args.parentOverride, std::move(config));
} else {
Ui::Toast::Show(std::move(config));
}
}
} // namespace Ui

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
struct MultilineToastArgs {
QWidget *parentOverride = nullptr;
TextWithEntities text;
crl::time duration = 0;
};

View File

@@ -658,8 +658,8 @@ void MainWindow::savePosition(Qt::WindowState state) {
auto centerY = realPosition.y + realPosition.h / 2;
int minDelta = 0;
QScreen *chosen = nullptr;
auto screens = QGuiApplication::screens();
for (auto screen : QGuiApplication::screens()) {
const auto screens = QGuiApplication::screens();
for (auto screen : screens) {
auto delta = (screen->geometry().center() - QPoint(centerX, centerY)).manhattanLength();
if (!chosen || delta < minDelta) {
minDelta = delta;

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_changes.h"
#include "data/data_group_call.h"
#include "data/data_chat_filters.h"
#include "passport/passport_form_controller.h"
#include "chat_helpers/tabbed_selector.h"
@@ -151,7 +152,9 @@ void SessionNavigation::resolveChannelById(
return;
}
const auto fail = [=] {
Ui::Toast::Show(tr::lng_error_post_link_invalid(tr::now));
Ui::ShowMultilineToast({
.text = { tr::lng_error_post_link_invalid(tr::now) }
});
};
_session->api().request(base::take(_resolveRequestId)).cancel();
_resolveRequestId = _session->api().request(MTPchannels_GetChannels(
@@ -176,17 +179,48 @@ void SessionNavigation::showPeerByLinkResolved(
not_null<PeerData*> peer,
const PeerByLinkInfo &info) {
if (info.voicechatHash && peer->isChannel()) {
const auto bad = [=] {
Ui::ShowMultilineToast({
.text = { tr::lng_group_invite_bad_link(tr::now) }
});
};
const auto hash = *info.voicechatHash;
_session->api().request(base::take(_resolveRequestId)).cancel();
_resolveRequestId = _session->api().request(
MTPchannels_GetFullChannel(peer->asChannel()->inputChannel)
).done([=](const MTPmessages_ChatFull &result) {
_session->api().processFullPeer(peer, result);
if (const auto call = peer->groupCall()) {
parentController()->startOrJoinGroupCall(peer, hash);
} else {
Ui::Toast::Show(tr::lng_error_post_link_invalid(tr::now));
const auto call = peer->groupCall();
if (!call) {
bad();
return;
}
const auto join = [=] {
parentController()->startOrJoinGroupCall(
peer,
hash,
SessionController::GroupCallJoinConfirm::Always);
};
if (call->loaded()) {
join();
return;
}
const auto id = call->id();
_resolveRequestId = _session->api().request(
MTPphone_GetGroupCall(call->input())
).done([=](const MTPphone_GroupCall &result) {
if (const auto now = peer->groupCall()
; now && now->id() == id) {
if (!now->loaded()) {
now->processFullCall(result);
}
join();
} else {
bad();
}
}).fail([=](const MTP::Error &error) {
bad();
}).send();
}).send();
return;
}
@@ -949,30 +983,32 @@ void SessionController::closeThirdSection() {
void SessionController::startOrJoinGroupCall(
not_null<PeerData*> peer,
QString joinHash,
bool confirmedLeaveOther) {
GroupCallJoinConfirm confirm) {
auto &calls = Core::App().calls();
const auto confirm = [&](QString text, QString button) {
const auto askConfirmation = [&](QString text, QString button) {
Ui::show(Box<ConfirmBox>(text, button, crl::guard(this, [=] {
Ui::hideLayer();
startOrJoinGroupCall(peer, joinHash, true);
startOrJoinGroupCall(peer, joinHash, GroupCallJoinConfirm::None);
})));
};
if (!confirmedLeaveOther && calls.inCall()) {
if (confirm != GroupCallJoinConfirm::None && calls.inCall()) {
// Do you want to leave your active voice chat
// to join a voice chat in this group?
confirm(
askConfirmation(
tr::lng_call_leave_to_other_sure(tr::now),
tr::lng_call_bar_hangup(tr::now));
} else if (!confirmedLeaveOther && calls.inGroupCall()) {
} else if (confirm != GroupCallJoinConfirm::None
&& calls.inGroupCall()) {
if (calls.currentGroupCall()->peer() == peer) {
calls.activateCurrentCall(joinHash);
} else {
confirm(
askConfirmation(
tr::lng_group_call_leave_to_other_sure(tr::now),
tr::lng_group_call_leave(tr::now));
}
} else {
calls.startOrJoinGroupCall(peer, joinHash);
const auto confirmNeeded = (confirm == GroupCallJoinConfirm::Always);
calls.startOrJoinGroupCall(peer, joinHash, confirmNeeded);
}
}

View File

@@ -298,10 +298,15 @@ public:
void resizeForThirdSection();
void closeThirdSection();
enum class GroupCallJoinConfirm {
None,
IfNowInAnother,
Always,
};
void startOrJoinGroupCall(
not_null<PeerData*> peer,
QString joinHash = QString(),
bool confirmedLeaveOther = false);
GroupCallJoinConfirm confirm = GroupCallJoinConfirm::IfNowInAnother);
void showSection(
std::shared_ptr<SectionMemento> memento,

View File

@@ -138,10 +138,17 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
echo "Copying from docker result folder."
cp "$ReleasePath/root/$BinaryName" "$ReleasePath/$BinaryName"
cp "$ReleasePath/root/$BinaryName.sym" "$ReleasePath/$BinaryName.sym"
cp "$ReleasePath/root/Updater" "$ReleasePath/Updater"
cp "$ReleasePath/root/Packer" "$ReleasePath/Packer"
echo "Dumping debug symbols.."
"$ReleasePath/dump_syms" "$ReleasePath/$BinaryName" > "$ReleasePath/$BinaryName.sym"
echo "Done!"
echo "Stripping the executable.."
strip -s "$ReleasePath/$BinaryName"
echo "Done!"
echo "Preparing version $AppVersionStrFull, executing Packer.."
cd "$ReleasePath"
"./Packer" -path "$BinaryName" -path Updater -version $VersionForPacker $AlphaBetaParam

View File

@@ -16,7 +16,7 @@ if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then
fi
Run () {
scl enable devtoolset-8 -- "$@"
scl enable devtoolset-9 -- "$@"
}
HomePath="$FullScriptPath/../.."
@@ -86,17 +86,8 @@ if [ "$BadCount" != "0" ]; then
Error "Bad GCC usages found: $BadCount"
fi
echo "Dumping debug symbols.."
/dump_syms "$ReleasePath/$BinaryName" > "$ReleasePath/$BinaryName.sym"
echo "Done!"
echo "Stripping the executable.."
strip -s "$ReleasePath/$BinaryName"
echo "Done!"
rm -rf "$ReleasePath/root"
mkdir "$ReleasePath/root"
mv "$ReleasePath/$BinaryName" "$ReleasePath/root/"
mv "$ReleasePath/$BinaryName.sym" "$ReleasePath/root/"
mv "$ReleasePath/Updater" "$ReleasePath/root/"
mv "$ReleasePath/Packer" "$ReleasePath/root/"

View File

@@ -19,10 +19,10 @@ RUN yum -y install git cmake3 meson ninja-build autoconf automake libtool \
freetype-devel libX11-devel at-spi2-core-devel alsa-lib-devel \
pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel \
webkitgtk4-devel pkgconfig bison yasm file which xorg-x11-util-macros \
devtoolset-8-make devtoolset-8-gcc devtoolset-8-gcc-c++ \
devtoolset-8-binutils
devtoolset-9-make devtoolset-9-gcc devtoolset-9-gcc-c++ \
devtoolset-9-binutils
SHELL [ "scl", "enable", "devtoolset-8", "--", "bash", "-c" ]
SHELL [ "scl", "enable", "devtoolset-9", "--", "bash", "-c" ]
RUN ln -s cmake3 /usr/bin/cmake
ENV LibrariesPath /usr/src/Libraries
@@ -402,6 +402,7 @@ RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/ffmpeg-cache" install
FROM builder AS openal
ADD https://api.github.com/repos/telegramdesktop/openal-soft/git/refs/heads/fix_pulse_default openal-soft-version.json
RUN git clone -b fix_pulse_default --depth=1 $GIT/telegramdesktop/openal-soft.git
WORKDIR openal-soft
@@ -597,7 +598,7 @@ RUN git checkout bc8fb886
RUN git clone https://chromium.googlesource.com/linux-syscall-support.git src/third_party/lss
WORKDIR src/third_party/lss
RUN git checkout a91633d1
RUN git checkout 8048ece
WORKDIR ${LibrariesPath}
ENV BreakpadCache ${LibrariesPath}/breakpad-cache

View File

@@ -1,10 +1,10 @@
#!/bin/bash
cd Telegram
scl enable devtoolset-8 -- ./configure.sh "$@"
scl enable devtoolset-9 -- ./configure.sh "$@"
if [ -n "$DEBUG" ]; then
scl enable devtoolset-8 -- cmake3 --build ../out/Debug -j$(nproc)
scl enable devtoolset-9 -- cmake3 --build ../out/Debug -j$(nproc)
else
scl enable devtoolset-8 -- cmake3 --build ../out/Release -j$(nproc)
scl enable devtoolset-9 -- cmake3 --build ../out/Release -j$(nproc)
fi

View File

@@ -15,7 +15,7 @@ fi
Command="$1"
if [ "$Command" == "" ]; then
Command="scl enable devtoolset-8 -- bash"
Command="scl enable devtoolset-9 -- bash"
fi
docker run -it --rm --cpus=8 --memory=22g -v $HOME/Telegram/DesktopPrivate:/usr/src/DesktopPrivate -v $HOME/Telegram/tdesktop:/usr/src/tdesktop tdesktop:centos_env $Command

View File

@@ -1,7 +1,7 @@
AppVersion 2006002
AppVersion 2006007
AppVersionStrMajor 2.6
AppVersionStrSmall 2.6.2
AppVersionStr 2.6.2
AppVersionStrSmall 2.6.7
AppVersionStr 2.6.7
BetaChannel 1
AlphaVersion 0
AppVersionOriginal 2.6.2.beta
AppVersionOriginal 2.6.7.beta

View File

@@ -1,3 +1,26 @@
2.6.7 beta (18.03.21)
- Improve voice chat participants list updating.
2.6.6 beta (18.03.21)
- Fix joining popular voice chats.
2.6.5 beta (17.03.21)
- Improvements and fixes in new voice chat features.
2.6.4 beta (16.03.21)
- Fix freeze in voice chats.
- Make default interface scale 110% on macOS Retina screens.
2.6.3 beta (16.03.21)
- Fix audio device selection in voice chats.
- Fix blinking self profile photo in case the profile photo privacy is used.
- Fix voice chat admin menu on macOS.
2.6.2 beta (13.03.21)
- Fix text disappearing because of cloud drafts sync.

2
cmake

Submodule cmake updated: 25f0733a60...bd9c097fea