Add some more actions to info profile.

This commit is contained in:
John Preston 2017-11-07 15:53:05 +04:00
parent 9f37820901
commit fcf2b9d1a7
12 changed files with 807 additions and 435 deletions

View file

@ -565,11 +565,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_block_bot" = "Stop and block bot";
"lng_profile_unblock_bot" = "Unblock bot";
"lng_profile_invite_to_group" = "Add to Group";
"lng_profile_delete_contact" = "Delete";
"lng_profile_delete_contact" = "Delete Contact";
"lng_profile_set_group_photo" = "Set Photo";
"lng_profile_add_participant" = "Add Members";
"lng_profile_view_channel" = "View Channel";
"lng_profile_join_channel" = "Join";
"lng_profile_join_channel" = "Join Channel";
"lng_profile_delete_and_exit" = "Leave";
"lng_profile_kick" = "Remove";
"lng_profile_sure_kick" = "Remove {user} from the group?";

View file

@ -1242,8 +1242,9 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) {
_menu = new Ui::PopupMenu(nullptr);
Window::PeerMenuOptions options;
options.pinToggle = options.showInfo = options.search = true;
options.fromChatsList = options.showInfo = true;
Window::FillPeerMenu(
_controller,
_menuPeer,
[this](const QString &text, base::lambda<void()> callback) {
return _menu->addAction(text, std::move(callback));

View file

@ -0,0 +1,680 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/profile/info_profile_actions.h"
#include <rpl/flatten_latest.h>
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/shadow.h"
#include "boxes/abstract_box.h"
#include "boxes/confirm_box.h"
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/add_contact_box.h"
#include "boxes/report_box.h"
#include "lang/lang_keys.h"
#include "info/info_controller.h"
#include "info/info_top_bar_override.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_text.h"
#include "window/window_controller.h"
#include "mainwidget.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "styles/style_info.h"
#include "styles/style_boxes.h"
namespace Info {
namespace Profile {
namespace {
object_ptr<Ui::RpWidget> CreateSkipWidget(
not_null<Ui::RpWidget*> parent) {
return Ui::CreateSkipWidget(parent, st::infoProfileSkip);
}
object_ptr<Ui::SlideWrap<>> CreateSlideSkipWidget(
not_null<Ui::RpWidget*> parent) {
return Ui::CreateSlideSkipWidget(parent, st::infoProfileSkip);
}
template <typename Text, typename ToggleOn, typename Callback>
auto AddActionButton(
not_null<Ui::VerticalLayout*> parent,
Text &&text,
ToggleOn &&toggleOn,
Callback &&callback,
const style::InfoProfileButton &st
= st::infoSharedMediaButton) {
auto result = parent->add(object_ptr<Ui::SlideWrap<Button>>(
parent,
object_ptr<Button>(
parent,
std::move(text),
st))
);
result->toggleOn(
std::move(toggleOn)
)->entity()->addClickHandler(std::move(callback));
return result;
};
template <typename Text, typename ToggleOn, typename Callback>
auto AddMainButton(
not_null<Ui::VerticalLayout*> parent,
Text &&text,
ToggleOn &&toggleOn,
Callback &&callback,
Ui::MultiSlideTracker &tracker) {
tracker.track(AddActionButton(
parent,
std::move(text) | ToUpperValue(),
std::move(toggleOn),
std::move(callback),
st::infoMainButton));
}
void ShareContactBox(not_null<UserData*> user) {
auto callback = [user](not_null<PeerData*> peer) {
if (!peer->canWrite()) {
Ui::show(Box<InformBox>(
lang(lng_forward_share_cant)),
LayerOption::KeepOther);
return;
}
auto recipient = peer->isUser()
? peer->name
: '\xAB' + peer->name + '\xBB';
Ui::show(Box<ConfirmBox>(
lng_forward_share_contact(lt_recipient, recipient),
lang(lng_forward_send),
[peer, user] {
App::main()->onShareContact(
peer->id,
user);
Ui::hideLayer();
}), LayerOption::KeepOther);
};
Ui::show(Box<PeerListBox>(
std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
[](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_cancel), [box] {
box->closeBox();
});
}));
}
class DetailsFiller {
public:
DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
object_ptr<Ui::RpWidget> fill();
private:
object_ptr<Ui::RpWidget> setupInfo();
object_ptr<Ui::RpWidget> setupMuteToggle();
void setupMainButtons();
Ui::MultiSlideTracker fillUserButtons(
not_null<UserData*> user);
Ui::MultiSlideTracker fillChannelButtons(
not_null<ChannelData*> channel);
template <
typename Widget,
typename = std::enable_if_t<
std::is_base_of_v<Ui::RpWidget, Widget>>>
Widget *add(
object_ptr<Widget> &&child,
const style::margins &margin = style::margins()) {
return _wrap->add(
std::move(child),
margin);
}
not_null<Controller*> _controller;
not_null<Ui::RpWidget*> _parent;
not_null<PeerData*> _peer;
object_ptr<Ui::VerticalLayout> _wrap;
};
class ActionsFiller {
public:
ActionsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
object_ptr<Ui::RpWidget> fill();
private:
void addInviteToGroupAction(not_null<UserData*> user);
void addShareContactAction(not_null<UserData*> user);
void addEditContactAction(not_null<UserData*> user);
void addDeleteContactAction(not_null<UserData*> user);
void addClearHistoryAction(not_null<UserData*> user);
void addDeleteConversationAction(not_null<UserData*> user);
void addBotCommandActions(not_null<UserData*> user);
void addReportAction();
void addBlockAction(not_null<UserData*> user);
void addLeaveChannelAction(not_null<ChannelData*> channel);
void addJoinChannelAction(not_null<ChannelData*> channel);
void fillUserActions(not_null<UserData*> user);
void fillChannelActions(not_null<ChannelData*> channel);
not_null<Controller*> _controller;
not_null<Ui::RpWidget*> _parent;
not_null<PeerData*> _peer;
object_ptr<Ui::VerticalLayout> _wrap = { nullptr };
};
DetailsFiller::DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer)
: _controller(controller)
, _parent(parent)
, _peer(peer)
, _wrap(_parent) {
}
object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
auto result = object_ptr<Ui::VerticalLayout>(_wrap);
auto tracker = Ui::MultiSlideTracker();
auto addInfoLine = [&](
LangKey label,
rpl::producer<TextWithEntities> &&text,
bool selectByDoubleClick = false,
const style::FlatLabel &textSt = st::infoLabeled) {
auto line = result->add(CreateTextWithLabel(
result,
Lang::Viewer(label) | WithEmptyEntities(),
std::move(text),
textSt,
st::infoProfileLabeledPadding,
selectByDoubleClick));
tracker.track(line);
return line;
};
auto addInfoOneLine = [&](
LangKey label,
rpl::producer<TextWithEntities> &&text) {
addInfoLine(
label,
std::move(text),
true,
st::infoLabeledOneLine);
};
if (auto user = _peer->asUser()) {
addInfoOneLine(lng_info_mobile_label, PhoneValue(user));
if (user->botInfo) {
addInfoLine(lng_info_about_label, AboutValue(user));
} else {
addInfoLine(lng_info_bio_label, BioValue(user));
}
addInfoOneLine(lng_info_username_label, UsernameValue(user));
} else {
addInfoOneLine(lng_info_link_label, LinkValue(_peer));
addInfoLine(lng_info_about_label, AboutValue(_peer));
}
result->add(object_ptr<Ui::SlideWrap<>>(
result,
object_ptr<Ui::PlainShadow>(result),
st::infoProfileSeparatorPadding)
)->toggleOn(std::move(tracker).atLeastOneShownValue());
object_ptr<FloatingIcon>(
result,
st::infoIconInformation,
st::infoInformationIconPosition);
return std::move(result);
}
object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
auto peer = _peer;
auto result = object_ptr<Button>(
_wrap,
Lang::Viewer(lng_profile_enable_notifications),
st::infoNotificationsButton);
result->toggleOn(
NotificationsEnabledValue(peer)
)->addClickHandler([=] {
App::main()->updateNotifySetting(
_peer,
_peer->isMuted()
? NotifySettingSetNotify
: NotifySettingSetMuted);
});
object_ptr<FloatingIcon>(
result,
st::infoIconNotifications,
st::infoNotificationsIconPosition);
return std::move(result);
}
void DetailsFiller::setupMainButtons() {
auto wrapButtons = [=](auto &&callback) {
auto topSkip = _wrap->add(CreateSlideSkipWidget(_wrap));
auto tracker = callback();
topSkip->toggleOn(std::move(tracker).atLeastOneShownValue());
};
if (auto user = _peer->asUser()) {
wrapButtons([=] {
return fillUserButtons(user);
});
} else if (auto channel = _peer->asChannel()) {
if (!channel->isMegagroup()) {
wrapButtons([=] {
return fillChannelButtons(channel);
});
}
}
}
Ui::MultiSlideTracker DetailsFiller::fillUserButtons(
not_null<UserData*> user) {
using namespace rpl::mappers;
Ui::MultiSlideTracker tracker;
auto window = _controller->window();
auto sendMessageVisible = window->historyPeer.value()
| rpl::map($1 != user);
auto sendMessage = [window, user] {
window->showPeerHistory(
user,
Window::SectionShow::Way::Forward);
};
AddMainButton(
_wrap,
Lang::Viewer(lng_profile_send_message),
std::move(sendMessageVisible),
std::move(sendMessage),
tracker);
auto addContact = [user] {
auto firstName = user->firstName;
auto lastName = user->lastName;
auto phone = user->phone().isEmpty()
? App::phoneFromSharedContact(user->bareId())
: user->phone();
Ui::show(Box<AddContactBox>(firstName, lastName, phone));
};
AddMainButton(
_wrap,
Lang::Viewer(lng_info_add_as_contact),
CanAddContactValue(user),
std::move(addContact),
tracker);
return tracker;
}
Ui::MultiSlideTracker DetailsFiller::fillChannelButtons(
not_null<ChannelData*> channel) {
using namespace rpl::mappers;
Ui::MultiSlideTracker tracker;
auto window = _controller->window();
auto viewChannelVisible = window->historyPeer.value()
| rpl::map($1 != channel);
auto viewChannel = [=] {
window->showPeerHistory(
channel,
Window::SectionShow::Way::Forward);
};
AddMainButton(
_wrap,
Lang::Viewer(lng_profile_view_channel),
std::move(viewChannelVisible),
std::move(viewChannel),
tracker);
return tracker;
}
object_ptr<Ui::RpWidget> DetailsFiller::fill() {
add(object_ptr<BoxContentDivider>(_wrap));
add(CreateSkipWidget(_wrap));
add(setupInfo());
add(setupMuteToggle());
setupMainButtons();
add(CreateSkipWidget(_wrap));
return std::move(_wrap);
}
ActionsFiller::ActionsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer)
: _controller(controller)
, _parent(parent)
, _peer(peer) {
}
void ActionsFiller::addInviteToGroupAction(
not_null<UserData*> user) {
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_invite_to_group),
CanInviteBotToGroupValue(user),
[user] { AddBotToGroupBoxController::Start(user); });
}
void ActionsFiller::addShareContactAction(not_null<UserData*> user) {
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_share_contact),
CanShareContactValue(user),
[user] { ShareContactBox(user); });
}
void ActionsFiller::addEditContactAction(not_null<UserData*> user) {
AddActionButton(
_wrap,
Lang::Viewer(lng_info_edit_contact),
IsContactValue(user),
[user] { Ui::show(Box<AddContactBox>(user)); });
}
void ActionsFiller::addDeleteContactAction(
not_null<UserData*> user) {
auto callback = [=] {
auto text = lng_sure_delete_contact(
lt_contact,
App::peerName(user));
auto deleteSure = [=] {
Ui::showChatsList();
Ui::hideLayer();
MTP::send(
MTPcontacts_DeleteContact(user->inputUser),
App::main()->rpcDone(
&MainWidget::deletedContact,
user.get()));
};
auto box = Box<ConfirmBox>(
text,
lang(lng_box_delete),
std::move(deleteSure));
Ui::show(std::move(box));
};
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_delete_contact),
IsContactValue(user),
std::move(callback));
}
void ActionsFiller::addClearHistoryAction(not_null<UserData*> user) {
auto callback = [user] {
auto confirmation = lng_sure_delete_history(
lt_contact,
App::peerName(user));
auto confirmCallback = [user] {
Ui::hideLayer();
App::main()->clearHistory(user);
Ui::showPeerHistory(user, ShowAtUnreadMsgId);
};
auto box = Box<ConfirmBox>(
confirmation,
lang(lng_box_delete),
st::attentionBoxButton,
std::move(confirmCallback));
Ui::show(std::move(box));
};
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_clear_history),
rpl::single(true),
std::move(callback));
}
void ActionsFiller::addDeleteConversationAction(
not_null<UserData*> user) {
auto callback = [user] {
auto confirmation = lng_sure_delete_history(
lt_contact,
App::peerName(user));
auto confirmButton = lang(lng_box_delete);
auto confirmCallback = [user] {
Ui::hideLayer();
Ui::showChatsList();
App::main()->deleteConversation(user);
};
auto box = Box<ConfirmBox>(
confirmation,
confirmButton,
st::attentionBoxButton,
std::move(confirmCallback));
Ui::show(std::move(box));
};
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_delete_conversation),
rpl::single(true),
std::move(callback));
}
void ActionsFiller::addBotCommandActions(not_null<UserData*> user) {
auto findBotCommand = [user](const QString &command) {
if (!user->botInfo) {
return QString();
}
for_const (auto &data, user->botInfo->commands) {
auto isSame = data.command.compare(
command,
Qt::CaseInsensitive) == 0;
if (isSame) {
return data.command;
}
}
return QString();
};
auto hasBotCommandValue = [=](const QString &command) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::BotCommandsChanged)
| rpl::map([=] {
return !findBotCommand(command).isEmpty();
});
};
auto sendBotCommand = [=](const QString &command) {
auto original = findBotCommand(command);
if (!original.isEmpty()) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
App::sendBotCommand(user, user, '/' + original);
}
};
auto addBotCommand = [=](LangKey key, const QString &command) {
AddActionButton(
_wrap,
Lang::Viewer(key),
hasBotCommandValue(command),
[=] { sendBotCommand(command); });
};
addBotCommand(lng_profile_bot_help, qsl("help"));
addBotCommand(lng_profile_bot_settings, qsl("settings"));
}
void ActionsFiller::addReportAction() {
auto peer = _peer;
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_report),
rpl::single(true),
[peer] { Ui::show(Box<ReportBox>(peer)); },
st::infoBlockButton);
}
void ActionsFiller::addBlockAction(not_null<UserData*> user) {
auto text = Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserIsBlocked)
| rpl::map([user]() -> rpl::producer<QString> {
switch (user->blockStatus()) {
case UserData::BlockStatus::Blocked:
return Lang::Viewer(user->botInfo
? lng_profile_unblock_bot
: lng_profile_unblock_user);
case UserData::BlockStatus::NotBlocked:
return Lang::Viewer(user->botInfo
? lng_profile_block_bot
: lng_profile_block_user);
default:
return rpl::single(QString());
}
})
| rpl::flatten_latest()
| rpl::start_spawning(_wrap->lifetime());
auto toggleOn = rpl::duplicate(text)
| rpl::map([](const QString &text) {
return !text.isEmpty();
});
auto callback = [user] {
if (user->isBlocked()) {
Auth().api().unblockUser(user);
} else {
Auth().api().blockUser(user);
}
};
AddActionButton(
_wrap,
rpl::duplicate(text),
std::move(toggleOn),
std::move(callback),
st::infoBlockButton);
}
void ActionsFiller::addLeaveChannelAction(
not_null<ChannelData*> channel) {
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_leave_channel),
AmInChannelValue(channel),
[channel] { Auth().api().leaveChannel(channel); },
st::infoBlockButton);
}
void ActionsFiller::addJoinChannelAction(
not_null<ChannelData*> channel) {
using namespace rpl::mappers;
auto joinVisible = AmInChannelValue(channel)
| rpl::map(!$1)
| rpl::start_spawning(_wrap->lifetime());
AddActionButton(
_wrap,
Lang::Viewer(lng_profile_join_channel),
rpl::duplicate(joinVisible),
[channel] { Auth().api().joinChannel(channel); });
_wrap->add(object_ptr<Ui::SlideWrap<Ui::FixedHeightWidget>>(
_wrap,
CreateSkipWidget(
_wrap,
st::infoBlockButtonSkip))
)->toggleOn(rpl::duplicate(joinVisible));
}
void ActionsFiller::fillUserActions(not_null<UserData*> user) {
if (user->botInfo) {
addInviteToGroupAction(user);
}
addShareContactAction(user);
addEditContactAction(user);
addDeleteContactAction(user);
addClearHistoryAction(user);
addDeleteConversationAction(user);
if (!user->isSelf()) {
if (user->botInfo) {
addBotCommandActions(user);
}
_wrap->add(CreateSkipWidget(
_wrap,
st::infoBlockButtonSkip));
if (user->botInfo) {
addReportAction();
}
addBlockAction(user);
}
}
void ActionsFiller::fillChannelActions(
not_null<ChannelData*> channel) {
using namespace rpl::mappers;
addJoinChannelAction(channel);
if (!channel->amCreator()) {
addReportAction();
}
addLeaveChannelAction(channel);
}
object_ptr<Ui::RpWidget> ActionsFiller::fill() {
auto wrapResult = [=](auto &&callback) {
_wrap = object_ptr<Ui::VerticalLayout>(_parent);
_wrap->add(CreateSkipWidget(_wrap));
callback();
_wrap->add(CreateSkipWidget(_wrap));
object_ptr<FloatingIcon>(
_wrap,
st::infoIconActions,
st::infoIconPosition);
return std::move(_wrap);
};
if (auto user = _peer->asUser()) {
return wrapResult([=] {
fillUserActions(user);
});
} else if (auto channel = _peer->asChannel()) {
if (!channel->isMegagroup()) {
return wrapResult([=] {
fillChannelActions(channel);
});
}
}
return { nullptr };
}
} // namespace
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer) {
DetailsFiller filler(controller, parent, peer);
return filler.fill();
}
object_ptr<Ui::RpWidget> SetupActions(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer) {
ActionsFiller filler(controller, parent, peer);
return filler.fill();
}
} // namespace Profile
} // namespace Info

View file

@ -0,0 +1,44 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class RpWidget;
}
namespace Info {
class Controller;
namespace Profile {
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
object_ptr<Ui::RpWidget> SetupActions(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
} // namespace Profile
} // namespace Info

View file

@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/profile/info_profile_cover.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_members.h"
#include "info/profile/info_profile_actions.h"
#include "info/media/info_media_buttons.h"
#include "boxes/abstract_box.h"
#include "boxes/add_contact_box.h"
@ -58,268 +59,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Info {
namespace Profile {
namespace {
template <typename Text, typename ToggleOn, typename Callback>
void AddActionButton(
not_null<Ui::VerticalLayout*> parent,
Text &&text,
ToggleOn &&toggleOn,
Callback &&callback,
const style::InfoProfileButton &st
= st::infoSharedMediaButton) {
parent->add(object_ptr<Ui::SlideWrap<Button>>(
parent,
object_ptr<Button>(
parent,
std::move(text),
st))
)->toggleOn(
std::move(toggleOn)
)->entity()->addClickHandler(std::move(callback));
};
void ShareContactBox(not_null<UserData*> user) {
auto callback = [user](not_null<PeerData*> peer) {
if (!peer->canWrite()) {
Ui::show(Box<InformBox>(
lang(lng_forward_share_cant)),
LayerOption::KeepOther);
return;
}
auto recipient = peer->isUser()
? peer->name
: '\xAB' + peer->name + '\xBB';
Ui::show(Box<ConfirmBox>(
lng_forward_share_contact(lt_recipient, recipient),
lang(lng_forward_send),
[peer, user] {
App::main()->onShareContact(
peer->id,
user);
Ui::hideLayer();
}), LayerOption::KeepOther);
};
Ui::show(Box<PeerListBox>(
std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
[](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_cancel), [box] {
box->closeBox();
});
}));
}
object_ptr<Ui::RpWidget> createSkipWidget(
not_null<Ui::RpWidget*> parent) {
return Ui::CreateSkipWidget(parent, st::infoProfileSkip);
}
object_ptr<Ui::SlideWrap<>> createSlideSkipWidget(
not_null<Ui::RpWidget*> parent) {
return Ui::CreateSlideSkipWidget(parent, st::infoProfileSkip);
}
void addShareContactAction(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
AddActionButton(
parent,
Lang::Viewer(lng_profile_share_contact),
CanShareContactValue(user),
[user] { ShareContactBox(user); });
}
void addEditContactAction(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
AddActionButton(
parent,
Lang::Viewer(lng_info_edit_contact),
IsContactValue(user),
[user] { Ui::show(Box<AddContactBox>(user)); });
}
void addClearHistoryAction(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
auto callback = [user] {
auto confirmation = lng_sure_delete_history(
lt_contact,
App::peerName(user));
auto confirmCallback = [user] {
Ui::hideLayer();
App::main()->clearHistory(user);
Ui::showPeerHistory(user, ShowAtUnreadMsgId);
};
auto box = Box<ConfirmBox>(
confirmation,
lang(lng_box_delete),
st::attentionBoxButton,
std::move(confirmCallback));
Ui::show(std::move(box));
};
AddActionButton(
parent,
Lang::Viewer(lng_profile_clear_history),
rpl::single(true),
std::move(callback));
}
void addDeleteConversationAction(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
auto callback = [user] {
auto confirmation = lng_sure_delete_history(
lt_contact,
App::peerName(user));
auto confirmButton = lang(lng_box_delete);
auto confirmCallback = [user] {
Ui::hideLayer();
Ui::showChatsList();
App::main()->deleteConversation(user);
};
auto box = Box<ConfirmBox>(
confirmation,
confirmButton,
st::attentionBoxButton,
std::move(confirmCallback));
Ui::show(std::move(box));
};
AddActionButton(
parent,
Lang::Viewer(lng_profile_delete_conversation),
rpl::single(true),
std::move(callback));
}
void addBotCommandActions(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
auto findBotCommand = [user](const QString &command) {
if (!user->botInfo) {
return QString();
}
for_const (auto &data, user->botInfo->commands) {
auto isSame = data.command.compare(
command,
Qt::CaseInsensitive) == 0;
if (isSame) {
return data.command;
}
}
return QString();
};
auto hasBotCommandValue = [=](const QString &command) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::BotCommandsChanged)
| rpl::map([=] {
return !findBotCommand(command).isEmpty();
});
};
auto sendBotCommand = [=](const QString &command) {
auto original = findBotCommand(command);
if (!original.isEmpty()) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
App::sendBotCommand(user, user, '/' + original);
}
};
auto addBotCommand = [=](LangKey key, const QString &command) {
AddActionButton(
parent,
Lang::Viewer(key),
hasBotCommandValue(command),
[=] { sendBotCommand(command); });
};
addBotCommand(lng_profile_bot_help, qsl("help"));
addBotCommand(lng_profile_bot_settings, qsl("settings"));
}
void addReportAction(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
AddActionButton(
parent,
Lang::Viewer(lng_profile_report),
rpl::single(true),
[user] { Ui::show(Box<ReportBox>(user)); },
st::infoBlockButton);
}
void addBlockAction(
not_null<Ui::VerticalLayout*> parent,
not_null<UserData*> user) {
auto text = Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserIsBlocked)
| rpl::map([user]() -> rpl::producer<QString> {
switch (user->blockStatus()) {
case UserData::BlockStatus::Blocked:
return Lang::Viewer(user->botInfo
? lng_profile_unblock_bot
: lng_profile_unblock_user);
case UserData::BlockStatus::NotBlocked:
return Lang::Viewer(user->botInfo
? lng_profile_block_bot
: lng_profile_block_user);
default:
return rpl::single(QString());
}
})
| rpl::flatten_latest()
| rpl::start_spawning(parent->lifetime());
auto toggleOn = rpl::duplicate(text)
| rpl::map([](const QString &text) {
return !text.isEmpty();
});
auto callback = [user] {
if (user->isBlocked()) {
Auth().api().unblockUser(user);
} else {
Auth().api().blockUser(user);
}
};
AddActionButton(
parent,
rpl::duplicate(text),
std::move(toggleOn),
std::move(callback),
st::infoBlockButton);
}
object_ptr<Ui::RpWidget> setupUserActions(
not_null<Ui::RpWidget*> parent,
not_null<UserData*> user) {
auto result = object_ptr<Ui::VerticalLayout>(parent);
result->add(createSkipWidget(result));
addShareContactAction(result, user);
addEditContactAction(result, user);
addClearHistoryAction(result, user);
addDeleteConversationAction(result, user);
if (!user->isSelf()) {
if (user->botInfo) {
addBotCommandActions(result, user);
}
result->add(CreateSkipWidget(
result,
st::infoBlockButtonSkip));
if (user->botInfo) {
addReportAction(result, user);
}
addBlockAction(result, user);
}
result->add(createSkipWidget(result));
object_ptr<FloatingIcon>(
result,
st::infoIconActions,
st::infoIconPosition);
return std::move(result);
}
} // namespace
InnerWidget::InnerWidget(
QWidget *parent,
@ -347,13 +86,13 @@ rpl::producer<bool> InnerWidget::canHideDetails() const {
}
object_ptr<Ui::RpWidget> InnerWidget::setupContent(
RpWidget *parent) {
not_null<RpWidget*> parent) {
auto result = object_ptr<Ui::VerticalLayout>(parent);
_cover = result->add(object_ptr<Cover>(
result,
_peer));
_cover->setOnlineCount(rpl::single(0));
auto details = setupDetails(parent);
auto details = SetupDetails(_controller, parent, _peer);
if (canHideDetailsEver()) {
_cover->setToggleShown(canHideDetails());
_infoWrap = result->add(object_ptr<Ui::SlideWrap<>>(
@ -363,15 +102,13 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
} else {
result->add(std::move(details));
}
result->add(setupSharedMedia(result));
result->add(setupSharedMedia(result.data()));
result->add(object_ptr<BoxContentDivider>(result));
if (auto user = _peer->asUser()) {
result->add(setupUserActions(result, user));
//} else if (auto channel = _peer->asChannel()) {
// if (!channel->isMegagroup()) {
// setupChannelActions(result, channel);
// }
if (auto actions = SetupActions(_controller, result.data(), _peer)) {
result->add(std::move(actions));
}
if (_peer->isChat() || _peer->isMegagroup()) {
_members = result->add(object_ptr<Members>(
result,
@ -395,143 +132,8 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
return std::move(result);
}
object_ptr<Ui::RpWidget> InnerWidget::setupDetails(
RpWidget *parent) const {
auto result = object_ptr<Ui::VerticalLayout>(parent);
result->add(object_ptr<BoxContentDivider>(result));
result->add(createSkipWidget(result));
result->add(setupInfo(result));
result->add(setupMuteToggle(result));
if (auto user = _peer->asUser()) {
setupUserButtons(result, user);
//} else if (auto channel = _peer->asChannel()) {
// if (!channel->isMegagroup()) {
// setupChannelButtons(result, channel);
// }
}
result->add(createSkipWidget(result));
return std::move(result);
}
object_ptr<Ui::RpWidget> InnerWidget::setupInfo(
RpWidget *parent) const {
auto result = object_ptr<Ui::VerticalLayout>(parent);
auto tracker = Ui::MultiSlideTracker();
auto addInfoLine = [&](
LangKey label,
rpl::producer<TextWithEntities> &&text,
bool selectByDoubleClick = false,
const style::FlatLabel &textSt = st::infoLabeled) {
auto line = result->add(CreateTextWithLabel(
result,
Lang::Viewer(label) | WithEmptyEntities(),
std::move(text),
textSt,
st::infoProfileLabeledPadding,
selectByDoubleClick));
tracker.track(line);
return line;
};
auto addInfoOneLine = [&](
LangKey label,
rpl::producer<TextWithEntities> &&text) {
addInfoLine(
label,
std::move(text),
true,
st::infoLabeledOneLine);
};
if (auto user = _peer->asUser()) {
addInfoOneLine(lng_info_mobile_label, PhoneValue(user));
if (user->botInfo) {
addInfoLine(lng_info_about_label, AboutValue(user));
} else {
addInfoLine(lng_info_bio_label, BioValue(user));
}
addInfoOneLine(lng_info_username_label, UsernameValue(user));
} else {
addInfoOneLine(lng_info_link_label, LinkValue(_peer));
addInfoLine(lng_info_about_label, AboutValue(_peer));
}
result->add(object_ptr<Ui::SlideWrap<>>(
result,
object_ptr<Ui::PlainShadow>(result),
st::infoProfileSeparatorPadding)
)->toggleOn(std::move(tracker).atLeastOneShownValue());
object_ptr<FloatingIcon>(
result,
st::infoIconInformation,
st::infoInformationIconPosition);
return std::move(result);
}
object_ptr<Ui::RpWidget> InnerWidget::setupMuteToggle(
RpWidget *parent) const {
auto result = object_ptr<Button>(
parent,
Lang::Viewer(lng_profile_enable_notifications),
st::infoNotificationsButton);
result->toggleOn(
NotificationsEnabledValue(_peer)
)->addClickHandler([this] {
App::main()->updateNotifySetting(
_peer,
_peer->isMuted()
? NotifySettingSetNotify
: NotifySettingSetMuted);
});
object_ptr<FloatingIcon>(
result,
st::infoIconNotifications,
st::infoNotificationsIconPosition);
return std::move(result);
}
void InnerWidget::setupUserButtons(
Ui::VerticalLayout *wrap,
not_null<UserData*> user) const {
using namespace rpl::mappers;
auto tracker = Ui::MultiSlideTracker();
auto topSkip = wrap->add(createSlideSkipWidget(wrap));
auto addButton = [&](auto &&text) {
auto result = wrap->add(object_ptr<Ui::SlideWrap<Button>>(
wrap,
object_ptr<Button>(
wrap,
std::move(text),
st::infoMainButton)));
tracker.track(result);
return result;
};
addButton(
Lang::Viewer(lng_profile_send_message) | ToUpperValue()
)->toggleOn(
_controller->window()->historyPeer.value()
| rpl::map($1 != user)
)->entity()->addClickHandler([this, user] {
_controller->window()->showPeerHistory(
user,
Window::SectionShow::Way::Forward);
});
addButton(
Lang::Viewer(lng_info_add_as_contact) | ToUpperValue()
)->toggleOn(
CanAddContactValue(user)
)->entity()->addClickHandler([user] {
auto firstName = user->firstName;
auto lastName = user->lastName;
auto phone = user->phone().isEmpty()
? App::phoneFromSharedContact(user->bareId())
: user->phone();
Ui::show(Box<AddContactBox>(firstName, lastName, phone));
});
topSkip->toggleOn(std::move(tracker).atLeastOneShownValue());
}
object_ptr<Ui::RpWidget> InnerWidget::setupSharedMedia(
RpWidget *parent) {
not_null<RpWidget*> parent) {
using namespace rpl::mappers;
using MediaType = Media::Type;

View file

@ -32,6 +32,7 @@ class VerticalLayout;
template <typename Widget>
class SlideWrap;
struct ScrollToRequest;
class MultiSlideTracker;
} // namespace Ui
namespace Info {
@ -72,15 +73,8 @@ protected:
int visibleBottom) override;
private:
object_ptr<RpWidget> setupContent(RpWidget *parent);
object_ptr<RpWidget> setupDetails(RpWidget *parent) const;
object_ptr<RpWidget> setupSharedMedia(RpWidget *parent);
object_ptr<RpWidget> setupMuteToggle(RpWidget *parent) const;
object_ptr<RpWidget> setupInfo(RpWidget *parent) const;
void setupUserButtons(
Ui::VerticalLayout *wrap,
not_null<UserData*> user) const;
object_ptr<RpWidget> setupContent(not_null<RpWidget*> parent);
object_ptr<RpWidget> setupSharedMedia(not_null<RpWidget*> parent);
int countDesiredHeight() const;

View file

@ -132,6 +132,19 @@ rpl::producer<bool> IsContactValue(
| rpl::map([user] { return user->isContact(); });
}
rpl::producer<bool> CanInviteBotToGroupValue(
not_null<UserData*> user) {
if (!user->botInfo) {
return rpl::single(false);
}
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::BotCanAddToGroups)
| rpl::map([user] {
return !user->botInfo->cantJoinGroups;
});
}
rpl::producer<bool> CanShareContactValue(
not_null<UserData*> user) {
return Notify::PeerUpdateValue(
@ -151,12 +164,20 @@ rpl::producer<bool> CanAddContactValue(
!$1 && $2);
}
rpl::producer<bool> AmInChannelValue(
not_null<ChannelData*> channel) {
return Notify::PeerUpdateValue(
channel,
Notify::PeerUpdate::Flag::ChannelAmIn)
| rpl::map([channel] { return channel->amIn(); });
}
rpl::producer<int> MembersCountValue(
not_null<PeerData*> peer) {
if (auto chat = peer->asChat()) {
return Notify::PeerUpdateValue(
peer,
Notify::PeerUpdate::Flag::MembersChanged)
peer,
Notify::PeerUpdate::Flag::MembersChanged)
| rpl::map([chat] {
return chat->amIn()
? qMax(chat->count, chat->participants.size())

View file

@ -65,10 +65,14 @@ rpl::producer<bool> NotificationsEnabledValue(
not_null<PeerData*> peer);
rpl::producer<bool> IsContactValue(
not_null<UserData*> user);
rpl::producer<bool> CanInviteBotToGroupValue(
not_null<UserData*> user);
rpl::producer<bool> CanShareContactValue(
not_null<UserData*> user);
rpl::producer<bool> CanAddContactValue(
not_null<UserData*> user);
rpl::producer<bool> AmInChannelValue(
not_null<ChannelData*> channel);
rpl::producer<int> MembersCountValue(
not_null<PeerData*> peer);
rpl::producer<int> SharedMediaCountValue(

View file

@ -198,6 +198,7 @@ void TopBarWidget::showMenu() {
return false;
}();
Window::FillPeerMenu(
_controller,
peer,
[this](const QString &text, base::lambda<void()> callback) {
return _menu->addAction(text, std::move(callback));

View file

@ -23,11 +23,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
#include "boxes/mute_settings_box.h"
#include "boxes/report_box.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "mainwidget.h"
#include "observer_peer.h"
#include "styles/style_boxes.h"
#include "window/window_controller.h"
namespace Window {
namespace{
@ -35,6 +37,7 @@ namespace{
class Filler {
public:
Filler(
not_null<Controller*> controller,
not_null<PeerData*> peer,
const PeerMenuCallback &callback,
const PeerMenuOptions &options);
@ -50,6 +53,7 @@ private:
void addChatActions(not_null<ChatData*> chat);
void addChannelActions(not_null<ChannelData*> channel);
not_null<Controller*> _controller;
not_null<PeerData*> _peer;
const PeerMenuCallback &_callback;
const PeerMenuOptions &_options;
@ -97,9 +101,12 @@ auto DeleteAndLeaveHandler(not_null<PeerData*> peer) {
} else if (auto chat = peer->asChat()) {
App::main()->deleteAndExit(chat);
} else if (auto channel = peer->asChannel()) {
if (auto migrateFrom = channel->migrateFrom()) {
App::main()->deleteConversation(migrateFrom);
}
// Don't delete old history by default,
// because Android app doesn't.
//
//if (auto migrateFrom = channel->migrateFrom()) {
// App::main()->deleteConversation(migrateFrom);
//}
Auth().api().leaveChannel(channel);
}
}));
@ -107,10 +114,12 @@ auto DeleteAndLeaveHandler(not_null<PeerData*> peer) {
}
Filler::Filler(
not_null<Controller*> controller,
not_null<PeerData*> peer,
const PeerMenuCallback &callback,
const PeerMenuOptions &options)
: _peer(peer)
: _controller(controller)
, _peer(peer)
, _callback(callback)
, _options(options) {
}
@ -172,13 +181,15 @@ void Filler::addPinToggle() {
}
void Filler::addInfo() {
auto infoKey = (_peer->isChat() || _peer->isMegagroup())
auto controller = _controller;
auto peer = _peer;
auto infoKey = (peer->isChat() || peer->isMegagroup())
? lng_context_view_group
: (_peer->isUser()
: (peer->isUser()
? lng_context_view_profile
: lng_context_view_channel);
_callback(lang(infoKey), [peer = _peer] {
Ui::showPeerProfile(peer);
_callback(lang(infoKey), [=] {
controller->showPeerInfo(peer);
});
}
@ -286,23 +297,32 @@ void Filler::addChatActions(not_null<ChatData*> chat) {
}
void Filler::addChannelActions(not_null<ChannelData*> channel) {
if (channel->amIn() && !channel->amCreator()) {
if (channel->amIn()) {
auto leaveText = lang(channel->isMegagroup()
? lng_profile_leave_group
: lng_profile_leave_channel);
_callback(leaveText, DeleteAndLeaveHandler(channel));
}
if (!_options.fromChatsList) {
auto needReport = !channel->amCreator()
&& (!channel->isMegagroup() || channel->isPublic());
if (needReport) {
_callback(lang(lng_profile_report), [channel] {
Ui::show(Box<ReportBox>(channel));
});
}
}
}
void Filler::fill() {
if (_options.pinToggle) {
if (_options.fromChatsList) {
addPinToggle();
}
if (_options.showInfo) {
addInfo();
}
addNotifications();
if (_options.search) {
if (_options.fromChatsList) {
addSearch();
}
@ -318,10 +338,11 @@ void Filler::fill() {
} // namespace
void FillPeerMenu(
not_null<Controller*> controller,
not_null<PeerData*> peer,
const PeerMenuCallback &callback,
const PeerMenuOptions &options) {
Filler filler(peer, callback, options);
Filler filler(controller, peer, callback, options);
filler.fill();
}

View file

@ -22,10 +22,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Window {
class Controller;
struct PeerMenuOptions {
bool pinToggle = false;
bool fromChatsList = false;
bool showInfo = false;
bool search = false;
};
using PeerMenuCallback = base::lambda<QAction*(
@ -33,6 +34,7 @@ using PeerMenuCallback = base::lambda<QAction*(
base::lambda<void()> handler)>;
void FillPeerMenu(
not_null<Controller*> controller,
not_null<PeerData*> peer,
const PeerMenuCallback &callback,
const PeerMenuOptions &options);

View file

@ -240,6 +240,8 @@
<(src_loc)/info/media/info_media_list_widget.h
<(src_loc)/info/media/info_media_widget.cpp
<(src_loc)/info/media/info_media_widget.h
<(src_loc)/info/profile/info_profile_actions.cpp
<(src_loc)/info/profile/info_profile_actions.h
<(src_loc)/info/profile/info_profile_button.cpp
<(src_loc)/info/profile/info_profile_button.h
<(src_loc)/info/profile/info_profile_cover.cpp