Compare commits

..

31 Commits

Author SHA1 Message Date
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
72 changed files with 753 additions and 362 deletions

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

@@ -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.3.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,3,0
PRODUCTVERSION 2,6,3,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.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.6.2.0"
VALUE "ProductVersion", "2.6.3.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,3,0
PRODUCTVERSION 2,6,3,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.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.6.2.0"
VALUE "ProductVersion", "2.6.3.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -557,10 +557,10 @@ void Updates::feedDifference(
}
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);
}

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,50 @@ void ChooseJoinAsProcess::start(
}
return list;
});
const auto selectedId = peer->groupCallDefaultJoinAs();
if (list.empty()) {
_request->showToast(Lang::Hard::ServerError());
return;
} else if (list.size() == 1
} else if (!changingJoinAsFrom
&& list.size() == 1
&& list.front() == self
&& (!peer->isChannel()
|| !peer->asChannel()->amAnonymous()
|| (peer->isBroadcast() && !peer->canWrite()))) {
info.possibleJoinAs = std::move(list);
finish(info);
if (context != Context::JoinWithConfirm
|| (selectedId && self->id == selectedId)) {
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;
}
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 +288,13 @@ void ChooseJoinAsProcess::start(
}();
info.possibleJoinAs = std::move(list);
if (!changingJoinAsFrom
&& selectedId
&& info.joinAs->id == selectedId) {
// We already joined this voice chat, just rejoin with the same.
finish(info);
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

@@ -426,6 +426,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) {
@@ -885,18 +890,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 +934,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();
}
@@ -1369,11 +1378,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

@@ -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);
@@ -304,6 +312,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;
@@ -355,6 +361,9 @@ private:
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 +488,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 +504,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 +734,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 +767,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 +826,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 +857,7 @@ MembersController::MembersController(
: _call(call)
, _peer(call->peer())
, _menuParent(menuParent)
, _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); })
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
setupListChangeViewers(call);
@@ -1378,6 +1403,42 @@ 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,
@@ -1747,8 +1808,7 @@ std::unique_ptr<Row> MembersController::createInvitedRow(
} // namespace
GroupMembers::GroupMembers(
Members::Members(
not_null<QWidget*> parent,
not_null<GroupCall*> call)
: RpWidget(parent)
@@ -1762,25 +1822,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 +1858,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 +1870,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 +1927,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 +1953,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 +1974,7 @@ void GroupMembers::resizeToList() {
}
}
void GroupMembers::updateControlsGeometry() {
void Members::updateControlsGeometry() {
if (!_list) {
return;
}
@@ -1912,7 +1988,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 +2051,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,7 +472,7 @@ void GroupPanel::initControls() {
_hangup->setClickedCallback([=] { endCall(); });
_settings->setClickedCallback([=] {
if (_call) {
_layerBg->showBox(Box(Group::SettingsBox, _call));
_layerBg->showBox(Box(SettingsBox, _call));
}
});
@@ -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),
@@ -1052,7 +1148,7 @@ void GroupPanel::refreshTitle() {
top);
}
void GroupPanel::refreshTitleGeometry() {
void Panel::refreshTitleGeometry() {
if (!_title) {
return;
}
@@ -1091,7 +1187,7 @@ void GroupPanel::refreshTitleGeometry() {
}
}
void GroupPanel::paint(QRect clip) {
void Panel::paint(QRect clip) {
Painter p(widget());
auto region = QRegion(clip);
@@ -1100,7 +1196,7 @@ void GroupPanel::paint(QRect clip) {
}
}
bool GroupPanel::handleClose() {
bool Panel::handleClose() {
if (_call) {
_window->hide();
return true;
@@ -1108,8 +1204,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);
}

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,7 +45,8 @@ 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);
@@ -146,7 +147,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,16 @@ 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"
},
};
};

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(120); // 120% 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 = 2006003;
constexpr auto AppVersionStr = "2.6.3";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

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

@@ -212,6 +212,22 @@ void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
});
}
void GroupCall::processFullCall(const MTPphone_GroupCall &call) {
call.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({});
});
}
void GroupCall::reload() {
if (_reloadRequestId) {
return;
@@ -222,19 +238,7 @@ void GroupCall::reload() {
_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({});
});
processFullCall(result);
_reloadRequestId = 0;
}).fail([=](const MTP::Error &error) {
_reloadRequestId = 0;

View File

@@ -99,6 +99,7 @@ public:
void setInCall();
void reload();
void processFullCall(const MTPphone_GroupCall &call);
void setJoinMutedLocally(bool muted);
[[nodiscard]] bool joinMuted() const;

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,37 @@ 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(","));
Unexpected("Bad slice in GroupThumbs.");
} else {
keys.emplace(key);
}
}
}
template <typename Slice>
void GroupThumbs::fillItems(
const Slice &slice,
@@ -487,6 +555,10 @@ void GroupThumbs::fillItems(
const auto current = (index - from);
const auto old = base::take(_items);
if (Logs::DebugEnabled()) {
ValidateSlice(slice, _context, from, index, till);
}
markCacheStale();
_items.reserve(till - from);
for (auto i = from; i != till; ++i) {
@@ -514,12 +586,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

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

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,6 +179,11 @@ 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(
@@ -183,9 +191,25 @@ void SessionNavigation::showPeerByLinkResolved(
).done([=](const MTPmessages_ChatFull &result) {
_session->api().processFullPeer(peer, result);
if (const auto call = peer->groupCall()) {
parentController()->startOrJoinGroupCall(peer, hash);
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) {
now->processFullCall(result);
parentController()->startOrJoinGroupCall(
peer,
hash,
SessionController::GroupCallJoinConfirm::Always);
} else {
bad();
}
}).fail([=](const MTP::Error &error) {
bad();
}).send();
} else {
Ui::Toast::Show(tr::lng_error_post_link_invalid(tr::now));
bad();
}
}).send();
return;
@@ -949,30 +973,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

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

View File

@@ -1,3 +1,9 @@
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.