Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb6db82809 | ||
|
|
ad2d2c203f | ||
|
|
7ab919e249 | ||
|
|
d69caacded | ||
|
|
b6483cb65c | ||
|
|
baba7e272d | ||
|
|
77775b5f7c | ||
|
|
dc7a754418 | ||
|
|
24b8377a2a | ||
|
|
c8643aa1ee | ||
|
|
de6b460754 | ||
|
|
bb4be4f3dd | ||
|
|
62fe44bde8 | ||
|
|
de20ff05eb | ||
|
|
1b624d67b8 | ||
|
|
cd3b989e70 | ||
|
|
e9e4c7a8cc | ||
|
|
144e2e217f | ||
|
|
dcd1e62c36 | ||
|
|
c9d2ef278f | ||
|
|
78a666a19d | ||
|
|
2b91eedcd4 | ||
|
|
7217d14f09 | ||
|
|
fe23ba086a | ||
|
|
424ba1dbea | ||
|
|
49b8340695 | ||
|
|
4b31b4792a | ||
|
|
df420e4ccf | ||
|
|
cc35653c2c | ||
|
|
64c791a9ce | ||
|
|
7809cb8d30 | ||
|
|
379a3d74e2 | ||
|
|
c4f985ca38 | ||
|
|
6af180d6b5 | ||
|
|
05147016b0 | ||
|
|
3297bdadb5 |
6
.github/workflows/linux.yml
vendored
6
.github/workflows/linux.yml
vendored
@@ -72,12 +72,6 @@ jobs:
|
||||
- name: Get repository name.
|
||||
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Yum install.
|
||||
run: |
|
||||
yum -y autoremove git
|
||||
yum -y install https://packages.endpoint.com/rhel/7/os/x86_64/endpoint-repo-1.7-1.x86_64.rpm
|
||||
yum -y install git
|
||||
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
|
||||
2
.github/workflows/win.yml
vendored
2
.github/workflows/win.yml
vendored
@@ -218,7 +218,7 @@ jobs:
|
||||
-A Win32 ^
|
||||
-D LIBTYPE:STRING=STATIC ^
|
||||
-D FORCE_STATIC_VCRT=ON ^
|
||||
-D ALSOFT_BACKEND_WASAPI=OFF
|
||||
-D ALSOFT_BACKEND_DSOUND=OFF
|
||||
|
||||
msbuild -m OpenAL.vcxproj /property:Configuration=Debug
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ include(cmake/td_mtproto.cmake)
|
||||
include(cmake/td_lang.cmake)
|
||||
include(cmake/td_scheme.cmake)
|
||||
include(cmake/td_ui.cmake)
|
||||
include(cmake/generate_appdata_changelog.cmake)
|
||||
|
||||
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
|
||||
|
||||
@@ -1319,6 +1320,7 @@ endif()
|
||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
include(GNUInstallDirs)
|
||||
configure_file("../lib/xdg/telegramdesktop.appdata.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml" @ONLY)
|
||||
generate_appdata_changelog(Telegram "../changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml")
|
||||
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "telegram.png")
|
||||
|
||||
@@ -1345,6 +1345,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_record_cancel" = "Release outside this field to cancel";
|
||||
"lng_record_lock_cancel" = "Click outside of the circle to cancel";
|
||||
"lng_record_lock_cancel_sure" = "Are you sure you want to stop recording and discard your voice message?";
|
||||
"lng_record_listen_cancel_sure" = "Are you sure you want to discard your recorded voice message?";
|
||||
"lng_record_lock_discard" = "Discard";
|
||||
"lng_will_be_notified" = "Members will be notified when you post";
|
||||
"lng_wont_be_notified" = "Members will not be notified when you post";
|
||||
@@ -1820,7 +1821,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_settings" = "Settings";
|
||||
"lng_group_call_unmute" = "Unmute";
|
||||
"lng_group_call_you_are_live" = "You are Live";
|
||||
"lng_group_call_force_muted" = "You are muted";
|
||||
"lng_group_call_force_muted" = "You are in Listen Only mode";
|
||||
"lng_group_call_connecting" = "Connecting...";
|
||||
"lng_group_call_leave" = "Leave";
|
||||
"lng_group_call_leave_title" = "Leave voice chat";
|
||||
|
||||
@@ -122,12 +122,12 @@ userStatusLastWeek#7bf09fc = UserStatus;
|
||||
userStatusLastMonth#77ebc742 = UserStatus;
|
||||
|
||||
chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
chatFull#dc8c181 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull;
|
||||
channelFull#ef3a6acd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
@@ -365,8 +365,9 @@ updatePeerBlocked#246a4b22 peer_id:Peer blocked:Bool = Update;
|
||||
updateChannelUserTyping#ff2abe9f flags:# channel_id:int top_msg_id:flags.0?int user_id:int action:SendMessageAction = Update;
|
||||
updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector<int> pts:int pts_count:int = Update;
|
||||
updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int messages:Vector<int> pts:int pts_count:int = Update;
|
||||
updateChat#1330a196 chat_id:int = Update;
|
||||
updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<GroupCallParticipant> version:int = Update;
|
||||
updateGroupCall#5724806e channel_id:int call:GroupCall = Update;
|
||||
updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -547,7 +548,7 @@ inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
|
||||
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
|
||||
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
|
||||
|
||||
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
|
||||
stickerSet#40e237a8 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
|
||||
|
||||
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
|
||||
|
||||
@@ -1194,7 +1195,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2
|
||||
|
||||
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
|
||||
|
||||
groupCallParticipant#56b087c9 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true user_id:int date:int active_date:flags.3?int source:int = GroupCallParticipant;
|
||||
groupCallParticipant#56b087c9 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true user_id:int date:int active_date:flags.3?int source:int = GroupCallParticipant;
|
||||
|
||||
phone.groupCall#66ab0bfc call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string users:Vector<User> = phone.GroupCall;
|
||||
|
||||
@@ -1560,7 +1561,7 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati
|
||||
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
|
||||
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
|
||||
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
|
||||
phone.createGroupCall#e428fa02 channel:InputChannel random_id:int = Updates;
|
||||
phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates;
|
||||
phone.joinGroupCall#5f9c8e62 flags:# muted:flags.0?true call:InputGroupCall params:DataJSON = Updates;
|
||||
phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
|
||||
phone.editGroupCallMember#63146ae4 flags:# muted:flags.0?true call:InputGroupCall user_id:InputUser = Updates;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.4.13.0" />
|
||||
Version="2.4.14.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,4,13,0
|
||||
PRODUCTVERSION 2,4,13,0
|
||||
FILEVERSION 2,4,14,0
|
||||
PRODUCTVERSION 2,4,14,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.4.13.0"
|
||||
VALUE "FileVersion", "2.4.14.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.4.13.0"
|
||||
VALUE "ProductVersion", "2.4.14.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,4,13,0
|
||||
PRODUCTVERSION 2,4,13,0
|
||||
FILEVERSION 2,4,14,0
|
||||
PRODUCTVERSION 2,4,14,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.4.13.0"
|
||||
VALUE "FileVersion", "2.4.14.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.4.13.0"
|
||||
VALUE "ProductVersion", "2.4.14.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_folder.h"
|
||||
@@ -232,6 +233,24 @@ Updates::Updates(not_null<Main::Session*> session)
|
||||
)).done([=](const MTPupdates_State &result) {
|
||||
stateDone(result);
|
||||
}).send();
|
||||
|
||||
using namespace rpl::mappers;
|
||||
base::ObservableViewer(
|
||||
api().fullPeerUpdated()
|
||||
) | rpl::filter([](not_null<PeerData*> peer) {
|
||||
return peer->isChat() || peer->isMegagroup();
|
||||
}) | rpl::start_with_next([=](not_null<PeerData*> peer) {
|
||||
if (const auto users = _pendingSpeakingCallMembers.take(peer)) {
|
||||
if (const auto call = peer->groupCall()) {
|
||||
for (const auto [userId, when] : *users) {
|
||||
call->applyActiveUpdate(
|
||||
userId,
|
||||
when,
|
||||
peer->owner().userLoaded(userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
Main::Session &Updates::session() const {
|
||||
@@ -890,6 +909,52 @@ bool Updates::isQuitPrevent() {
|
||||
updateOnline();
|
||||
return true;
|
||||
}
|
||||
void Updates::handleSendActionUpdate(
|
||||
PeerId peerId,
|
||||
MsgId rootId,
|
||||
UserId userId,
|
||||
const MTPSendMessageAction &action) {
|
||||
const auto history = session().data().historyLoaded(peerId);
|
||||
if (!history) {
|
||||
return;
|
||||
}
|
||||
const auto peer = history->peer;
|
||||
const auto user = (userId == session().userId())
|
||||
? session().user().get()
|
||||
: session().data().userLoaded(userId);
|
||||
const auto isSpeakingInCall = (action.type()
|
||||
== mtpc_speakingInGroupCallAction);
|
||||
if (isSpeakingInCall) {
|
||||
const auto call = peer->groupCall();
|
||||
const auto now = crl::now();
|
||||
if (call) {
|
||||
call->applyActiveUpdate(userId, now, user);
|
||||
} else {
|
||||
const auto chat = peer->asChat();
|
||||
const auto channel = peer->asChannel();
|
||||
const auto active = chat
|
||||
? (chat->flags() & MTPDchat::Flag::f_call_active)
|
||||
: (channel->flags() & MTPDchannel::Flag::f_call_active);
|
||||
if (active) {
|
||||
_pendingSpeakingCallMembers.emplace(
|
||||
channel).first->second[userId] = now;
|
||||
session().api().requestFullPeer(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!user || user->isSelf()) {
|
||||
return;
|
||||
}
|
||||
const auto when = requestingDifference()
|
||||
? 0
|
||||
: base::unixtime::now();
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
rootId,
|
||||
user,
|
||||
action,
|
||||
when);
|
||||
}
|
||||
|
||||
void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
switch (updates.type()) {
|
||||
@@ -1580,57 +1645,29 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateUserTyping: {
|
||||
auto &d = update.c_updateUserTyping();
|
||||
const auto userId = peerFromUser(d.vuser_id());
|
||||
const auto history = session().data().historyLoaded(userId);
|
||||
const auto user = session().data().userLoaded(d.vuser_id().v);
|
||||
if (history && user) {
|
||||
const auto when = requestingDifference() ? 0 : base::unixtime::now();
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
MsgId(),
|
||||
user,
|
||||
d.vaction(),
|
||||
when);
|
||||
}
|
||||
handleSendActionUpdate(
|
||||
peerFromUser(d.vuser_id()),
|
||||
0,
|
||||
d.vuser_id().v,
|
||||
d.vaction());
|
||||
} break;
|
||||
|
||||
case mtpc_updateChatUserTyping: {
|
||||
auto &d = update.c_updateChatUserTyping();
|
||||
const auto history = session().data().historyLoaded(
|
||||
peerFromChat(d.vchat_id()));
|
||||
const auto user = (d.vuser_id().v == session().userId())
|
||||
? nullptr
|
||||
: session().data().userLoaded(d.vuser_id().v);
|
||||
if (history && user) {
|
||||
const auto when = requestingDifference() ? 0 : base::unixtime::now();
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
MsgId(),
|
||||
user,
|
||||
d.vaction(),
|
||||
when);
|
||||
}
|
||||
handleSendActionUpdate(
|
||||
peerFromChat(d.vchat_id()),
|
||||
0,
|
||||
d.vuser_id().v,
|
||||
d.vaction());
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelUserTyping: {
|
||||
const auto &d = update.c_updateChannelUserTyping();
|
||||
const auto history = session().data().historyLoaded(
|
||||
peerFromChannel(d.vchannel_id()));
|
||||
const auto user = (d.vuser_id().v == session().userId())
|
||||
? nullptr
|
||||
: session().data().userLoaded(d.vuser_id().v);
|
||||
if (history && user) {
|
||||
const auto when = requestingDifference()
|
||||
? 0
|
||||
: base::unixtime::now();
|
||||
const auto rootId = d.vtop_msg_id().value_or_empty();
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
rootId,
|
||||
user,
|
||||
d.vaction(),
|
||||
when);
|
||||
}
|
||||
handleSendActionUpdate(
|
||||
peerFromChannel(d.vchannel_id()),
|
||||
d.vtop_msg_id().value_or_empty(),
|
||||
d.vuser_id().v,
|
||||
d.vaction());
|
||||
} break;
|
||||
|
||||
case mtpc_updateChatParticipants: {
|
||||
|
||||
@@ -122,6 +122,12 @@ private:
|
||||
base::flat_map<not_null<ChannelData*>, crl::time> &whenMap,
|
||||
crl::time &curTime);
|
||||
|
||||
void handleSendActionUpdate(
|
||||
PeerId peerId,
|
||||
MsgId rootId,
|
||||
UserId userId,
|
||||
const MTPSendMessageAction &action);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
int32 _updatesDate = 0;
|
||||
@@ -160,6 +166,9 @@ private:
|
||||
bool _handlingChannelDifference = false;
|
||||
|
||||
base::flat_map<int, ActiveChatTracker> _activeChats;
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
base::flat_map<UserId, crl::time>> _pendingSpeakingCallMembers;
|
||||
|
||||
mtpRequestId _onlineRequest = 0;
|
||||
base::Timer _idleFinishTimer;
|
||||
|
||||
@@ -3412,7 +3412,7 @@ void ApiWrap::jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date) {
|
||||
// requestMessageAfterDate(feed, date, [=](Data::MessagePosition result) {
|
||||
// Ui::hideLayer();
|
||||
// App::wnd()->sessionController()->showSection(
|
||||
// HistoryFeed::Memento(feed, result));
|
||||
// std::make_shared<HistoryFeed::Memento>(feed, result));
|
||||
// });
|
||||
//}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ auto ListFromMimeData(not_null<const QMimeData*> data) {
|
||||
if (result.error == Error::None) {
|
||||
return result;
|
||||
} else if (data->hasImage()) {
|
||||
auto image = Platform::GetClipboardImage();
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
|
||||
@@ -1000,13 +1000,15 @@ void Controller::fillManageSection() {
|
||||
st::infoIconBlacklist);
|
||||
}
|
||||
if (hasRecentActions) {
|
||||
auto callback = [=] {
|
||||
_navigation->showSection(
|
||||
std::make_shared<AdminLog::SectionMemento>(channel));
|
||||
};
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_recent_actions(),
|
||||
rpl::single(QString()), //Empty count.
|
||||
[=] {
|
||||
_navigation->showSection(AdminLog::SectionMemento(channel));
|
||||
},
|
||||
std::move(callback),
|
||||
st::infoIconRecentActions);
|
||||
}
|
||||
|
||||
|
||||
@@ -767,7 +767,7 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||
if (result.error == Ui::PreparedList::Error::None) {
|
||||
return result;
|
||||
} else if (data->hasImage()) {
|
||||
auto image = Platform::GetClipboardImage();
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
|
||||
@@ -334,14 +334,20 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
||||
_setHash = set.vhash().v;
|
||||
_setFlags = set.vflags().v;
|
||||
_setInstallDate = set.vinstalled_date().value_or(0);
|
||||
if (const auto thumb = set.vthumb()) {
|
||||
_setThumbnail = Images::FromPhotoSize(
|
||||
&_controller->session(),
|
||||
set,
|
||||
*thumb);
|
||||
} else {
|
||||
_setThumbnail = ImageWithLocation();
|
||||
}
|
||||
_setThumbnail = [&] {
|
||||
if (const auto thumbs = set.vthumbs()) {
|
||||
for (const auto &thumb : thumbs->v) {
|
||||
const auto result = Images::FromPhotoSize(
|
||||
&_controller->session(),
|
||||
set,
|
||||
thumb);
|
||||
if (result.location.valid()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImageWithLocation();
|
||||
}();
|
||||
const auto &sets = _controller->session().data().stickers().sets();
|
||||
const auto it = sets.find(_setId);
|
||||
if (it != sets.cend()) {
|
||||
|
||||
@@ -750,16 +750,11 @@ groupCallTitle: WindowTitle(defaultWindowTitle) {
|
||||
closeIconActiveOver: groupCallTitleCloseIconOver;
|
||||
}
|
||||
|
||||
groupCallMajorBlobMinRadius: 2px;
|
||||
groupCallMajorBlobMaxRadius: 2px;
|
||||
groupCallMajorBlobIdleRadius: 2px;
|
||||
groupCallMajorBlobMaxRadius: 4px;
|
||||
|
||||
groupCallMinorBlobMinRadius: 3px;
|
||||
groupCallMinorBlobMaxRadius: 9px;
|
||||
|
||||
groupCallMajorBlobTopOffset: 0px;
|
||||
groupCallMinorBlobTopOffset: 6px;
|
||||
|
||||
groupCallBlobWidthAdditional: 40px;
|
||||
groupCallMinorBlobIdleRadius: 3px;
|
||||
groupCallMinorBlobMaxRadius: 12px;
|
||||
|
||||
callTopBarMuteCrossLine: CrossLineAnimation {
|
||||
fg: callBarFg;
|
||||
|
||||
@@ -12,13 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lang/lang_hardcoded.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -46,12 +47,12 @@ constexpr auto kUpdateSendActionEach = crl::time(500);
|
||||
|
||||
GroupCall::GroupCall(
|
||||
not_null<Delegate*> delegate,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
const MTPInputGroupCall &inputCall)
|
||||
: _delegate(delegate)
|
||||
, _channel(channel)
|
||||
, _history(channel->owner().history(channel))
|
||||
, _api(&_channel->session().mtp())
|
||||
, _peer(peer)
|
||||
, _history(peer->owner().history(peer))
|
||||
, _api(&peer->session().mtp())
|
||||
, _lastSpokeCheckTimer([=] { checkLastSpoke(); })
|
||||
, _checkJoinedTimer([=] { checkJoined(); })
|
||||
, _pushToTalkCancelTimer([=] { pushToTalkCancel(); }) {
|
||||
@@ -70,8 +71,8 @@ GroupCall::GroupCall(
|
||||
|
||||
const auto id = inputCall.c_inputGroupCall().vid().v;
|
||||
if (id) {
|
||||
if (const auto call = _channel->call(); call && call->id() == id) {
|
||||
if (!_channel->canManageCall() && call->joinMuted()) {
|
||||
if (const auto call = _peer->groupCall(); call && call->id() == id) {
|
||||
if (!_peer->canManageGroupCall() && call->joinMuted()) {
|
||||
_muted = MuteState::ForceMuted;
|
||||
}
|
||||
}
|
||||
@@ -108,9 +109,14 @@ void GroupCall::setState(State state) {
|
||||
}
|
||||
_state = state;
|
||||
|
||||
if (_state.current() == State::Joined && !_pushToTalkStarted) {
|
||||
_pushToTalkStarted = true;
|
||||
applyGlobalShortcutChanges();
|
||||
if (_state.current() == State::Joined) {
|
||||
if (!_pushToTalkStarted) {
|
||||
_pushToTalkStarted = true;
|
||||
applyGlobalShortcutChanges();
|
||||
}
|
||||
if (const auto call = _peer->groupCall(); call && call->id() == _id) {
|
||||
call->setInCall();
|
||||
}
|
||||
}
|
||||
|
||||
if (false
|
||||
@@ -137,11 +143,11 @@ void GroupCall::setState(State state) {
|
||||
|
||||
void GroupCall::start() {
|
||||
_createRequestId = _api.request(MTPphone_CreateGroupCall(
|
||||
_channel->inputChannel,
|
||||
_peer->input,
|
||||
MTP_int(rand_value<int32>())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_acceptFields = true;
|
||||
_channel->session().api().applyUpdates(result);
|
||||
_peer->session().api().applyUpdates(result);
|
||||
_acceptFields = false;
|
||||
}).fail([=](const RPCError &error) {
|
||||
LOG(("Call Error: Could not create, error: %1"
|
||||
@@ -157,7 +163,13 @@ void GroupCall::start() {
|
||||
|
||||
void GroupCall::join(const MTPInputGroupCall &inputCall) {
|
||||
setState(State::Joining);
|
||||
_channel->setCall(inputCall);
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
chat->setGroupCall(inputCall);
|
||||
} else if (const auto group = _peer->asMegagroup()) {
|
||||
group->setGroupCall(inputCall);
|
||||
} else {
|
||||
Unexpected("Peer type in GroupCall::join.");
|
||||
}
|
||||
|
||||
inputCall.match([&](const MTPDinputGroupCall &data) {
|
||||
_id = data.vid().v;
|
||||
@@ -166,7 +178,7 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
|
||||
});
|
||||
|
||||
using Update = Data::GroupCall::ParticipantUpdate;
|
||||
_channel->call()->participantUpdated(
|
||||
_peer->groupCall()->participantUpdated(
|
||||
) | rpl::filter([=](const Update &update) {
|
||||
return (_instance != nullptr) && !update.now;
|
||||
}) | rpl::start_with_next([=](const Update &update) {
|
||||
@@ -174,6 +186,10 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
|
||||
|
||||
_instance->removeSsrcs({ update.was->ssrc });
|
||||
}, _lifetime);
|
||||
|
||||
SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
|
||||
_peer = group;
|
||||
});
|
||||
}
|
||||
|
||||
void GroupCall::rejoin() {
|
||||
@@ -229,7 +245,7 @@ void GroupCall::rejoin() {
|
||||
: State::Connecting);
|
||||
applySelfInCallLocally();
|
||||
maybeSendMutedUpdate(wasMuteState);
|
||||
_channel->session().api().applyUpdates(updates);
|
||||
_peer->session().api().applyUpdates(updates);
|
||||
}).fail([=](const RPCError &error) {
|
||||
const auto type = error.type();
|
||||
LOG(("Call Error: Could not join, error: %1").arg(type));
|
||||
@@ -255,13 +271,13 @@ void GroupCall::rejoin() {
|
||||
}
|
||||
|
||||
void GroupCall::applySelfInCallLocally() {
|
||||
const auto call = _channel->call();
|
||||
const auto call = _peer->groupCall();
|
||||
if (!call || call->id() != _id) {
|
||||
return;
|
||||
}
|
||||
using Flag = MTPDgroupCallParticipant::Flag;
|
||||
const auto &participants = call->participants();
|
||||
const auto self = _channel->session().user();
|
||||
const auto self = _peer->session().user();
|
||||
const auto i = ranges::find(
|
||||
participants,
|
||||
self,
|
||||
@@ -307,7 +323,7 @@ void GroupCall::discard() {
|
||||
// Here 'this' could be destroyed by updates, so we set Ended after
|
||||
// updates being handled, but in a guarded way.
|
||||
crl::on_main(this, [=] { hangup(); });
|
||||
_channel->session().api().applyUpdates(result);
|
||||
_peer->session().api().applyUpdates(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
hangup();
|
||||
}).send();
|
||||
@@ -338,7 +354,7 @@ void GroupCall::finish(FinishType type) {
|
||||
|
||||
// We want to leave request still being sent and processed even if
|
||||
// the call is already destroyed.
|
||||
const auto session = &_channel->session();
|
||||
const auto session = &_peer->session();
|
||||
const auto weak = base::make_weak(this);
|
||||
session->api().request(MTPphone_LeaveGroupCall(
|
||||
inputCall(),
|
||||
@@ -453,7 +469,7 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto self = _channel->session().userId();
|
||||
const auto self = _peer->session().userId();
|
||||
for (const auto &participant : data.vparticipants().v) {
|
||||
participant.match([&](const MTPDgroupCallParticipant &data) {
|
||||
if (data.vuser_id().v != self) {
|
||||
@@ -560,7 +576,7 @@ void GroupCall::handleLevelsUpdated(
|
||||
&& (!_lastSendProgressUpdate
|
||||
|| _lastSendProgressUpdate + kUpdateSendActionEach < now)) {
|
||||
_lastSendProgressUpdate = now;
|
||||
_channel->session().sendProgressManager().update(
|
||||
_peer->session().sendProgressManager().update(
|
||||
_history,
|
||||
Api::SendProgressType::Speaking);
|
||||
}
|
||||
@@ -595,7 +611,7 @@ void GroupCall::audioLevelsUpdated(
|
||||
}
|
||||
|
||||
void GroupCall::checkLastSpoke() {
|
||||
const auto real = _channel->call();
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id) {
|
||||
return;
|
||||
}
|
||||
@@ -678,7 +694,7 @@ void GroupCall::sendMutedUpdate() {
|
||||
MTP_inputUserSelf()
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_updateMuteRequestId = 0;
|
||||
_channel->session().api().applyUpdates(result);
|
||||
_peer->session().api().applyUpdates(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_updateMuteRequestId = 0;
|
||||
if (error.type() == u"GROUPCALL_FORBIDDEN"_q) {
|
||||
@@ -711,7 +727,7 @@ void GroupCall::toggleMute(not_null<UserData*> user, bool mute) {
|
||||
inputCall(),
|
||||
user->inputUser
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_channel->session().api().applyUpdates(result);
|
||||
_peer->session().api().applyUpdates(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
if (error.type() == u"GROUPCALL_FORBIDDEN"_q) {
|
||||
LOG(("Call Info: Rejoin after error '%1' in editGroupCallMember."
|
||||
@@ -723,11 +739,11 @@ void GroupCall::toggleMute(not_null<UserData*> user, bool mute) {
|
||||
|
||||
std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
|
||||
const std::vector<not_null<UserData*>> &users) {
|
||||
const auto real = _channel->call();
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id) {
|
||||
return 0;
|
||||
}
|
||||
const auto owner = &_channel->owner();
|
||||
const auto owner = &_peer->owner();
|
||||
const auto &invited = owner->invitedToCallUsers(_id);
|
||||
const auto &participants = real->participants();
|
||||
auto &&toInvite = users | ranges::view::filter([&](
|
||||
@@ -748,7 +764,7 @@ std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
|
||||
inputCall(),
|
||||
MTP_vector<MTPInputUser>(slice)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_channel->session().api().applyUpdates(result);
|
||||
_peer->session().api().applyUpdates(result);
|
||||
}).send();
|
||||
slice.clear();
|
||||
};
|
||||
@@ -756,7 +772,7 @@ std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
|
||||
if (!count && slice.empty()) {
|
||||
result = user;
|
||||
}
|
||||
owner->registerInvitedToCallUser(_id, _channel, user);
|
||||
owner->registerInvitedToCallUser(_id, _peer, user);
|
||||
slice.push_back(user->inputUser);
|
||||
if (slice.size() == kMaxInvitePerSlice) {
|
||||
sendSlice();
|
||||
@@ -864,7 +880,7 @@ void GroupCall::handleControllerError(const QString &error) {
|
||||
// "{user}",
|
||||
// _user->name)));
|
||||
} else if (error == u"ERROR_AUDIO_IO"_q) {
|
||||
Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
|
||||
//Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
|
||||
}
|
||||
//finish(FinishType::Failed);
|
||||
}
|
||||
|
||||
@@ -61,15 +61,15 @@ public:
|
||||
|
||||
GroupCall(
|
||||
not_null<Delegate*> delegate,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
const MTPInputGroupCall &inputCall);
|
||||
~GroupCall();
|
||||
|
||||
[[nodiscard]] uint64 id() const {
|
||||
return _id;
|
||||
}
|
||||
[[nodiscard]] not_null<ChannelData*> channel() const {
|
||||
return _channel;
|
||||
[[nodiscard]] not_null<PeerData*> peer() const {
|
||||
return _peer;
|
||||
}
|
||||
|
||||
void start();
|
||||
@@ -161,8 +161,8 @@ private:
|
||||
[[nodiscard]] MTPInputGroupCall inputCall() const;
|
||||
|
||||
const not_null<Delegate*> _delegate;
|
||||
const not_null<ChannelData*> _channel;
|
||||
const not_null<History*> _history;
|
||||
not_null<PeerData*> _peer; // Can change in legacy group migration.
|
||||
not_null<History*> _history; // Can change in legacy group migration.
|
||||
MTP::Sender _api;
|
||||
rpl::variable<State> _state = State::Creating;
|
||||
bool _instanceConnected = false;
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "calls/calls_group_call.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_group_call.h"
|
||||
@@ -24,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_domain.h" // Core::App().domain().activate.
|
||||
#include "main/main_session.h"
|
||||
#include "base/timer.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
|
||||
#include "lang/lang_keys.h"
|
||||
#include "window/window_controller.h" // Controller::sessionController.
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -237,7 +238,7 @@ private:
|
||||
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
|
||||
|
||||
const base::weak_ptr<GroupCall> _call;
|
||||
const not_null<ChannelData*> _channel;
|
||||
not_null<PeerData*> _peer;
|
||||
|
||||
// Use only resolvedRealCall() method, not this value directly.
|
||||
Data::GroupCall *_realCallRawValue = nullptr;
|
||||
@@ -417,6 +418,7 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||
if (_blobsAnimation) {
|
||||
const auto shift = QPointF(x + size / 2., y + size / 2.);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.translate(shift);
|
||||
_blobsAnimation->blobs.paint(p, st::groupCallMemberActiveStatus);
|
||||
p.translate(-shift);
|
||||
@@ -517,7 +519,7 @@ MembersController::MembersController(
|
||||
not_null<GroupCall*> call,
|
||||
not_null<QWidget*> menuParent)
|
||||
: _call(call)
|
||||
, _channel(call->channel())
|
||||
, _peer(call->peer())
|
||||
, _menuParent(menuParent)
|
||||
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
|
||||
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
|
||||
@@ -572,12 +574,12 @@ MembersController::~MembersController() {
|
||||
}
|
||||
|
||||
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
||||
const auto channel = call->channel();
|
||||
channel->session().changes().peerFlagsValue(
|
||||
channel,
|
||||
const auto peer = call->peer();
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::GroupCall
|
||||
) | rpl::map([=] {
|
||||
return channel->call();
|
||||
return peer->groupCall();
|
||||
}) | rpl::filter([=](Data::GroupCall *real) {
|
||||
const auto call = _call.get();
|
||||
return call && real && (real->id() == call->id());
|
||||
@@ -590,7 +592,7 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
||||
call->stateValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto call = _call.get();
|
||||
const auto real = channel->call();
|
||||
const auto real = peer->groupCall();
|
||||
if (call && real && (real->id() == call->id())) {
|
||||
//updateRow(channel->session().user());
|
||||
}
|
||||
@@ -750,14 +752,14 @@ Row *MembersController::findRow(not_null<UserData*> user) const {
|
||||
|
||||
Data::GroupCall *MembersController::resolvedRealCall() const {
|
||||
return (_realCallRawValue
|
||||
&& (_channel->call() == _realCallRawValue)
|
||||
&& (_peer->groupCall() == _realCallRawValue)
|
||||
&& (_realCallRawValue->id() == _realId))
|
||||
? _realCallRawValue
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
Main::Session &MembersController::session() const {
|
||||
return _call->channel()->session();
|
||||
return _call->peer()->session();
|
||||
}
|
||||
|
||||
void MembersController::prepare() {
|
||||
@@ -767,7 +769,7 @@ void MembersController::prepare() {
|
||||
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
||||
|
||||
const auto call = _call.get();
|
||||
if (const auto real = _channel->call();
|
||||
if (const auto real = _peer->groupCall();
|
||||
real && call && real->id() == call->id()) {
|
||||
prepareRows(real);
|
||||
} else if (auto row = createSelfRow()) {
|
||||
@@ -803,10 +805,10 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
||||
}
|
||||
}
|
||||
if (!foundSelf) {
|
||||
const auto self = _channel->session().user();
|
||||
const auto self = _peer->session().user();
|
||||
const auto i = ranges::find(
|
||||
participants,
|
||||
_channel->session().user(),
|
||||
_peer->session().user(),
|
||||
&Data::GroupCall::Participant::user);
|
||||
auto row = (i != end(participants)) ? createRow(*i) : createSelfRow();
|
||||
if (row) {
|
||||
@@ -826,7 +828,7 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
||||
}
|
||||
|
||||
void MembersController::loadMoreRows() {
|
||||
if (const auto real = _channel->call()) {
|
||||
if (const auto real = _peer->groupCall()) {
|
||||
real->requestParticipants();
|
||||
}
|
||||
}
|
||||
@@ -837,7 +839,7 @@ auto MembersController::toggleMuteRequests() const
|
||||
}
|
||||
|
||||
bool MembersController::rowCanMuteMembers() {
|
||||
return _channel->canManageCall();
|
||||
return _peer->canManageGroupCall();
|
||||
}
|
||||
|
||||
void MembersController::rowUpdateRow(not_null<Row*> row) {
|
||||
@@ -998,7 +1000,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
|
||||
_kickMemberRequests.fire_copy(user);
|
||||
});
|
||||
|
||||
if (_channel->canManageCall()) {
|
||||
if (_peer->canManageGroupCall()) {
|
||||
result->addAction(
|
||||
(mute
|
||||
? tr::lng_group_call_context_mute(tr::now)
|
||||
@@ -1011,7 +1013,16 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
|
||||
result->addAction(
|
||||
tr::lng_context_send_message(tr::now),
|
||||
showHistory);
|
||||
if (_channel->canRestrictUser(user)) {
|
||||
const auto canKick = [&] {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
return chat->amCreator()
|
||||
|| (chat->canBanMembers() && !chat->admins.contains(user));
|
||||
} else if (const auto group = _peer->asMegagroup()) {
|
||||
return group->canRestrictUser(user);
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (canKick) {
|
||||
result->addAction(
|
||||
tr::lng_context_remove_from_group(tr::now),
|
||||
removeFromGroup);
|
||||
@@ -1020,7 +1031,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
|
||||
}
|
||||
|
||||
std::unique_ptr<Row> MembersController::createSelfRow() {
|
||||
const auto self = _channel->session().user();
|
||||
const auto self = _peer->session().user();
|
||||
auto result = std::make_unique<Row>(this, self);
|
||||
updateRow(result.get(), nullptr);
|
||||
return result;
|
||||
@@ -1074,7 +1085,7 @@ int GroupMembers::desiredHeight() const {
|
||||
auto desired = _header ? _header->height() : 0;
|
||||
auto count = [&] {
|
||||
if (const auto call = _call.get()) {
|
||||
if (const auto real = call->channel()->call()) {
|
||||
if (const auto real = call->peer()->groupCall()) {
|
||||
if (call->id() == real->id()) {
|
||||
return real->fullCount();
|
||||
}
|
||||
@@ -1137,9 +1148,15 @@ object_ptr<Ui::FlatLabel> GroupMembers::setupTitle(
|
||||
void GroupMembers::setupButtons(not_null<GroupCall*> call) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
_addMember->showOn(Data::CanWriteValue(
|
||||
call->channel().get()
|
||||
));
|
||||
_canAddMembers = Data::CanWriteValue(call->peer().get());
|
||||
SubscribeToMigration(
|
||||
call->peer(),
|
||||
lifetime(),
|
||||
[=](not_null<ChannelData*> channel) {
|
||||
_canAddMembers = Data::CanWriteValue(channel.get());
|
||||
});
|
||||
|
||||
_addMember->showOn(_canAddMembers.value());
|
||||
_addMember->addClickHandler([=] { // TODO throttle(ripple duration)
|
||||
_addMemberRequests.fire({});
|
||||
});
|
||||
|
||||
@@ -86,6 +86,7 @@ private:
|
||||
Ui::RpWidget *_titleWrap = nullptr;
|
||||
Ui::FlatLabel *_title = nullptr;
|
||||
Ui::IconButton *_addMember = nullptr;
|
||||
rpl::variable<bool> _canAddMembers;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -47,7 +48,7 @@ namespace {
|
||||
class InviteController final : public ParticipantsBoxController {
|
||||
public:
|
||||
InviteController(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> alreadyIn,
|
||||
int fullInCount);
|
||||
|
||||
@@ -65,7 +66,6 @@ public:
|
||||
not_null<GroupCall*> call) const;
|
||||
|
||||
private:
|
||||
void updateTitle() const;
|
||||
[[nodiscard]] int alreadyInCount() const;
|
||||
[[nodiscard]] bool isAlreadyIn(not_null<UserData*> user) const;
|
||||
[[nodiscard]] int fullCount() const;
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) const override;
|
||||
|
||||
const not_null<ChannelData*> _channel;
|
||||
not_null<PeerData*> _peer;
|
||||
const base::flat_set<not_null<UserData*>> _alreadyIn;
|
||||
const int _fullInCount = 0;
|
||||
mutable base::flat_set<not_null<UserData*>> _skippedUsers;
|
||||
@@ -81,24 +81,27 @@ private:
|
||||
};
|
||||
|
||||
InviteController::InviteController(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> alreadyIn,
|
||||
int fullInCount)
|
||||
: ParticipantsBoxController(CreateTag{}, nullptr, channel, Role::Members)
|
||||
, _channel(channel)
|
||||
: ParticipantsBoxController(CreateTag{}, nullptr, peer, Role::Members)
|
||||
, _peer(peer)
|
||||
, _alreadyIn(std::move(alreadyIn))
|
||||
, _fullInCount(std::max(fullInCount, int(_alreadyIn.size()))) {
|
||||
_skippedUsers.emplace(channel->session().user());
|
||||
_skippedUsers.emplace(peer->session().user());
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
lifetime(),
|
||||
[=](not_null<ChannelData*> channel) { _peer = channel; });
|
||||
}
|
||||
|
||||
void InviteController::prepare() {
|
||||
ParticipantsBoxController::prepare();
|
||||
updateTitle();
|
||||
delegate()->peerListSetTitle(tr::lng_group_call_invite_title());
|
||||
}
|
||||
|
||||
void InviteController::rowClicked(not_null<PeerListRow*> row) {
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> InviteController::rowContextMenu(
|
||||
@@ -108,7 +111,6 @@ base::unique_qptr<Ui::PopupMenu> InviteController::rowContextMenu(
|
||||
}
|
||||
|
||||
void InviteController::itemDeselectedHook(not_null<PeerData*> peer) {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
int InviteController::alreadyInCount() const {
|
||||
@@ -126,9 +128,7 @@ int InviteController::fullCount() const {
|
||||
std::unique_ptr<PeerListRow> InviteController::createRow(
|
||||
not_null<UserData*> user) const {
|
||||
if (user->isSelf() || user->isBot()) {
|
||||
if (_skippedUsers.emplace(user).second) {
|
||||
updateTitle();
|
||||
}
|
||||
_skippedUsers.emplace(user);
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
@@ -138,20 +138,6 @@ std::unique_ptr<PeerListRow> InviteController::createRow(
|
||||
return result;
|
||||
}
|
||||
|
||||
void InviteController::updateTitle() const {
|
||||
const auto inOrInvited = fullCount() - 1; // minus self
|
||||
const auto canBeInvited = std::max({
|
||||
delegate()->peerListFullRowsCount(), // minus self and bots
|
||||
_channel->membersCount() - int(_skippedUsers.size()), // self + bots
|
||||
inOrInvited
|
||||
});
|
||||
const auto additional = canBeInvited
|
||||
? qsl("%1 / %2").arg(inOrInvited).arg(canBeInvited)
|
||||
: QString();
|
||||
delegate()->peerListSetTitle(tr::lng_group_call_invite_title());
|
||||
delegate()->peerListSetAdditionalTitle(rpl::single(additional));
|
||||
}
|
||||
|
||||
std::variant<int, not_null<UserData*>> InviteController::inviteSelectedUsers(
|
||||
not_null<PeerListBox*> box,
|
||||
not_null<GroupCall*> call) const {
|
||||
@@ -180,7 +166,7 @@ void LeaveGroupCallBox(
|
||||
box.get(),
|
||||
tr::lng_group_call_leave_sure(),
|
||||
(inCall ? st::groupCallBoxLabel : st::boxLabel)));
|
||||
const auto discard = call->channel()->canManageCall()
|
||||
const auto discard = call->peer()->canManageGroupCall()
|
||||
? box->addRow(object_ptr<Ui::Checkbox>(
|
||||
box.get(),
|
||||
tr::lng_group_call_end(),
|
||||
@@ -211,7 +197,7 @@ void LeaveGroupCallBox(
|
||||
|
||||
GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
||||
: _call(call)
|
||||
, _channel(call->channel())
|
||||
, _peer(call->peer())
|
||||
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
|
||||
, _layerBg(std::make_unique<Ui::LayerManager>(_window->body()))
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -232,6 +218,11 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
||||
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
|
||||
_settings->setColorOverrides(_mute->colorOverrides());
|
||||
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
_window->lifetime(),
|
||||
[=](not_null<ChannelData*> channel) { migrate(channel); });
|
||||
|
||||
initWindow();
|
||||
initWidget();
|
||||
initControls();
|
||||
@@ -251,12 +242,31 @@ void GroupPanel::showAndActivate() {
|
||||
if (_window->isHidden()) {
|
||||
_window->show();
|
||||
}
|
||||
const auto state = _window->windowState();
|
||||
if (state & Qt::WindowMinimized) {
|
||||
_window->setWindowState(state & ~Qt::WindowMinimized);
|
||||
}
|
||||
_window->raise();
|
||||
_window->setWindowState(_window->windowState() | Qt::WindowActive);
|
||||
_window->activateWindow();
|
||||
_window->setFocus();
|
||||
}
|
||||
|
||||
void GroupPanel::migrate(not_null<ChannelData*> channel) {
|
||||
_peer = channel;
|
||||
_peerLifetime.destroy();
|
||||
subscribeToPeerChanges();
|
||||
_title.destroy();
|
||||
refreshTitle();
|
||||
}
|
||||
|
||||
void GroupPanel::subscribeToPeerChanges() {
|
||||
Info::Profile::NameValue(
|
||||
_peer
|
||||
) | rpl::start_with_next([=](const TextWithEntities &name) {
|
||||
_window->setTitle(name.text);
|
||||
}, _peerLifetime);
|
||||
}
|
||||
|
||||
void GroupPanel::initWindow() {
|
||||
_window->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
_window->setAttribute(Qt::WA_NoSystemBackground);
|
||||
@@ -264,11 +274,7 @@ void GroupPanel::initWindow() {
|
||||
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
|
||||
_window->setTitleStyle(st::callTitle);
|
||||
|
||||
Info::Profile::NameValue(
|
||||
_channel
|
||||
) | rpl::start_with_next([=](const TextWithEntities &name) {
|
||||
_window->setTitle(name.text);
|
||||
}, _window->lifetime());
|
||||
subscribeToPeerChanges();
|
||||
|
||||
base::install_event_filter(_window.get(), [=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close && handleClose()) {
|
||||
@@ -357,7 +363,7 @@ void GroupPanel::initWithCall(GroupCall *call) {
|
||||
return;
|
||||
}
|
||||
|
||||
_channel = _call->channel();
|
||||
_peer = _call->peer();
|
||||
|
||||
call->stateValue(
|
||||
) | rpl::filter([](State state) {
|
||||
@@ -425,17 +431,17 @@ void GroupPanel::initWithCall(GroupCall *call) {
|
||||
}
|
||||
|
||||
void GroupPanel::addMembers() {
|
||||
const auto real = _channel->call();
|
||||
const auto real = _peer->groupCall();
|
||||
if (!_call || !real || real->id() != _call->id()) {
|
||||
return;
|
||||
}
|
||||
auto alreadyIn = _channel->owner().invitedToCallUsers(real->id());
|
||||
auto alreadyIn = _peer->owner().invitedToCallUsers(real->id());
|
||||
for (const auto &participant : real->participants()) {
|
||||
alreadyIn.emplace(participant.user);
|
||||
}
|
||||
alreadyIn.emplace(_channel->session().user());
|
||||
alreadyIn.emplace(_peer->session().user());
|
||||
auto controller = std::make_unique<InviteController>(
|
||||
_channel,
|
||||
_peer,
|
||||
std::move(alreadyIn),
|
||||
real->fullCount());
|
||||
controller->setStyleOverrides(
|
||||
@@ -508,17 +514,21 @@ void GroupPanel::kickMember(not_null<UserData*> user) {
|
||||
}
|
||||
|
||||
void GroupPanel::kickMemberSure(not_null<UserData*> user) {
|
||||
const auto currentRestrictedRights = [&]() -> MTPChatBannedRights {
|
||||
const auto it = _channel->mgInfo->lastRestricted.find(user);
|
||||
return (it != _channel->mgInfo->lastRestricted.cend())
|
||||
? it->second.rights
|
||||
: MTP_chatBannedRights(MTP_flags(0), MTP_int(0));
|
||||
}();
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
chat->session().api().kickParticipant(chat, user);
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
const auto currentRestrictedRights = [&]() -> MTPChatBannedRights {
|
||||
const auto it = channel->mgInfo->lastRestricted.find(user);
|
||||
return (it != channel->mgInfo->lastRestricted.cend())
|
||||
? it->second.rights
|
||||
: MTP_chatBannedRights(MTP_flags(0), MTP_int(0));
|
||||
}();
|
||||
|
||||
_channel->session().api().kickParticipant(
|
||||
_channel,
|
||||
user,
|
||||
currentRestrictedRights);
|
||||
channel->session().api().kickParticipant(
|
||||
channel,
|
||||
user,
|
||||
currentRestrictedRights);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupPanel::initLayout() {
|
||||
@@ -609,7 +619,7 @@ void GroupPanel::refreshTitle() {
|
||||
if (!_title) {
|
||||
_title.create(
|
||||
widget(),
|
||||
Info::Profile::NameValue(_channel),
|
||||
Info::Profile::NameValue(_peer),
|
||||
st::groupCallHeaderLabel);
|
||||
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
@@ -99,8 +99,11 @@ private:
|
||||
[[nodiscard]] std::optional<QRect> computeTitleRect() const;
|
||||
void refreshTitle();
|
||||
|
||||
void migrate(not_null<ChannelData*> channel);
|
||||
void subscribeToPeerChanges();
|
||||
|
||||
GroupCall *_call = nullptr;
|
||||
not_null<ChannelData*> _channel;
|
||||
not_null<PeerData*> _peer;
|
||||
|
||||
const std::unique_ptr<Ui::Window> _window;
|
||||
const std::unique_ptr<Ui::LayerManager> _layerBg;
|
||||
@@ -118,6 +121,8 @@ private:
|
||||
std::unique_ptr<Ui::CallMuteButton> _mute;
|
||||
object_ptr<Ui::CallButton> _hangup;
|
||||
|
||||
rpl::lifetime _peerLifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Calls
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/global_shortcuts.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "core/application.h"
|
||||
#include "boxes/single_choice_box.h"
|
||||
@@ -44,19 +45,19 @@ constexpr auto kDelaysCount = 201;
|
||||
constexpr auto kCheckAccessibilityInterval = crl::time(500);
|
||||
|
||||
void SaveCallJoinMuted(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 callId,
|
||||
bool joinMuted) {
|
||||
const auto call = channel->call();
|
||||
const auto call = peer->groupCall();
|
||||
if (!call
|
||||
|| call->id() != callId
|
||||
|| !channel->canManageCall()
|
||||
|| !peer->canManageGroupCall()
|
||||
|| !call->canChangeJoinMuted()
|
||||
|| call->joinMuted() == joinMuted) {
|
||||
return;
|
||||
}
|
||||
call->setJoinMutedLocally(joinMuted);
|
||||
channel->session().api().request(MTPphone_ToggleGroupCallSettings(
|
||||
peer->session().api().request(MTPphone_ToggleGroupCallSettings(
|
||||
MTP_flags(MTPphone_ToggleGroupCallSettings::Flag::f_join_muted),
|
||||
call->input(),
|
||||
MTP_bool(joinMuted)
|
||||
@@ -101,8 +102,8 @@ void GroupCallSettingsBox(
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
|
||||
const auto channel = call->channel();
|
||||
const auto real = channel->call();
|
||||
const auto peer = call->peer();
|
||||
const auto real = peer->groupCall();
|
||||
const auto id = call->id();
|
||||
const auto goodReal = (real && real->id() == id);
|
||||
|
||||
@@ -111,7 +112,7 @@ void GroupCallSettingsBox(
|
||||
|
||||
const auto joinMuted = goodReal ? real->joinMuted() : false;
|
||||
const auto canChangeJoinMuted = (goodReal && real->canChangeJoinMuted());
|
||||
const auto addCheck = (channel->canManageCall() && canChangeJoinMuted);
|
||||
const auto addCheck = (peer->canManageGroupCall() && canChangeJoinMuted);
|
||||
if (addCheck) {
|
||||
AddSkip(layout);
|
||||
}
|
||||
@@ -402,11 +403,24 @@ void GroupCallSettingsBox(
|
||||
//AddSkip(layout);
|
||||
|
||||
const auto lookupLink = [=] {
|
||||
return channel->hasUsername()
|
||||
? channel->session().createInternalLinkFull(channel->username)
|
||||
: channel->inviteLink();
|
||||
if (const auto group = peer->asMegagroup()) {
|
||||
return group->hasUsername()
|
||||
? group->session().createInternalLinkFull(group->username)
|
||||
: group->inviteLink();
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
return chat->inviteLink();
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
if (!lookupLink().isEmpty() || channel->canHaveInviteLink()) {
|
||||
const auto canCreateLink = [&] {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
return chat->canHaveInviteLink();
|
||||
} else if (const auto group = peer->asMegagroup()) {
|
||||
return group->canHaveInviteLink();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (!lookupLink().isEmpty() || canCreateLink()) {
|
||||
const auto copyLink = [=] {
|
||||
const auto link = lookupLink();
|
||||
if (link.isEmpty()) {
|
||||
@@ -427,12 +441,17 @@ void GroupCallSettingsBox(
|
||||
)->addClickHandler([=] {
|
||||
if (!copyLink() && !state->generatingLink) {
|
||||
state->generatingLink = true;
|
||||
channel->session().api().request(MTPmessages_ExportChatInvite(
|
||||
channel->input
|
||||
peer->session().api().request(MTPmessages_ExportChatInvite(
|
||||
peer->input
|
||||
)).done([=](const MTPExportedChatInvite &result) {
|
||||
if (result.type() == mtpc_chatInviteExported) {
|
||||
channel->setInviteLink(
|
||||
qs(result.c_chatInviteExported().vlink()));
|
||||
const auto link = qs(
|
||||
result.c_chatInviteExported().vlink());
|
||||
if (const auto chat = peer->asChat()) {
|
||||
chat->setInviteLink(link);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
channel->setInviteLink(link);
|
||||
}
|
||||
copyLink();
|
||||
}
|
||||
}).send();
|
||||
@@ -440,7 +459,7 @@ void GroupCallSettingsBox(
|
||||
});
|
||||
}
|
||||
|
||||
if (channel->canManageCall()) {
|
||||
if (peer->canManageGroupCall()) {
|
||||
AddButton(
|
||||
layout,
|
||||
tr::lng_group_call_end(),
|
||||
@@ -472,7 +491,7 @@ void GroupCallSettingsBox(
|
||||
if (canChangeJoinMuted
|
||||
&& muteJoined
|
||||
&& muteJoined->toggled() != joinMuted) {
|
||||
SaveCallJoinMuted(channel, id, muteJoined->toggled());
|
||||
SaveCallJoinMuted(peer, id, muteJoined->toggled());
|
||||
}
|
||||
}, box->lifetime());
|
||||
box->addButton(tr::lng_box_done(), [=] {
|
||||
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_session.h"
|
||||
#include "media/audio/media_audio_track.h"
|
||||
#include "platform/platform_specific.h"
|
||||
@@ -58,12 +59,12 @@ void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
|
||||
}), video);
|
||||
}
|
||||
|
||||
void Instance::startOrJoinGroupCall(not_null<ChannelData*> channel) {
|
||||
void Instance::startOrJoinGroupCall(not_null<PeerData*> peer) {
|
||||
destroyCurrentCall();
|
||||
|
||||
const auto call = channel->call();
|
||||
const auto call = peer->groupCall();
|
||||
createGroupCall(
|
||||
channel,
|
||||
peer,
|
||||
call ? call->input() : MTP_inputGroupCall(MTPlong(), MTPlong()));
|
||||
}
|
||||
|
||||
@@ -183,17 +184,17 @@ void Instance::destroyGroupCall(not_null<GroupCall*> call) {
|
||||
}
|
||||
|
||||
void Instance::createGroupCall(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
const MTPInputGroupCall &inputCall) {
|
||||
destroyCurrentCall();
|
||||
|
||||
auto call = std::make_unique<GroupCall>(
|
||||
getGroupCallDelegate(),
|
||||
channel,
|
||||
peer,
|
||||
inputCall);
|
||||
const auto raw = call.get();
|
||||
|
||||
channel->session().account().sessionChanges(
|
||||
peer->session().account().sessionChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
destroyGroupCall(raw);
|
||||
}, raw->lifetime());
|
||||
@@ -387,7 +388,7 @@ void Instance::handleGroupCallUpdate(
|
||||
existing->applyUpdate(call);
|
||||
}
|
||||
if (_currentGroupCall
|
||||
&& (&_currentGroupCall->channel()->session() == session)) {
|
||||
&& (&_currentGroupCall->peer()->session() == session)) {
|
||||
_currentGroupCall->handleUpdate(call);
|
||||
}
|
||||
}
|
||||
@@ -402,7 +403,7 @@ void Instance::handleGroupCallUpdate(
|
||||
existing->applyUpdate(update);
|
||||
}
|
||||
if (_currentGroupCall
|
||||
&& (&_currentGroupCall->channel()->session() == session)
|
||||
&& (&_currentGroupCall->peer()->session() == session)
|
||||
&& (_currentGroupCall->id() == callId)) {
|
||||
_currentGroupCall->handleUpdate(update);
|
||||
}
|
||||
@@ -458,7 +459,7 @@ bool Instance::hasActivePanel(not_null<Main::Session*> session) const {
|
||||
return (&_currentCall->user()->session() == session)
|
||||
&& _currentCallPanel->isActive();
|
||||
} else if (inGroupCall()) {
|
||||
return (&_currentGroupCall->channel()->session() == session)
|
||||
return (&_currentGroupCall->peer()->session() == session)
|
||||
&& _currentGroupCallPanel->isActive();
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
~Instance();
|
||||
|
||||
void startOutgoingCall(not_null<UserData*> user, bool video);
|
||||
void startOrJoinGroupCall(not_null<ChannelData*> channel);
|
||||
void startOrJoinGroupCall(not_null<PeerData*> peer);
|
||||
void handleUpdate(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPUpdate &update);
|
||||
@@ -94,7 +94,7 @@ private:
|
||||
void destroyCall(not_null<Call*> call);
|
||||
|
||||
void createGroupCall(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
const MTPInputGroupCall &inputCall);
|
||||
void destroyGroupCall(not_null<GroupCall*> call);
|
||||
|
||||
|
||||
@@ -209,8 +209,14 @@ bool Panel::isActive() const {
|
||||
}
|
||||
|
||||
void Panel::showAndActivate() {
|
||||
if (_window->isHidden()) {
|
||||
_window->show();
|
||||
}
|
||||
const auto state = _window->windowState();
|
||||
if (state & Qt::WindowMinimized) {
|
||||
_window->setWindowState(state & ~Qt::WindowMinimized);
|
||||
}
|
||||
_window->raise();
|
||||
_window->setWindowState(_window->windowState() | Qt::WindowActive);
|
||||
_window->activateWindow();
|
||||
_window->setFocus();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_group_call_tracker.h" // ContentByCall.
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
@@ -36,45 +36,43 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxUsersInBar = 3;
|
||||
constexpr auto kUpdateDebugTimeoutMs = crl::time(500);
|
||||
constexpr auto kSwitchStateDuration = 120;
|
||||
|
||||
constexpr auto kMinorBlobAlpha = 76. / 255.;
|
||||
|
||||
constexpr auto kBlobLevelDuration1 = 250;
|
||||
constexpr auto kBlobLevelDuration2 = 120;
|
||||
constexpr auto kHideBlobsDuration = crl::time(500);
|
||||
constexpr auto kBlobLevelDuration = crl::time(250);
|
||||
constexpr auto kBlobUpdateInterval = crl::time(100);
|
||||
|
||||
auto LinearBlobs() -> std::array<Ui::Paint::LinearBlobs::BlobData, 3> {
|
||||
return { {
|
||||
auto LinearBlobs() {
|
||||
return std::vector<Ui::Paint::LinearBlobs::BlobData>{
|
||||
{
|
||||
.segmentsCount = 5,
|
||||
.minScale = 1.,
|
||||
.minRadius = (float)st::groupCallMajorBlobMinRadius,
|
||||
.minRadius = 0.,
|
||||
.maxRadius = (float)st::groupCallMajorBlobMaxRadius,
|
||||
.idleRadius = (float)st::groupCallMinorBlobIdleRadius,
|
||||
.speedScale = .3,
|
||||
.alpha = 1.,
|
||||
.topOffset = st::groupCallMajorBlobTopOffset,
|
||||
},
|
||||
{
|
||||
.segmentsCount = 7,
|
||||
.minScale = 1.,
|
||||
.minRadius = (float)st::groupCallMinorBlobMinRadius,
|
||||
.minRadius = 0.,
|
||||
.maxRadius = (float)st::groupCallMinorBlobMaxRadius,
|
||||
.idleRadius = (float)st::groupCallMinorBlobIdleRadius,
|
||||
.speedScale = .7,
|
||||
.alpha = kMinorBlobAlpha,
|
||||
.topOffset = st::groupCallMinorBlobTopOffset,
|
||||
},
|
||||
{
|
||||
.segmentsCount = 8,
|
||||
.minScale = 1.,
|
||||
.minRadius = (float)st::groupCallMinorBlobMinRadius,
|
||||
.minRadius = 0.,
|
||||
.maxRadius = (float)st::groupCallMinorBlobMaxRadius,
|
||||
.idleRadius = (float)st::groupCallMinorBlobIdleRadius,
|
||||
.speedScale = .7,
|
||||
.alpha = kMinorBlobAlpha,
|
||||
.topOffset = st::groupCallMinorBlobTopOffset,
|
||||
},
|
||||
} };
|
||||
};
|
||||
}
|
||||
|
||||
auto Colors() {
|
||||
@@ -82,7 +80,7 @@ auto Colors() {
|
||||
return base::flat_map<MuteState, Vector>{
|
||||
{
|
||||
MuteState::ForceMuted,
|
||||
Vector{ st::groupCallMembersBg->c, st::groupCallMembersBg->c }
|
||||
Vector{ st::groupCallForceMuted1->c, st::groupCallForceMuted2->c }
|
||||
},
|
||||
{
|
||||
MuteState::Active,
|
||||
@@ -139,6 +137,10 @@ void DebugInfoBox::updateText() {
|
||||
|
||||
} // namespace
|
||||
|
||||
struct TopBar::User {
|
||||
Ui::GroupCallBarContent::User data;
|
||||
};
|
||||
|
||||
class Mute final : public Ui::IconButton {
|
||||
public:
|
||||
Mute(QWidget *parent, const style::IconButton &st)
|
||||
@@ -290,7 +292,7 @@ void TopBar::initControls() {
|
||||
|
||||
const auto crossProgress = (crossFrom == crossTo)
|
||||
? crossTo
|
||||
: (crossFrom + float64(crossTo - crossFrom) * value);
|
||||
: anim::interpolateF(crossFrom, crossTo, value);
|
||||
_mute->setProgress(crossProgress);
|
||||
};
|
||||
|
||||
@@ -353,14 +355,12 @@ void TopBar::initBlobsUnder(
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr auto kHideDuration = kBlobLevelDuration1 * 2;
|
||||
|
||||
struct State {
|
||||
Ui::Paint::LinearBlobs paint = {
|
||||
LinearBlobs() | ranges::to_vector,
|
||||
kBlobLevelDuration1,
|
||||
kBlobLevelDuration2,
|
||||
1.
|
||||
LinearBlobs(),
|
||||
kBlobLevelDuration,
|
||||
1.,
|
||||
Ui::Paint::LinearBlob::Direction::TopDown
|
||||
};
|
||||
Ui::Animations::Simple hideAnimation;
|
||||
Ui::Animations::Basic animation;
|
||||
@@ -369,8 +369,6 @@ void TopBar::initBlobsUnder(
|
||||
crl::time lastTime = 0;
|
||||
float lastLevel = 0.;
|
||||
float levelBeforeLast = 0.;
|
||||
int maxHeight = st::groupCallMinorBlobMinRadius
|
||||
+ st::groupCallMinorBlobMaxRadius;
|
||||
};
|
||||
|
||||
_blobs = base::make_unique_q<Ui::RpWidget>(blobsParent);
|
||||
@@ -387,7 +385,7 @@ void TopBar::initBlobsUnder(
|
||||
|
||||
state->animation.init([=](crl::time now) {
|
||||
if (const auto last = state->hideLastTime; (last > 0)
|
||||
&& (now - last >= kHideDuration)) {
|
||||
&& (now - last >= kHideBlobsDuration)) {
|
||||
state->animation.stop();
|
||||
return false;
|
||||
}
|
||||
@@ -443,7 +441,7 @@ void TopBar::initBlobsUnder(
|
||||
const auto to = hide ? 1. : 0.;
|
||||
state->hideAnimation.start([=](float64) {
|
||||
_blobs->update();
|
||||
}, from, to, kHideDuration);
|
||||
}, from, to, kHideBlobsDuration);
|
||||
}, lifetime());
|
||||
|
||||
std::move(
|
||||
@@ -451,7 +449,7 @@ void TopBar::initBlobsUnder(
|
||||
) | rpl::start_with_next([=](QRect rect) {
|
||||
_blobs->resize(
|
||||
rect.width(),
|
||||
std::min(state->maxHeight, rect.height()));
|
||||
(int)state->paint.maxRadius());
|
||||
_blobs->moveToLeft(rect.x(), rect.y() + rect.height());
|
||||
}, lifetime());
|
||||
|
||||
@@ -473,12 +471,9 @@ void TopBar::initBlobsUnder(
|
||||
p.setOpacity(1. - hidden);
|
||||
}
|
||||
const auto top = -_blobs->height() * hidden;
|
||||
const auto drawUnder = QRect(
|
||||
0,
|
||||
top,
|
||||
_blobs->width() + st::groupCallBlobWidthAdditional,
|
||||
0);
|
||||
state->paint.paint(p, _groupBrush, drawUnder, 0, 0);
|
||||
const auto width = _blobs->width();
|
||||
p.translate(0, top);
|
||||
state->paint.paint(p, _groupBrush, width);
|
||||
}, _blobs->lifetime());
|
||||
|
||||
group->levelUpdates(
|
||||
@@ -501,12 +496,12 @@ void TopBar::initBlobsUnder(
|
||||
}
|
||||
|
||||
void TopBar::subscribeToMembersChanges(not_null<GroupCall*> call) {
|
||||
const auto channel = call->channel();
|
||||
channel->session().changes().peerFlagsValue(
|
||||
channel,
|
||||
const auto peer = call->peer();
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::GroupCall
|
||||
) | rpl::map([=] {
|
||||
return channel->call();
|
||||
return peer->groupCall();
|
||||
}) | rpl::filter([=](Data::GroupCall *real) {
|
||||
const auto call = _groupCall.get();
|
||||
return call && real && (real->id() == call->id());
|
||||
@@ -521,14 +516,75 @@ void TopBar::subscribeToMembersChanges(not_null<GroupCall*> call) {
|
||||
.stroke = st::groupCallTopBarUserpicStroke,
|
||||
});
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([=](const Ui::GroupCallBarContent &content) {
|
||||
const auto changed = (_userpics.size() != content.userpics.size());
|
||||
_userpics = content.userpics;
|
||||
if (changed) {
|
||||
) | rpl::filter([=](const Ui::GroupCallBarContent &content) {
|
||||
if (_users.size() != content.users.size()) {
|
||||
return true;
|
||||
}
|
||||
for (auto i = 0, count = int(_users.size()); i != count; ++i) {
|
||||
if (_users[i].data.userpicKey != content.users[i].userpicKey
|
||||
|| _users[i].data.id != content.users[i].id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}) | rpl::start_with_next([=](const Ui::GroupCallBarContent &content) {
|
||||
const auto sizeChanged = (_users.size() != content.users.size());
|
||||
_users = ranges::view::all(
|
||||
content.users
|
||||
) | ranges::view::transform([](const auto &user) {
|
||||
return User{ user };
|
||||
}) | ranges::to_vector;
|
||||
generateUserpicsInRow();
|
||||
if (sizeChanged) {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
call->peer()->session().changes().peerUpdates(
|
||||
Data::PeerUpdate::Flag::Name
|
||||
) | rpl::filter([=](const Data::PeerUpdate &update) {
|
||||
// _peer may change for the same Panel.
|
||||
const auto call = _groupCall.get();
|
||||
return (call != nullptr) && (update.peer == call->peer());
|
||||
}) | rpl::start_with_next([=] {
|
||||
updateInfoLabels();
|
||||
}, lifetime());
|
||||
|
||||
}
|
||||
|
||||
void TopBar::generateUserpicsInRow() {
|
||||
const auto count = int(_users.size());
|
||||
if (!count) {
|
||||
_userpics = QImage();
|
||||
return;
|
||||
}
|
||||
const auto limit = std::min(count, kMaxUsersInBar);
|
||||
const auto single = st::groupCallTopBarUserpicSize;
|
||||
const auto shift = st::groupCallTopBarUserpicShift;
|
||||
const auto width = single + (limit - 1) * (single - shift);
|
||||
if (_userpics.width() != width * cIntRetinaFactor()) {
|
||||
_userpics = QImage(
|
||||
QSize(width, single) * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
_userpics.fill(Qt::transparent);
|
||||
_userpics.setDevicePixelRatio(cRetinaFactor());
|
||||
|
||||
auto q = Painter(&_userpics);
|
||||
auto hq = PainterHighQualityEnabler(q);
|
||||
auto pen = QPen(Qt::transparent);
|
||||
pen.setWidth(st::groupCallTopBarUserpicStroke);
|
||||
auto x = (count - 1) * (single - shift);
|
||||
for (auto i = count; i != 0;) {
|
||||
q.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
q.drawImage(x, 0, _users[--i].data.userpic);
|
||||
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
q.setBrush(Qt::NoBrush);
|
||||
q.setPen(pen);
|
||||
q.drawEllipse(x, 0, single, single);
|
||||
x -= single - shift;
|
||||
}
|
||||
}
|
||||
|
||||
void TopBar::updateInfoLabels() {
|
||||
@@ -544,8 +600,8 @@ void TopBar::setInfoLabels() {
|
||||
_fullInfoLabel->setText(fullName.toUpper());
|
||||
_shortInfoLabel->setText(shortName.toUpper());
|
||||
} else if (const auto group = _groupCall.get()) {
|
||||
const auto channel = group->channel();
|
||||
const auto name = channel->name;
|
||||
const auto peer = group->peer();
|
||||
const auto name = peer->name;
|
||||
_fullInfoLabel->setText(name.toUpper());
|
||||
_shortInfoLabel->setText(name.toUpper());
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
struct User;
|
||||
|
||||
TopBar(
|
||||
QWidget *parent,
|
||||
const base::weak_ptr<Call> &call,
|
||||
@@ -63,11 +65,13 @@ private:
|
||||
void setMuted(bool mute);
|
||||
|
||||
void subscribeToMembersChanges(not_null<GroupCall*> call);
|
||||
void generateUserpicsInRow();
|
||||
|
||||
const base::weak_ptr<Call> _call;
|
||||
const base::weak_ptr<GroupCall> _groupCall;
|
||||
|
||||
bool _muted = false;
|
||||
std::vector<User> _users;
|
||||
QImage _userpics;
|
||||
object_ptr<Ui::LabelSimple> _durationLabel;
|
||||
object_ptr<SignalBars> _signalBars;
|
||||
|
||||
@@ -789,9 +789,17 @@ bool Application::openCustomUrl(
|
||||
|
||||
}
|
||||
|
||||
void Application::preventOrInvoke(Fn<void()> &&callback) {
|
||||
_window->preventOrInvoke(std::move(callback));
|
||||
}
|
||||
|
||||
void Application::lockByPasscode() {
|
||||
_passcodeLock = true;
|
||||
_window->setupPasscodeLock();
|
||||
preventOrInvoke([=] {
|
||||
if (_window) {
|
||||
_passcodeLock = true;
|
||||
_window->setupPasscodeLock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Application::unlockPasscode() {
|
||||
|
||||
@@ -277,6 +277,8 @@ public:
|
||||
void switchFreeType();
|
||||
void writeInstallBetaVersionsSetting();
|
||||
|
||||
void preventOrInvoke(Fn<void()> &&callback);
|
||||
|
||||
void call_handleObservables();
|
||||
|
||||
protected:
|
||||
|
||||
@@ -109,6 +109,16 @@ std::map<int, const char*> BetaLogs() {
|
||||
2004012,
|
||||
"- Voice chats in groups. (alpha version)\n"
|
||||
},
|
||||
{
|
||||
2004014,
|
||||
"- Create voice chats in legacy groups.\n"
|
||||
|
||||
"- Fix sticker pack opening.\n"
|
||||
|
||||
"- Fix group status display.\n"
|
||||
|
||||
"- Fix group members display.\n"
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1591,7 +1591,7 @@ void UpdateApplication() {
|
||||
if (const auto window = App::wnd()) {
|
||||
if (const auto controller = window->sessionController()) {
|
||||
controller->showSection(
|
||||
Info::Memento(
|
||||
std::make_shared<Info::Memento>(
|
||||
Info::Settings::Tag{ controller->session().user() },
|
||||
Info::Section::SettingsType::Advanced),
|
||||
Window::SectionShow());
|
||||
|
||||
@@ -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 = 2004013;
|
||||
constexpr auto AppVersionStr = "2.4.13";
|
||||
constexpr auto AppVersion = 2004014;
|
||||
constexpr auto AppVersionStr = "2.4.14";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -104,13 +104,9 @@ void ChannelData::setInviteLink(const QString &newInviteLink) {
|
||||
}
|
||||
}
|
||||
|
||||
QString ChannelData::inviteLink() const {
|
||||
return _inviteLink;
|
||||
}
|
||||
|
||||
bool ChannelData::canHaveInviteLink() const {
|
||||
return (adminRights() & AdminRight::f_invite_users)
|
||||
|| amCreator();
|
||||
return amCreator()
|
||||
|| (adminRights() & AdminRight::f_invite_users);
|
||||
}
|
||||
|
||||
void ChannelData::setLocation(const MTPChannelLocation &data) {
|
||||
@@ -524,10 +520,6 @@ bool ChannelData::canRestrictUser(not_null<UserData*> user) const {
|
||||
return adminRights() & AdminRight::f_ban_users;
|
||||
}
|
||||
|
||||
bool ChannelData::canManageCall() const {
|
||||
return amCreator() || (adminRights() & AdminRight::f_manage_call);
|
||||
}
|
||||
|
||||
void ChannelData::setAdminRights(const MTPChatAdminRights &rights) {
|
||||
if (rights.c_chatAdminRights().vflags().v == adminRights()) {
|
||||
return;
|
||||
@@ -677,14 +669,24 @@ void ChannelData::privateErrorReceived() {
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelData::setCall(const MTPInputGroupCall &call) {
|
||||
void ChannelData::migrateCall(std::unique_ptr<Data::GroupCall> call) {
|
||||
Expects(_call == nullptr);
|
||||
Expects(call != nullptr);
|
||||
|
||||
_call = std::move(call);
|
||||
_call->setPeer(this);
|
||||
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
|
||||
addFlags(MTPDchannel::Flag::f_call_active);
|
||||
}
|
||||
|
||||
void ChannelData::setGroupCall(const MTPInputGroupCall &call) {
|
||||
call.match([&](const MTPDinputGroupCall &data) {
|
||||
if (_call && _call->id() == data.vid().v) {
|
||||
return;
|
||||
} else if (!_call && !data.vid().v) {
|
||||
return;
|
||||
} else if (!data.vid().v) {
|
||||
clearCall();
|
||||
clearGroupCall();
|
||||
return;
|
||||
}
|
||||
const auto hasCall = (_call != nullptr);
|
||||
@@ -701,7 +703,7 @@ void ChannelData::setCall(const MTPInputGroupCall &call) {
|
||||
});
|
||||
}
|
||||
|
||||
void ChannelData::clearCall() {
|
||||
void ChannelData::clearGroupCall() {
|
||||
if (!_call) {
|
||||
return;
|
||||
}
|
||||
@@ -744,9 +746,9 @@ void ApplyChannelUpdate(
|
||||
auto canEditStickers = channel->canEditStickers();
|
||||
|
||||
if (const auto call = update.vcall()) {
|
||||
channel->setCall(*call);
|
||||
channel->setGroupCall(*call);
|
||||
} else {
|
||||
channel->clearCall();
|
||||
channel->clearGroupCall();
|
||||
}
|
||||
|
||||
channel->setFullFlags(update.vflags().v);
|
||||
|
||||
@@ -11,10 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_pts_waiter.h"
|
||||
#include "data/data_location.h"
|
||||
|
||||
namespace Data {
|
||||
class GroupCall;
|
||||
} // namespace Data
|
||||
|
||||
struct ChannelLocation {
|
||||
QString address;
|
||||
Data::LocationPoint point;
|
||||
@@ -307,10 +303,11 @@ public:
|
||||
[[nodiscard]] bool canDelete() const;
|
||||
[[nodiscard]] bool canEditAdmin(not_null<UserData*> user) const;
|
||||
[[nodiscard]] bool canRestrictUser(not_null<UserData*> user) const;
|
||||
[[nodiscard]] bool canManageCall() const;
|
||||
|
||||
void setInviteLink(const QString &newInviteLink);
|
||||
[[nodiscard]] QString inviteLink() const;
|
||||
[[nodiscard]] QString inviteLink() const {
|
||||
return _inviteLink;
|
||||
}
|
||||
[[nodiscard]] bool canHaveInviteLink() const;
|
||||
|
||||
void setLocation(const MTPChannelLocation &data);
|
||||
@@ -399,11 +396,12 @@ public:
|
||||
[[nodiscard]] QString invitePeekHash() const;
|
||||
void privateErrorReceived();
|
||||
|
||||
[[nodiscard]] Data::GroupCall *call() const {
|
||||
[[nodiscard]] Data::GroupCall *groupCall() const {
|
||||
return _call.get();
|
||||
}
|
||||
void setCall(const MTPInputGroupCall &call);
|
||||
void clearCall();
|
||||
void migrateCall(std::unique_ptr<Data::GroupCall> call);
|
||||
void setGroupCall(const MTPInputGroupCall &call);
|
||||
void clearGroupCall();
|
||||
|
||||
// Still public data members.
|
||||
uint64 access = 0;
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -24,6 +25,14 @@ using UpdateFlag = Data::PeerUpdate::Flag;
|
||||
ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
|
||||
: PeerData(owner, id)
|
||||
, inputChat(MTP_int(bareId())) {
|
||||
_flags.changes(
|
||||
) | rpl::start_with_next([=](const Flags::Change &change) {
|
||||
if (change.diff & MTPDchat::Flag::f_call_not_empty) {
|
||||
if (const auto history = this->owner().historyLoaded(this)) {
|
||||
history->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void ChatData::setPhoto(const MTPChatPhoto &photo) {
|
||||
@@ -124,6 +133,11 @@ void ChatData::setInviteLink(const QString &newInviteLink) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatData::canHaveInviteLink() const {
|
||||
return amCreator()
|
||||
|| (adminRights() & AdminRight::f_invite_users);
|
||||
}
|
||||
|
||||
void ChatData::setAdminRights(const MTPChatAdminRights &rights) {
|
||||
if (rights.c_chatAdminRights().vflags().v == adminRights()) {
|
||||
return;
|
||||
@@ -176,6 +190,47 @@ void ChatData::setMigrateToChannel(ChannelData *channel) {
|
||||
}
|
||||
}
|
||||
|
||||
void ChatData::setGroupCall(const MTPInputGroupCall &call) {
|
||||
if (migrateTo()) {
|
||||
return;
|
||||
}
|
||||
call.match([&](const MTPDinputGroupCall &data) {
|
||||
if (_call && _call->id() == data.vid().v) {
|
||||
return;
|
||||
} else if (!_call && !data.vid().v) {
|
||||
return;
|
||||
} else if (!data.vid().v) {
|
||||
clearGroupCall();
|
||||
return;
|
||||
}
|
||||
const auto hasCall = (_call != nullptr);
|
||||
if (hasCall) {
|
||||
owner().unregisterGroupCall(_call.get());
|
||||
}
|
||||
_call = std::make_unique<Data::GroupCall>(
|
||||
this,
|
||||
data.vid().v,
|
||||
data.vaccess_hash().v);
|
||||
owner().registerGroupCall(_call.get());
|
||||
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
|
||||
addFlags(MTPDchat::Flag::f_call_active);
|
||||
});
|
||||
}
|
||||
|
||||
void ChatData::clearGroupCall() {
|
||||
if (!_call) {
|
||||
return;
|
||||
} else if (const auto group = migrateTo(); group && !group->groupCall()) {
|
||||
group->migrateCall(base::take(_call));
|
||||
} else {
|
||||
owner().unregisterGroupCall(_call.get());
|
||||
_call = nullptr;
|
||||
}
|
||||
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
|
||||
removeFlags(MTPDchat::Flag::f_call_active
|
||||
| MTPDchat::Flag::f_call_not_empty);
|
||||
}
|
||||
|
||||
namespace Data {
|
||||
|
||||
void ApplyChatUpdate(
|
||||
@@ -310,6 +365,12 @@ void ApplyChatUpdate(
|
||||
void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
|
||||
ApplyChatUpdate(chat, update.vparticipants());
|
||||
|
||||
if (const auto call = update.vcall()) {
|
||||
chat->setGroupCall(*call);
|
||||
} else {
|
||||
chat->clearGroupCall();
|
||||
}
|
||||
|
||||
if (const auto info = update.vbot_info()) {
|
||||
for (const auto &item : info->v) {
|
||||
item.match([&](const MTPDbotInfo &data) {
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
| MTPDchat::Flag::f_deactivated
|
||||
| MTPDchat::Flag::f_migrated_to
|
||||
| MTPDchat::Flag::f_admin_rights
|
||||
| MTPDchat::Flag::f_call_not_empty
|
||||
| MTPDchat::Flag::f_default_banned_rights;
|
||||
using Flags = Data::Flags<
|
||||
MTPDchat::Flags,
|
||||
@@ -141,9 +142,10 @@ public:
|
||||
void applyEditAdmin(not_null<UserData*> user, bool isAdmin);
|
||||
|
||||
void setInviteLink(const QString &newInviteLink);
|
||||
QString inviteLink() const {
|
||||
[[nodiscard]] QString inviteLink() const {
|
||||
return _inviteLink;
|
||||
}
|
||||
[[nodiscard]] bool canHaveInviteLink() const;
|
||||
void refreshBotStatus();
|
||||
|
||||
enum class UpdateStatus {
|
||||
@@ -162,6 +164,12 @@ public:
|
||||
ChannelData *getMigrateToChannel() const;
|
||||
void setMigrateToChannel(ChannelData *channel);
|
||||
|
||||
[[nodiscard]] Data::GroupCall *groupCall() const {
|
||||
return _call.get();
|
||||
}
|
||||
void setGroupCall(const MTPInputGroupCall &call);
|
||||
void clearGroupCall();
|
||||
|
||||
// Still public data members.
|
||||
const MTPint inputChat;
|
||||
|
||||
@@ -185,7 +193,10 @@ private:
|
||||
AdminRightFlags _adminRights;
|
||||
int _version = 0;
|
||||
|
||||
std::unique_ptr<Data::GroupCall> _call;
|
||||
|
||||
ChannelData *_migratedTo = nullptr;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -7,30 +7,38 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_group_call.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "calls/calls_group_call.h"
|
||||
#include "core/application.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kRequestPerPage = 30;
|
||||
constexpr auto kSpeakingAfterActive = crl::time(6000);
|
||||
constexpr auto kActiveAfterJoined = crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
GroupCall::GroupCall(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 id,
|
||||
uint64 accessHash)
|
||||
: _channel(channel)
|
||||
, _id(id)
|
||||
, _accessHash(accessHash) {
|
||||
: _id(id)
|
||||
, _accessHash(accessHash)
|
||||
, _peer(peer) // #TODO calls migration
|
||||
, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); }) {
|
||||
}
|
||||
|
||||
GroupCall::~GroupCall() {
|
||||
api().request(_unknownSsrcsRequestId).cancel();
|
||||
api().request(_unknownUsersRequestId).cancel();
|
||||
api().request(_participantsRequestId).cancel();
|
||||
api().request(_reloadRequestId).cancel();
|
||||
}
|
||||
@@ -39,14 +47,21 @@ uint64 GroupCall::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
not_null<ChannelData*> GroupCall::channel() const {
|
||||
return _channel;
|
||||
not_null<PeerData*> GroupCall::peer() const {
|
||||
return _peer;
|
||||
}
|
||||
|
||||
MTPInputGroupCall GroupCall::input() const {
|
||||
return MTP_inputGroupCall(MTP_long(_id), MTP_long(_accessHash));
|
||||
}
|
||||
|
||||
void GroupCall::setPeer(not_null<PeerData*> peer) {
|
||||
Expects(peer->migrateFrom() == _peer);
|
||||
Expects(_peer->migrateTo() == peer);
|
||||
|
||||
_peer = peer;
|
||||
}
|
||||
|
||||
auto GroupCall::participants() const
|
||||
-> const std::vector<Participant> & {
|
||||
return _participants;
|
||||
@@ -70,7 +85,7 @@ void GroupCall::requestParticipants() {
|
||||
)).done([=](const MTPphone_GroupParticipants &result) {
|
||||
result.match([&](const MTPDphone_groupParticipants &data) {
|
||||
_nextOffset = qs(data.vnext_offset());
|
||||
_channel->owner().processUsers(data.vusers());
|
||||
_peer->owner().processUsers(data.vusers());
|
||||
applyParticipantsSlice(
|
||||
data.vparticipants().v,
|
||||
ApplySliceSource::SliceLoaded);
|
||||
@@ -85,30 +100,43 @@ void GroupCall::requestParticipants() {
|
||||
});
|
||||
_participantsSliceAdded.fire({});
|
||||
_participantsRequestId = 0;
|
||||
changeChannelEmptyCallFlag();
|
||||
changePeerEmptyCallFlag();
|
||||
}).fail([=](const RPCError &error) {
|
||||
_fullCount = _participants.size();
|
||||
_allReceived = true;
|
||||
_participantsRequestId = 0;
|
||||
changeChannelEmptyCallFlag();
|
||||
changePeerEmptyCallFlag();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupCall::changeChannelEmptyCallFlag() {
|
||||
constexpr auto flag = MTPDchannel::Flag::f_call_not_empty;
|
||||
if (_channel->call() != this) {
|
||||
void GroupCall::changePeerEmptyCallFlag() {
|
||||
const auto chat = _peer->asChat();
|
||||
const auto channel = _peer->asChannel();
|
||||
constexpr auto chatFlag = MTPDchat::Flag::f_call_not_empty;
|
||||
constexpr auto channelFlag = MTPDchannel::Flag::f_call_not_empty;
|
||||
if (_peer->groupCall() != this) {
|
||||
return;
|
||||
} else if (fullCount() > 0) {
|
||||
if (!(_channel->flags() & flag)) {
|
||||
_channel->addFlags(flag);
|
||||
_channel->session().changes().peerUpdated(
|
||||
_channel,
|
||||
if (chat && !(chat->flags() & chatFlag)) {
|
||||
chat->addFlags(chatFlag);
|
||||
chat->session().changes().peerUpdated(
|
||||
chat,
|
||||
Data::PeerUpdate::Flag::GroupCall);
|
||||
} else if (channel && !(channel->flags() & channelFlag)) {
|
||||
channel->addFlags(channelFlag);
|
||||
channel->session().changes().peerUpdated(
|
||||
channel,
|
||||
Data::PeerUpdate::Flag::GroupCall);
|
||||
}
|
||||
} else if (_channel->flags() & flag) {
|
||||
_channel->removeFlags(flag);
|
||||
_channel->session().changes().peerUpdated(
|
||||
_channel,
|
||||
} else if (chat && (chat->flags() & chatFlag)) {
|
||||
chat->removeFlags(chatFlag);
|
||||
chat->session().changes().peerUpdated(
|
||||
chat,
|
||||
Data::PeerUpdate::Flag::GroupCall);
|
||||
} else if (channel && (channel->flags() & channelFlag)) {
|
||||
channel->removeFlags(channelFlag);
|
||||
channel->session().changes().peerUpdated(
|
||||
channel,
|
||||
Data::PeerUpdate::Flag::GroupCall);
|
||||
}
|
||||
}
|
||||
@@ -159,13 +187,17 @@ void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
|
||||
_canChangeJoinMuted = data.is_can_change_join_muted();
|
||||
_version = data.vversion().v;
|
||||
_fullCount = data.vparticipants_count().v;
|
||||
changeChannelEmptyCallFlag();
|
||||
changePeerEmptyCallFlag();
|
||||
}, [&](const MTPDgroupCallDiscarded &data) {
|
||||
const auto id = _id;
|
||||
const auto channel = _channel;
|
||||
crl::on_main(&channel->session(), [=] {
|
||||
if (channel->call() && channel->call()->id() == id) {
|
||||
channel->clearCall();
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -182,8 +214,9 @@ void GroupCall::reload() {
|
||||
MTPphone_GetGroupCall(input())
|
||||
).done([=](const MTPphone_GroupCall &result) {
|
||||
result.match([&](const MTPDphone_groupCall &data) {
|
||||
_channel->owner().processUsers(data.vusers());
|
||||
_peer->owner().processUsers(data.vusers());
|
||||
_participants.clear();
|
||||
_speakingByActiveFinishes.clear();
|
||||
_userBySsrc.clear();
|
||||
applyParticipantsSlice(
|
||||
data.vparticipants().v,
|
||||
@@ -201,11 +234,15 @@ void GroupCall::reload() {
|
||||
void GroupCall::applyParticipantsSlice(
|
||||
const QVector<MTPGroupCallParticipant> &list,
|
||||
ApplySliceSource sliceSource) {
|
||||
const auto amInCall = inCall();
|
||||
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 userId = data.vuser_id().v;
|
||||
const auto user = _channel->owner().user(userId);
|
||||
const auto user = _peer->owner().user(userId);
|
||||
const auto i = ranges::find(
|
||||
_participants,
|
||||
user,
|
||||
@@ -216,6 +253,7 @@ void GroupCall::applyParticipantsSlice(
|
||||
.was = *i,
|
||||
};
|
||||
_userBySsrc.erase(i->ssrc);
|
||||
_speakingByActiveFinishes.remove(user);
|
||||
_participants.erase(i);
|
||||
if (sliceSource != ApplySliceSource::SliceLoaded) {
|
||||
_participantUpdates.fire(std::move(update));
|
||||
@@ -231,10 +269,16 @@ void GroupCall::applyParticipantsSlice(
|
||||
: std::nullopt;
|
||||
const auto canSelfUnmute = !data.is_muted()
|
||||
|| data.is_can_self_unmute();
|
||||
const auto lastActive = data.vactive_date().value_or(
|
||||
was ? was->lastActive : 0);
|
||||
const auto speaking = canSelfUnmute
|
||||
&& ((was ? was->speaking : false)
|
||||
|| (!amInCall
|
||||
&& (lastActive + speakingAfterActive > now)));
|
||||
const auto value = Participant{
|
||||
.user = user,
|
||||
.date = data.vdate().v,
|
||||
.lastActive = was ? was->lastActive : 0,
|
||||
.lastActive = lastActive,
|
||||
.ssrc = uint32(data.vsource().v),
|
||||
.speaking = canSelfUnmute && (was ? was->speaking : false),
|
||||
.muted = data.is_muted(),
|
||||
@@ -243,8 +287,7 @@ void GroupCall::applyParticipantsSlice(
|
||||
if (i == end(_participants)) {
|
||||
_userBySsrc.emplace(value.ssrc, user);
|
||||
_participants.push_back(value);
|
||||
_channel->owner().unregisterInvitedToCallUser(_id, user);
|
||||
++changedCount;
|
||||
_peer->owner().unregisterInvitedToCallUser(_id, user);
|
||||
} else {
|
||||
if (i->ssrc != value.ssrc) {
|
||||
_userBySsrc.erase(i->ssrc);
|
||||
@@ -252,6 +295,9 @@ void GroupCall::applyParticipantsSlice(
|
||||
}
|
||||
*i = value;
|
||||
}
|
||||
if (data.is_just_joined()) {
|
||||
++changedCount;
|
||||
}
|
||||
if (sliceSource != ApplySliceSource::SliceLoaded) {
|
||||
_participantUpdates.fire({
|
||||
.was = was,
|
||||
@@ -262,7 +308,7 @@ void GroupCall::applyParticipantsSlice(
|
||||
}
|
||||
if (sliceSource == ApplySliceSource::UpdateReceived) {
|
||||
_fullCount = changedCount;
|
||||
changeChannelEmptyCallFlag();
|
||||
changePeerEmptyCallFlag();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +320,7 @@ void GroupCall::applyParticipantsMutes(
|
||||
return;
|
||||
}
|
||||
const auto userId = data.vuser_id().v;
|
||||
const auto user = _channel->owner().user(userId);
|
||||
const auto user = _peer->owner().user(userId);
|
||||
const auto i = ranges::find(
|
||||
_participants,
|
||||
user,
|
||||
@@ -285,6 +331,7 @@ void GroupCall::applyParticipantsMutes(
|
||||
i->canSelfUnmute = !i->muted || data.is_can_self_unmute();
|
||||
if (!i->canSelfUnmute) {
|
||||
i->speaking = false;
|
||||
_speakingByActiveFinishes.remove(i->user);
|
||||
}
|
||||
_participantUpdates.fire({
|
||||
.was = was,
|
||||
@@ -298,13 +345,14 @@ void GroupCall::applyParticipantsMutes(
|
||||
void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
|
||||
const auto i = _userBySsrc.find(ssrc);
|
||||
if (i == end(_userBySsrc)) {
|
||||
_unknownSpokenSsrcs.emplace(ssrc, when);
|
||||
requestUnknownSsrcs();
|
||||
_unknownSpokenSsrcs[ssrc] = when;
|
||||
requestUnknownParticipants();
|
||||
return;
|
||||
}
|
||||
const auto j = ranges::find(_participants, i->second, &Participant::user);
|
||||
Assert(j != end(_participants));
|
||||
|
||||
_speakingByActiveFinishes.remove(j->user);
|
||||
const auto speaking = (when + kSpeakStatusKeptFor >= now)
|
||||
&& j->canSelfUnmute;
|
||||
if (j->speaking != speaking) {
|
||||
@@ -317,8 +365,85 @@ void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::requestUnknownSsrcs() {
|
||||
if (_unknownSsrcsRequestId || _unknownSpokenSsrcs.empty()) {
|
||||
void GroupCall::applyActiveUpdate(
|
||||
UserId userId,
|
||||
crl::time when,
|
||||
UserData *userLoaded) {
|
||||
if (inCall()) {
|
||||
return;
|
||||
}
|
||||
const auto i = userLoaded
|
||||
? ranges::find(
|
||||
_participants,
|
||||
not_null{ userLoaded },
|
||||
&Participant::user)
|
||||
: _participants.end();
|
||||
if (i == end(_participants)) {
|
||||
_unknownSpokenUids[userId] = when;
|
||||
requestUnknownParticipants();
|
||||
return;
|
||||
} else if (!i->canSelfUnmute) {
|
||||
return;
|
||||
}
|
||||
const auto was = std::make_optional(*i);
|
||||
const auto now = crl::now();
|
||||
const auto elapsed = TimeId((now - when) / crl::time(1000));
|
||||
const auto lastActive = base::unixtime::now() - elapsed;
|
||||
const auto finishes = when + kSpeakingAfterActive;
|
||||
if (lastActive <= i->lastActive || finishes <= now) {
|
||||
return;
|
||||
}
|
||||
_speakingByActiveFinishes[i->user] = finishes;
|
||||
if (!_speakingByActiveFinishTimer.isActive()) {
|
||||
_speakingByActiveFinishTimer.callOnce(finishes - now);
|
||||
}
|
||||
|
||||
i->lastActive = lastActive;
|
||||
i->speaking = true;
|
||||
i->canSelfUnmute = true;
|
||||
if (!was->speaking || !was->canSelfUnmute) {
|
||||
_participantUpdates.fire({
|
||||
.was = was,
|
||||
.now = *i,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::checkFinishSpeakingByActive() {
|
||||
const auto now = crl::now();
|
||||
auto nearest = 0;
|
||||
auto stop = std::vector<not_null<UserData*>>();
|
||||
for (auto i = begin(_speakingByActiveFinishes); i != end(_speakingByActiveFinishes);) {
|
||||
const auto when = i->second;
|
||||
if (now >= when) {
|
||||
stop.push_back(i->first);
|
||||
i = _speakingByActiveFinishes.erase(i);
|
||||
} else {
|
||||
if (!nearest || nearest > when) {
|
||||
nearest = when;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for (const auto user : stop) {
|
||||
const auto i = ranges::find(_participants, user, &Participant::user);
|
||||
if (i->speaking) {
|
||||
const auto was = *i;
|
||||
i->speaking = false;
|
||||
_participantUpdates.fire({
|
||||
.was = was,
|
||||
.now = *i,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (nearest) {
|
||||
_speakingByActiveFinishTimer.callOnce(nearest - now);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::requestUnknownParticipants() {
|
||||
if (_unknownUsersRequestId
|
||||
|| (_unknownSpokenSsrcs.empty() && _unknownSpokenUids.empty())) {
|
||||
return;
|
||||
}
|
||||
const auto ssrcs = [&] {
|
||||
@@ -334,40 +459,102 @@ void GroupCall::requestUnknownSsrcs() {
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
auto inputs = QVector<MTPint>();
|
||||
inputs.reserve(ssrcs.size());
|
||||
const auto uids = [&] {
|
||||
if (_unknownSpokenUids.size() + ssrcs.size() < kRequestPerPage) {
|
||||
return base::take(_unknownSpokenUids);
|
||||
}
|
||||
auto result = base::flat_map<UserId, crl::time>();
|
||||
const auto available = (kRequestPerPage - int(ssrcs.size()));
|
||||
if (available > 0) {
|
||||
result.reserve(available);
|
||||
while (result.size() < available) {
|
||||
const auto [userId, when] = _unknownSpokenUids.back();
|
||||
result.emplace(userId, when);
|
||||
_unknownSpokenUids.erase(_unknownSpokenUids.end() - 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
auto ssrcInputs = QVector<MTPint>();
|
||||
ssrcInputs.reserve(ssrcs.size());
|
||||
for (const auto [ssrc, when] : ssrcs) {
|
||||
inputs.push_back(MTP_int(ssrc));
|
||||
ssrcInputs.push_back(MTP_int(ssrc));
|
||||
}
|
||||
_unknownSsrcsRequestId = api().request(MTPphone_GetGroupParticipants(
|
||||
auto uidInputs = QVector<MTPint>();
|
||||
uidInputs.reserve(uids.size());
|
||||
for (const auto [userId, when] : uids) {
|
||||
uidInputs.push_back(MTP_int(userId));
|
||||
}
|
||||
_unknownUsersRequestId = api().request(MTPphone_GetGroupParticipants(
|
||||
input(),
|
||||
MTP_vector<MTPint>(), // ids
|
||||
MTP_vector<MTPint>(inputs),
|
||||
MTP_vector<MTPint>(uidInputs),
|
||||
MTP_vector<MTPint>(ssrcInputs),
|
||||
MTP_string(QString()),
|
||||
MTP_int(kRequestPerPage)
|
||||
)).done([=](const MTPphone_GroupParticipants &result) {
|
||||
result.match([&](const MTPDphone_groupParticipants &data) {
|
||||
_channel->owner().processUsers(data.vusers());
|
||||
_peer->owner().processUsers(data.vusers());
|
||||
applyParticipantsSlice(
|
||||
data.vparticipants().v,
|
||||
ApplySliceSource::UnknownLoaded);
|
||||
});
|
||||
_unknownSsrcsRequestId = 0;
|
||||
_unknownUsersRequestId = 0;
|
||||
const auto now = crl::now();
|
||||
for (const auto [ssrc, when] : ssrcs) {
|
||||
applyLastSpoke(ssrc, when, now);
|
||||
_unknownSpokenSsrcs.remove(ssrc);
|
||||
}
|
||||
requestUnknownSsrcs();
|
||||
for (const auto [userId, when] : uids) {
|
||||
if (const auto user = _peer->owner().userLoaded(userId)) {
|
||||
const auto isParticipant = ranges::contains(
|
||||
_participants,
|
||||
not_null{ user },
|
||||
&Participant::user);
|
||||
if (isParticipant) {
|
||||
applyActiveUpdate(userId, when, user);
|
||||
}
|
||||
}
|
||||
_unknownSpokenUids.remove(userId);
|
||||
}
|
||||
requestUnknownParticipants();
|
||||
}).fail([=](const RPCError &error) {
|
||||
_unknownSsrcsRequestId = 0;
|
||||
_unknownUsersRequestId = 0;
|
||||
for (const auto [ssrc, when] : ssrcs) {
|
||||
_unknownSpokenSsrcs.remove(ssrc);
|
||||
}
|
||||
requestUnknownSsrcs();
|
||||
for (const auto [userId, when] : uids) {
|
||||
_unknownSpokenUids.remove(userId);
|
||||
}
|
||||
requestUnknownParticipants();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupCall::setInCall() {
|
||||
_unknownSpokenUids.clear();
|
||||
if (_speakingByActiveFinishes.empty()) {
|
||||
return;
|
||||
}
|
||||
auto restartTimer = true;
|
||||
const auto latest = crl::now() + kActiveAfterJoined;
|
||||
for (auto &[user, when] : _speakingByActiveFinishes) {
|
||||
if (when > latest) {
|
||||
when = latest;
|
||||
} else {
|
||||
restartTimer = false;
|
||||
}
|
||||
}
|
||||
if (restartTimer) {
|
||||
_speakingByActiveFinishTimer.callOnce(kActiveAfterJoined);
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupCall::inCall() const {
|
||||
const auto current = Core::App().calls().currentGroupCall();
|
||||
return (current != nullptr)
|
||||
&& (current->id() == _id)
|
||||
&& (current->state() == Calls::GroupCall::State::Joined);
|
||||
}
|
||||
|
||||
void GroupCall::applyUpdate(const MTPDupdateGroupCallParticipants &update) {
|
||||
const auto version = update.vversion().v;
|
||||
if (version < _version) {
|
||||
@@ -404,7 +591,7 @@ bool GroupCall::canChangeJoinMuted() const {
|
||||
}
|
||||
|
||||
ApiWrap &GroupCall::api() const {
|
||||
return _channel->session().api();
|
||||
return _peer->session().api();
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
@@ -7,8 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/timer.h"
|
||||
|
||||
class UserData;
|
||||
class ChannelData;
|
||||
class PeerData;
|
||||
|
||||
class ApiWrap;
|
||||
|
||||
@@ -16,13 +18,15 @@ namespace Data {
|
||||
|
||||
class GroupCall final {
|
||||
public:
|
||||
GroupCall(not_null<ChannelData*> channel, uint64 id, uint64 accessHash);
|
||||
GroupCall(not_null<PeerData*> peer, uint64 id, uint64 accessHash);
|
||||
~GroupCall();
|
||||
|
||||
[[nodiscard]] uint64 id() const;
|
||||
[[nodiscard]] not_null<ChannelData*> channel() const;
|
||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||
[[nodiscard]] MTPInputGroupCall input() const;
|
||||
|
||||
void setPeer(not_null<PeerData*> peer);
|
||||
|
||||
struct Participant {
|
||||
not_null<UserData*> user;
|
||||
TimeId date = 0;
|
||||
@@ -53,10 +57,15 @@ public:
|
||||
void applyUpdateChecked(
|
||||
const MTPDupdateGroupCallParticipants &update);
|
||||
void applyLastSpoke(uint32 ssrc, crl::time when, crl::time now);
|
||||
void applyActiveUpdate(
|
||||
UserId userId,
|
||||
crl::time when,
|
||||
UserData *userLoaded);
|
||||
|
||||
[[nodiscard]] int fullCount() const;
|
||||
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
||||
|
||||
void setInCall();
|
||||
void reload();
|
||||
|
||||
void setJoinMutedLocally(bool muted);
|
||||
@@ -71,30 +80,35 @@ private:
|
||||
};
|
||||
[[nodiscard]] ApiWrap &api() const;
|
||||
|
||||
[[nodiscard]] bool inCall() const;
|
||||
void applyCall(const MTPGroupCall &call, bool force);
|
||||
void applyParticipantsSlice(
|
||||
const QVector<MTPGroupCallParticipant> &list,
|
||||
ApplySliceSource sliceSource);
|
||||
void applyParticipantsMutes(
|
||||
const MTPDupdateGroupCallParticipants &update);
|
||||
void requestUnknownSsrcs();
|
||||
void changeChannelEmptyCallFlag();
|
||||
void requestUnknownParticipants();
|
||||
void changePeerEmptyCallFlag();
|
||||
void checkFinishSpeakingByActive();
|
||||
|
||||
const not_null<ChannelData*> _channel;
|
||||
const uint64 _id = 0;
|
||||
const uint64 _accessHash = 0;
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
int _version = 0;
|
||||
mtpRequestId _participantsRequestId = 0;
|
||||
mtpRequestId _reloadRequestId = 0;
|
||||
|
||||
std::vector<Participant> _participants;
|
||||
base::flat_map<uint32, not_null<UserData*>> _userBySsrc;
|
||||
base::flat_map<not_null<UserData*>, crl::time> _speakingByActiveFinishes;
|
||||
base::Timer _speakingByActiveFinishTimer;
|
||||
QString _nextOffset;
|
||||
rpl::variable<int> _fullCount = 0;
|
||||
|
||||
base::flat_map<uint32, crl::time> _unknownSpokenSsrcs;
|
||||
mtpRequestId _unknownSsrcsRequestId = 0;
|
||||
base::flat_map<UserId, crl::time> _unknownSpokenUids;
|
||||
mtpRequestId _unknownUsersRequestId = 0;
|
||||
|
||||
rpl::event_stream<ParticipantUpdate> _participantUpdates;
|
||||
rpl::event_stream<> _participantsSliceAdded;
|
||||
|
||||
@@ -888,6 +888,26 @@ bool PeerData::canSendPolls() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::canManageGroupCall() const {
|
||||
if (const auto chat = asChat()) {
|
||||
return chat->amCreator()
|
||||
|| (chat->adminRights() & ChatAdminRight::f_manage_call);
|
||||
} else if (const auto group = asMegagroup()) {
|
||||
return group->amCreator()
|
||||
|| (group->adminRights() & ChatAdminRight::f_manage_call);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Data::GroupCall *PeerData::groupCall() const {
|
||||
if (const auto chat = asChat()) {
|
||||
return chat->groupCall();
|
||||
} else if (const auto group = asMegagroup()) {
|
||||
return group->groupCall();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PeerData::setIsBlocked(bool is) {
|
||||
const auto status = is
|
||||
? BlockStatus::Blocked
|
||||
|
||||
@@ -29,6 +29,7 @@ class Session;
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class GroupCall;
|
||||
|
||||
int PeerColorIndex(PeerId peerId);
|
||||
int PeerColorIndex(int32 bareId);
|
||||
@@ -202,6 +203,7 @@ public:
|
||||
[[nodiscard]] rpl::producer<bool> slowmodeAppliedValue() const;
|
||||
[[nodiscard]] int slowmodeSecondsLeft() const;
|
||||
[[nodiscard]] bool canSendPolls() const;
|
||||
[[nodiscard]] bool canManageGroupCall() const;
|
||||
|
||||
[[nodiscard]] UserData *asUser();
|
||||
[[nodiscard]] const UserData *asUser() const;
|
||||
@@ -383,6 +385,8 @@ public:
|
||||
}
|
||||
void setLoadedStatus(LoadedStatus status);
|
||||
|
||||
[[nodiscard]] Data::GroupCall *groupCall() const;
|
||||
|
||||
const PeerId id;
|
||||
QString name;
|
||||
MTPinputPeer input = MTP_inputPeerEmpty();
|
||||
|
||||
@@ -604,7 +604,12 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
||||
});
|
||||
}
|
||||
|
||||
chat->setFlags(data.vflags().v);
|
||||
const auto callFlag = MTPDchat::Flag::f_call_not_empty;
|
||||
const auto callNotEmpty = (data.vflags().v & callFlag)
|
||||
|| (chat->groupCall()
|
||||
&& chat->groupCall()->fullCount() > 0);
|
||||
chat->setFlags(data.vflags().v
|
||||
| (callNotEmpty ? callFlag : MTPDchat::Flag(0)));
|
||||
chat->count = data.vparticipants_count().v;
|
||||
|
||||
if (canAddMembers != chat->canAddMembers()) {
|
||||
@@ -651,13 +656,21 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
||||
channel->setDefaultRestrictions(
|
||||
MTP_chatBannedRights(MTP_flags(0), MTP_int(0)));
|
||||
}
|
||||
const auto callFlag = MTPDchannel::Flag::f_call_not_empty;
|
||||
const auto callNotEmpty = (data.vflags().v & callFlag)
|
||||
|| (channel->groupCall()
|
||||
&& channel->groupCall()->fullCount() > 0);
|
||||
if (minimal) {
|
||||
auto mask = 0
|
||||
| MTPDchannel::Flag::f_broadcast
|
||||
| MTPDchannel::Flag::f_verified
|
||||
| MTPDchannel::Flag::f_megagroup
|
||||
| MTPDchannel::Flag::f_call_active
|
||||
| MTPDchannel::Flag::f_call_not_empty
|
||||
| MTPDchannel_ClientFlag::f_forbidden;
|
||||
channel->setFlags((channel->flags() & ~mask) | (data.vflags().v & mask));
|
||||
channel->setFlags((channel->flags() & ~mask)
|
||||
| (data.vflags().v & mask)
|
||||
| (callNotEmpty ? callFlag : MTPDchannel::Flag(0)));
|
||||
if (channel->input.type() == mtpc_inputPeerEmpty
|
||||
|| channel->inputChannel.type() == mtpc_inputChannelEmpty) {
|
||||
channel->setAccessHash(data.vaccess_hash().value_or_empty());
|
||||
@@ -686,9 +699,6 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|
||||
} else {
|
||||
channel->setUnavailableReasons({});
|
||||
}
|
||||
const auto callFlag = MTPDchannel::Flag::f_call_not_empty;
|
||||
const auto callNotEmpty = (data.vflags().v & callFlag)
|
||||
|| (channel->call() && channel->call()->fullCount() > 0);
|
||||
channel->setFlags(data.vflags().v
|
||||
| (callNotEmpty ? callFlag : MTPDchannel::Flag(0)));
|
||||
//if (const auto feedId = data.vfeed_id()) { // #feed
|
||||
@@ -826,9 +836,9 @@ auto Session::invitedToCallUsers(uint64 callId) const
|
||||
|
||||
void Session::registerInvitedToCallUser(
|
||||
uint64 callId,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user) {
|
||||
const auto call = channel->call();
|
||||
const auto call = peer->groupCall();
|
||||
if (call && call->id() == callId) {
|
||||
const auto inCall = ranges::contains(
|
||||
call->participants(),
|
||||
@@ -1164,6 +1174,7 @@ void Session::setupMigrationViewer() {
|
||||
return;
|
||||
}
|
||||
|
||||
chat->clearGroupCall();
|
||||
if (const auto from = historyLoaded(chat)) {
|
||||
if (const auto to = historyLoaded(channel)) {
|
||||
if (to->inChatList() && from->inChatList()) {
|
||||
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
-> const base::flat_set<not_null<UserData*>> &;
|
||||
void registerInvitedToCallUser(
|
||||
uint64 callId,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user);
|
||||
void unregisterInvitedToCallUser(uint64 callId, not_null<UserData*> user);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ void SharedMediaShowOverview(
|
||||
return;
|
||||
}
|
||||
}
|
||||
windows.front()->showSection(Info::Memento(
|
||||
windows.front()->showSection(std::make_shared<Info::Memento>(
|
||||
history->peer,
|
||||
Info::Section(type)));
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ bool Stickers::applyArchivedResultFake() {
|
||||
MTP_long(raw->access),
|
||||
MTP_string(raw->title),
|
||||
MTP_string(raw->shortName),
|
||||
MTP_photoSizeEmpty(MTP_string()),
|
||||
MTP_vector<MTPPhotoSize>(),
|
||||
MTP_int(0),
|
||||
MTP_int(raw->count),
|
||||
MTP_int(raw->hash));
|
||||
@@ -814,10 +814,20 @@ void Stickers::featuredSetsReceived(
|
||||
auto it = sets.find(data->vid().v);
|
||||
const auto title = getSetTitle(*data);
|
||||
const auto installDate = data->vinstalled_date().value_or_empty();
|
||||
const auto thumb = data->vthumb();
|
||||
const auto thumbnail = thumb
|
||||
? Images::FromPhotoSize(&session(), *data, *thumb)
|
||||
: ImageWithLocation();
|
||||
const auto thumbnail = [&] {
|
||||
if (const auto thumbs = data->vthumbs()) {
|
||||
for (const auto &thumb : thumbs->v) {
|
||||
const auto result = Images::FromPhotoSize(
|
||||
&session(),
|
||||
*data,
|
||||
thumb);
|
||||
if (result.location.valid()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImageWithLocation();
|
||||
}();
|
||||
if (it == sets.cend()) {
|
||||
auto setClientFlags = MTPDstickerSet_ClientFlag::f_featured
|
||||
| MTPDstickerSet_ClientFlag::f_not_loaded;
|
||||
@@ -1126,10 +1136,20 @@ StickersSet *Stickers::feedSet(const MTPDstickerSet &data) {
|
||||
auto it = sets.find(data.vid().v);
|
||||
auto title = getSetTitle(data);
|
||||
auto flags = MTPDstickerSet::Flags(0);
|
||||
const auto thumb = data.vthumb();
|
||||
const auto thumbnail = thumb
|
||||
? Images::FromPhotoSize(&session(), data, *thumb)
|
||||
: ImageWithLocation();
|
||||
const auto thumbnail = [&] {
|
||||
if (const auto thumbs = data.vthumbs()) {
|
||||
for (const auto &thumb : thumbs->v) {
|
||||
const auto result = Images::FromPhotoSize(
|
||||
&session(),
|
||||
data,
|
||||
thumb);
|
||||
if (result.location.valid()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImageWithLocation();
|
||||
}();
|
||||
if (it == sets.cend()) {
|
||||
it = sets.emplace(data.vid().v, std::make_unique<StickersSet>(
|
||||
&owner(),
|
||||
|
||||
@@ -1776,7 +1776,7 @@ bool Widget::onCancelSearch() {
|
||||
if (const auto peer = _searchInChat.peer()) {
|
||||
Ui::showPeerHistory(peer, ShowAtUnreadMsgId);
|
||||
//} else if (const auto feed = _searchInChat.feed()) { // #feed
|
||||
// controller()->showSection(HistoryFeed::Memento(feed));
|
||||
// controller()->showSection(std::make_shared<HistoryFeed::Memento>(feed));
|
||||
} else {
|
||||
Unexpected("Empty key in onCancelSearch().");
|
||||
}
|
||||
@@ -1800,7 +1800,7 @@ void Widget::onCancelSearchInChat() {
|
||||
if (const auto peer = _searchInChat.peer()) {
|
||||
Ui::showPeerHistory(peer, ShowAtUnreadMsgId);
|
||||
//} else if (const auto feed = _searchInChat.feed()) { // #feed
|
||||
// controller()->showSection(HistoryFeed::Memento(feed));
|
||||
// controller()->showSection(std::make_shared<HistoryFeed::Memento>(feed));
|
||||
} else {
|
||||
Unexpected("Empty key in onCancelSearchInPeer().");
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "boxes/sticker_set_box.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "core/application.h"
|
||||
@@ -1220,9 +1219,7 @@ void InnerWidget::copyContextImage(not_null<PhotoData*> photo) {
|
||||
}
|
||||
|
||||
const auto image = media->image(Data::PhotoSize::Large)->original();
|
||||
if (!Platform::SetClipboardImage(image)) {
|
||||
QGuiApplication::clipboard()->setImage(image);
|
||||
}
|
||||
QGuiApplication::clipboard()->setImage(image);
|
||||
}
|
||||
|
||||
void InnerWidget::copySelectedText() {
|
||||
|
||||
@@ -371,8 +371,8 @@ void Widget::setupShortcuts() {
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||
auto result = std::make_unique<SectionMemento>(channel());
|
||||
std::shared_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||
auto result = std::make_shared<SectionMemento>(channel());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
std::shared_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
void setInternalState(const QRect &geometry, not_null<SectionMemento*> memento);
|
||||
|
||||
|
||||
@@ -1053,9 +1053,11 @@ void History::applyServiceChanges(
|
||||
} break;
|
||||
|
||||
case mtpc_messageActionGroupCall: {
|
||||
const auto &d = action.c_messageActionGroupCall();
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
const auto &d = action.c_messageActionGroupCall();
|
||||
channel->setCall(d.vcall());
|
||||
channel->setGroupCall(d.vcall());
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
chat->setGroupCall(d.vcall());
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/sticker_set_box.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "history/history_widget.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "mainwindow.h"
|
||||
@@ -1900,9 +1899,7 @@ void HistoryInner::copyContextImage(not_null<PhotoData*> photo) {
|
||||
}
|
||||
|
||||
const auto image = media->image(Data::PhotoSize::Large)->original();
|
||||
if (!Platform::SetClipboardImage(image)) {
|
||||
QGuiApplication::clipboard()->setImage(image);
|
||||
}
|
||||
QGuiApplication::clipboard()->setImage(image);
|
||||
}
|
||||
|
||||
void HistoryInner::showStickerPackInfo(not_null<DocumentData*> document) {
|
||||
|
||||
@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_game.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_group_call.h" // Data::GroupCall::id().
|
||||
#include "core/application.h"
|
||||
@@ -38,18 +39,27 @@ namespace {
|
||||
|
||||
constexpr auto kPinnedMessageTextLimit = 16;
|
||||
|
||||
[[nodiscard]] rpl::producer<bool> ChannelHasThisCallValue(
|
||||
not_null<ChannelData*> channel,
|
||||
[[nodiscard]] bool PeerCallKnown(not_null<PeerData*> peer) {
|
||||
if (peer->groupCall() != nullptr) {
|
||||
return true;
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
return !(chat->flags() & MTPDchat::Flag::f_call_active);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return !(channel->flags() & MTPDchannel::Flag::f_call_active);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<bool> PeerHasThisCallValue(
|
||||
not_null<PeerData*> peer,
|
||||
uint64 id) {
|
||||
return channel->session().changes().peerFlagsValue(
|
||||
channel,
|
||||
return peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::GroupCall
|
||||
) | rpl::filter([=] {
|
||||
return (channel->call() != nullptr)
|
||||
|| !(channel->flags()
|
||||
& MTPDchannel::Flag::f_call_active);
|
||||
return PeerCallKnown(peer);
|
||||
}) | rpl::map([=] {
|
||||
const auto call = channel->call();
|
||||
const auto call = peer->groupCall();
|
||||
return (call && call->id() == id);
|
||||
}) | rpl::distinct_until_changed(
|
||||
) | rpl::take_while([=](bool hasThisCall) {
|
||||
@@ -59,15 +69,15 @@ constexpr auto kPinnedMessageTextLimit = 16;
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<bool> ChannelHasThisCall(
|
||||
not_null<ChannelData*> channel,
|
||||
[[nodiscard]] std::optional<bool> PeerHasThisCall(
|
||||
not_null<PeerData*> peer,
|
||||
uint64 id) {
|
||||
const auto call = channel->call();
|
||||
const auto call = peer->groupCall();
|
||||
return call
|
||||
? std::make_optional(call->id() == id)
|
||||
: (channel->flags() & MTPDchannel::Flag::f_call_active)
|
||||
? std::nullopt
|
||||
: std::make_optional(false);
|
||||
: PeerCallKnown(peer)
|
||||
? std::make_optional(false)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64 CallIdFromInput(const MTPInputGroupCall &data) {
|
||||
@@ -76,20 +86,20 @@ constexpr auto kPinnedMessageTextLimit = 16;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr ChannelCallClickHandler(
|
||||
not_null<ChannelData*> megagroup,
|
||||
[[nodiscard]] ClickHandlerPtr GroupCallClickHandler(
|
||||
not_null<PeerData*> peer,
|
||||
uint64 callId) {
|
||||
return std::make_shared<LambdaClickHandler>([=] {
|
||||
const auto call = megagroup->call();
|
||||
const auto call = peer->groupCall();
|
||||
if (call && call->id() == callId) {
|
||||
const auto &windows = megagroup->session().windows();
|
||||
const auto &windows = peer->session().windows();
|
||||
if (windows.empty()) {
|
||||
Core::App().domain().activate(&megagroup->session().account());
|
||||
Core::App().domain().activate(&peer->session().account());
|
||||
if (windows.empty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
windows.front()->startOrJoinGroupCall(megagroup);
|
||||
windows.front()->startOrJoinGroupCall(peer);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -338,10 +348,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||
return prepareDiscardedCallText(duration->v);
|
||||
}
|
||||
const auto callId = CallIdFromInput(action.vcall());
|
||||
const auto channel = history()->peer->asChannel();
|
||||
const auto linkCallId = !channel
|
||||
? 0
|
||||
: ChannelHasThisCall(channel, callId).value_or(false)
|
||||
const auto peer = history()->peer;
|
||||
const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false)
|
||||
? callId
|
||||
: 0;
|
||||
return prepareStartedCallText(linkCallId);
|
||||
@@ -350,16 +358,14 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||
auto prepareInviteToGroupCall = [this](const MTPDmessageActionInviteToGroupCall &action) {
|
||||
const auto callId = CallIdFromInput(action.vcall());
|
||||
const auto owner = &history()->owner();
|
||||
const auto channel = history()->peer->asChannel();
|
||||
const auto peer = history()->peer;
|
||||
for (const auto id : action.vusers().v) {
|
||||
const auto user = owner->user(id.v);
|
||||
if (channel && callId) {
|
||||
owner->registerInvitedToCallUser(callId, channel, user);
|
||||
if (callId) {
|
||||
owner->registerInvitedToCallUser(callId, peer, user);
|
||||
}
|
||||
};
|
||||
const auto linkCallId = !channel
|
||||
? 0
|
||||
: ChannelHasThisCall(channel, callId).value_or(false)
|
||||
const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false)
|
||||
? callId
|
||||
: 0;
|
||||
return prepareInvitedToCallText(action.vusers().v, linkCallId);
|
||||
@@ -534,10 +540,10 @@ HistoryService::PreparedText HistoryService::prepareStartedCallText(
|
||||
uint64 linkCallId) {
|
||||
auto result = PreparedText{};
|
||||
result.links.push_back(fromLink());
|
||||
const auto channel = history()->peer->asChannel();
|
||||
auto chatText = tr::lng_action_group_call_started_chat(tr::now);
|
||||
if (channel && linkCallId) {
|
||||
result.links.push_back(ChannelCallClickHandler(channel, linkCallId));
|
||||
if (linkCallId) {
|
||||
const auto peer = history()->peer;
|
||||
result.links.push_back(GroupCallClickHandler(peer, linkCallId));
|
||||
chatText = textcmdLink(2, chatText);
|
||||
}
|
||||
result.text = tr::lng_action_group_call_started(
|
||||
@@ -552,14 +558,14 @@ HistoryService::PreparedText HistoryService::prepareStartedCallText(
|
||||
HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
|
||||
const QVector<MTPint> &users,
|
||||
uint64 linkCallId) {
|
||||
const auto channel = history()->peer->asChannel();
|
||||
const auto owner = &channel->owner();
|
||||
const auto owner = &history()->owner();
|
||||
auto chatText = tr::lng_action_invite_user_chat(tr::now);
|
||||
auto result = PreparedText{};
|
||||
result.links.push_back(fromLink());
|
||||
auto linkIndex = 1;
|
||||
if (channel && linkCallId) {
|
||||
result.links.push_back(ChannelCallClickHandler(channel, linkCallId));
|
||||
if (linkCallId) {
|
||||
const auto peer = history()->peer;
|
||||
result.links.push_back(GroupCallClickHandler(peer, linkCallId));
|
||||
chatText = textcmdLink(++linkIndex, chatText);
|
||||
}
|
||||
if (users.size() == 1) {
|
||||
@@ -938,38 +944,35 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||
updateText(prepareDiscardedCallText(discard.duration));
|
||||
}, call->lifetime);
|
||||
|
||||
if (const auto channel = history()->peer->asChannel()) {
|
||||
const auto has = ChannelHasThisCall(channel, id);
|
||||
if (!has.has_value()) {
|
||||
ChannelHasThisCallValue(
|
||||
channel,
|
||||
id
|
||||
) | rpl::start_with_next([=](bool has) {
|
||||
updateText(prepareStartedCallText(has ? id : 0));
|
||||
}, call->lifetime);
|
||||
} else if (*has) {
|
||||
ChannelHasThisCallValue(
|
||||
channel,
|
||||
id
|
||||
) | rpl::skip(1) | rpl::start_with_next([=](bool has) {
|
||||
Assert(!has);
|
||||
updateText(prepareStartedCallText(0));
|
||||
}, call->lifetime);
|
||||
}
|
||||
const auto peer = history()->peer;
|
||||
const auto has = PeerHasThisCall(peer, id);
|
||||
if (!has.has_value()) {
|
||||
PeerHasThisCallValue(
|
||||
peer,
|
||||
id
|
||||
) | rpl::start_with_next([=](bool has) {
|
||||
updateText(prepareStartedCallText(has ? id : 0));
|
||||
}, call->lifetime);
|
||||
} else if (*has) {
|
||||
PeerHasThisCallValue(
|
||||
peer,
|
||||
id
|
||||
) | rpl::skip(1) | rpl::start_with_next([=](bool has) {
|
||||
Assert(!has);
|
||||
updateText(prepareStartedCallText(0));
|
||||
}, call->lifetime);
|
||||
}
|
||||
}
|
||||
} else if (message.vaction().type() == mtpc_messageActionInviteToGroupCall) {
|
||||
const auto &data = message.vaction().c_messageActionInviteToGroupCall();
|
||||
const auto id = CallIdFromInput(data.vcall());
|
||||
const auto channel = history()->peer->asChannel();
|
||||
const auto has = channel
|
||||
? ChannelHasThisCall(channel, id)
|
||||
: std::make_optional(false);
|
||||
const auto peer = history()->peer;
|
||||
const auto has = PeerHasThisCall(peer, id);
|
||||
auto hasLink = !has.has_value()
|
||||
? ChannelHasThisCallValue(channel, id)
|
||||
? PeerHasThisCallValue(peer, id)
|
||||
: (*has)
|
||||
? ChannelHasThisCallValue(
|
||||
channel,
|
||||
? PeerHasThisCallValue(
|
||||
peer,
|
||||
id) | rpl::skip(1) | rpl::type_erased()
|
||||
: rpl::producer<bool>();
|
||||
if (!hasLink) {
|
||||
|
||||
@@ -687,7 +687,7 @@ HistoryWidget::HistoryWidget(
|
||||
cancelReply(lastKeyboardUsed);
|
||||
crl::on_main(this, [=, history = action.history]{
|
||||
controller->showSection(
|
||||
HistoryView::ScheduledMemento(history));
|
||||
std::make_shared<HistoryView::ScheduledMemento>(history));
|
||||
});
|
||||
} else {
|
||||
fastShowAtEnd(action.history);
|
||||
@@ -757,9 +757,6 @@ void HistoryWidget::initVoiceRecordBar() {
|
||||
});
|
||||
_voiceRecordBar->setLockBottom(std::move(scrollHeight));
|
||||
}
|
||||
_voiceRecordBar->setEscFilter([=]() -> bool {
|
||||
return _replyToId || (_nonEmptySelection && _list);
|
||||
});
|
||||
|
||||
_voiceRecordBar->setSendButtonGeometryValue(_send->geometryValue());
|
||||
|
||||
@@ -820,6 +817,13 @@ void HistoryWidget::initVoiceRecordBar() {
|
||||
_scroll->viewportEvent(e);
|
||||
}, lifetime());
|
||||
|
||||
_voiceRecordBar->shownValue(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
if (!shown) {
|
||||
applyDraft();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_voiceRecordBar->hideFast();
|
||||
}
|
||||
|
||||
@@ -1498,7 +1502,7 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U
|
||||
} else if (to.section == Section::Scheduled) {
|
||||
history->setDraft(Data::DraftKey::Scheduled(), std::move(draft));
|
||||
controller()->showSection(
|
||||
HistoryView::ScheduledMemento(history));
|
||||
std::make_shared<HistoryView::ScheduledMemento>(history));
|
||||
} else {
|
||||
history->setLocalDraft(std::move(draft));
|
||||
if (history == _history) {
|
||||
@@ -1621,7 +1625,8 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
||||
: _history->localEditDraft()
|
||||
? _history->localEditDraft()
|
||||
: _history->localDraft();
|
||||
auto fieldAvailable = canWriteMessage();
|
||||
auto fieldAvailable = canWriteMessage()
|
||||
&& !_voiceRecordBar->isActive();
|
||||
if (!draft || (!_history->localEditDraft() && !fieldAvailable)) {
|
||||
auto fieldWillBeHiddenAfterEdit = (!fieldAvailable && _editMsgId != 0);
|
||||
clearFieldText(0, fieldHistoryAction);
|
||||
@@ -2065,7 +2070,7 @@ void HistoryWidget::refreshScheduledToggle() {
|
||||
_scheduled->show();
|
||||
_scheduled->addClickHandler([=] {
|
||||
controller()->showSection(
|
||||
HistoryView::ScheduledMemento(_history));
|
||||
std::make_shared<HistoryView::ScheduledMemento>(_history));
|
||||
});
|
||||
orderWidgets(); // Raise drag areas to the top.
|
||||
} else if (_scheduled && !has) {
|
||||
@@ -3091,7 +3096,7 @@ void HistoryWidget::send(Api::SendOptions options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_voiceRecordBar && _voiceRecordBar->isListenState()) {
|
||||
if (_voiceRecordBar->isListenState()) {
|
||||
_voiceRecordBar->requestToSendWithOptions(options);
|
||||
return;
|
||||
}
|
||||
@@ -3875,7 +3880,7 @@ bool HistoryWidget::pushTabbedSelectorToThirdSection(
|
||||
Core::App().settings().setTabbedReplacedWithInfo(false);
|
||||
controller()->resizeForThirdSection();
|
||||
controller()->showSection(
|
||||
ChatHelpers::TabbedMemento(),
|
||||
std::make_shared<ChatHelpers::TabbedMemento>(),
|
||||
params.withThirdColumn());
|
||||
return true;
|
||||
}
|
||||
@@ -3906,6 +3911,14 @@ void HistoryWidget::setTabbedPanel(std::unique_ptr<TabbedPanel> panel) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryWidget::preventsClose(Fn<void()> &&continueCallback) const {
|
||||
if (_voiceRecordBar->isActive()) {
|
||||
_voiceRecordBar->showDiscardBox(std::move(continueCallback));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryWidget::toggleTabbedSelectorMode() {
|
||||
if (!_peer) {
|
||||
return;
|
||||
@@ -4321,7 +4334,7 @@ bool HistoryWidget::confirmSendingFiles(
|
||||
}
|
||||
|
||||
if (hasImage) {
|
||||
auto image = Platform::GetClipboardImage();
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
@@ -5374,7 +5387,7 @@ void HistoryWidget::refreshPinnedBarButton(bool many) {
|
||||
const auto id = _pinnedTracker->currentMessageId();
|
||||
if (id.message) {
|
||||
controller()->showSection(
|
||||
HistoryView::PinnedMemento(
|
||||
std::make_shared<HistoryView::PinnedMemento>(
|
||||
_history,
|
||||
((!_migrated || id.message.channel)
|
||||
? id.message.msg
|
||||
@@ -5419,16 +5432,15 @@ void HistoryWidget::setupGroupCallTracker() {
|
||||
_groupCallBar->barClicks(),
|
||||
_groupCallBar->joinClicks()
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto channel = _history->peer->asChannel();
|
||||
if (!channel) {
|
||||
return;
|
||||
} else if (channel->amAnonymous()) {
|
||||
const auto peer = _history->peer;
|
||||
const auto channel = peer->asChannel();
|
||||
if (channel && channel->amAnonymous()) {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = tr::lng_group_call_no_anonymous(tr::now),
|
||||
});
|
||||
return;
|
||||
} else if (channel->call()) {
|
||||
controller()->startOrJoinGroupCall(channel);
|
||||
} else if (peer->groupCall()) {
|
||||
controller()->startOrJoinGroupCall(peer);
|
||||
}
|
||||
}, _groupCallBar->lifetime());
|
||||
|
||||
@@ -5633,7 +5645,7 @@ void HistoryWidget::editMessage(FullMsgId itemId) {
|
||||
}
|
||||
|
||||
void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
||||
if (_voiceRecordBar && _voiceRecordBar->isListenState()) {
|
||||
if (_voiceRecordBar->isListenState()) {
|
||||
Ui::show(Box<InformBox>(tr::lng_edit_caption_voice(tr::now)));
|
||||
return;
|
||||
}
|
||||
@@ -6107,6 +6119,8 @@ void HistoryWidget::escape() {
|
||||
_fieldAutocomplete->hideAnimated();
|
||||
} else if (_replyToId && _field->getTextWithTags().text.isEmpty()) {
|
||||
cancelReply();
|
||||
} else if (auto &voice = _voiceRecordBar; voice->isActive()) {
|
||||
voice->showDiscardBox(nullptr, anim::type::normal);
|
||||
} else {
|
||||
_cancelRequests.fire({});
|
||||
}
|
||||
|
||||
@@ -119,6 +119,8 @@ public:
|
||||
|
||||
void historyLoaded();
|
||||
|
||||
[[nodiscard]] bool preventsClose(Fn<void()> &&continueCallback) const;
|
||||
|
||||
// When resizing the widget with top edge moved up or down and we
|
||||
// want to add this top movement to the scroll position, so inner
|
||||
// content will not move.
|
||||
|
||||
@@ -1567,10 +1567,6 @@ void ComposeControls::initVoiceRecordBar() {
|
||||
_voiceRecordBar->setLockBottom(std::move(bottom));
|
||||
}
|
||||
|
||||
_voiceRecordBar->setEscFilter([=] {
|
||||
return (isEditingMessage() || replyingToMessage());
|
||||
});
|
||||
|
||||
_voiceRecordBar->updateSendButtonTypeRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateSendButtonType();
|
||||
@@ -1639,7 +1635,15 @@ void ComposeControls::updateControlsGeometry(QSize size) {
|
||||
- _send->width()
|
||||
- _tabbedSelectorToggle->width()
|
||||
- (_botCommandShown ? _botCommandStart->width() : 0);
|
||||
_field->resizeToWidth(fieldWidth);
|
||||
{
|
||||
const auto oldFieldHeight = _field->height();
|
||||
_field->resizeToWidth(fieldWidth);
|
||||
// If a height of the field is changed
|
||||
// then this method will be called with the updated size.
|
||||
if (oldFieldHeight != _field->height()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto buttonsTop = size.height() - _attachToggle->height();
|
||||
|
||||
@@ -1708,7 +1712,11 @@ void ComposeControls::paintBackground(QRect clip) {
|
||||
}
|
||||
|
||||
void ComposeControls::escape() {
|
||||
_cancelRequests.fire({});
|
||||
if (auto &voice = _voiceRecordBar; !voice->isActive()) {
|
||||
voice->showDiscardBox(nullptr, anim::type::normal);
|
||||
} else {
|
||||
_cancelRequests.fire({});
|
||||
}
|
||||
}
|
||||
|
||||
bool ComposeControls::pushTabbedSelectorToThirdSection(
|
||||
@@ -1728,7 +1736,7 @@ bool ComposeControls::pushTabbedSelectorToThirdSection(
|
||||
&st::historyRecordVoiceRippleBgActive);
|
||||
_window->resizeForThirdSection();
|
||||
_window->showSection(
|
||||
ChatHelpers::TabbedMemento(),
|
||||
std::make_shared<ChatHelpers::TabbedMemento>(),
|
||||
params.withThirdColumn());
|
||||
return true;
|
||||
}
|
||||
@@ -2130,6 +2138,14 @@ bool ComposeControls::isRecording() const {
|
||||
return _voiceRecordBar->isRecording();
|
||||
}
|
||||
|
||||
bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const {
|
||||
if (_voiceRecordBar->isActive()) {
|
||||
_voiceRecordBar->showDiscardBox(std::move(continueCallback));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ComposeControls::updateInlineBotQuery() {
|
||||
if (!_history) {
|
||||
return;
|
||||
|
||||
@@ -135,6 +135,8 @@ public:
|
||||
[[nodiscard]] bool isEditingMessage() const;
|
||||
[[nodiscard]] FullMsgId replyingToMessage() const;
|
||||
|
||||
[[nodiscard]] bool preventsClose(Fn<void()> &&continueCallback) const;
|
||||
|
||||
void showForGrab();
|
||||
void showStarted();
|
||||
void showFinished();
|
||||
|
||||
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "ui/controls/send_button.h"
|
||||
#include "ui/effects/animation_value.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -61,10 +62,6 @@ enum class FilterType {
|
||||
Cancel,
|
||||
};
|
||||
|
||||
inline float64 InterpolateF(int a, int b, float64 b_ratio) {
|
||||
return a + float64(b - a) * b_ratio;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto InactiveColor(const QColor &c) {
|
||||
return QColor(c.red(), c.green(), c.blue(), kInactiveWaveformBarAlpha);
|
||||
}
|
||||
@@ -786,15 +783,15 @@ void RecordLock::drawProgress(Painter &p) {
|
||||
|
||||
const auto &blockHeight = st::historyRecordLockIconBottomHeight;
|
||||
|
||||
const auto blockRectWidth = InterpolateF(
|
||||
const auto blockRectWidth = anim::interpolateF(
|
||||
size.width(),
|
||||
st::historyRecordStopIconWidth,
|
||||
lockToStopProgress);
|
||||
const auto blockRectHeight = InterpolateF(
|
||||
const auto blockRectHeight = anim::interpolateF(
|
||||
blockHeight,
|
||||
st::historyRecordStopIconWidth,
|
||||
lockToStopProgress);
|
||||
const auto blockRectTop = InterpolateF(
|
||||
const auto blockRectTop = anim::interpolateF(
|
||||
size.height() - blockHeight,
|
||||
std::round((size.height() - blockRectHeight) / 2.),
|
||||
lockToStopProgress);
|
||||
@@ -1073,7 +1070,6 @@ void VoiceRecordBar::init() {
|
||||
|
||||
_lock->locks(
|
||||
) | rpl::start_with_next([=] {
|
||||
installClickOutsideFilter();
|
||||
_level->setType(VoiceRecordButton::Type::Send);
|
||||
|
||||
_level->clicks(
|
||||
@@ -1176,10 +1172,6 @@ void VoiceRecordBar::visibilityAnimate(bool show, Fn<void()> &&callback) {
|
||||
_showAnimation.start(std::move(animationCallback), from, to, duration);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::setEscFilter(Fn<bool()> &&callback) {
|
||||
_escFilter = std::move(callback);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::setStartRecordingFilter(Fn<bool()> &&callback) {
|
||||
_startRecordingFilter = std::move(callback);
|
||||
}
|
||||
@@ -1230,6 +1222,9 @@ void VoiceRecordBar::startRecording() {
|
||||
}, [=] {
|
||||
stop(false);
|
||||
}, _recordingLifetime);
|
||||
_recordingLifetime.add([=] {
|
||||
_recording = false;
|
||||
});
|
||||
};
|
||||
visibilityAnimate(true, std::move(appearanceCallback));
|
||||
show();
|
||||
@@ -1290,7 +1285,6 @@ void VoiceRecordBar::stop(bool send) {
|
||||
void VoiceRecordBar::finish() {
|
||||
_recordingLifetime.destroy();
|
||||
_lockShowing = false;
|
||||
_recording = false;
|
||||
_inField = false;
|
||||
_redCircleProgress = 0.;
|
||||
_recordingSamples = 0;
|
||||
@@ -1414,11 +1408,15 @@ bool VoiceRecordBar::isRecording() const {
|
||||
return _recording.current();
|
||||
}
|
||||
|
||||
bool VoiceRecordBar::isActive() const {
|
||||
return isRecording() || isListenState();
|
||||
}
|
||||
|
||||
void VoiceRecordBar::hideAnimated() {
|
||||
if (isHidden()) {
|
||||
return;
|
||||
}
|
||||
visibilityAnimate(false, [=] { hide(); });
|
||||
visibilityAnimate(false, [=] { hideFast(); });
|
||||
}
|
||||
|
||||
void VoiceRecordBar::finishAnimating() {
|
||||
@@ -1493,76 +1491,6 @@ void VoiceRecordBar::orderControls() {
|
||||
_lock->raise();
|
||||
}
|
||||
|
||||
void VoiceRecordBar::installClickOutsideFilter() {
|
||||
const auto box = _recordingLifetime.make_state<QPointer<ConfirmBox>>();
|
||||
const auto showBox = [=] {
|
||||
if (*box) {
|
||||
return;
|
||||
}
|
||||
auto sure = [=](Fn<void()> &&close) {
|
||||
stop(false);
|
||||
close();
|
||||
};
|
||||
*box = Ui::show(Box<ConfirmBox>(
|
||||
tr::lng_record_lock_cancel_sure(tr::now),
|
||||
tr::lng_record_lock_discard(tr::now),
|
||||
st::attentionBoxButton,
|
||||
std::move(sure)));
|
||||
};
|
||||
|
||||
const auto computeResult = [=](not_null<QEvent*> e) {
|
||||
using Type = FilterType;
|
||||
if (!_lock->isLocked()) {
|
||||
return Type::Continue;
|
||||
}
|
||||
const auto type = e->type();
|
||||
const auto noBox = !(*box);
|
||||
if (type == QEvent::KeyPress) {
|
||||
const auto key = static_cast<QKeyEvent*>(e.get())->key();
|
||||
const auto isEsc = (key == Qt::Key_Escape);
|
||||
const auto isEnter = (key == Qt::Key_Enter
|
||||
|| key == Qt::Key_Return);
|
||||
if (noBox) {
|
||||
if (isEnter) {
|
||||
stop(true);
|
||||
return Type::Cancel;
|
||||
} else if (isEsc && (_escFilter && _escFilter())) {
|
||||
return Type::Continue;
|
||||
}
|
||||
return Type::ShowBox;
|
||||
}
|
||||
return (isEsc || isEnter) ? Type::Continue : Type::ShowBox;
|
||||
} else if (type == QEvent::ContextMenu || type == QEvent::Shortcut) {
|
||||
return Type::ShowBox;
|
||||
} else if (type == QEvent::MouseButtonPress) {
|
||||
return (noBox && !_inField.current() && !_lock->underMouse())
|
||||
? Type::ShowBox
|
||||
: Type::Continue;
|
||||
}
|
||||
return Type::Continue;
|
||||
};
|
||||
|
||||
auto filterCallback = [=](not_null<QEvent*> e) {
|
||||
using Result = base::EventFilterResult;
|
||||
switch(computeResult(e)) {
|
||||
case FilterType::ShowBox: {
|
||||
showBox();
|
||||
return Result::Cancel;
|
||||
}
|
||||
case FilterType::Continue: return Result::Continue;
|
||||
case FilterType::Cancel: return Result::Cancel;
|
||||
default: return Result::Continue;
|
||||
}
|
||||
};
|
||||
|
||||
auto filter = base::install_event_filter(
|
||||
QCoreApplication::instance(),
|
||||
std::move(filterCallback));
|
||||
|
||||
_recordingLifetime.make_state<base::unique_qptr<QObject>>(
|
||||
std::move(filter));
|
||||
}
|
||||
|
||||
void VoiceRecordBar::installListenStateFilter() {
|
||||
auto keyFilterCallback = [=](not_null<QEvent*> e) {
|
||||
using Result = base::EventFilterResult;
|
||||
@@ -1575,7 +1503,6 @@ void VoiceRecordBar::installListenStateFilter() {
|
||||
const auto keyEvent = static_cast<QKeyEvent*>(e.get());
|
||||
const auto key = keyEvent->key();
|
||||
const auto isSpace = (key == Qt::Key_Space);
|
||||
const auto isEsc = (key == Qt::Key_Escape);
|
||||
const auto isEnter = (key == Qt::Key_Enter
|
||||
|| key == Qt::Key_Return);
|
||||
if (isSpace && !keyEvent->isAutoRepeat() && _listen) {
|
||||
@@ -1586,14 +1513,6 @@ void VoiceRecordBar::installListenStateFilter() {
|
||||
requestToSendWithOptions({});
|
||||
return Result::Cancel;
|
||||
}
|
||||
if (isEsc) {
|
||||
if (_escFilter && _escFilter()) {
|
||||
return Result::Continue;
|
||||
} else {
|
||||
hideAnimated();
|
||||
return Result::Cancel;
|
||||
}
|
||||
}
|
||||
return Result::Continue;
|
||||
}
|
||||
default: return Result::Continue;
|
||||
@@ -1608,4 +1527,30 @@ void VoiceRecordBar::installListenStateFilter() {
|
||||
std::move(keyFilter));
|
||||
}
|
||||
|
||||
void VoiceRecordBar::showDiscardBox(
|
||||
Fn<void()> &&callback,
|
||||
anim::type animated) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
auto sure = [=, callback = std::move(callback)](Fn<void()> &&close) {
|
||||
if (animated == anim::type::instant) {
|
||||
hideFast();
|
||||
} else {
|
||||
hideAnimated();
|
||||
}
|
||||
close();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
Ui::show(Box<ConfirmBox>(
|
||||
(isListenState()
|
||||
? tr::lng_record_listen_cancel_sure
|
||||
: tr::lng_record_lock_cancel_sure)(tr::now),
|
||||
tr::lng_record_lock_discard(tr::now),
|
||||
st::attentionBoxButton,
|
||||
std::move(sure)));
|
||||
}
|
||||
|
||||
} // namespace HistoryView::Controls
|
||||
|
||||
@@ -47,6 +47,10 @@ public:
|
||||
int recorderHeight);
|
||||
~VoiceRecordBar();
|
||||
|
||||
void showDiscardBox(
|
||||
Fn<void()> &&callback,
|
||||
anim::type animated = anim::type::instant);
|
||||
|
||||
void startRecording();
|
||||
void finishAnimating();
|
||||
void hideAnimated();
|
||||
@@ -66,12 +70,12 @@ public:
|
||||
|
||||
void setLockBottom(rpl::producer<int> &&bottom);
|
||||
void setSendButtonGeometryValue(rpl::producer<QRect> &&geometry);
|
||||
void setEscFilter(Fn<bool()> &&callback);
|
||||
void setStartRecordingFilter(Fn<bool()> &&callback);
|
||||
|
||||
[[nodiscard]] bool isRecording() const;
|
||||
[[nodiscard]] bool isLockPresent() const;
|
||||
[[nodiscard]] bool isListenState() const;
|
||||
[[nodiscard]] bool isActive() const;
|
||||
|
||||
private:
|
||||
enum class StopType {
|
||||
@@ -99,7 +103,6 @@ private:
|
||||
void drawMessage(Painter &p, float64 recordActive);
|
||||
|
||||
void startRedCircleAnimation();
|
||||
void installClickOutsideFilter();
|
||||
void installListenStateFilter();
|
||||
|
||||
bool isTypeRecord() const;
|
||||
@@ -134,7 +137,6 @@ private:
|
||||
|
||||
Ui::Text::String _message;
|
||||
|
||||
Fn<bool()> _escFilter;
|
||||
Fn<bool()> _startRecordingFilter;
|
||||
|
||||
rpl::variable<bool> _recording = false;
|
||||
|
||||
@@ -39,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -126,9 +125,7 @@ void CopyImage(not_null<PhotoData*> photo) {
|
||||
}
|
||||
|
||||
const auto image = media->image(Data::PhotoSize::Large)->original();
|
||||
if (!Platform::SetClipboardImage(image)) {
|
||||
QGuiApplication::clipboard()->setImage(image);
|
||||
}
|
||||
QGuiApplication::clipboard()->setImage(image);
|
||||
}
|
||||
|
||||
void ShowStickerPackInfo(
|
||||
|
||||
@@ -61,8 +61,8 @@ void GenerateUserpicsInRow(
|
||||
}
|
||||
}
|
||||
|
||||
GroupCallTracker::GroupCallTracker(not_null<ChannelData*> channel)
|
||||
: _channel(channel) {
|
||||
GroupCallTracker::GroupCallTracker(not_null<PeerData*> peer)
|
||||
: _peer(peer) {
|
||||
}
|
||||
|
||||
rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
@@ -118,7 +118,10 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
}
|
||||
for (auto i = 0; i != kLimit - already; ++i) {
|
||||
if (adding[i]) {
|
||||
state->userpics.push_back(UserpicInRow{ adding[i]->user });
|
||||
state->userpics.push_back(UserpicInRow{
|
||||
.peer = adding[i]->user,
|
||||
.speaking = adding[i]->speaking,
|
||||
});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -133,12 +136,19 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
GenerateUserpicsInRow(
|
||||
state->current.userpics,
|
||||
state->userpics,
|
||||
st);
|
||||
state->current.users.reserve(state->userpics.size());
|
||||
state->current.users.clear();
|
||||
state->someUserpicsNotLoaded = false;
|
||||
for (const auto &userpic : state->userpics) {
|
||||
for (auto &userpic : state->userpics) {
|
||||
userpic.peer->loadUserpic();
|
||||
const auto pic = userpic.peer->genUserpic(userpic.view, st.size);
|
||||
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
|
||||
state->current.users.push_back({
|
||||
.userpic = pic.toImage(),
|
||||
.userpicKey = userpic.uniqueKey,
|
||||
.id = userpic.peer->bareId(),
|
||||
.speaking = userpic.speaking,
|
||||
});
|
||||
if (userpic.peer->hasUserpic()
|
||||
&& userpic.peer->useEmptyUserpic(userpic.view)) {
|
||||
state->someUserpicsNotLoaded = true;
|
||||
@@ -172,12 +182,17 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
Expects(state->userpics.size() <= kLimit);
|
||||
|
||||
const auto &participants = call->participants();
|
||||
auto i = state->userpics.begin();
|
||||
auto i = begin(state->userpics);
|
||||
|
||||
// Find where to put a new speaking userpic.
|
||||
for (; i != state->userpics.end(); ++i) {
|
||||
for (; i != end(state->userpics); ++i) {
|
||||
if (i->peer == user) {
|
||||
return false;
|
||||
if (i->speaking) {
|
||||
return false;
|
||||
}
|
||||
const auto index = i - begin(state->userpics);
|
||||
state->current.users[index].speaking = i->speaking = true;
|
||||
return true;
|
||||
}
|
||||
const auto j = ranges::find(
|
||||
participants,
|
||||
@@ -194,7 +209,10 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
}
|
||||
|
||||
// Add the new speaking to the place we found.
|
||||
const auto added = state->userpics.insert(i, UserpicInRow{ user });
|
||||
const auto added = state->userpics.insert(i, UserpicInRow{
|
||||
.peer = user,
|
||||
.speaking = true,
|
||||
});
|
||||
|
||||
// Remove him from the tail, if he was there.
|
||||
for (auto i = added + 1; i != state->userpics.end(); ++i) {
|
||||
@@ -252,8 +270,27 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
if (CheckPushToFront(state, call, user, st)) {
|
||||
pushNext();
|
||||
}
|
||||
} else if (RegenerateUserpics(state, call, st)) {
|
||||
pushNext();
|
||||
} else {
|
||||
auto updateSpeakingState = update.was.has_value()
|
||||
&& (update.now->speaking != update.was->speaking);
|
||||
if (updateSpeakingState) {
|
||||
const auto i = ranges::find(
|
||||
state->userpics,
|
||||
user,
|
||||
&UserpicInRow::peer);
|
||||
if (i != end(state->userpics)) {
|
||||
const auto index = i - begin(state->userpics);
|
||||
state->current.users[index].speaking
|
||||
= i->speaking
|
||||
= update.now->speaking;
|
||||
} else {
|
||||
updateSpeakingState = false;
|
||||
}
|
||||
}
|
||||
if (RegenerateUserpics(state, call, st)
|
||||
|| updateSpeakingState) {
|
||||
pushNext();
|
||||
}
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
@@ -262,7 +299,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
return RegenerateUserpics(state, call, st);
|
||||
}) | rpl::start_with_next(pushNext, lifetime);
|
||||
|
||||
call->channel()->session().downloaderTaskFinished(
|
||||
call->peer()->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return state->someUserpicsNotLoaded;
|
||||
}) | rpl::start_with_next([=] {
|
||||
@@ -289,15 +326,15 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
|
||||
}
|
||||
|
||||
rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::content() const {
|
||||
const auto channel = _channel;
|
||||
const auto peer = _peer;
|
||||
return rpl::combine(
|
||||
channel->session().changes().peerFlagsValue(
|
||||
channel,
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::GroupCall),
|
||||
Core::App().calls().currentGroupCallValue()
|
||||
) | rpl::map([=](const auto&, Calls::GroupCall *current) {
|
||||
const auto call = channel->call();
|
||||
return (call && (!current || current->channel() != channel))
|
||||
const auto call = peer->groupCall();
|
||||
return (call && (!current || current->peer() != peer))
|
||||
? call
|
||||
: nullptr;
|
||||
}) | rpl::distinct_until_changed(
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace HistoryView {
|
||||
|
||||
struct UserpicInRow {
|
||||
not_null<PeerData*> peer;
|
||||
bool speaking = false;
|
||||
mutable std::shared_ptr<Data::CloudImageView> view;
|
||||
mutable InMemoryKey uniqueKey;
|
||||
};
|
||||
@@ -40,7 +41,7 @@ void GenerateUserpicsInRow(
|
||||
|
||||
class GroupCallTracker final {
|
||||
public:
|
||||
GroupCallTracker(not_null<ChannelData*> channel);
|
||||
explicit GroupCallTracker(not_null<PeerData*> peer);
|
||||
|
||||
[[nodiscard]] rpl::producer<Ui::GroupCallBarContent> content() const;
|
||||
[[nodiscard]] rpl::producer<> joinClicks() const;
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
const UserpicsInRowStyle &st);
|
||||
|
||||
private:
|
||||
not_null<ChannelData*> _channel;
|
||||
const not_null<PeerData*> _peer;
|
||||
|
||||
rpl::event_stream<> _joinClicks;
|
||||
|
||||
|
||||
@@ -342,8 +342,8 @@ void PinnedWidget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> PinnedWidget::createMemento() {
|
||||
auto result = std::make_unique<PinnedMemento>(history());
|
||||
std::shared_ptr<Window::SectionMemento> PinnedWidget::createMemento() {
|
||||
auto result = std::make_shared<PinnedMemento>(history());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
std::shared_ptr<Window::SectionMemento> createMemento() override;
|
||||
bool showMessage(
|
||||
PeerId peerId,
|
||||
const Window::SectionShow ¶ms,
|
||||
|
||||
@@ -603,7 +603,7 @@ bool RepliesWidget::confirmSendingFiles(
|
||||
}
|
||||
|
||||
if (hasImage) {
|
||||
auto image = Platform::GetClipboardImage();
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
@@ -1315,6 +1315,10 @@ Dialogs::RowDescriptor RepliesWidget::activeChat() const {
|
||||
};
|
||||
}
|
||||
|
||||
bool RepliesWidget::preventsClose(Fn<void()> &&continueCallback) const {
|
||||
return _composeControls->preventsClose(std::move(continueCallback));
|
||||
}
|
||||
|
||||
QPixmap RepliesWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||
_topBar->updateControlsVisibility();
|
||||
if (params.withTopBarShadow) _topBarShadow->hide();
|
||||
@@ -1363,8 +1367,8 @@ bool RepliesWidget::returnTabbedSelector() {
|
||||
return _composeControls->returnTabbedSelector();
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> RepliesWidget::createMemento() {
|
||||
auto result = std::make_unique<RepliesMemento>(history(), _rootId);
|
||||
std::shared_ptr<Window::SectionMemento> RepliesWidget::createMemento() {
|
||||
auto result = std::make_shared<RepliesMemento>(history(), _rootId);
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
Dialogs::RowDescriptor activeChat() const override;
|
||||
bool preventsClose(Fn<void()> &&continueCallback) const override;
|
||||
|
||||
bool hasTopBarShadow() const override {
|
||||
return true;
|
||||
@@ -86,7 +87,7 @@ public:
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
std::shared_ptr<Window::SectionMemento> createMemento() override;
|
||||
bool showMessage(
|
||||
PeerId peerId,
|
||||
const Window::SectionShow ¶ms,
|
||||
|
||||
@@ -335,7 +335,7 @@ bool ScheduledWidget::confirmSendingFiles(
|
||||
}
|
||||
|
||||
if (hasImage) {
|
||||
auto image = Platform::GetClipboardImage();
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
@@ -894,6 +894,10 @@ Dialogs::RowDescriptor ScheduledWidget::activeChat() const {
|
||||
};
|
||||
}
|
||||
|
||||
bool ScheduledWidget::preventsClose(Fn<void()> &&continueCallback) const {
|
||||
return _composeControls->preventsClose(std::move(continueCallback));
|
||||
}
|
||||
|
||||
QPixmap ScheduledWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||
_topBar->updateControlsVisibility();
|
||||
if (params.withTopBarShadow) _topBarShadow->hide();
|
||||
@@ -937,8 +941,8 @@ bool ScheduledWidget::returnTabbedSelector() {
|
||||
return _composeControls->returnTabbedSelector();
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> ScheduledWidget::createMemento() {
|
||||
auto result = std::make_unique<ScheduledMemento>(history());
|
||||
std::shared_ptr<Window::SectionMemento> ScheduledWidget::createMemento() {
|
||||
auto result = std::make_shared<ScheduledMemento>(history());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
not_null<History*> history() const;
|
||||
Dialogs::RowDescriptor activeChat() const override;
|
||||
bool preventsClose(Fn<void()> &&continueCallback) const override;
|
||||
|
||||
bool hasTopBarShadow() const override {
|
||||
return true;
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
std::shared_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
void setInternalState(
|
||||
const QRect &geometry,
|
||||
|
||||
@@ -220,23 +220,7 @@ void TopBarWidget::call() {
|
||||
|
||||
void TopBarWidget::groupCall() {
|
||||
if (const auto peer = _activeChat.key.peer()) {
|
||||
if (const auto megagroup = peer->asMegagroup()) {
|
||||
_controller->startOrJoinGroupCall(megagroup);
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
const auto callback = [=] {
|
||||
Ui::hideLayer();
|
||||
const auto start = [=](not_null<ChannelData*> megagroup) {
|
||||
_controller->startOrJoinGroupCall(megagroup, true);
|
||||
};
|
||||
peer->session().api().migrateChat(
|
||||
chat,
|
||||
crl::guard(this, start));
|
||||
};
|
||||
Ui::show(Box<ConfirmBox>(
|
||||
tr::lng_group_call_create_sure(tr::now),
|
||||
tr::lng_continue(tr::now),
|
||||
crl::guard(this, callback)));
|
||||
}
|
||||
_controller->startOrJoinGroupCall(peer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,15 +493,15 @@ void TopBarWidget::infoClicked() {
|
||||
} else if (key.folder()) {
|
||||
_controller->closeFolder();
|
||||
//} else if (const auto feed = _activeChat.feed()) { // #feed
|
||||
// _controller->showSection(Info::Memento(
|
||||
// _controller->showSection(std::make_shared<Info::Memento>(
|
||||
// feed,
|
||||
// Info::Section(Info::Section::Type::Profile)));
|
||||
} else if (key.peer()->isSelf()) {
|
||||
_controller->showSection(Info::Memento(
|
||||
_controller->showSection(std::make_shared<Info::Memento>(
|
||||
key.peer(),
|
||||
Info::Section(Storage::SharedMediaType::Photo)));
|
||||
} else if (key.peer()->isRepliesChat()) {
|
||||
_controller->showSection(Info::Memento(
|
||||
_controller->showSection(std::make_shared<Info::Memento>(
|
||||
key.peer(),
|
||||
Info::Section(Storage::SharedMediaType::Photo)));
|
||||
} else {
|
||||
@@ -543,6 +527,7 @@ void TopBarWidget::setActiveChat(
|
||||
}
|
||||
_activeChat = activeChat;
|
||||
_sendAction = sendAction;
|
||||
_titlePeerText.clear();
|
||||
_back->clearState();
|
||||
update();
|
||||
|
||||
@@ -662,11 +647,14 @@ void TopBarWidget::updateControlsGeometry() {
|
||||
}
|
||||
_infoToggle->moveToRight(_rightTaken, otherButtonsTop);
|
||||
if (!_infoToggle->isHidden()) {
|
||||
_rightTaken += _infoToggle->width() + st::topBarSkip;
|
||||
_infoToggle->moveToRight(_rightTaken, otherButtonsTop);
|
||||
_rightTaken += _infoToggle->width();
|
||||
}
|
||||
if (!_call->isHidden() || !_groupCall->isHidden()) {
|
||||
_call->moveToRight(_rightTaken, otherButtonsTop);
|
||||
_groupCall->moveToRight(_rightTaken, otherButtonsTop);
|
||||
_rightTaken += _call->width();
|
||||
}
|
||||
_call->moveToRight(_rightTaken, otherButtonsTop);
|
||||
_groupCall->moveToRight(_rightTaken, otherButtonsTop);
|
||||
_rightTaken += _call->width();
|
||||
_search->moveToRight(_rightTaken, otherButtonsTop);
|
||||
_rightTaken += _search->width() + st::topBarCallSkip;
|
||||
|
||||
@@ -726,7 +714,7 @@ void TopBarWidget::updateControlsVisibility() {
|
||||
const auto callsEnabled = [&] {
|
||||
if (const auto peer = _activeChat.key.peer()) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
return true;
|
||||
return !user->isSelf() && !user->isBot();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -734,11 +722,7 @@ void TopBarWidget::updateControlsVisibility() {
|
||||
_call->setVisible(historyMode && callsEnabled);
|
||||
const auto groupCallsEnabled = [&] {
|
||||
if (const auto peer = _activeChat.key.peer()) {
|
||||
if (const auto megagroup = peer->asMegagroup()) {
|
||||
return megagroup->canManageCall();
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
return chat->amCreator();
|
||||
}
|
||||
return peer->canManageGroupCall();
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
@@ -87,8 +87,8 @@ void Widget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(user());
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(user());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
std::unique_ptr<ContentMemento> doCreateMemento() override;
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
InnerWidget *_inner = nullptr;
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ void Channels::showChannelsWithSearch(bool withSearch) {
|
||||
auto mementoStack = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
mementoStack.push_back(std::move(contentMemento));
|
||||
_controller->showSection(
|
||||
Info::Memento(std::move(mementoStack)));
|
||||
std::make_unique<Info::Memento>(std::move(mementoStack)));
|
||||
}
|
||||
|
||||
void Channels::visibleTopBottomUpdated(
|
||||
|
||||
@@ -84,7 +84,7 @@ void Cover::refreshStatusText() {
|
||||
}();
|
||||
_status->setRichText(textcmdLink(1, statusText));
|
||||
_status->setLink(1, std::make_shared<LambdaClickHandler>([=] {
|
||||
_controller->showSection(Info::Memento(
|
||||
_controller->showSection(std::make_unique<Info::Memento>(
|
||||
_feed,
|
||||
Section::Type::Channels));
|
||||
}));
|
||||
|
||||
@@ -748,7 +748,7 @@ topBarSearch: IconButton {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
topBarSkip: -2px;
|
||||
topBarSkip: -5px;
|
||||
topBarCallSkip: -1px;
|
||||
topBarMenuToggle: IconButton(topBarSearch) {
|
||||
width: 44px;
|
||||
@@ -766,7 +766,7 @@ topBarCall: IconButton(topBarSearch) {
|
||||
topBarGroupCall: IconButton(topBarSearch) {
|
||||
icon: icon {{ "top_bar_group_call", menuIconFg }};
|
||||
iconOver: icon {{ "top_bar_group_call", menuIconFgOver }};
|
||||
iconPosition: point(8px, 15px);
|
||||
iconPosition: point(8px, 16px);
|
||||
}
|
||||
topBarInfo: IconButton(topBarSearch) {
|
||||
icon: icon {{ "top_bar_profile", menuIconFg }};
|
||||
|
||||
@@ -94,7 +94,7 @@ void ContentWidget::updateControlsGeometry() {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> ContentWidget::createMemento() {
|
||||
std::shared_ptr<ContentMemento> ContentWidget::createMemento() {
|
||||
auto result = doCreateMemento();
|
||||
_controller->saveSearchState(result.get());
|
||||
return result;
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
|
||||
virtual bool showInternal(
|
||||
not_null<ContentMemento*> memento) = 0;
|
||||
std::unique_ptr<ContentMemento> createMemento();
|
||||
std::shared_ptr<ContentMemento> createMemento();
|
||||
|
||||
virtual void setIsStackBottom(bool isStackBottom) {
|
||||
}
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
void updateControlsGeometry();
|
||||
void refreshSearchField(bool shown);
|
||||
|
||||
virtual std::unique_ptr<ContentMemento> doCreateMemento() = 0;
|
||||
virtual std::shared_ptr<ContentMemento> doCreateMemento() = 0;
|
||||
|
||||
const not_null<Controller*> _controller;
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ PollData *AbstractController::poll() const {
|
||||
}
|
||||
|
||||
void AbstractController::showSection(
|
||||
Window::SectionMemento &&memento,
|
||||
std::shared_ptr<Window::SectionMemento> memento,
|
||||
const Window::SectionShow ¶ms) {
|
||||
return parentController()->showSection(std::move(memento), params);
|
||||
}
|
||||
@@ -170,7 +170,7 @@ void Controller::setupMigrationViewer() {
|
||||
const auto section = _section;
|
||||
InvokeQueued(_widget, [=] {
|
||||
window->showSection(
|
||||
Memento(peer, section),
|
||||
std::make_shared<Memento>(peer, section),
|
||||
Window::SectionShow(
|
||||
Window::SectionShow::Way::Backward,
|
||||
anim::type::instant,
|
||||
@@ -261,9 +261,9 @@ void Controller::saveSearchState(not_null<ContentMemento*> memento) {
|
||||
}
|
||||
|
||||
void Controller::showSection(
|
||||
Window::SectionMemento &&memento,
|
||||
std::shared_ptr<Window::SectionMemento> memento,
|
||||
const Window::SectionShow ¶ms) {
|
||||
if (!_widget->showInternal(&memento, params)) {
|
||||
if (!_widget->showInternal(memento.get(), params)) {
|
||||
AbstractController::showSection(std::move(memento), params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
virtual rpl::producer<QString> mediaSourceQueryValue() const;
|
||||
|
||||
void showSection(
|
||||
Window::SectionMemento &&memento,
|
||||
std::shared_ptr<Window::SectionMemento> memento,
|
||||
const Window::SectionShow ¶ms = Window::SectionShow()) override;
|
||||
void showBackFromStack(
|
||||
const Window::SectionShow ¶ms = Window::SectionShow()) override;
|
||||
@@ -202,7 +202,7 @@ public:
|
||||
void saveSearchState(not_null<ContentMemento*> memento);
|
||||
|
||||
void showSection(
|
||||
Window::SectionMemento &&memento,
|
||||
std::shared_ptr<Window::SectionMemento> memento,
|
||||
const Window::SectionShow ¶ms = Window::SectionShow()) override;
|
||||
void showBackFromStack(
|
||||
const Window::SectionShow ¶ms = Window::SectionShow()) override;
|
||||
|
||||
@@ -106,7 +106,7 @@ void LayerWidget::parentResized() {
|
||||
Ui::FocusPersister persister(this);
|
||||
restoreFloatPlayerDelegate();
|
||||
|
||||
auto memento = MoveMemento(std::move(_content));
|
||||
auto memento = std::make_shared<MoveMemento>(std::move(_content));
|
||||
|
||||
// We want to call hideSpecialLayer synchronously to avoid glitches,
|
||||
// but we can't destroy LayerStackWidget from its' resizeEvent,
|
||||
|
||||
@@ -47,41 +47,41 @@ Memento::Memento(not_null<PollData*> poll, FullMsgId contextId)
|
||||
: Memento(DefaultStack(poll, contextId)) {
|
||||
}
|
||||
|
||||
Memento::Memento(std::vector<std::unique_ptr<ContentMemento>> stack)
|
||||
Memento::Memento(std::vector<std::shared_ptr<ContentMemento>> stack)
|
||||
: _stack(std::move(stack)) {
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack(
|
||||
std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack(
|
||||
not_null<PeerData*> peer,
|
||||
Section section) {
|
||||
auto result = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
auto result = std::vector<std::shared_ptr<ContentMemento>>();
|
||||
result.push_back(DefaultContent(peer, section));
|
||||
return result;
|
||||
}
|
||||
|
||||
//std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack( // #feed
|
||||
//std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack( // #feed
|
||||
// not_null<Data::Feed*> feed,
|
||||
// Section section) {
|
||||
// auto result = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
// auto result = std::vector<std::shared_ptr<ContentMemento>>();
|
||||
// result.push_back(DefaultContent(feed, section));
|
||||
// return result;
|
||||
//}
|
||||
//
|
||||
std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack(
|
||||
std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack(
|
||||
Settings::Tag settings,
|
||||
Section section) {
|
||||
auto result = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
result.push_back(std::make_unique<Settings::Memento>(
|
||||
auto result = std::vector<std::shared_ptr<ContentMemento>>();
|
||||
result.push_back(std::make_shared<Settings::Memento>(
|
||||
settings.self,
|
||||
section.settingsType()));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack(
|
||||
std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId contextId) {
|
||||
auto result = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
result.push_back(std::make_unique<Polls::Memento>(poll, contextId));
|
||||
auto result = std::vector<std::shared_ptr<ContentMemento>>();
|
||||
result.push_back(std::make_shared<Polls::Memento>(poll, contextId));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ Section Memento::DefaultSection(not_null<PeerData*> peer) {
|
||||
// return Section(Section::Type::Profile);
|
||||
//}
|
||||
|
||||
Memento Memento::Default(not_null<PeerData*> peer) {
|
||||
return Memento(peer, DefaultSection(peer));
|
||||
std::shared_ptr<Memento> Memento::Default(not_null<PeerData*> peer) {
|
||||
return std::make_shared<Memento>(peer, DefaultSection(peer));
|
||||
}
|
||||
// // #feed
|
||||
//Memento Memento::Default(Dialogs::Key key) {
|
||||
@@ -112,7 +112,7 @@ Memento Memento::Default(not_null<PeerData*> peer) {
|
||||
// return Memento(key.feed(), DefaultSection(key));
|
||||
//}
|
||||
|
||||
std::unique_ptr<ContentMemento> Memento::DefaultContent(
|
||||
std::shared_ptr<ContentMemento> Memento::DefaultContent(
|
||||
not_null<PeerData*> peer,
|
||||
Section section) {
|
||||
if (auto to = peer->migrateTo()) {
|
||||
@@ -123,32 +123,32 @@ std::unique_ptr<ContentMemento> Memento::DefaultContent(
|
||||
|
||||
switch (section.type()) {
|
||||
case Section::Type::Profile:
|
||||
return std::make_unique<Profile::Memento>(
|
||||
return std::make_shared<Profile::Memento>(
|
||||
peer,
|
||||
migratedPeerId);
|
||||
case Section::Type::Media:
|
||||
return std::make_unique<Media::Memento>(
|
||||
return std::make_shared<Media::Memento>(
|
||||
peer,
|
||||
migratedPeerId,
|
||||
section.mediaType());
|
||||
case Section::Type::CommonGroups:
|
||||
return std::make_unique<CommonGroups::Memento>(peer->asUser());
|
||||
return std::make_shared<CommonGroups::Memento>(peer->asUser());
|
||||
case Section::Type::Members:
|
||||
return std::make_unique<Members::Memento>(
|
||||
return std::make_shared<Members::Memento>(
|
||||
peer,
|
||||
migratedPeerId);
|
||||
}
|
||||
Unexpected("Wrong section type in Info::Memento::DefaultContent()");
|
||||
}
|
||||
//
|
||||
//std::unique_ptr<ContentMemento> Memento::DefaultContent( // #feed
|
||||
//std::shared_ptr<ContentMemento> Memento::DefaultContent( // #feed
|
||||
// not_null<Data::Feed*> feed,
|
||||
// Section section) {
|
||||
// switch (section.type()) {
|
||||
// case Section::Type::Profile:
|
||||
// return std::make_unique<FeedProfile::Memento>(feed);
|
||||
// return std::make_shared<FeedProfile::Memento>(feed);
|
||||
// case Section::Type::Channels:
|
||||
// return std::make_unique<Channels::Memento>(feed);
|
||||
// return std::make_shared<Channels::Memento>(feed);
|
||||
// }
|
||||
// Unexpected("Wrong feed section in Info::Memento::DefaultContent()");
|
||||
//}
|
||||
@@ -179,7 +179,7 @@ object_ptr<Ui::LayerWidget> Memento::createLayer(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ContentMemento>> Memento::takeStack() {
|
||||
std::vector<std::shared_ptr<ContentMemento>> Memento::takeStack() {
|
||||
return std::move(_stack);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
//Memento(not_null<Data::Feed*> feed, Section section); // #feed
|
||||
Memento(Settings::Tag settings, Section section);
|
||||
Memento(not_null<PollData*> poll, FullMsgId contextId);
|
||||
explicit Memento(std::vector<std::unique_ptr<ContentMemento>> stack);
|
||||
explicit Memento(std::vector<std::shared_ptr<ContentMemento>> stack);
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(
|
||||
QWidget *parent,
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
int stackSize() const {
|
||||
return int(_stack.size());
|
||||
}
|
||||
std::vector<std::unique_ptr<ContentMemento>> takeStack();
|
||||
std::vector<std::shared_ptr<ContentMemento>> takeStack();
|
||||
|
||||
not_null<ContentMemento*> content() {
|
||||
Expects(!_stack.empty());
|
||||
@@ -62,33 +62,33 @@ public:
|
||||
|
||||
static Section DefaultSection(not_null<PeerData*> peer);
|
||||
//static Section DefaultSection(Dialogs::Key key); // #feed
|
||||
static Memento Default(not_null<PeerData*> peer);
|
||||
static std::shared_ptr<Memento> Default(not_null<PeerData*> peer);
|
||||
//static Memento Default(Dialogs::Key key); // #feed
|
||||
|
||||
~Memento();
|
||||
|
||||
private:
|
||||
static std::vector<std::unique_ptr<ContentMemento>> DefaultStack(
|
||||
static std::vector<std::shared_ptr<ContentMemento>> DefaultStack(
|
||||
not_null<PeerData*> peer,
|
||||
Section section);
|
||||
//static std::vector<std::unique_ptr<ContentMemento>> DefaultStack( // #feed
|
||||
//static std::vector<std::shared_ptr<ContentMemento>> DefaultStack( // #feed
|
||||
// not_null<Data::Feed*> feed,
|
||||
// Section section);
|
||||
static std::vector<std::unique_ptr<ContentMemento>> DefaultStack(
|
||||
static std::vector<std::shared_ptr<ContentMemento>> DefaultStack(
|
||||
Settings::Tag settings,
|
||||
Section section);
|
||||
static std::vector<std::unique_ptr<ContentMemento>> DefaultStack(
|
||||
static std::vector<std::shared_ptr<ContentMemento>> DefaultStack(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId contextId);
|
||||
|
||||
//static std::unique_ptr<ContentMemento> DefaultContent( // #feed
|
||||
//static std::shared_ptr<ContentMemento> DefaultContent( // #feed
|
||||
// not_null<Data::Feed*> feed,
|
||||
// Section section);
|
||||
static std::unique_ptr<ContentMemento> DefaultContent(
|
||||
static std::shared_ptr<ContentMemento> DefaultContent(
|
||||
not_null<PeerData*> peer,
|
||||
Section section);
|
||||
|
||||
std::vector<std::unique_ptr<ContentMemento>> _stack;
|
||||
std::vector<std::shared_ptr<ContentMemento>> _stack;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ bool SectionWidget::showInternal(
|
||||
return _content->showInternal(memento, params);
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> SectionWidget::createMemento() {
|
||||
std::shared_ptr<Window::SectionMemento> SectionWidget::createMemento() {
|
||||
return _content->createMemento();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
std::shared_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
object_ptr<Ui::LayerWidget> moveContentToLayer(
|
||||
QRect bodyGeometry) override;
|
||||
|
||||
@@ -50,8 +50,8 @@ const style::InfoTopBar &TopBarStyle(Wrap wrap) {
|
||||
} // namespace
|
||||
|
||||
struct WrapWidget::StackItem {
|
||||
std::unique_ptr<ContentMemento> section;
|
||||
// std::unique_ptr<ContentMemento> anotherTab;
|
||||
std::shared_ptr<ContentMemento> section;
|
||||
// std::shared_ptr<ContentMemento> anotherTab;
|
||||
};
|
||||
|
||||
WrapWidget::WrapWidget(
|
||||
@@ -96,7 +96,7 @@ void WrapWidget::setupShortcuts() {
|
||||
}
|
||||
|
||||
void WrapWidget::restoreHistoryStack(
|
||||
std::vector<std::unique_ptr<ContentMemento>> stack) {
|
||||
std::vector<std::shared_ptr<ContentMemento>> stack) {
|
||||
Expects(!stack.empty());
|
||||
Expects(!hasStackHistory());
|
||||
|
||||
@@ -188,7 +188,7 @@ void WrapWidget::injectActivePeerProfile(not_null<PeerData*> peer) {
|
||||
//}
|
||||
|
||||
void WrapWidget::injectActiveProfileMemento(
|
||||
std::unique_ptr<ContentMemento> memento) {
|
||||
std::shared_ptr<ContentMemento> memento) {
|
||||
auto injected = StackItem();
|
||||
injected.section = std::move(memento);
|
||||
_historyStack.insert(
|
||||
@@ -712,13 +712,13 @@ rpl::producer<SelectedItems> WrapWidget::selectedListValue() const {
|
||||
|
||||
// Was done for top level tabs support.
|
||||
//
|
||||
//std::unique_ptr<ContentMemento> WrapWidget::createTabMemento(
|
||||
//std::shared_ptr<ContentMemento> WrapWidget::createTabMemento(
|
||||
// Tab tab) {
|
||||
// switch (tab) {
|
||||
// case Tab::Profile: return std::make_unique<Profile::Memento>(
|
||||
// case Tab::Profile: return std::make_shared<Profile::Memento>(
|
||||
// _controller->peerId(),
|
||||
// _controller->migratedPeerId());
|
||||
// case Tab::Media: return std::make_unique<Media::Memento>(
|
||||
// case Tab::Media: return std::make_shared<Media::Memento>(
|
||||
// _controller->peerId(),
|
||||
// _controller->migratedPeerId(),
|
||||
// Media::Type::Photo);
|
||||
@@ -884,8 +884,8 @@ void WrapWidget::highlightTopBar() {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> WrapWidget::createMemento() {
|
||||
auto stack = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
std::shared_ptr<Window::SectionMemento> WrapWidget::createMemento() {
|
||||
auto stack = std::vector<std::shared_ptr<ContentMemento>>();
|
||||
stack.reserve(_historyStack.size() + 1);
|
||||
for (auto &stackItem : base::take(_historyStack)) {
|
||||
stack.push_back(std::move(stackItem.section));
|
||||
@@ -895,7 +895,7 @@ std::unique_ptr<Window::SectionMemento> WrapWidget::createMemento() {
|
||||
// We're not in valid state anymore and supposed to be destroyed.
|
||||
_controller = nullptr;
|
||||
|
||||
return std::make_unique<Memento>(std::move(stack));
|
||||
return std::make_shared<Memento>(std::move(stack));
|
||||
}
|
||||
|
||||
rpl::producer<int> WrapWidget::desiredHeightValue() const {
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
bool showBackFromStackInternal(const Window::SectionShow ¶ms);
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
std::shared_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
rpl::producer<int> desiredHeightValue() const override;
|
||||
|
||||
@@ -146,10 +146,10 @@ private:
|
||||
void injectActivePeerProfile(not_null<PeerData*> peer);
|
||||
//void injectActiveFeedProfile(not_null<Data::Feed*> feed); // #feed
|
||||
void injectActiveProfileMemento(
|
||||
std::unique_ptr<ContentMemento> memento);
|
||||
std::shared_ptr<ContentMemento> memento);
|
||||
void checkBeforeClose(Fn<void()> close);
|
||||
void restoreHistoryStack(
|
||||
std::vector<std::unique_ptr<ContentMemento>> stack);
|
||||
std::vector<std::shared_ptr<ContentMemento>> stack);
|
||||
bool hasStackHistory() const {
|
||||
return !_historyStack.empty();
|
||||
}
|
||||
@@ -178,7 +178,7 @@ private:
|
||||
|
||||
//void showTab(Tab tab);
|
||||
void showContent(object_ptr<ContentWidget> content);
|
||||
//std::unique_ptr<ContentMemento> createTabMemento(Tab tab);
|
||||
//std::shared_ptr<ContentMemento> createTabMemento(Tab tab);
|
||||
object_ptr<ContentWidget> createContent(
|
||||
not_null<ContentMemento*> memento,
|
||||
not_null<Controller*> controller);
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
base::unique_qptr<Ui::DropdownMenu> _topBarMenu;
|
||||
|
||||
// Tab _tab = Tab::Profile;
|
||||
// std::unique_ptr<ContentMemento> _anotherTabMemento;
|
||||
// std::shared_ptr<ContentMemento> _anotherTabMemento;
|
||||
std::vector<StackItem> _historyStack;
|
||||
|
||||
rpl::event_stream<rpl::producer<int>> _desiredHeights;
|
||||
|
||||
@@ -92,7 +92,7 @@ inline auto AddButton(
|
||||
tracker)->entity();
|
||||
result->addClickHandler([=] {
|
||||
navigation->showSection(
|
||||
Info::Memento(peer, Section(type)));
|
||||
std::make_shared<Info::Memento>(peer, Section(type)));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
@@ -111,7 +111,7 @@ inline auto AddCommonGroupsButton(
|
||||
tracker)->entity();
|
||||
result->addClickHandler([=] {
|
||||
navigation->showSection(
|
||||
Info::Memento(user, Section::Type::CommonGroups));
|
||||
std::make_shared<Info::Memento>(user, Section::Type::CommonGroups));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -115,8 +115,8 @@ void Widget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(controller());
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(controller());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
std::unique_ptr<ContentMemento> doCreateMemento() override;
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
InnerWidget *_inner = nullptr;
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ void Widget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(controller());
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(controller());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
std::unique_ptr<ContentMemento> doCreateMemento() override;
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
Profile::Members *_inner = nullptr;
|
||||
|
||||
|
||||
@@ -90,8 +90,8 @@ void Widget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(poll(), contextId());
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(poll(), contextId());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
std::unique_ptr<ContentMemento> doCreateMemento() override;
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
not_null<InnerWidget*> _inner;
|
||||
|
||||
|
||||
@@ -843,7 +843,7 @@ object_ptr<Ui::RpWidget> SetupChannelMembers(
|
||||
lt_count_decimal,
|
||||
MembersCountValue(channel) | tr::to_count());
|
||||
auto membersCallback = [=] {
|
||||
controller->showSection(Info::Memento(
|
||||
controller->showSection(std::make_shared<Info::Memento>(
|
||||
channel,
|
||||
Section::Type::Members));
|
||||
};
|
||||
|
||||
@@ -81,7 +81,8 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
||||
_controller->parentController()));
|
||||
_cover->showSection(
|
||||
) | rpl::start_with_next([=](Section section) {
|
||||
_controller->showSection(Info::Memento(_peer, section));
|
||||
_controller->showSection(
|
||||
std::make_shared<Info::Memento>(_peer, section));
|
||||
}, _cover->lifetime());
|
||||
_cover->setOnlineCount(rpl::single(0));
|
||||
auto details = SetupDetails(_controller, parent, _peer);
|
||||
|
||||
@@ -342,14 +342,14 @@ void Members::showMembersWithSearch(bool withSearch) {
|
||||
//if (!_searchShown) {
|
||||
// toggleSearch();
|
||||
//}
|
||||
auto contentMemento = std::make_unique<Info::Members::Memento>(
|
||||
auto contentMemento = std::make_shared<Info::Members::Memento>(
|
||||
_controller);
|
||||
contentMemento->setState(saveState());
|
||||
contentMemento->setSearchStartsFocused(withSearch);
|
||||
auto mementoStack = std::vector<std::unique_ptr<ContentMemento>>();
|
||||
auto mementoStack = std::vector<std::shared_ptr<ContentMemento>>();
|
||||
mementoStack.push_back(std::move(contentMemento));
|
||||
_controller->showSection(
|
||||
Info::Memento(std::move(mementoStack)));
|
||||
std::make_shared<Info::Memento>(std::move(mementoStack)));
|
||||
}
|
||||
|
||||
//void Members::toggleSearch(anim::type animated) {
|
||||
|
||||
@@ -99,8 +99,8 @@ void Widget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(controller());
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(controller());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
std::unique_ptr<ContentMemento> doCreateMemento() override;
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
InnerWidget *_inner = nullptr;
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ rpl::producer<bool> Widget::desiredShadowVisibility() const {
|
||||
: rpl::single(true);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(self(), _type);
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(self(), _type);
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
std::unique_ptr<ContentMemento> doCreateMemento() override;
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
not_null<UserData*> _self;
|
||||
Type _type = Type();
|
||||
|
||||
@@ -385,6 +385,12 @@ void Domain::checkForLastProductionConfig(
|
||||
Core::App().refreshFallbackProductionConfig(mtp->config());
|
||||
}
|
||||
|
||||
void Domain::maybeActivate(not_null<Main::Account*> account) {
|
||||
Core::App().preventOrInvoke(crl::guard(account, [=] {
|
||||
activate(account);
|
||||
}));
|
||||
}
|
||||
|
||||
void Domain::activate(not_null<Main::Account*> account) {
|
||||
if (_active.current() == account.get()) {
|
||||
return;
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
void notifyUnreadBadgeChanged();
|
||||
|
||||
[[nodiscard]] not_null<Main::Account*> add(MTP::Environment environment);
|
||||
void maybeActivate(not_null<Main::Account*> account);
|
||||
void activate(not_null<Main::Account*> account);
|
||||
void addActivated(MTP::Environment environment);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user