mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Improve compile time.
This commit is contained in:
parent
6ca105a290
commit
d93c1ccbaa
69 changed files with 692 additions and 5022 deletions
|
@ -190,6 +190,88 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
|
|||
}
|
||||
}
|
||||
|
||||
void AuthSessionData::markItemLayoutChanged(not_null<const HistoryItem*> item) {
|
||||
_itemLayoutChanged.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemLayoutChanged() const {
|
||||
return _itemLayoutChanged.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::requestItemRepaint(not_null<const HistoryItem*> item) {
|
||||
_itemRepaintRequest.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRepaintRequest() const {
|
||||
return _itemRepaintRequest.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markItemRemoved(not_null<const HistoryItem*> item) {
|
||||
_itemRemoved.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRemoved() const {
|
||||
return _itemRemoved.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markHistoryUnloaded(not_null<const History*> history) {
|
||||
_historyUnloaded.fire_copy(history);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const History*>> AuthSessionData::historyUnloaded() const {
|
||||
return _historyUnloaded.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markHistoryCleared(not_null<const History*> history) {
|
||||
_historyCleared.fire_copy(history);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const History*>> AuthSessionData::historyCleared() const {
|
||||
return _historyCleared.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::removeMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
_megagroupParticipantRemoved.fire({ channel, user });
|
||||
}
|
||||
|
||||
auto AuthSessionData::megagroupParticipantRemoved() const -> rpl::producer<MegagroupParticipant> {
|
||||
return _megagroupParticipantRemoved.events();
|
||||
}
|
||||
|
||||
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantRemoved(
|
||||
not_null<ChannelData*> channel) const {
|
||||
return megagroupParticipantRemoved()
|
||||
| rpl::filter([channel](auto updateChannel, auto user) {
|
||||
return (updateChannel == channel);
|
||||
})
|
||||
| rpl::map([](auto updateChannel, auto user) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
void AuthSessionData::addNewMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
_megagroupParticipantAdded.fire({ channel, user });
|
||||
}
|
||||
|
||||
auto AuthSessionData::megagroupParticipantAdded() const -> rpl::producer<MegagroupParticipant> {
|
||||
return _megagroupParticipantAdded.events();
|
||||
}
|
||||
|
||||
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantAdded(
|
||||
not_null<ChannelData*> channel) const {
|
||||
return megagroupParticipantAdded()
|
||||
| rpl::filter([channel](auto updateChannel, auto user) {
|
||||
return (updateChannel == channel);
|
||||
})
|
||||
| rpl::map([](auto updateChannel, auto user) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
|
||||
_variables.tabbedSelectorSectionEnabled = enabled;
|
||||
if (enabled) {
|
||||
|
@ -198,6 +280,11 @@ void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
|
|||
setTabbedReplacedWithInfo(false);
|
||||
}
|
||||
|
||||
rpl::producer<bool> AuthSessionData::tabbedReplacedWithInfoValue() const {
|
||||
return _tabbedReplacedWithInfoValue.events_starting_with(
|
||||
tabbedReplacedWithInfo());
|
||||
}
|
||||
|
||||
void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
|
||||
if (_variables.thirdSectionInfoEnabled != enabled) {
|
||||
_variables.thirdSectionInfoEnabled = enabled;
|
||||
|
@ -209,6 +296,11 @@ void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
|
|||
}
|
||||
}
|
||||
|
||||
rpl::producer<bool> AuthSessionData::thirdSectionInfoEnabledValue() const {
|
||||
return _thirdSectionInfoEnabledValue.events_starting_with(
|
||||
thirdSectionInfoEnabled());
|
||||
}
|
||||
|
||||
void AuthSessionData::setTabbedReplacedWithInfo(bool enabled) {
|
||||
if (_tabbedReplacedWithInfo != enabled) {
|
||||
_tabbedReplacedWithInfo = enabled;
|
||||
|
@ -224,6 +316,46 @@ QString AuthSessionData::getSoundPath(const QString &key) const {
|
|||
return qsl(":/sounds/") + key + qsl(".mp3");
|
||||
}
|
||||
|
||||
void AuthSessionData::setDialogsWidthRatio(float64 ratio) {
|
||||
_variables.dialogsWidthRatio = ratio;
|
||||
}
|
||||
|
||||
float64 AuthSessionData::dialogsWidthRatio() const {
|
||||
return _variables.dialogsWidthRatio.current();
|
||||
}
|
||||
|
||||
rpl::producer<float64> AuthSessionData::dialogsWidthRatioChanges() const {
|
||||
return _variables.dialogsWidthRatio.changes();
|
||||
}
|
||||
|
||||
void AuthSessionData::setThirdColumnWidth(int width) {
|
||||
_variables.thirdColumnWidth = width;
|
||||
}
|
||||
|
||||
int AuthSessionData::thirdColumnWidth() const {
|
||||
return _variables.thirdColumnWidth.current();
|
||||
}
|
||||
|
||||
rpl::producer<int> AuthSessionData::thirdColumnWidthChanges() const {
|
||||
return _variables.thirdColumnWidth.changes();
|
||||
}
|
||||
|
||||
void AuthSessionData::markStickersUpdated() {
|
||||
_stickersUpdated.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> AuthSessionData::stickersUpdated() const {
|
||||
return _stickersUpdated.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markSavedGifsUpdated() {
|
||||
_savedGifsUpdated.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> AuthSessionData::savedGifsUpdated() const {
|
||||
return _savedGifsUpdated.events();
|
||||
}
|
||||
|
||||
AuthSession &Auth() {
|
||||
auto result = Messenger::Instance().authSession();
|
||||
Assert(result != nullptr);
|
||||
|
|
|
@ -70,75 +70,31 @@ public:
|
|||
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
|
||||
return _queryItemVisibility;
|
||||
}
|
||||
void markItemLayoutChanged(not_null<const HistoryItem*> item) {
|
||||
_itemLayoutChanged.fire_copy(item);
|
||||
}
|
||||
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const {
|
||||
return _itemLayoutChanged.events();
|
||||
}
|
||||
void requestItemRepaint(not_null<const HistoryItem*> item) {
|
||||
_itemRepaintRequest.fire_copy(item);
|
||||
}
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const {
|
||||
return _itemRepaintRequest.events();
|
||||
}
|
||||
void markItemRemoved(not_null<const HistoryItem*> item) {
|
||||
_itemRemoved.fire_copy(item);
|
||||
}
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const {
|
||||
return _itemRemoved.events();
|
||||
}
|
||||
void markHistoryUnloaded(not_null<const History*> history) {
|
||||
_historyUnloaded.fire_copy(history);
|
||||
}
|
||||
rpl::producer<not_null<const History*>> historyUnloaded() const {
|
||||
return _historyUnloaded.events();
|
||||
}
|
||||
void markHistoryCleared(not_null<const History*> history) {
|
||||
_historyCleared.fire_copy(history);
|
||||
}
|
||||
rpl::producer<not_null<const History*>> historyCleared() const {
|
||||
return _historyCleared.events();
|
||||
}
|
||||
void markItemLayoutChanged(not_null<const HistoryItem*> item);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
|
||||
void requestItemRepaint(not_null<const HistoryItem*> item);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
|
||||
void markItemRemoved(not_null<const HistoryItem*> item);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
|
||||
void markHistoryUnloaded(not_null<const History*> history);
|
||||
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
||||
void markHistoryCleared(not_null<const History*> history);
|
||||
rpl::producer<not_null<const History*>> historyCleared() const;
|
||||
using MegagroupParticipant = std::tuple<
|
||||
not_null<ChannelData*>,
|
||||
not_null<UserData*>>;
|
||||
void removeMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
_megagroupParticipantRemoved.fire({ channel, user });
|
||||
}
|
||||
auto megagroupParticipantRemoved() const {
|
||||
return _megagroupParticipantRemoved.events();
|
||||
}
|
||||
auto megagroupParticipantRemoved(
|
||||
not_null<ChannelData*> channel) const {
|
||||
return megagroupParticipantRemoved()
|
||||
| rpl::filter([channel](auto updateChannel, auto user) {
|
||||
return (updateChannel == channel);
|
||||
})
|
||||
| rpl::map([](auto updateChannel, auto user) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user);
|
||||
rpl::producer<MegagroupParticipant> megagroupParticipantRemoved() const;
|
||||
rpl::producer<not_null<UserData*>> megagroupParticipantRemoved(
|
||||
not_null<ChannelData*> channel) const;
|
||||
void addNewMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
_megagroupParticipantAdded.fire({ channel, user });
|
||||
}
|
||||
auto megagroupParticipantAdded() const {
|
||||
return _megagroupParticipantAdded.events();
|
||||
}
|
||||
auto megagroupParticipantAdded(
|
||||
not_null<ChannelData*> channel) const {
|
||||
return megagroupParticipantAdded()
|
||||
| rpl::filter([channel](auto updateChannel, auto user) {
|
||||
return (updateChannel == channel);
|
||||
})
|
||||
| rpl::map([](auto updateChannel, auto user) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user);
|
||||
rpl::producer<MegagroupParticipant> megagroupParticipantAdded() const;
|
||||
rpl::producer<not_null<UserData*>> megagroupParticipantAdded(
|
||||
not_null<ChannelData*> channel) const;
|
||||
|
||||
void moveFrom(AuthSessionData &&other) {
|
||||
_variables = std::move(other._variables);
|
||||
|
@ -166,10 +122,7 @@ public:
|
|||
return _variables.thirdSectionInfoEnabled;
|
||||
}
|
||||
void setThirdSectionInfoEnabled(bool enabled);
|
||||
auto thirdSectionInfoEnabledValue() const {
|
||||
return _thirdSectionInfoEnabledValue.events_starting_with(
|
||||
thirdSectionInfoEnabled());
|
||||
}
|
||||
rpl::producer<bool> thirdSectionInfoEnabledValue() const;
|
||||
int thirdSectionExtendedBy() const {
|
||||
return _variables.thirdSectionExtendedBy;
|
||||
}
|
||||
|
@ -180,10 +133,7 @@ public:
|
|||
return _tabbedReplacedWithInfo;
|
||||
}
|
||||
void setTabbedReplacedWithInfo(bool enabled);
|
||||
auto tabbedReplacedWithInfoValue() const {
|
||||
return _tabbedReplacedWithInfoValue.events_starting_with(
|
||||
tabbedReplacedWithInfo());
|
||||
}
|
||||
rpl::producer<bool> tabbedReplacedWithInfoValue() const;
|
||||
void setSmallDialogsList(bool enabled) {
|
||||
_variables.smallDialogsList = enabled;
|
||||
}
|
||||
|
@ -221,37 +171,17 @@ public:
|
|||
RectPart floatPlayerCorner() const {
|
||||
return _variables.floatPlayerCorner;
|
||||
}
|
||||
void setDialogsWidthRatio(float64 ratio) {
|
||||
_variables.dialogsWidthRatio = ratio;
|
||||
}
|
||||
float64 dialogsWidthRatio() const {
|
||||
return _variables.dialogsWidthRatio.current();
|
||||
}
|
||||
rpl::producer<float64> dialogsWidthRatioChanges() const {
|
||||
return _variables.dialogsWidthRatio.changes();
|
||||
}
|
||||
void setThirdColumnWidth(int width) {
|
||||
_variables.thirdColumnWidth = width;
|
||||
}
|
||||
int thirdColumnWidth() const {
|
||||
return _variables.thirdColumnWidth.current();
|
||||
}
|
||||
rpl::producer<int> thirdColumnWidthChanges() const {
|
||||
return _variables.thirdColumnWidth.changes();
|
||||
}
|
||||
void setDialogsWidthRatio(float64 ratio);
|
||||
float64 dialogsWidthRatio() const;
|
||||
rpl::producer<float64> dialogsWidthRatioChanges() const;
|
||||
void setThirdColumnWidth(int width);
|
||||
int thirdColumnWidth() const;
|
||||
rpl::producer<int> thirdColumnWidthChanges() const;
|
||||
|
||||
void markStickersUpdated() {
|
||||
_stickersUpdated.fire({});
|
||||
}
|
||||
rpl::producer<> stickersUpdated() const {
|
||||
return _stickersUpdated.events();
|
||||
}
|
||||
void markSavedGifsUpdated() {
|
||||
_savedGifsUpdated.fire({});
|
||||
}
|
||||
rpl::producer<> savedGifsUpdated() const {
|
||||
return _savedGifsUpdated.events();
|
||||
}
|
||||
void markStickersUpdated();
|
||||
rpl::producer<> stickersUpdated() const;
|
||||
void markSavedGifsUpdated();
|
||||
rpl::producer<> savedGifsUpdated() const;
|
||||
void setGroupStickersSectionHidden(PeerId peerId) {
|
||||
_variables.groupStickersSectionHidden.insert(peerId);
|
||||
}
|
||||
|
|
|
@ -77,4 +77,15 @@ void HandleObservables() {
|
|||
}
|
||||
}
|
||||
|
||||
rpl::producer<> ObservableViewer(base::Observable<void> &observable) {
|
||||
return [&observable](const auto &consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
lifetime.make_state<base::Subscription>(
|
||||
observable.add_subscription([consumer]() {
|
||||
consumer.put_next({});
|
||||
}));
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -477,16 +477,6 @@ inline auto ObservableViewer(base::Observable<Type> &observable) {
|
|||
});
|
||||
}
|
||||
|
||||
inline auto ObservableViewer(base::Observable<void> &observable) {
|
||||
return rpl::make_producer<>([&observable](
|
||||
const auto &consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
lifetime.make_state<base::Subscription>(
|
||||
observable.add_subscription([consumer]() {
|
||||
consumer.put_next({});
|
||||
}));
|
||||
return lifetime;
|
||||
});
|
||||
}
|
||||
rpl::producer<> ObservableViewer(base::Observable<void> &observable);
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -103,6 +103,10 @@ int PhotoCropBox::mouseState(QPoint p) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
rpl::producer<QImage> PhotoCropBox::ready() const {
|
||||
return _readyImages.events();
|
||||
}
|
||||
|
||||
void PhotoCropBox::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_downState) {
|
||||
_downState = 0;
|
||||
|
|
|
@ -29,9 +29,7 @@ public:
|
|||
|
||||
int32 mouseState(QPoint p);
|
||||
|
||||
rpl::producer<QImage> ready() const {
|
||||
return _readyImages.events();
|
||||
}
|
||||
rpl::producer<QImage> ready() const;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
|
126
Telegram/SourceFiles/data/data_peer_values.cpp
Normal file
126
Telegram/SourceFiles/data/data_peer_values.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
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 "data/data_peer_values.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
inline auto AdminRightsValue(not_null<ChannelData*> channel) {
|
||||
return channel->adminRightsValue();
|
||||
}
|
||||
|
||||
inline auto AdminRightsValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelAdminRights::Flags mask) {
|
||||
return FlagsValueWithMask(AdminRightsValue(channel), mask);
|
||||
}
|
||||
|
||||
inline auto AdminRightValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelAdminRights::Flag flag) {
|
||||
return SingleFlagValue(AdminRightsValue(channel), flag);
|
||||
}
|
||||
|
||||
inline auto RestrictionsValue(not_null<ChannelData*> channel) {
|
||||
return channel->restrictionsValue();
|
||||
}
|
||||
|
||||
inline auto RestrictionsValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelBannedRights::Flags mask) {
|
||||
return FlagsValueWithMask(RestrictionsValue(channel), mask);
|
||||
}
|
||||
|
||||
inline auto RestrictionValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelBannedRights::Flag flag) {
|
||||
return SingleFlagValue(RestrictionsValue(channel), flag);
|
||||
}
|
||||
|
||||
rpl::producer<bool> PeerFlagValue(
|
||||
ChatData *chat,
|
||||
MTPDchat_ClientFlag flag) {
|
||||
return PeerFlagValue(chat, static_cast<MTPDchat::Flag>(flag));
|
||||
}
|
||||
|
||||
rpl::producer<bool> PeerFlagValue(
|
||||
ChannelData *channel,
|
||||
MTPDchannel_ClientFlag flag) {
|
||||
return PeerFlagValue(channel, static_cast<MTPDchannel::Flag>(flag));
|
||||
}
|
||||
|
||||
rpl::producer<bool> CanWriteValue(UserData *user) {
|
||||
using namespace rpl::mappers;
|
||||
return PeerFlagValue(user, MTPDuser::Flag::f_deleted)
|
||||
| rpl::map(!_1);
|
||||
}
|
||||
|
||||
rpl::producer<bool> CanWriteValue(ChatData *chat) {
|
||||
using namespace rpl::mappers;
|
||||
auto mask = 0
|
||||
| MTPDchat::Flag::f_deactivated
|
||||
| MTPDchat_ClientFlag::f_forbidden
|
||||
| MTPDchat::Flag::f_left
|
||||
| MTPDchat::Flag::f_kicked;
|
||||
return PeerFlagsValue(chat, mask)
|
||||
| rpl::map(!_1);
|
||||
}
|
||||
|
||||
rpl::producer<bool> CanWriteValue(ChannelData *channel) {
|
||||
auto mask = 0
|
||||
| MTPDchannel::Flag::f_left
|
||||
| MTPDchannel_ClientFlag::f_forbidden
|
||||
| MTPDchannel::Flag::f_creator
|
||||
| MTPDchannel::Flag::f_broadcast;
|
||||
return rpl::combine(
|
||||
PeerFlagsValue(channel, mask),
|
||||
AdminRightValue(
|
||||
channel,
|
||||
MTPDchannelAdminRights::Flag::f_post_messages),
|
||||
RestrictionValue(
|
||||
channel,
|
||||
MTPDchannelBannedRights::Flag::f_send_messages),
|
||||
[](
|
||||
MTPDchannel::Flags flags,
|
||||
bool postMessagesRight,
|
||||
bool sendMessagesRestriction) {
|
||||
auto notAmInFlags = 0
|
||||
| MTPDchannel::Flag::f_left
|
||||
| MTPDchannel_ClientFlag::f_forbidden;
|
||||
return !(flags & notAmInFlags)
|
||||
&& (postMessagesRight
|
||||
|| (flags & MTPDchannel::Flag::f_creator)
|
||||
|| (!(flags & MTPDchannel::Flag::f_broadcast)
|
||||
&& !sendMessagesRestriction));
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer) {
|
||||
if (auto user = peer->asUser()) {
|
||||
return CanWriteValue(user);
|
||||
} else if (auto chat = peer->asChat()) {
|
||||
return CanWriteValue(chat);
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
return CanWriteValue(channel);
|
||||
}
|
||||
Unexpected("Bad peer value in CanWriteValue()");
|
||||
}
|
||||
|
||||
} // namespace Data
|
|
@ -82,17 +82,13 @@ inline auto PeerFlagValue(
|
|||
// return PeerFlagValue(user, static_cast<MTPDuser::Flag>(flag));
|
||||
//}
|
||||
|
||||
inline auto PeerFlagValue(
|
||||
ChatData *chat,
|
||||
MTPDchat_ClientFlag flag) {
|
||||
return PeerFlagValue(chat, static_cast<MTPDchat::Flag>(flag));
|
||||
}
|
||||
rpl::producer<bool> PeerFlagValue(
|
||||
ChatData *chat,
|
||||
MTPDchat_ClientFlag flag);
|
||||
|
||||
inline auto PeerFlagValue(
|
||||
ChannelData *channel,
|
||||
MTPDchannel_ClientFlag flag) {
|
||||
return PeerFlagValue(channel, static_cast<MTPDchannel::Flag>(flag));
|
||||
}
|
||||
rpl::producer<bool> PeerFlagValue(
|
||||
ChannelData *channel,
|
||||
MTPDchannel_ClientFlag flag);
|
||||
|
||||
template <
|
||||
typename PeerType,
|
||||
|
@ -120,93 +116,9 @@ inline auto PeerFullFlagValue(
|
|||
return SingleFlagValue(PeerFullFlagsValue(peer), flag);
|
||||
}
|
||||
|
||||
inline auto AdminRightsValue(not_null<ChannelData*> channel) {
|
||||
return channel->adminRightsValue();
|
||||
}
|
||||
|
||||
inline auto AdminRightsValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelAdminRights::Flags mask) {
|
||||
return FlagsValueWithMask(AdminRightsValue(channel), mask);
|
||||
}
|
||||
|
||||
inline auto AdminRightValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelAdminRights::Flag flag) {
|
||||
return SingleFlagValue(AdminRightsValue(channel), flag);
|
||||
}
|
||||
|
||||
inline auto RestrictionsValue(not_null<ChannelData*> channel) {
|
||||
return channel->restrictionsValue();
|
||||
}
|
||||
|
||||
inline auto RestrictionsValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelBannedRights::Flags mask) {
|
||||
return FlagsValueWithMask(RestrictionsValue(channel), mask);
|
||||
}
|
||||
|
||||
inline auto RestrictionValue(
|
||||
not_null<ChannelData*> channel,
|
||||
MTPDchannelBannedRights::Flag flag) {
|
||||
return SingleFlagValue(RestrictionsValue(channel), flag);
|
||||
}
|
||||
|
||||
inline auto CanWriteValue(UserData *user) {
|
||||
using namespace rpl::mappers;
|
||||
return PeerFlagValue(user, MTPDuser::Flag::f_deleted)
|
||||
| rpl::map(!_1);
|
||||
}
|
||||
|
||||
inline auto CanWriteValue(ChatData *chat) {
|
||||
using namespace rpl::mappers;
|
||||
auto mask = 0
|
||||
| MTPDchat::Flag::f_deactivated
|
||||
| MTPDchat_ClientFlag::f_forbidden
|
||||
| MTPDchat::Flag::f_left
|
||||
| MTPDchat::Flag::f_kicked;
|
||||
return PeerFlagsValue(chat, mask)
|
||||
| rpl::map(!_1);
|
||||
}
|
||||
|
||||
inline auto CanWriteValue(ChannelData *channel) {
|
||||
auto flagsMask = 0
|
||||
| MTPDchannel::Flag::f_left
|
||||
| MTPDchannel_ClientFlag::f_forbidden
|
||||
| MTPDchannel::Flag::f_creator
|
||||
| MTPDchannel::Flag::f_broadcast;
|
||||
return rpl::combine(
|
||||
PeerFlagsValue(channel, flagsMask),
|
||||
AdminRightValue(
|
||||
channel,
|
||||
MTPDchannelAdminRights::Flag::f_post_messages),
|
||||
RestrictionValue(
|
||||
channel,
|
||||
MTPDchannelBannedRights::Flag::f_send_messages),
|
||||
[](
|
||||
MTPDchannel::Flags flags,
|
||||
bool postMessagesRight,
|
||||
bool sendMessagesRestriction) {
|
||||
auto notAmInFlags = 0
|
||||
| MTPDchannel::Flag::f_left
|
||||
| MTPDchannel_ClientFlag::f_forbidden;
|
||||
return !(flags & notAmInFlags)
|
||||
&& (postMessagesRight
|
||||
|| (flags & MTPDchannel::Flag::f_creator)
|
||||
|| (!(flags & MTPDchannel::Flag::f_broadcast)
|
||||
&& !sendMessagesRestriction));
|
||||
});
|
||||
}
|
||||
|
||||
inline rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer) {
|
||||
if (auto user = peer->asUser()) {
|
||||
return CanWriteValue(user);
|
||||
} else if (auto chat = peer->asChat()) {
|
||||
return CanWriteValue(chat);
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
return CanWriteValue(channel);
|
||||
}
|
||||
Unexpected("Bad peer value in CanWriteValue()");
|
||||
}
|
||||
rpl::producer<bool> CanWriteValue(UserData *user);
|
||||
rpl::producer<bool> CanWriteValue(ChatData *chat);
|
||||
rpl::producer<bool> CanWriteValue(ChannelData *channel);
|
||||
rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer);
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -208,6 +208,10 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
|
|||
_listController->restoreState(memento->listState());
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
int InnerWidget::desiredHeight() const {
|
||||
auto desired = 0;
|
||||
auto count = qMax(_user->commonChatsCount(), 1);
|
||||
|
|
|
@ -45,9 +45,7 @@ public:
|
|||
return _user;
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||
|
||||
int desiredHeight() const;
|
||||
|
||||
|
|
|
@ -167,6 +167,14 @@ auto Controller::produceSearchQuery(
|
|||
return result;
|
||||
}
|
||||
|
||||
rpl::producer<bool> Controller::searchEnabledByContent() const {
|
||||
return _seachEnabledByContent.value();
|
||||
}
|
||||
|
||||
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
|
||||
return _searchController->currentQueryValue();
|
||||
}
|
||||
|
||||
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
||||
SparseIdsMergedSlice::UniversalMsgId aroundId,
|
||||
int limitBefore,
|
||||
|
|
|
@ -109,16 +109,12 @@ public:
|
|||
void setSearchEnabledByContent(bool enabled) {
|
||||
_seachEnabledByContent = enabled;
|
||||
}
|
||||
rpl::producer<bool> searchEnabledByContent() const {
|
||||
return _seachEnabledByContent.value();
|
||||
}
|
||||
rpl::producer<bool> searchEnabledByContent() const;
|
||||
rpl::producer<SparseIdsMergedSlice> mediaSource(
|
||||
SparseIdsMergedSlice::UniversalMsgId aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) const;
|
||||
rpl::producer<QString> mediaSourceQueryValue() const {
|
||||
return _searchController->currentQueryValue();
|
||||
}
|
||||
rpl::producer<QString> mediaSourceQueryValue() const;
|
||||
|
||||
void saveSearchState(not_null<ContentMemento*> memento);
|
||||
|
||||
|
|
|
@ -623,6 +623,10 @@ object_ptr<ContentWidget> WrapWidget::createContent(
|
|||
// _historyStack.erase(_historyStack.begin());
|
||||
//}
|
||||
|
||||
rpl::producer<Wrap> WrapWidget::wrapValue() const {
|
||||
return _wrap.value();
|
||||
}
|
||||
|
||||
void WrapWidget::setWrap(Wrap wrap) {
|
||||
// Was done for top level tabs support.
|
||||
//
|
||||
|
|
|
@ -94,9 +94,7 @@ public:
|
|||
Wrap wrap() const {
|
||||
return _wrap.current();
|
||||
}
|
||||
rpl::producer<Wrap> wrapValue() const {
|
||||
return _wrap.value();
|
||||
}
|
||||
rpl::producer<Wrap> wrapValue() const;
|
||||
void setWrap(Wrap wrap);
|
||||
|
||||
not_null<Controller*> controller() {
|
||||
|
|
|
@ -360,5 +360,9 @@ void InnerWidget::setScrollHeightValue(rpl::producer<int> value) {
|
|||
_1 - _2));
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
} // namespace Media
|
||||
} // namespace Info
|
||||
|
|
|
@ -55,9 +55,7 @@ public:
|
|||
|
||||
void setScrollHeightValue(rpl::producer<int> value);
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||
rpl::producer<SelectedItems> selectedListValue() const;
|
||||
void cancelSelection();
|
||||
|
||||
|
|
|
@ -585,6 +585,15 @@ void ListWidget::start() {
|
|||
}, lifetime());
|
||||
}
|
||||
|
||||
rpl::producer<int> ListWidget::scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<SelectedItems> ListWidget::selectedListValue() const {
|
||||
return _selectedListStream.events_starting_with(
|
||||
collectSelectedItems());
|
||||
}
|
||||
|
||||
void ListWidget::restart() {
|
||||
mouseActionCancel();
|
||||
|
||||
|
|
|
@ -55,13 +55,8 @@ public:
|
|||
|
||||
void restart();
|
||||
|
||||
rpl::producer<int> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
rpl::producer<SelectedItems> selectedListValue() const {
|
||||
return _selectedListStream.events_starting_with(
|
||||
collectSelectedItems());
|
||||
}
|
||||
rpl::producer<int> scrollToRequests() const;
|
||||
rpl::producer<SelectedItems> selectedListValue() const;
|
||||
void cancelSelection() {
|
||||
clearSelected();
|
||||
}
|
||||
|
|
|
@ -431,6 +431,11 @@ SharedMediaCover::SharedMediaCover(QWidget *parent)
|
|||
createLabel();
|
||||
}
|
||||
|
||||
SharedMediaCover *SharedMediaCover::setToggleShown(rpl::producer<bool> &&shown) {
|
||||
return static_cast<SharedMediaCover*>(
|
||||
SectionWithToggle::setToggleShown(std::move(shown)));
|
||||
}
|
||||
|
||||
void SharedMediaCover::createLabel() {
|
||||
using namespace rpl::mappers;
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
|
|
|
@ -102,10 +102,7 @@ class SharedMediaCover : public SectionWithToggle {
|
|||
public:
|
||||
SharedMediaCover(QWidget *parent);
|
||||
|
||||
SharedMediaCover *setToggleShown(rpl::producer<bool> &&shown) {
|
||||
return static_cast<SharedMediaCover*>(
|
||||
SectionWithToggle::setToggleShown(std::move(shown)));
|
||||
}
|
||||
SharedMediaCover *setToggleShown(rpl::producer<bool> &&shown);
|
||||
|
||||
QMargins getMargins() const override;
|
||||
|
||||
|
|
|
@ -274,6 +274,14 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
|
|||
}
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<int> InnerWidget::desiredHeightValue() const {
|
||||
return _desiredHeight.events_starting_with(countDesiredHeight());
|
||||
}
|
||||
|
||||
int InnerWidget::resizeGetHeight(int newWidth) {
|
||||
_inResize = true;
|
||||
auto guard = gsl::finally([&] { _inResize = false; });
|
||||
|
|
|
@ -58,13 +58,8 @@ public:
|
|||
void setIsStackBottom(bool isStackBottom) {
|
||||
_isStackBottom = isStackBottom;
|
||||
}
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<int> desiredHeightValue() const override {
|
||||
return _desiredHeight.events_starting_with(countDesiredHeight());
|
||||
}
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||
rpl::producer<int> desiredHeightValue() const override;
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
|
|
@ -90,6 +90,10 @@ rpl::producer<int> Members::onlineCountValue() const {
|
|||
return _listController->onlineCountValue();
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> Members::scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
std::unique_ptr<MembersState> Members::saveState() {
|
||||
auto result = std::make_unique<MembersState>();
|
||||
result->list = _listController->saveState();
|
||||
|
|
|
@ -60,9 +60,7 @@ public:
|
|||
not_null<Controller*> controller,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||
|
||||
std::unique_ptr<MembersState> saveState();
|
||||
void restoreState(std::unique_ptr<MembersState> state);
|
||||
|
|
|
@ -108,4 +108,35 @@ base::Observable<PeerUpdate, PeerUpdatedHandler> &PeerUpdated() {
|
|||
return PeerUpdatedObservable;
|
||||
}
|
||||
|
||||
rpl::producer<PeerUpdate> PeerUpdateViewer(
|
||||
PeerUpdate::Flags flags) {
|
||||
return [=](const auto &consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
lifetime.make_state<base::Subscription>(
|
||||
PeerUpdated().add_subscription({ flags, [=](
|
||||
const PeerUpdate &update) {
|
||||
consumer.put_next_copy(update);
|
||||
}}));
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
rpl::producer<PeerUpdate> PeerUpdateViewer(
|
||||
not_null<PeerData*> peer,
|
||||
PeerUpdate::Flags flags) {
|
||||
return PeerUpdateViewer(flags)
|
||||
| rpl::filter([=](const PeerUpdate &update) {
|
||||
return (update.peer == peer);
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<PeerUpdate> PeerUpdateValue(
|
||||
not_null<PeerData*> peer,
|
||||
PeerUpdate::Flags flags) {
|
||||
auto initial = PeerUpdate(peer);
|
||||
initial.flags = flags;
|
||||
return rpl::single(initial)
|
||||
| rpl::then(PeerUpdateViewer(peer, flags));
|
||||
}
|
||||
|
||||
} // namespace Notify
|
||||
|
|
|
@ -125,36 +125,15 @@ private:
|
|||
};
|
||||
base::Observable<PeerUpdate, PeerUpdatedHandler> &PeerUpdated();
|
||||
|
||||
inline auto PeerUpdateViewer(
|
||||
PeerUpdate::Flags flags) {
|
||||
return rpl::make_producer<PeerUpdate>([=](
|
||||
const auto &consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
lifetime.make_state<base::Subscription>(
|
||||
PeerUpdated().add_subscription({ flags, [=](
|
||||
const PeerUpdate &update) {
|
||||
consumer.put_next_copy(update);
|
||||
}}));
|
||||
return lifetime;
|
||||
});
|
||||
}
|
||||
rpl::producer<PeerUpdate> PeerUpdateViewer(
|
||||
PeerUpdate::Flags flags);
|
||||
|
||||
inline auto PeerUpdateViewer(
|
||||
not_null<PeerData*> peer,
|
||||
PeerUpdate::Flags flags) {
|
||||
return PeerUpdateViewer(flags)
|
||||
| rpl::filter([=](const PeerUpdate &update) {
|
||||
return (update.peer == peer);
|
||||
});
|
||||
}
|
||||
rpl::producer<PeerUpdate> PeerUpdateViewer(
|
||||
not_null<PeerData*> peer,
|
||||
PeerUpdate::Flags flags);
|
||||
|
||||
inline auto PeerUpdateValue(
|
||||
not_null<PeerData*> peer,
|
||||
PeerUpdate::Flags flags) {
|
||||
auto initial = PeerUpdate(peer);
|
||||
initial.flags = flags;
|
||||
return rpl::single(initial)
|
||||
| rpl::then(PeerUpdateViewer(peer, flags));
|
||||
}
|
||||
rpl::producer<PeerUpdate> PeerUpdateValue(
|
||||
not_null<PeerData*> peer,
|
||||
PeerUpdate::Flags flags);
|
||||
|
||||
} // namespace Notify
|
||||
|
|
|
@ -1,377 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_block_actions.h"
|
||||
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/report_box.h"
|
||||
#include "mainwidget.h"
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
constexpr auto kEnableSearchMembersAfterCount = 50;
|
||||
constexpr auto kMaxChannelMembersDeleteAllowed = 1000;
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
ActionsWidget::ActionsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_actions_section)) {
|
||||
auto observeEvents = UpdateFlag::ChannelAmIn
|
||||
| UpdateFlag::UserIsBlocked
|
||||
| UpdateFlag::BotCommandsChanged
|
||||
| UpdateFlag::MembersChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
validateBlockStatus();
|
||||
refreshButtons();
|
||||
}
|
||||
|
||||
void ActionsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto needFullRefresh = [&update, this]() {
|
||||
if (update.flags & UpdateFlag::BotCommandsChanged) {
|
||||
if (_hasBotHelp != hasBotCommand(qsl("help")) || _hasBotSettings != hasBotCommand(qsl("settings"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (update.flags & UpdateFlag::MembersChanged) {
|
||||
if (peer()->isMegagroup()) {
|
||||
// Search members button could change.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (needFullRefresh()) {
|
||||
refreshButtons();
|
||||
} else {
|
||||
if (update.flags & UpdateFlag::MembersChanged) {
|
||||
refreshDeleteChannel();
|
||||
}
|
||||
if (update.flags & UpdateFlag::ChannelAmIn) {
|
||||
refreshLeaveChannel();
|
||||
}
|
||||
if (update.flags & UpdateFlag::UserIsBlocked) {
|
||||
refreshBlockUser();
|
||||
}
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
contentSizeUpdated();
|
||||
}
|
||||
|
||||
void ActionsWidget::validateBlockStatus() const {
|
||||
auto needFullPeer = [this]() {
|
||||
if (auto user = peer()->asUser()) {
|
||||
if (user->blockStatus() == UserData::BlockStatus::Unknown) {
|
||||
return true;
|
||||
} else if (user->botInfo && !user->botInfo->inited) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (needFullPeer()) {
|
||||
Auth().api().requestFullPeer(peer());
|
||||
}
|
||||
}
|
||||
|
||||
Ui::LeftOutlineButton *ActionsWidget::addButton(const QString &text, const char *slot, const style::OutlineButton &st, int skipHeight) {
|
||||
auto result = new Ui::LeftOutlineButton(this, text, st);
|
||||
connect(result, SIGNAL(clicked()), this, slot);
|
||||
result->show();
|
||||
|
||||
int top = buttonsBottom() + skipHeight;
|
||||
resizeButton(result, width(), top);
|
||||
|
||||
_buttons.push_back(result);
|
||||
return result;
|
||||
};
|
||||
|
||||
void ActionsWidget::resizeButton(Ui::LeftOutlineButton *button, int newWidth, int top) {
|
||||
int left = defaultOutlineButtonLeft();
|
||||
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
|
||||
button->resizeToWidth(availableWidth);
|
||||
button->moveToLeft(left, top);
|
||||
}
|
||||
|
||||
void ActionsWidget::refreshButtons() {
|
||||
auto buttons = base::take(_buttons);
|
||||
for_const (auto &button, buttons) {
|
||||
delete button;
|
||||
}
|
||||
_blockUser = _leaveChannel = nullptr;
|
||||
|
||||
if (auto user = peer()->asUser()) {
|
||||
if ((_hasBotHelp = hasBotCommand(qsl("help")))) {
|
||||
addButton(lang(lng_profile_bot_help), SLOT(onBotHelp()));
|
||||
}
|
||||
if ((_hasBotSettings = hasBotCommand(qsl("settings")))) {
|
||||
addButton(lang(lng_profile_bot_settings), SLOT(onBotSettings()));
|
||||
}
|
||||
addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
|
||||
addButton(lang(lng_profile_delete_conversation), SLOT(onDeleteConversation()));
|
||||
if (user->botInfo) {
|
||||
addButton(lang(lng_profile_report), SLOT(onReport()), st::defaultLeftOutlineButton, st::profileBlockOneLineSkip);
|
||||
}
|
||||
refreshBlockUser();
|
||||
} else if (auto chat = peer()->asChat()) {
|
||||
if (chat->amCreator()) {
|
||||
addButton(lang(lng_profile_migrate_button), SLOT(onUpgradeToSupergroup()));
|
||||
}
|
||||
addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
|
||||
addButton(lang(lng_profile_clear_and_exit), SLOT(onDeleteConversation()));
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
if (channel->isMegagroup() && channel->membersCount() > kEnableSearchMembersAfterCount) {
|
||||
addButton(lang(lng_profile_search_members), SLOT(onSearchMembers()));
|
||||
}
|
||||
if (!channel->amCreator() && (!channel->isMegagroup() || channel->isPublic())) {
|
||||
addButton(lang(lng_profile_report), SLOT(onReport()));
|
||||
}
|
||||
refreshDeleteChannel();
|
||||
refreshLeaveChannel();
|
||||
}
|
||||
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
void ActionsWidget::refreshVisibility() {
|
||||
setVisible(!_buttons.isEmpty());
|
||||
}
|
||||
|
||||
QString ActionsWidget::getBlockButtonText() const {
|
||||
auto user = peer()->asUser();
|
||||
if (!user || (user->id == Auth().userPeerId())) return QString();
|
||||
if (user->blockStatus() == UserData::BlockStatus::Unknown) return QString();
|
||||
|
||||
if (user->isBlocked()) {
|
||||
if (user->botInfo) {
|
||||
return lang(lng_profile_unblock_bot);
|
||||
}
|
||||
return lang(lng_profile_unblock_user);
|
||||
} else if (user->botInfo) {
|
||||
return lang(lng_profile_block_bot);
|
||||
}
|
||||
return lang(lng_profile_block_user);
|
||||
}
|
||||
|
||||
bool ActionsWidget::hasBotCommand(const QString &command) const {
|
||||
auto user = peer()->asUser();
|
||||
if (!user || !user->botInfo || user->botInfo->commands.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for_const (auto &cmd, user->botInfo->commands) {
|
||||
if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionsWidget::sendBotCommand(const QString &command) {
|
||||
auto user = peer()->asUser();
|
||||
if (user && user->botInfo && !user->botInfo->commands.isEmpty()) {
|
||||
for_const (auto &cmd, user->botInfo->commands) {
|
||||
if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
|
||||
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
|
||||
App::sendBotCommand(user, user, '/' + cmd.command);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Command was not found.
|
||||
refreshButtons();
|
||||
contentSizeUpdated();
|
||||
}
|
||||
|
||||
void ActionsWidget::refreshBlockUser() {
|
||||
if (auto user = peer()->asUser()) {
|
||||
auto blockText = getBlockButtonText();
|
||||
if (_blockUser) {
|
||||
if (blockText.isEmpty()) {
|
||||
_buttons.removeOne(_blockUser);
|
||||
delete _blockUser;
|
||||
_blockUser = nullptr;
|
||||
} else {
|
||||
_blockUser->setText(blockText);
|
||||
}
|
||||
} else if (!blockText.isEmpty()) {
|
||||
_blockUser = addButton(blockText, SLOT(onBlockUser()), st::attentionLeftOutlineButton, st::profileBlockOneLineSkip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::refreshDeleteChannel() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (channel->canDelete() && !_deleteChannel) {
|
||||
_deleteChannel = addButton(lang(channel->isMegagroup() ? lng_profile_delete_group : lng_profile_delete_channel), SLOT(onDeleteChannel()), st::attentionLeftOutlineButton);
|
||||
} else if (!channel->canDelete() && _deleteChannel) {
|
||||
_buttons.removeOne(_deleteChannel);
|
||||
delete _deleteChannel;
|
||||
_deleteChannel = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::refreshLeaveChannel() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (!channel->amCreator()) {
|
||||
if (channel->amIn() && !_leaveChannel) {
|
||||
_leaveChannel = addButton(lang(channel->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), SLOT(onLeaveChannel()));
|
||||
} else if (!channel->amIn() && _leaveChannel) {
|
||||
_buttons.removeOne(_leaveChannel);
|
||||
delete _leaveChannel;
|
||||
_leaveChannel = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ActionsWidget::resizeGetHeight(int newWidth) {
|
||||
for_const (auto button, _buttons) {
|
||||
resizeButton(button, newWidth, button->y());
|
||||
}
|
||||
return buttonsBottom();
|
||||
}
|
||||
|
||||
int ActionsWidget::buttonsBottom() const {
|
||||
if (_buttons.isEmpty()) {
|
||||
return contentTop();
|
||||
}
|
||||
auto lastButton = _buttons.back();
|
||||
return lastButton->y() + lastButton->height();
|
||||
}
|
||||
|
||||
void ActionsWidget::onBotHelp() {
|
||||
sendBotCommand(qsl("help"));
|
||||
}
|
||||
|
||||
void ActionsWidget::onBotSettings() {
|
||||
sendBotCommand(qsl("settings"));
|
||||
}
|
||||
|
||||
void ActionsWidget::onClearHistory() {
|
||||
QString confirmation;
|
||||
if (auto user = peer()->asUser()) {
|
||||
confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
|
||||
} else if (auto chat = peer()->asChat()) {
|
||||
confirmation = lng_sure_delete_group_history(lt_group, App::peerName(peer()));
|
||||
}
|
||||
if (!confirmation.isEmpty()) {
|
||||
Ui::show(Box<ConfirmBox>(confirmation, lang(lng_box_delete), st::attentionBoxButton, base::lambda_guarded(this, [this] {
|
||||
Ui::hideLayer();
|
||||
App::main()->clearHistory(peer());
|
||||
Ui::showPeerHistory(peer(), ShowAtUnreadMsgId);
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::onDeleteConversation() {
|
||||
QString confirmation, confirmButton;
|
||||
if (auto user = peer()->asUser()) {
|
||||
confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
|
||||
confirmButton = lang(lng_box_delete);
|
||||
} else if (auto chat = peer()->asChat()) {
|
||||
confirmation = lng_sure_delete_and_exit(lt_group, App::peerName(peer()));
|
||||
confirmButton = lang(lng_box_leave);
|
||||
}
|
||||
if (!confirmation.isEmpty()) {
|
||||
Ui::show(Box<ConfirmBox>(confirmation, confirmButton, st::attentionBoxButton, base::lambda_guarded(this, [this] {
|
||||
Ui::hideLayer();
|
||||
Ui::showChatsList();
|
||||
if (auto user = peer()->asUser()) {
|
||||
App::main()->deleteConversation(peer());
|
||||
} else if (auto chat = peer()->asChat()) {
|
||||
App::main()->deleteAndExit(chat);
|
||||
}
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::onBlockUser() {
|
||||
if (auto user = peer()->asUser()) {
|
||||
if (user->isBlocked()) {
|
||||
Auth().api().unblockUser(user);
|
||||
} else {
|
||||
Auth().api().blockUser(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::onUpgradeToSupergroup() {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
Ui::show(Box<ConvertToSupergroupBox>(chat));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::onDeleteChannel() {
|
||||
auto text = lang(peer()->isMegagroup() ? lng_sure_delete_group : lng_sure_delete_channel);
|
||||
Ui::show(Box<ConfirmBox>(text, lang(lng_box_delete), st::attentionBoxButton, base::lambda_guarded(this, [this] {
|
||||
Ui::hideLayer();
|
||||
Ui::showChatsList();
|
||||
if (auto chat = peer()->migrateFrom()) {
|
||||
App::main()->deleteAndExit(chat);
|
||||
}
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
MTP::send(MTPchannels_DeleteChannel(channel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::deleteChannelFailed));
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
void ActionsWidget::onLeaveChannel() {
|
||||
auto channel = peer()->asChannel();
|
||||
if (!channel) return;
|
||||
|
||||
auto text = lang(channel->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel);
|
||||
Ui::show(Box<ConfirmBox>(text, lang(lng_box_leave), base::lambda_guarded(this, [this] {
|
||||
Auth().api().leaveChannel(peer()->asChannel());
|
||||
})));
|
||||
}
|
||||
|
||||
void ActionsWidget::onSearchMembers() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
ParticipantsBoxController::Start(
|
||||
App::wnd()->controller(),
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Members);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsWidget::onReport() {
|
||||
Ui::show(Box<ReportBox>(peer()));
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "profile/profile_block_widget.h"
|
||||
|
||||
namespace style {
|
||||
struct OutlineButton;
|
||||
} // namespace style
|
||||
|
||||
namespace st {
|
||||
extern const style::OutlineButton &defaultLeftOutlineButton;
|
||||
} // namespace st
|
||||
|
||||
namespace Ui {
|
||||
class LeftOutlineButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class ActionsWidget : public BlockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ActionsWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
protected:
|
||||
// Resizes content and counts natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private slots:
|
||||
void onBotHelp();
|
||||
void onBotSettings();
|
||||
void onClearHistory();
|
||||
void onDeleteConversation();
|
||||
void onBlockUser();
|
||||
void onUpgradeToSupergroup();
|
||||
void onSearchMembers();
|
||||
void onDeleteChannel();
|
||||
void onLeaveChannel();
|
||||
void onReport();
|
||||
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
void validateBlockStatus() const;
|
||||
|
||||
int buttonsBottom() const;
|
||||
|
||||
void refreshButtons();
|
||||
void refreshBlockUser();
|
||||
void refreshDeleteChannel();
|
||||
void refreshLeaveChannel();
|
||||
void refreshVisibility();
|
||||
|
||||
Ui::LeftOutlineButton *addButton(const QString &text, const char *slot
|
||||
, const style::OutlineButton &st = st::defaultLeftOutlineButton, int skipHeight = 0);
|
||||
void resizeButton(Ui::LeftOutlineButton *button, int newWidth, int top);
|
||||
|
||||
QString getBlockButtonText() const;
|
||||
bool hasBotCommand(const QString &command) const;
|
||||
void sendBotCommand(const QString &command);
|
||||
|
||||
QList<Ui::LeftOutlineButton*> _buttons;
|
||||
|
||||
// Hold some button pointers to update / toggle them.
|
||||
bool _hasBotHelp = false;
|
||||
bool _hasBotSettings = false;
|
||||
Ui::LeftOutlineButton *_blockUser = nullptr;
|
||||
Ui::LeftOutlineButton *_deleteChannel = nullptr;
|
||||
Ui::LeftOutlineButton *_leaveChannel = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_block_channel_members.h"
|
||||
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "observer_peer.h"
|
||||
#include "mainwidget.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
ChannelMembersWidget::ChannelMembersWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_participants_section)) {
|
||||
auto observeEvents = UpdateFlag::ChannelRightsChanged
|
||||
| UpdateFlag::AdminsChanged
|
||||
| UpdateFlag::MembersChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshButtons();
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged)) {
|
||||
refreshAdmins();
|
||||
}
|
||||
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::MembersChanged)) {
|
||||
refreshMembers();
|
||||
}
|
||||
refreshVisibility();
|
||||
|
||||
contentSizeUpdated();
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::addButton(const QString &text, object_ptr<Ui::LeftOutlineButton> *button, const char *slot) {
|
||||
if (text.isEmpty()) {
|
||||
button->destroy();
|
||||
} else if (*button) {
|
||||
(*button)->setText(text);
|
||||
} else {
|
||||
button->create(this, text, st::defaultLeftOutlineButton);
|
||||
(*button)->show();
|
||||
connect(*button, SIGNAL(clicked()), this, slot);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::refreshButtons() {
|
||||
refreshMembers();
|
||||
refreshAdmins();
|
||||
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::refreshAdmins() {
|
||||
auto getAdminsText = [this] {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (!channel->isMegagroup() && channel->canViewAdmins()) {
|
||||
auto adminsCount = qMax(channel->adminsCount(), 1);
|
||||
return lng_channel_admins_link(lt_count, adminsCount);
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
addButton(getAdminsText(), &_admins, SLOT(onAdmins()));
|
||||
|
||||
auto getRecentActionsText = [this] {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (!channel->isMegagroup() && (channel->hasAdminRights() || channel->amCreator())) {
|
||||
return lang(lng_profile_recent_actions);
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
addButton(getRecentActionsText(), &_recentActions, SLOT(onRecentActions()));
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::refreshMembers() {
|
||||
auto getMembersText = [this]() -> QString {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (!channel->isMegagroup() && channel->canViewMembers()) {
|
||||
int membersCount = qMax(channel->membersCount(), 1);
|
||||
return lng_channel_members_link(lt_count, membersCount);
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
addButton(getMembersText(), &_members, SLOT(onMembers()));
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::refreshVisibility() {
|
||||
setVisible(_admins || _members);
|
||||
}
|
||||
|
||||
int ChannelMembersWidget::resizeGetHeight(int newWidth) {
|
||||
int newHeight = contentTop();
|
||||
|
||||
auto resizeButton = [this, &newHeight, newWidth](object_ptr<Ui::LeftOutlineButton> &button) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
int left = defaultOutlineButtonLeft();
|
||||
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
|
||||
button->resizeToWidth(availableWidth);
|
||||
button->moveToLeft(left, newHeight);
|
||||
newHeight += button->height();
|
||||
};
|
||||
|
||||
resizeButton(_members);
|
||||
resizeButton(_admins);
|
||||
resizeButton(_recentActions);
|
||||
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::onMembers() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
ParticipantsBoxController::Start(
|
||||
App::wnd()->controller(),
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Members);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::onAdmins() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
ParticipantsBoxController::Start(
|
||||
App::wnd()->controller(),
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Admins);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelMembersWidget::onRecentActions() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (auto main = App::main()) {
|
||||
main->showSection(
|
||||
AdminLog::SectionMemento(channel),
|
||||
Window::SectionShow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "profile/profile_block_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class LeftOutlineButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class ChannelMembersWidget : public BlockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChannelMembersWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
protected:
|
||||
// Resizes content and counts natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private slots:
|
||||
void onMembers();
|
||||
void onAdmins();
|
||||
void onRecentActions();
|
||||
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
void refreshButtons();
|
||||
void refreshAdmins();
|
||||
void refreshMembers();
|
||||
void refreshVisibility();
|
||||
|
||||
void addButton(const QString &text, object_ptr<Ui::LeftOutlineButton> *button, const char *slot);
|
||||
|
||||
object_ptr<Ui::LeftOutlineButton> _members = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _admins = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _recentActions = { nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_block_info.h"
|
||||
|
||||
#include "styles/style_profile.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "mainwidget.h"
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "messenger.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
constexpr int kCommonGroupsLimit = 20;
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
InfoWidget::InfoWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_info_section)) {
|
||||
auto observeEvents = UpdateFlag::AboutChanged
|
||||
| UpdateFlag::UsernameChanged
|
||||
| UpdateFlag::UserPhoneChanged
|
||||
| UpdateFlag::UserCanShareContact;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshLabels();
|
||||
}
|
||||
|
||||
void InfoWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (update.flags & UpdateFlag::AboutChanged) {
|
||||
refreshAbout();
|
||||
}
|
||||
if (update.flags & UpdateFlag::UsernameChanged) {
|
||||
refreshUsername();
|
||||
refreshChannelLink();
|
||||
}
|
||||
if (update.flags & (UpdateFlag::UserPhoneChanged | UpdateFlag::UserCanShareContact)) {
|
||||
refreshMobileNumber();
|
||||
}
|
||||
refreshVisibility();
|
||||
|
||||
contentSizeUpdated();
|
||||
}
|
||||
|
||||
int InfoWidget::resizeGetHeight(int newWidth) {
|
||||
int initialHeight = contentTop();
|
||||
int newHeight = initialHeight;
|
||||
|
||||
int marginLeft = st::profileBlockTextPart.margin.left();
|
||||
int marginRight = st::profileBlockTextPart.margin.right();
|
||||
int left = st::profileBlockTitlePosition.x();
|
||||
if (_about) {
|
||||
int textWidth = _about->naturalWidth();
|
||||
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||
int maxWidth = st::msgMaxWidth;
|
||||
accumulate_min(textWidth, availableWidth);
|
||||
accumulate_min(textWidth, st::msgMaxWidth);
|
||||
_about->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||
_about->moveToLeft(left - marginLeft, newHeight - st::profileBlockTextPart.margin.top());
|
||||
newHeight += _about->height();
|
||||
}
|
||||
|
||||
auto moveLabeledText = [&newHeight, left, newWidth, marginLeft, marginRight](Ui::FlatLabel *label, Ui::FlatLabel *text, Ui::FlatLabel *shortText) {
|
||||
if (!label) return;
|
||||
|
||||
label->moveToLeft(left, newHeight);
|
||||
int textLeft = left + label->width() + st::normalFont->spacew;
|
||||
int textWidth = text->naturalWidth();
|
||||
int availableWidth = newWidth - textLeft - st::profileBlockMarginRight;
|
||||
bool doesNotFit = (textWidth > availableWidth);
|
||||
accumulate_min(textWidth, availableWidth);
|
||||
accumulate_min(textWidth, st::msgMaxWidth);
|
||||
text->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||
text->moveToLeft(textLeft - marginLeft, newHeight - st::profileBlockOneLineTextPart.margin.top());
|
||||
if (shortText) {
|
||||
shortText->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||
shortText->moveToLeft(textLeft - marginLeft, newHeight - st::profileBlockOneLineTextPart.margin.top());
|
||||
if (doesNotFit) {
|
||||
shortText->show();
|
||||
text->hide();
|
||||
} else {
|
||||
shortText->hide();
|
||||
text->show();
|
||||
}
|
||||
}
|
||||
newHeight += qMax(label->height(), text->height() - st::profileBlockTextPart.margin.top() - st::profileBlockTextPart.margin.bottom()) + st::profileBlockOneLineSkip;
|
||||
};
|
||||
moveLabeledText(_channelLinkLabel, _channelLink, _channelLinkShort);
|
||||
moveLabeledText(_mobileNumberLabel, _mobileNumber, nullptr);
|
||||
moveLabeledText(_bioLabel, _bio, nullptr);
|
||||
moveLabeledText(_usernameLabel, _username, nullptr);
|
||||
|
||||
newHeight += st::profileBlockMarginBottom;
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void InfoWidget::leaveEventHook(QEvent *e) {
|
||||
BotCommandClickHandler::setPeerForCommand(nullptr);
|
||||
BotCommandClickHandler::setBotForCommand(nullptr);
|
||||
}
|
||||
|
||||
void InfoWidget::refreshLabels() {
|
||||
refreshAbout();
|
||||
refreshMobileNumber();
|
||||
refreshUsername();
|
||||
refreshChannelLink();
|
||||
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
void InfoWidget::refreshVisibility() {
|
||||
setVisible(_about || _mobileNumber || _username || _bio || _channelLink);
|
||||
}
|
||||
|
||||
void InfoWidget::refreshAbout() {
|
||||
auto getAboutText = [this]() -> QString {
|
||||
if (auto user = peer()->asUser()) {
|
||||
return user->about();
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
return channel->about();
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
|
||||
_about.destroy();
|
||||
_bioLabel.destroy();
|
||||
_bio.destroy();
|
||||
auto aboutText = TextWithEntities { TextUtilities::Clean(getAboutText()) };
|
||||
auto displayAsBio = false;
|
||||
if (auto user = peer()->asUser()) {
|
||||
if (!user->botInfo) {
|
||||
displayAsBio = true;
|
||||
}
|
||||
}
|
||||
if (displayAsBio) {
|
||||
aboutText.text = TextUtilities::SingleLine(aboutText.text);
|
||||
}
|
||||
if (!aboutText.text.isEmpty()) {
|
||||
if (displayAsBio) {
|
||||
setLabeledText(&_bioLabel, lang(lng_profile_bio), &_bio, aboutText, st::profileBioLabel, QString());
|
||||
} else {
|
||||
_about.create(this, st::profileBlockTextPart);
|
||||
_about->show();
|
||||
|
||||
TextUtilities::ParseEntities(aboutText, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands);
|
||||
_about->setMarkedText(aboutText);
|
||||
_about->setSelectable(true);
|
||||
_about->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
BotCommandClickHandler::setPeerForCommand(peer());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InfoWidget::refreshMobileNumber() {
|
||||
TextWithEntities phoneText;
|
||||
if (auto user = peer()->asUser()) {
|
||||
if (!user->phone().isEmpty()) {
|
||||
phoneText.text = App::formatPhone(user->phone());
|
||||
} else {
|
||||
phoneText.text = App::phoneFromSharedContact(peerToUser(user->id));
|
||||
}
|
||||
}
|
||||
setSingleLineLabeledText(&_mobileNumberLabel, lang(lng_profile_mobile_number), &_mobileNumber, phoneText, lang(lng_profile_copy_phone));
|
||||
}
|
||||
|
||||
void InfoWidget::refreshUsername() {
|
||||
TextWithEntities usernameText;
|
||||
if (auto user = peer()->asUser()) {
|
||||
if (!user->username.isEmpty()) {
|
||||
usernameText.text = '@' + user->username;
|
||||
}
|
||||
}
|
||||
setSingleLineLabeledText(&_usernameLabel, lang(lng_profile_username), &_username, usernameText, lang(lng_context_copy_mention));
|
||||
}
|
||||
|
||||
void InfoWidget::refreshChannelLink() {
|
||||
TextWithEntities channelLinkText;
|
||||
TextWithEntities channelLinkTextShort;
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (!channel->username.isEmpty()) {
|
||||
channelLinkText.text = Messenger::Instance().createInternalLinkFull(channel->username);
|
||||
channelLinkText.entities.push_back(EntityInText(EntityInTextUrl, 0, channelLinkText.text.size()));
|
||||
channelLinkTextShort.text = Messenger::Instance().createInternalLink(channel->username);
|
||||
channelLinkTextShort.entities.push_back(EntityInText(EntityInTextCustomUrl, 0, channelLinkTextShort.text.size(), Messenger::Instance().createInternalLinkFull(channel->username)));
|
||||
}
|
||||
}
|
||||
setSingleLineLabeledText(nullptr, lang(lng_profile_link), &_channelLink, channelLinkText, QString());
|
||||
setSingleLineLabeledText(&_channelLinkLabel, lang(lng_profile_link), &_channelLinkShort, channelLinkTextShort, QString());
|
||||
auto copyLinkHandlerHook = [this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
auto link = Messenger::Instance().createInternalLinkFull(channel->username);
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(lang(lng_create_channel_link_copied));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (_channelLink) {
|
||||
_channelLink->setClickHandlerHook(copyLinkHandlerHook);
|
||||
}
|
||||
if (_channelLinkShort) {
|
||||
_channelLinkShort->setExpandLinksMode(ExpandLinksUrlOnly);
|
||||
_channelLinkShort->setClickHandlerHook(copyLinkHandlerHook);
|
||||
}
|
||||
}
|
||||
|
||||
void InfoWidget::setLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
|
||||
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities,
|
||||
const style::FlatLabel &st, const QString ©Text) {
|
||||
if (labelWidget) labelWidget->destroy();
|
||||
textWidget->destroy();
|
||||
if (textWithEntities.text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (labelWidget) {
|
||||
labelWidget->create(this, label, Ui::FlatLabel::InitType::Simple, st::profileBlockLabel);
|
||||
(*labelWidget)->show();
|
||||
}
|
||||
textWidget->create(this, QString(), Ui::FlatLabel::InitType::Simple, st);
|
||||
(*textWidget)->show();
|
||||
(*textWidget)->setMarkedText(textWithEntities);
|
||||
(*textWidget)->setContextCopyText(copyText);
|
||||
(*textWidget)->setSelectable(true);
|
||||
}
|
||||
|
||||
void InfoWidget::setSingleLineLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
|
||||
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString ©Text) {
|
||||
setLabeledText(labelWidget, label, textWidget, textWithEntities, st::profileBlockOneLineTextPart, copyText);
|
||||
if (*textWidget) {
|
||||
(*textWidget)->setDoubleClickSelectsParagraph(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "profile/profile_block_widget.h"
|
||||
|
||||
namespace style {
|
||||
struct FlatLabel;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class InfoWidget : public BlockWidget, public RPCSender {
|
||||
public:
|
||||
InfoWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
protected:
|
||||
// Resizes content and counts natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
void refreshLabels();
|
||||
void refreshAbout();
|
||||
void refreshMobileNumber();
|
||||
void refreshUsername();
|
||||
void refreshChannelLink();
|
||||
void refreshVisibility();
|
||||
|
||||
// labelWidget may be nullptr.
|
||||
void setLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
|
||||
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities,
|
||||
const style::FlatLabel &st, const QString ©Text);
|
||||
void setSingleLineLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
|
||||
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString ©Text);
|
||||
|
||||
object_ptr<Ui::FlatLabel> _about = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _channelLinkLabel = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _channelLink = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _channelLinkShort = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _mobileNumberLabel = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _mobileNumber = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _bioLabel = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _bio = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _usernameLabel = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _username = { nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_block_invite_link.h"
|
||||
|
||||
#include "styles/style_profile.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "observer_peer.h"
|
||||
#include "mainwindow.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
InviteLinkWidget::InviteLinkWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_invite_link_section)) {
|
||||
auto observeEvents = UpdateFlag::InviteLinkChanged | UpdateFlag::UsernameChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshLink();
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
void InviteLinkWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (update.flags & (UpdateFlag::InviteLinkChanged | UpdateFlag::UsernameChanged)) {
|
||||
refreshLink();
|
||||
refreshVisibility();
|
||||
|
||||
contentSizeUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
int InviteLinkWidget::resizeGetHeight(int newWidth) {
|
||||
int newHeight = contentTop();
|
||||
|
||||
int marginLeft = st::profileBlockTextPart.margin.left();
|
||||
int marginRight = st::profileBlockTextPart.margin.right();
|
||||
int left = st::profileBlockTitlePosition.x();
|
||||
if (_link) {
|
||||
int textWidth = _link->naturalWidth();
|
||||
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||
int maxWidth = st::msgMaxWidth;
|
||||
accumulate_min(textWidth, availableWidth);
|
||||
accumulate_min(textWidth, st::msgMaxWidth);
|
||||
_link->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||
_link->moveToLeft(left - marginLeft, newHeight - st::profileBlockTextPart.margin.top());
|
||||
newHeight += _link->height();
|
||||
}
|
||||
|
||||
newHeight += st::profileBlockMarginBottom;
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void InviteLinkWidget::refreshVisibility() {
|
||||
setVisible(_link != nullptr);
|
||||
}
|
||||
|
||||
QString InviteLinkWidget::getInviteLink() const {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
return chat->inviteLink();
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
return channel->isPublic() ? QString() : channel->inviteLink();
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
|
||||
void InviteLinkWidget::refreshLink() {
|
||||
_link.destroy();
|
||||
TextWithEntities linkData = { getInviteLink(), EntitiesInText() };
|
||||
if (linkData.text.isEmpty()) {
|
||||
_link.destroy();
|
||||
} else {
|
||||
_link.create(this, QString(), Ui::FlatLabel::InitType::Simple, st::profileInviteLinkText);
|
||||
_link->show();
|
||||
|
||||
linkData.entities.push_back(EntityInText(EntityInTextUrl, 0, linkData.text.size()));
|
||||
_link->setMarkedText(linkData);
|
||||
_link->setSelectable(true);
|
||||
_link->setContextCopyText(QString());
|
||||
_link->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||
auto link = getInviteLink();
|
||||
if (link.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(lang(lng_group_invite_copied));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "profile/profile_block_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class InviteLinkWidget : public BlockWidget {
|
||||
public:
|
||||
InviteLinkWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
protected:
|
||||
// Resizes content and counts natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
QString getInviteLink() const;
|
||||
void refreshLink();
|
||||
void refreshVisibility();
|
||||
|
||||
object_ptr<Ui::FlatLabel> _link = { nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,270 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_block_settings.h"
|
||||
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_settings_section))
|
||||
, _enableNotifications(this, lang(lng_profile_enable_notifications), true, st::defaultCheckbox) {
|
||||
subscribe(_enableNotifications->checkedChanged, [this](bool checked) { onNotificationsChange(); });
|
||||
|
||||
Notify::PeerUpdate::Flags observeEvents = UpdateFlag::NotificationsEnabled;
|
||||
if (auto chat = peer->asChat()) {
|
||||
if (chat->amCreator()) {
|
||||
observeEvents |= UpdateFlag::ChatCanEdit | UpdateFlag::InviteLinkChanged;
|
||||
}
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
observeEvents |= UpdateFlag::ChannelRightsChanged | UpdateFlag::BannedUsersChanged | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged;
|
||||
}
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
refreshButtons();
|
||||
_enableNotifications->finishAnimating();
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
void SettingsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (update.flags & UpdateFlag::NotificationsEnabled) {
|
||||
refreshEnableNotifications();
|
||||
}
|
||||
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::ChatCanEdit | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged)) {
|
||||
refreshInviteLinkButton();
|
||||
}
|
||||
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::ChatCanEdit)) {
|
||||
refreshManageAdminsButton();
|
||||
}
|
||||
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::BannedUsersChanged)) {
|
||||
refreshManageBannedUsersButton();
|
||||
}
|
||||
|
||||
contentSizeUpdated();
|
||||
}
|
||||
|
||||
int SettingsWidget::resizeGetHeight(int newWidth) {
|
||||
int newHeight = contentTop() + st::profileEnableNotificationsTop;
|
||||
|
||||
_enableNotifications->moveToLeft(st::profileBlockTitlePosition.x(), newHeight);
|
||||
newHeight += _enableNotifications->heightNoMargins() + st::profileSettingsBlockSkip;
|
||||
|
||||
auto moveLink = [&newHeight, newWidth](Ui::LeftOutlineButton *button) {
|
||||
if (!button) return;
|
||||
|
||||
int left = defaultOutlineButtonLeft();
|
||||
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
|
||||
button->resizeToWidth(availableWidth);
|
||||
button->moveToLeft(left, newHeight);
|
||||
newHeight += button->height();
|
||||
};
|
||||
moveLink(_manageAdmins);
|
||||
moveLink(_recentActions);
|
||||
moveLink(_manageBannedUsers);
|
||||
moveLink(_manageRestrictedUsers);
|
||||
moveLink(_inviteLink);
|
||||
|
||||
newHeight += st::profileBlockMarginBottom;
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void SettingsWidget::refreshButtons() {
|
||||
refreshEnableNotifications();
|
||||
refreshManageAdminsButton();
|
||||
refreshManageBannedUsersButton();
|
||||
refreshInviteLinkButton();
|
||||
}
|
||||
|
||||
void SettingsWidget::refreshEnableNotifications() {
|
||||
if (peer()->notify == UnknownNotifySettings) {
|
||||
Auth().api().requestNotifySetting(peer());
|
||||
} else {
|
||||
auto ¬ifySettings = peer()->notify;
|
||||
bool enabled = (notifySettings == EmptyNotifySettings || notifySettings->mute < unixtime());
|
||||
_enableNotifications->setChecked(enabled, Ui::Checkbox::NotifyAboutChange::DontNotify);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::refreshManageAdminsButton() {
|
||||
auto hasManageAdmins = [this] {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
return (chat->amCreator() && chat->canEdit());
|
||||
} else if (auto channel = peer()->asMegagroup()) {
|
||||
return channel->hasAdminRights() || channel->amCreator();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_manageAdmins.destroy();
|
||||
if (hasManageAdmins()) {
|
||||
_manageAdmins.create(this, lang(lng_profile_manage_admins), st::defaultLeftOutlineButton);
|
||||
_manageAdmins->show();
|
||||
connect(_manageAdmins, SIGNAL(clicked()), this, SLOT(onManageAdmins()));
|
||||
}
|
||||
|
||||
auto hasRecentActions = [this] {
|
||||
if (auto channel = peer()->asMegagroup()) {
|
||||
return channel->hasAdminRights() || channel->amCreator();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_recentActions.destroy();
|
||||
if (hasRecentActions()) {
|
||||
_recentActions.create(this, lang(lng_profile_recent_actions), st::defaultLeftOutlineButton);
|
||||
_recentActions->show();
|
||||
connect(_recentActions, SIGNAL(clicked()), this, SLOT(onRecentActions()));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::refreshManageBannedUsersButton() {
|
||||
auto hasManageBannedUsers = [this] {
|
||||
if (auto channel = peer()->asMegagroup()) {
|
||||
return channel->canViewBanned() && (channel->kickedCount() > 0);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_manageBannedUsers.destroy();
|
||||
if (hasManageBannedUsers()) {
|
||||
_manageBannedUsers.create(this, lang(lng_profile_manage_blocklist), st::defaultLeftOutlineButton);
|
||||
_manageBannedUsers->show();
|
||||
connect(_manageBannedUsers, SIGNAL(clicked()), this, SLOT(onManageBannedUsers()));
|
||||
}
|
||||
|
||||
auto hasManageRestrictedUsers = [this] {
|
||||
if (auto channel = peer()->asMegagroup()) {
|
||||
return channel->canViewBanned() && (channel->restrictedCount() > 0);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_manageRestrictedUsers.destroy();
|
||||
if (hasManageRestrictedUsers()) {
|
||||
_manageRestrictedUsers.create(this, lang(lng_profile_manage_restrictedlist), st::defaultLeftOutlineButton);
|
||||
_manageRestrictedUsers->show();
|
||||
connect(_manageRestrictedUsers, SIGNAL(clicked()), this, SLOT(onManageRestrictedUsers()));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::refreshInviteLinkButton() {
|
||||
auto getInviteLinkText = [this]() -> QString {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
if (chat->amCreator() && chat->canEdit()) {
|
||||
return lang(chat->inviteLink().isEmpty() ? lng_group_invite_create : lng_group_invite_create_new);
|
||||
}
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
if (channel->canHaveInviteLink() && !channel->isPublic()) {
|
||||
return lang(channel->inviteLink().isEmpty() ? lng_group_invite_create : lng_group_invite_create_new);
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
auto inviteLinkText = getInviteLinkText();
|
||||
if (inviteLinkText.isEmpty()) {
|
||||
_inviteLink.destroy();
|
||||
} else {
|
||||
_inviteLink.create(this, inviteLinkText, st::defaultLeftOutlineButton);
|
||||
_inviteLink->show();
|
||||
connect(_inviteLink, SIGNAL(clicked()), this, SLOT(onInviteLink()));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onNotificationsChange() {
|
||||
App::main()->updateNotifySetting(peer(), _enableNotifications->checked() ? NotifySettingSetNotify : NotifySettingSetMuted);
|
||||
}
|
||||
|
||||
void SettingsWidget::onManageAdmins() {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
EditChatAdminsBoxController::Start(chat);
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
ParticipantsBoxController::Start(
|
||||
App::wnd()->controller(),
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Admins);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onRecentActions() {
|
||||
if (auto channel = peer()->asChannel()) {
|
||||
if (auto main = App::main()) {
|
||||
main->showSection(
|
||||
AdminLog::SectionMemento(channel),
|
||||
Window::SectionShow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onManageBannedUsers() {
|
||||
if (auto channel = peer()->asMegagroup()) {
|
||||
ParticipantsBoxController::Start(
|
||||
App::wnd()->controller(),
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Kicked);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onManageRestrictedUsers() {
|
||||
if (auto channel = peer()->asMegagroup()) {
|
||||
ParticipantsBoxController::Start(
|
||||
App::wnd()->controller(),
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Restricted);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onInviteLink() {
|
||||
auto getInviteLink = [this]() {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
return chat->inviteLink();
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
return channel->inviteLink();
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
auto link = getInviteLink();
|
||||
|
||||
auto text = lang(link.isEmpty() ? lng_group_invite_about : lng_group_invite_about_new);
|
||||
Ui::show(Box<ConfirmBox>(text, base::lambda_guarded(this, [this] {
|
||||
Ui::hideLayer();
|
||||
Auth().api().exportInviteLink(peer());
|
||||
})));
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "profile/profile_block_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class Checkbox;
|
||||
class LeftOutlineButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class SettingsWidget : public BlockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SettingsWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
protected:
|
||||
// Resizes content and counts natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private slots:
|
||||
void onNotificationsChange();
|
||||
void onManageAdmins();
|
||||
void onRecentActions();
|
||||
void onManageBannedUsers();
|
||||
void onManageRestrictedUsers();
|
||||
void onInviteLink();
|
||||
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
void refreshButtons();
|
||||
void refreshEnableNotifications();
|
||||
void refreshManageAdminsButton();
|
||||
void refreshManageBannedUsersButton();
|
||||
void refreshInviteLinkButton();
|
||||
|
||||
object_ptr<Ui::Checkbox> _enableNotifications;
|
||||
object_ptr<Ui::LeftOutlineButton> _manageAdmins = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _recentActions = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _manageBannedUsers = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _manageRestrictedUsers = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _inviteLink = { nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_block_shared_media.h"
|
||||
|
||||
#include "profile/profile_common_groups_section.h"
|
||||
#include "profile/profile_section_memento.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "observer_peer.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "mainwidget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
namespace Profile {
|
||||
namespace {
|
||||
|
||||
QString getButtonText(MediaOverviewType type, int count) {
|
||||
if (count <= 0) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case OverviewPhotos: return lng_profile_photos(lt_count, count);
|
||||
case OverviewVideos: return lng_profile_videos(lt_count, count);
|
||||
case OverviewMusicFiles: return lng_profile_songs(lt_count, count);
|
||||
case OverviewFiles: return lng_profile_files(lt_count, count);
|
||||
case OverviewVoiceFiles: return lng_profile_audios(lt_count, count);
|
||||
case OverviewLinks: return lng_profile_shared_links(lt_count, count);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SharedMediaWidget::SharedMediaWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_shared_media))
|
||||
, _history(App::history(peer))
|
||||
, _migrated(_history->migrateFrom()) {
|
||||
auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged
|
||||
| Notify::PeerUpdate::Flag::UserCommonChatsChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
for (auto i = 0; i != OverviewCount; ++i) {
|
||||
auto type = static_cast<MediaOverviewType>(i);
|
||||
if (!getButtonText(type, 1).isEmpty()) {
|
||||
App::main()->preloadOverview(peer, type);
|
||||
if (_migrated) {
|
||||
App::main()->preloadOverview(_migrated->peer, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshButtons();
|
||||
refreshVisibility();
|
||||
}
|
||||
|
||||
void SharedMediaWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != peer() && (!_migrated || update.peer != _migrated->peer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto updated = false;
|
||||
for (auto i = 0; i != OverviewCount; ++i) {
|
||||
if (update.mediaTypesMask & (1 << i)) {
|
||||
refreshButton(static_cast<MediaOverviewType>(i));
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (update.flags & Notify::PeerUpdate::Flag::UserCommonChatsChanged) {
|
||||
refreshCommonGroups();
|
||||
updated = true;
|
||||
}
|
||||
if (updated) {
|
||||
refreshVisibility();
|
||||
|
||||
contentSizeUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
int SharedMediaWidget::resizeGetHeight(int newWidth) {
|
||||
int newHeight = contentTop();
|
||||
|
||||
resizeButtons(newWidth, &newHeight);
|
||||
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void SharedMediaWidget::refreshButtons() {
|
||||
for (int typeIndex = 0; typeIndex < OverviewCount; ++typeIndex) {
|
||||
refreshButton(static_cast<MediaOverviewType>(typeIndex));
|
||||
}
|
||||
refreshCommonGroups();
|
||||
}
|
||||
|
||||
void SharedMediaWidget::refreshButton(MediaOverviewType type) {
|
||||
int count = _history->overviewCount(type), migrated = _migrated ? _migrated->overviewCount(type) : 0;
|
||||
int finalCount = (count >= 0 && migrated >= 0) ? (count + migrated) : -1;
|
||||
auto text = getButtonText(type, finalCount);
|
||||
if (text.isEmpty()) {
|
||||
if (_mediaButtons[type]) {
|
||||
delete _mediaButtons[type];
|
||||
_mediaButtons[type] = nullptr;
|
||||
}
|
||||
} else {
|
||||
if (_mediaButtons[type]) {
|
||||
_mediaButtons[type]->setText(text);
|
||||
} else {
|
||||
_mediaButtons[type] = new Ui::LeftOutlineButton(this, text, st::defaultLeftOutlineButton);
|
||||
_mediaButtons[type]->show();
|
||||
connect(_mediaButtons[type], SIGNAL(clicked()), this, SLOT(onMediaChosen()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SharedMediaWidget::refreshVisibility() {
|
||||
for_const (auto button, _mediaButtons) {
|
||||
if (button) {
|
||||
show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
setVisible(_commonGroups != nullptr);
|
||||
}
|
||||
|
||||
void SharedMediaWidget::onMediaChosen() {
|
||||
for (int i = 0; i < OverviewCount; ++i) {
|
||||
auto button = _mediaButtons[i];
|
||||
if (button && button == sender()) {
|
||||
// SharedMediaShowOverview();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SharedMediaWidget::resizeButtons(int newWidth, int *top) {
|
||||
Assert(top != nullptr);
|
||||
|
||||
int left = defaultOutlineButtonLeft();
|
||||
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
|
||||
for_const (auto button, _mediaButtons) {
|
||||
if (!button) continue;
|
||||
|
||||
button->resizeToWidth(availableWidth);
|
||||
button->moveToLeft(left, *top);
|
||||
*top += button->height();
|
||||
}
|
||||
|
||||
if (_commonGroups) {
|
||||
_commonGroups->resizeToWidth(availableWidth);
|
||||
_commonGroups->moveToLeft(left, *top);
|
||||
*top += _commonGroups->height();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int SharedMediaWidget::getCommonGroupsCount() const {
|
||||
if (auto user = peer()->asUser()) {
|
||||
return user->commonChatsCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SharedMediaWidget::refreshCommonGroups() {
|
||||
if (auto count = getCommonGroupsCount()) {
|
||||
auto text = lng_profile_common_groups(lt_count, count);
|
||||
if (_commonGroups) {
|
||||
_commonGroups->setText(text);
|
||||
} else {
|
||||
_commonGroups.create(this, text, st::defaultLeftOutlineButton);
|
||||
_commonGroups->setClickedCallback([this] { onShowCommonGroups(); });
|
||||
_commonGroups->show();
|
||||
}
|
||||
} else if (_commonGroups) {
|
||||
_commonGroups.destroyDelayed();
|
||||
}
|
||||
}
|
||||
|
||||
void SharedMediaWidget::onShowCommonGroups() {
|
||||
auto count = getCommonGroupsCount();
|
||||
if (count <= 0) {
|
||||
refreshCommonGroups();
|
||||
return;
|
||||
}
|
||||
if (auto main = App::main()) {
|
||||
main->showSection(
|
||||
Profile::CommonGroups::SectionMemento(peer()->asUser()),
|
||||
Window::SectionShow());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "profile/profile_block_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class LeftOutlineButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class SharedMediaWidget : public BlockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SharedMediaWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
protected:
|
||||
// Resizes content and counts natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private slots:
|
||||
void onMediaChosen();
|
||||
|
||||
private:
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
void refreshButtons();
|
||||
void refreshButton(MediaOverviewType type);
|
||||
void refreshCommonGroups();
|
||||
void refreshVisibility();
|
||||
|
||||
int getCommonGroupsCount() const;
|
||||
void onShowCommonGroups();
|
||||
void slideCommonGroupsDown();
|
||||
|
||||
void resizeButtons(int newWidth, int *top);
|
||||
|
||||
Ui::LeftOutlineButton *_mediaButtons[OverviewCount] = { nullptr };
|
||||
object_ptr<Ui::LeftOutlineButton> _commonGroups = { nullptr };
|
||||
|
||||
History *_history;
|
||||
History *_migrated;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -331,6 +331,10 @@ void ParticipantsBoxController::restoreState(
|
|||
}
|
||||
}
|
||||
|
||||
rpl::producer<int> ParticipantsBoxController::onlineCountValue() const {
|
||||
return _onlineCount.value();
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &participant, Role role, not_null<Additional*> additional, Callback callback) {
|
||||
if ((role == Role::Profile
|
||||
|
|
|
@ -95,9 +95,7 @@ public:
|
|||
not_null<Additional*> additional,
|
||||
Callback callback);
|
||||
|
||||
rpl::producer<int> onlineCountValue() const override {
|
||||
return _onlineCount.value();
|
||||
}
|
||||
rpl::producer<int> onlineCountValue() const override;
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
|
||||
|
|
|
@ -1,470 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_common_groups_section.h"
|
||||
|
||||
#include "profile/profile_section_memento.h"
|
||||
#include "profile/profile_back_button.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
namespace Profile {
|
||||
namespace CommonGroups {
|
||||
namespace {
|
||||
|
||||
constexpr int kCommonGroupsPerPage = 40;
|
||||
|
||||
} // namespace
|
||||
|
||||
object_ptr<Window::SectionWidget> SectionMemento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) {
|
||||
auto result = object_ptr<Widget>(parent, controller, _user);
|
||||
result->setInternalState(geometry, this);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
FixedBar::FixedBar(QWidget *parent) : TWidget(parent)
|
||||
, _backButton(this, lang(lng_profile_common_groups_section)) {
|
||||
_backButton->moveToLeft(0, 0);
|
||||
connect(_backButton, SIGNAL(clicked()), this, SLOT(onBack()));
|
||||
}
|
||||
|
||||
void FixedBar::onBack() {
|
||||
App::main()->showBackFromStack(Window::SectionShow());
|
||||
}
|
||||
|
||||
int FixedBar::resizeGetHeight(int newWidth) {
|
||||
auto newHeight = 0;
|
||||
|
||||
auto buttonLeft = newWidth;
|
||||
_backButton->resizeToWidth(newWidth);
|
||||
_backButton->moveToLeft(0, 0);
|
||||
newHeight += _backButton->height();
|
||||
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void FixedBar::setAnimatingMode(bool enabled) {
|
||||
if (_animatingMode != enabled) {
|
||||
_animatingMode = enabled;
|
||||
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
|
||||
if (_animatingMode) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||
hideChildren();
|
||||
} else {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
showChildren();
|
||||
}
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
onBack();
|
||||
} else {
|
||||
TWidget::mousePressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
InnerWidget::Item::Item(PeerData *peer) : peer(peer) {
|
||||
}
|
||||
|
||||
InnerWidget::Item::~Item() = default;
|
||||
|
||||
InnerWidget::InnerWidget(QWidget *parent, not_null<UserData*> user) : TWidget(parent)
|
||||
, _user(user) {
|
||||
setMouseTracking(true);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
_rowHeight = st::profileCommonGroupsPadding.top() + st::profileCommonGroupsPhotoSize + st::profileCommonGroupsPadding.bottom();
|
||||
_contentTop = st::profileCommonGroupsSkip;
|
||||
}
|
||||
|
||||
void InnerWidget::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
_visibleTop = visibleTop;
|
||||
_visibleBottom = visibleBottom;
|
||||
|
||||
checkPreloadMore();
|
||||
}
|
||||
|
||||
void InnerWidget::checkPreloadMore() {
|
||||
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) > height()) {
|
||||
preloadMore();
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
|
||||
if (auto count = _items.size()) {
|
||||
QList<not_null<PeerData*>> groups;
|
||||
groups.reserve(count);
|
||||
for_const (auto item, _items) {
|
||||
groups.push_back(item->peer);
|
||||
}
|
||||
memento->setCommonGroups(groups);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
|
||||
auto list = memento->getCommonGroups();
|
||||
_allLoaded = false;
|
||||
if (!list.empty()) {
|
||||
showInitial(list);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::showInitial(const QList<not_null<PeerData*>> &list) {
|
||||
for_const (auto group, list) {
|
||||
if (auto item = computeItem(group)) {
|
||||
_items.push_back(item);
|
||||
}
|
||||
_preloadGroupId = group->bareId();
|
||||
}
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void InnerWidget::preloadMore() {
|
||||
if (_preloadRequestId || _allLoaded) {
|
||||
return;
|
||||
}
|
||||
auto request = MTPmessages_GetCommonChats(user()->inputUser, MTP_int(_preloadGroupId), MTP_int(kCommonGroupsPerPage));
|
||||
_preloadRequestId = MTP::send(request, ::rpcDone(base::lambda_guarded(this, [this](const MTPmessages_Chats &result) {
|
||||
_preloadRequestId = 0;
|
||||
_preloadGroupId = 0;
|
||||
_allLoaded = true;
|
||||
if (auto chats = Api::getChatsFromMessagesChats(result)) {
|
||||
auto &list = chats->v;
|
||||
if (!list.empty()) {
|
||||
_items.reserve(_items.size() + list.size());
|
||||
for_const (auto &chatData, list) {
|
||||
if (auto chat = App::feedChat(chatData)) {
|
||||
auto found = false;
|
||||
for_const (auto item, _items) {
|
||||
if (item->peer == chat) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
if (auto item = computeItem(chat)) {
|
||||
_items.push_back(item);
|
||||
}
|
||||
}
|
||||
_preloadGroupId = chat->bareId();
|
||||
_allLoaded = false;
|
||||
}
|
||||
}
|
||||
updateSize();
|
||||
}
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
void InnerWidget::updateSize() {
|
||||
TWidget::resizeToWidth(width());
|
||||
checkPreloadMore();
|
||||
}
|
||||
|
||||
int InnerWidget::resizeGetHeight(int newWidth) {
|
||||
update();
|
||||
|
||||
auto contentLeftMin = st::profileCommonGroupsLeftMin;
|
||||
auto contentLeftMax = st::profileCommonGroupsLeftMax;
|
||||
auto widthWithMin = st::columnMinimalWidthMain;
|
||||
auto widthWithMax = st::profileCommonGroupsWidthMax + 2 * contentLeftMax;
|
||||
_contentLeft = anim::interpolate(contentLeftMax, contentLeftMin, qMax(widthWithMax - newWidth, 0) / float64(widthWithMax - widthWithMin));
|
||||
_contentWidth = qMin(newWidth - 2 * _contentLeft, st::profileCommonGroupsWidthMax);
|
||||
|
||||
auto newHeight = _contentTop;
|
||||
newHeight += _items.size() * _rowHeight;
|
||||
newHeight += st::profileCommonGroupsSkip;
|
||||
return qMax(newHeight, _minHeight);
|
||||
}
|
||||
|
||||
void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
auto ms = getms();
|
||||
auto clip = e->rect();
|
||||
p.fillRect(clip, st::profileBg);
|
||||
|
||||
auto from = floorclamp(clip.y() - _contentTop, _rowHeight, 0, _items.size());
|
||||
auto to = ceilclamp(clip.y() + clip.height() - _contentTop, _rowHeight, 0, _items.size());
|
||||
for (auto i = from; i != to; ++i) {
|
||||
paintRow(p, i, ms);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::paintRow(Painter &p, int index, TimeMs ms) {
|
||||
auto item = _items[index];
|
||||
auto selected = (_pressed >= 0) ? (index == _pressed) : (index == _selected);
|
||||
|
||||
auto x = _contentLeft;
|
||||
auto y = _contentTop + index * _rowHeight;
|
||||
if (selected) {
|
||||
p.fillRect(myrtlrect(x, y, _contentWidth, _rowHeight), st::profileCommonGroupsBgOver);
|
||||
}
|
||||
if (auto &ripple = item->ripple) {
|
||||
ripple->paint(p, x, y, width(), ms);
|
||||
if (ripple->empty()) {
|
||||
ripple.reset();
|
||||
}
|
||||
}
|
||||
|
||||
x += st::profileCommonGroupsPadding.left();
|
||||
y += st::profileCommonGroupsPadding.top();
|
||||
item->peer->paintUserpic(p, rtl() ? (width() - x - st::profileCommonGroupsPhotoSize) : x, y, st::profileCommonGroupsPhotoSize);
|
||||
|
||||
p.setPen(st::profileMemberNameFg);
|
||||
x += st::profileCommonGroupsPhotoSize + st::profileCommonGroupsNameLeft;
|
||||
y += st::profileCommonGroupsNameTop;
|
||||
auto nameWidth = _contentWidth - (x - _contentLeft) - st::profileCommonGroupsPadding.right();
|
||||
if (item->name.isEmpty()) {
|
||||
item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions);
|
||||
}
|
||||
_items[index]->name.drawLeftElided(p, x, y, nameWidth, width());
|
||||
}
|
||||
|
||||
void InnerWidget::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
emit cancelled();
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::updateSelected(QPoint localPos) {
|
||||
auto selected = -1;
|
||||
auto selectedKick = false;
|
||||
|
||||
if (rtl()) localPos.setX(width() - localPos.x());
|
||||
if (localPos.x() >= _contentLeft && localPos.x() < _contentLeft + _contentWidth && localPos.y() >= _contentTop) {
|
||||
selected = (localPos.y() - _contentTop) / _rowHeight;
|
||||
if (selected >= _items.size()) {
|
||||
selected = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (_selected != selected) {
|
||||
updateRow(_selected);
|
||||
_selected = selected;
|
||||
updateRow(_selected);
|
||||
if (_pressed < 0) {
|
||||
setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::updateRow(int index) {
|
||||
rtlupdate(_contentLeft, _contentTop + index * _rowHeight, _contentWidth, _rowHeight);
|
||||
}
|
||||
|
||||
void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
||||
_pressed = _selected;
|
||||
if (_pressed >= 0) {
|
||||
auto item = _items[_pressed];
|
||||
if (!item->ripple) {
|
||||
auto mask = Ui::RippleAnimation::rectMask(QSize(_contentWidth, _rowHeight));
|
||||
item->ripple = std::make_unique<Ui::RippleAnimation>(st::profileCommonGroupsRipple, std::move(mask), [this, index = _pressed] {
|
||||
updateRow(index);
|
||||
});
|
||||
}
|
||||
auto left = _contentLeft;
|
||||
auto top = _contentTop + _rowHeight * _pressed;
|
||||
item->ripple->add(e->pos() - QPoint(left, top));
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::mouseMoveEvent(QMouseEvent *e) {
|
||||
updateSelected(e->pos());
|
||||
}
|
||||
|
||||
void InnerWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
setCursor(_selected ? style::cur_pointer : style::cur_default);
|
||||
updateRow(_selected);
|
||||
updateRow(_pressed);
|
||||
auto pressed = std::exchange(_pressed, -1);
|
||||
if (pressed >= 0 && pressed < _items.size()) {
|
||||
if (auto &ripple = _items[pressed]->ripple) {
|
||||
ripple->lastStop();
|
||||
}
|
||||
if (pressed == _selected) {
|
||||
App::wnd()->controller()->showPeerHistory(
|
||||
_items[pressed]->peer,
|
||||
Window::SectionShow::Way::Forward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerWidget::Item *InnerWidget::computeItem(PeerData *group) {
|
||||
// Skip groups that migrated to supergroups.
|
||||
if (group->migrateTo()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = _dataMap.constFind(group);
|
||||
if (it == _dataMap.cend()) {
|
||||
it = _dataMap.insert(group, new Item(group));
|
||||
}
|
||||
return it.value();
|
||||
}
|
||||
|
||||
InnerWidget::~InnerWidget() {
|
||||
for (auto item : base::take(_dataMap)) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
Widget::Widget(QWidget *parent, not_null<Window::Controller*> controller, not_null<UserData*> user) : Window::SectionWidget(parent, controller)
|
||||
, _scroll(this, st::settingsScroll)
|
||||
, _fixedBar(this)
|
||||
, _fixedBarShadow(this) {
|
||||
_fixedBar->move(0, 0);
|
||||
_fixedBar->resizeToWidth(width());
|
||||
_fixedBar->show();
|
||||
|
||||
_fixedBarShadow->raise();
|
||||
updateAdaptiveLayout();
|
||||
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
|
||||
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, user));
|
||||
_scroll->move(0, _fixedBar->height());
|
||||
_scroll->show();
|
||||
|
||||
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack()));
|
||||
}
|
||||
|
||||
void Widget::updateAdaptiveLayout() {
|
||||
_fixedBarShadow->moveToLeft(Adaptive::OneColumn() ? 0 : st::lineWidth, _fixedBar->height());
|
||||
}
|
||||
|
||||
not_null<UserData*> Widget::user() const {
|
||||
return _inner->user();
|
||||
}
|
||||
|
||||
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||
if (params.withTopBarShadow) _fixedBarShadow->hide();
|
||||
auto result = myGrab(this);
|
||||
if (params.withTopBarShadow) _fixedBarShadow->show();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Widget::doSetInnerFocus() {
|
||||
_inner->setFocus();
|
||||
}
|
||||
|
||||
bool Widget::showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) {
|
||||
if (auto profileMemento = dynamic_cast<SectionMemento*>(memento.get())) {
|
||||
if (profileMemento->getUser() == user()) {
|
||||
restoreState(profileMemento);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento*> memento) {
|
||||
setGeometry(geometry);
|
||||
myEnsureResized(this);
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||
auto result = std::make_unique<SectionMemento>(user());
|
||||
saveState(result.get());
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
void Widget::saveState(not_null<SectionMemento*> memento) {
|
||||
memento->setScrollTop(_scroll->scrollTop());
|
||||
_inner->saveState(memento);
|
||||
}
|
||||
|
||||
void Widget::restoreState(not_null<SectionMemento*> memento) {
|
||||
_inner->restoreState(memento);
|
||||
auto scrollTop = memento->getScrollTop();
|
||||
_scroll->scrollToY(scrollTop);
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
}
|
||||
|
||||
void Widget::resizeEvent(QResizeEvent *e) {
|
||||
if (!width() || !height()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newScrollTop = _scroll->scrollTop() + topDelta();
|
||||
_fixedBar->resizeToWidth(width());
|
||||
_fixedBarShadow->resize(width(), st::lineWidth);
|
||||
|
||||
QSize scrollSize(width(), height() - _fixedBar->height());
|
||||
if (_scroll->size() != scrollSize) {
|
||||
_scroll->resize(scrollSize);
|
||||
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
|
||||
}
|
||||
|
||||
if (!_scroll->isHidden()) {
|
||||
if (topDelta()) {
|
||||
_scroll->scrollToY(newScrollTop);
|
||||
}
|
||||
int scrollTop = _scroll->scrollTop();
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::onScroll() {
|
||||
int scrollTop = _scroll->scrollTop();
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
}
|
||||
|
||||
void Widget::showAnimatedHook(
|
||||
const Window::SectionSlideParams ¶ms) {
|
||||
_fixedBar->setAnimatingMode(true);
|
||||
}
|
||||
|
||||
void Widget::showFinishedHook() {
|
||||
_fixedBar->setAnimatingMode(false);
|
||||
}
|
||||
|
||||
bool Widget::wheelEventFromFloatPlayer(QEvent *e) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect Widget::rectForFloatPlayer() const {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
} // namespace CommonGroups
|
||||
} // namespace Profile
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "window/section_widget.h"
|
||||
#include "window/section_memento.h"
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
class PlainShadow;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class BackButton;
|
||||
|
||||
namespace CommonGroups {
|
||||
|
||||
class SectionMemento : public Window::SectionMemento {
|
||||
public:
|
||||
SectionMemento(not_null<UserData*> user) : _user(user) {
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) override;
|
||||
|
||||
not_null<UserData*> getUser() const {
|
||||
return _user;
|
||||
}
|
||||
void setScrollTop(int scrollTop) {
|
||||
_scrollTop = scrollTop;
|
||||
}
|
||||
int getScrollTop() const {
|
||||
return _scrollTop;
|
||||
}
|
||||
void setCommonGroups(const QList<not_null<PeerData*>> &groups) {
|
||||
_commonGroups = groups;
|
||||
}
|
||||
const QList<not_null<PeerData*>> &getCommonGroups() const {
|
||||
return _commonGroups;
|
||||
}
|
||||
|
||||
private:
|
||||
not_null<UserData*> _user;
|
||||
int _scrollTop = 0;
|
||||
QList<not_null<PeerData*>> _commonGroups;
|
||||
|
||||
};
|
||||
|
||||
class FixedBar final : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FixedBar(QWidget *parent);
|
||||
|
||||
// When animating mode is enabled the content is hidden and the
|
||||
// whole fixed bar acts like a back button.
|
||||
void setAnimatingMode(bool enabled);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
public slots:
|
||||
void onBack();
|
||||
|
||||
private:
|
||||
object_ptr<BackButton> _backButton;
|
||||
|
||||
bool _animatingMode = false;
|
||||
|
||||
};
|
||||
|
||||
class InnerWidget final : public TWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InnerWidget(QWidget *parent, not_null<UserData*> user);
|
||||
|
||||
not_null<UserData*> user() const {
|
||||
return _user;
|
||||
}
|
||||
|
||||
void resizeToWidth(int newWidth, int minHeight) {
|
||||
_minHeight = minHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
void saveState(not_null<SectionMemento*> memento);
|
||||
void restoreState(not_null<SectionMemento*> memento);
|
||||
|
||||
~InnerWidget();
|
||||
|
||||
signals:
|
||||
void cancelled();
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateSelected(QPoint localPos);
|
||||
void updateRow(int index);
|
||||
void showInitial(const QList<not_null<PeerData*>> &list);
|
||||
void checkPreloadMore();
|
||||
void preloadMore();
|
||||
void updateSize();
|
||||
void paintRow(Painter &p, int index, TimeMs ms);
|
||||
|
||||
not_null<UserData*> _user;
|
||||
|
||||
int _minHeight = 0;
|
||||
int _rowHeight = 0;
|
||||
int _contentLeft = 0;
|
||||
int _contentTop = 0;
|
||||
int _contentWidth = 0;
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
|
||||
struct Item {
|
||||
explicit Item(PeerData *peer);
|
||||
~Item();
|
||||
|
||||
PeerData * const peer;
|
||||
Text name;
|
||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||
};
|
||||
Item *computeItem(PeerData *group);
|
||||
QMap<PeerData*, Item*> _dataMap;
|
||||
QList<Item*> _items;
|
||||
int _selected = -1;
|
||||
int _pressed = -1;
|
||||
|
||||
int32 _preloadGroupId = 0;
|
||||
mtpRequestId _preloadRequestId = 0;
|
||||
bool _allLoaded = true;
|
||||
|
||||
};
|
||||
|
||||
class Widget final : public Window::SectionWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget(QWidget *parent, not_null<Window::Controller*> controller, not_null<UserData*> user);
|
||||
|
||||
not_null<UserData*> user() const;
|
||||
PeerData *activePeer() const override {
|
||||
return user();
|
||||
}
|
||||
|
||||
bool hasTopBarShadow() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
QPixmap grabForShowAnimation(const Window::SectionSlideParams ¶ms) override;
|
||||
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
void setInternalState(const QRect &geometry, not_null<SectionMemento*> memento);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e) override;
|
||||
QRect rectForFloatPlayer() const override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
void showAnimatedHook(
|
||||
const Window::SectionSlideParams ¶ms) override;
|
||||
void showFinishedHook() override;
|
||||
void doSetInnerFocus() override;
|
||||
|
||||
private slots:
|
||||
void onScroll();
|
||||
|
||||
private:
|
||||
void updateAdaptiveLayout();
|
||||
void saveState(not_null<SectionMemento*> memento);
|
||||
void restoreState(not_null<SectionMemento*> memento);
|
||||
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<InnerWidget> _inner;
|
||||
object_ptr<FixedBar> _fixedBar;
|
||||
object_ptr<Ui::PlainShadow> _fixedBarShadow;
|
||||
|
||||
};
|
||||
|
||||
} // namespace CommonGroups
|
||||
} // namespace Profile
|
|
@ -1,570 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_cover.h"
|
||||
|
||||
#include "data/data_photo.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "profile/profile_cover_drop_area.h"
|
||||
#include "profile/profile_userpic_button.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "observer_peer.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/photo_crop_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "messenger.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "window/main_window.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
namespace Profile {
|
||||
namespace {
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact
|
||||
| UpdateFlag::BotCanAddToGroups
|
||||
| UpdateFlag::ChatCanEdit
|
||||
| UpdateFlag::ChannelRightsChanged
|
||||
| UpdateFlag::ChannelAmIn;
|
||||
|
||||
} // namespace
|
||||
|
||||
CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||
, _peer(peer)
|
||||
, _peerUser(peer->asUser())
|
||||
, _peerChat(peer->asChat())
|
||||
, _peerChannel(peer->asChannel())
|
||||
, _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr)
|
||||
, _userpicButton(this, peer)
|
||||
, _name(this, st::profileNameLabel) {
|
||||
_peer->updateFull();
|
||||
|
||||
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
||||
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setAcceptDrops(true);
|
||||
|
||||
_name->setSelectable(true);
|
||||
_name->setContextCopyText(lang(lng_profile_copy_fullname));
|
||||
|
||||
auto observeEvents = ButtonsUpdateFlags
|
||||
| UpdateFlag::NameChanged
|
||||
| UpdateFlag::UserOnlineChanged
|
||||
| UpdateFlag::MembersChanged
|
||||
| UpdateFlag::PhotoChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
|
||||
connect(&Messenger::Instance(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
|
||||
connect(&Messenger::Instance(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
|
||||
|
||||
connect(_userpicButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
|
||||
validatePhoto();
|
||||
|
||||
refreshNameText();
|
||||
refreshStatusText();
|
||||
|
||||
refreshButtons();
|
||||
}
|
||||
|
||||
void CoverWidget::refreshLang() {
|
||||
InvokeQueued(this, [this] { moveAndToggleButtons(width()); });
|
||||
}
|
||||
|
||||
PhotoData *CoverWidget::validatePhoto() const {
|
||||
auto photo = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId) ? App::photo(_peer->photoId) : nullptr;
|
||||
_userpicButton->setPointerCursor(photo != nullptr && photo->date != 0);
|
||||
if ((_peer->photoId == UnknownPeerPhotoId) || (_peer->photoId && (!photo || !photo->date))) {
|
||||
Auth().api().requestFullPeer(_peer);
|
||||
return nullptr;
|
||||
}
|
||||
return photo;
|
||||
}
|
||||
|
||||
void CoverWidget::onPhotoShow() {
|
||||
if (auto photo = validatePhoto()) {
|
||||
Messenger::Instance().showPhoto(photo, _peer);
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::onCancelPhotoUpload() {
|
||||
Messenger::Instance().cancelPhotoUpdate(_peer->id);
|
||||
refreshStatusText();
|
||||
}
|
||||
|
||||
int CoverWidget::countPhotoLeft(int newWidth) const {
|
||||
int result = st::profilePhotoLeftMin;
|
||||
result += (newWidth - st::columnMinimalWidthMain) / 2;
|
||||
return qMin(result, st::profilePhotoLeftMax);
|
||||
}
|
||||
|
||||
int CoverWidget::resizeGetHeight(int newWidth) {
|
||||
int newHeight = 0;
|
||||
|
||||
newHeight += st::profileMarginTop;
|
||||
|
||||
_photoLeft = countPhotoLeft(newWidth);
|
||||
_userpicButton->moveToLeft(_photoLeft, newHeight);
|
||||
|
||||
refreshNameGeometry(newWidth);
|
||||
|
||||
int infoLeft = _userpicButton->x() + _userpicButton->width();
|
||||
_statusPosition = QPoint(infoLeft + st::profileStatusLeft, _userpicButton->y() + st::profileStatusTop);
|
||||
if (_cancelPhotoUpload) {
|
||||
_cancelPhotoUpload->moveToLeft(_statusPosition.x() + st::profileStatusFont->width(_statusText) + st::profileStatusFont->spacew, _statusPosition.y());
|
||||
}
|
||||
|
||||
moveAndToggleButtons(newWidth);
|
||||
|
||||
newHeight += st::profilePhotoSize;
|
||||
newHeight += st::profileMarginBottom;
|
||||
|
||||
_dividerTop = newHeight;
|
||||
newHeight += st::profileDividerLeft.height();
|
||||
|
||||
newHeight += st::profileBlocksTop;
|
||||
|
||||
resizeDropArea(newWidth);
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void CoverWidget::refreshNameGeometry(int newWidth) {
|
||||
int infoLeft = _userpicButton->x() + _userpicButton->width();
|
||||
int nameLeft = infoLeft + st::profileNameLeft - st::profileNameLabel.margin.left();
|
||||
int nameTop = _userpicButton->y() + st::profileNameTop - st::profileNameLabel.margin.top();
|
||||
int nameWidth = newWidth - infoLeft - st::profileNameLeft;
|
||||
if (_peer->isVerified()) {
|
||||
nameWidth -= st::profileVerifiedCheckShift + st::profileVerifiedCheck.width();
|
||||
}
|
||||
int marginsAdd = st::profileNameLabel.margin.left() + st::profileNameLabel.margin.right();
|
||||
_name->resizeToWidth(qMin(nameWidth - marginsAdd, _name->naturalWidth()) + marginsAdd);
|
||||
_name->moveToLeft(nameLeft, nameTop);
|
||||
}
|
||||
|
||||
// A more generic solution would be allowing an optional icon button
|
||||
// for each text button. But currently we use only one, so it is done easily:
|
||||
// There can be primary + secondary + icon buttons. If primary + secondary fit,
|
||||
// then icon is hidden, otherwise secondary is hidden and icon is shown.
|
||||
void CoverWidget::moveAndToggleButtons(int newWidth) {
|
||||
int buttonLeft = _userpicButton->x() + _userpicButton->width() + st::profileButtonLeft;
|
||||
int buttonsRight = newWidth - st::profileButtonSkip;
|
||||
for (int i = 0, count = _buttons.size(); i < count; ++i) {
|
||||
auto &button = _buttons.at(i);
|
||||
button.widget->moveToLeft(buttonLeft, st::profileButtonTop);
|
||||
if (button.replacement) {
|
||||
button.replacement->moveToLeft(buttonLeft, st::profileButtonTop);
|
||||
if (buttonLeft + button.widget->width() > buttonsRight) {
|
||||
button.widget->hide();
|
||||
button.replacement->show();
|
||||
buttonLeft += button.replacement->width() + st::profileButtonSkip;
|
||||
} else {
|
||||
button.widget->show();
|
||||
button.replacement->hide();
|
||||
buttonLeft += button.widget->width() + st::profileButtonSkip;
|
||||
}
|
||||
} else if (i == 1 && (buttonLeft + button.widget->width() > buttonsRight)) {
|
||||
// If second button is not fitting.
|
||||
button.widget->hide();
|
||||
} else {
|
||||
button.widget->show();
|
||||
buttonLeft += button.widget->width() + st::profileButtonSkip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::showFinished() {
|
||||
_userpicButton->showFinished();
|
||||
}
|
||||
|
||||
bool CoverWidget::shareContactButtonShown() const {
|
||||
return _peerUser && (_buttons.size() > 1) && !(_buttons.at(1).widget->isHidden());
|
||||
}
|
||||
|
||||
void CoverWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
p.fillRect(e->rect(), st::profileBg);
|
||||
|
||||
p.setFont(st::profileStatusFont);
|
||||
p.setPen(_statusTextIsOnline ? st::profileStatusFgActive : st::profileStatusFg);
|
||||
p.drawTextLeft(_statusPosition.x(), _statusPosition.y(), width(), _statusText);
|
||||
|
||||
if (_peer->isVerified()) {
|
||||
st::profileVerifiedCheck.paint(p, _name->x() + _name->width() + st::profileVerifiedCheckShift, _name->y(), width());
|
||||
}
|
||||
|
||||
paintDivider(p);
|
||||
}
|
||||
|
||||
void CoverWidget::resizeDropArea(int newWidth) {
|
||||
if (_dropArea) {
|
||||
_dropArea->setGeometry(0, 0, newWidth, _dividerTop);
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::dropAreaHidden(CoverDropArea *dropArea) {
|
||||
if (_dropArea == dropArea) {
|
||||
_dropArea.destroyDelayed();
|
||||
}
|
||||
}
|
||||
|
||||
bool CoverWidget::canEditPhoto() const {
|
||||
if (_peerChat && _peerChat->canEdit()) {
|
||||
return true;
|
||||
} else if (_peerMegagroup && _peerMegagroup->canEditInformation()) {
|
||||
return true;
|
||||
} else if (_peerChannel && _peerChannel->canEditInformation()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CoverWidget::mimeDataHasImage(const QMimeData *mimeData) const {
|
||||
if (!mimeData) return false;
|
||||
|
||||
if (mimeData->hasImage()) return true;
|
||||
|
||||
auto uriListFormat = qsl("text/uri-list");
|
||||
if (!mimeData->hasFormat(uriListFormat)) return false;
|
||||
|
||||
const auto &urls = mimeData->urls();
|
||||
if (urls.size() != 1) return false;
|
||||
|
||||
auto &url = urls.at(0);
|
||||
if (!url.isLocalFile()) return false;
|
||||
|
||||
auto file = Platform::File::UrlToLocal(url);
|
||||
|
||||
QFileInfo info(file);
|
||||
if (info.isDir()) return false;
|
||||
|
||||
if (info.size() > App::kImageSizeLimit) return false;
|
||||
|
||||
for (auto &ext : cImgExtensions()) {
|
||||
if (file.endsWith(ext, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CoverWidget::dragEnterEvent(QDragEnterEvent *e) {
|
||||
if (!canEditPhoto() || !mimeDataHasImage(e->mimeData())) {
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
if (!_dropArea) {
|
||||
auto title = lang(lng_profile_drop_area_title);
|
||||
QString subtitle;
|
||||
if (_peerChat || _peerMegagroup) {
|
||||
subtitle = lang(lng_profile_drop_area_subtitle);
|
||||
} else {
|
||||
subtitle = lang(lng_profile_drop_area_subtitle_channel);
|
||||
}
|
||||
_dropArea.create(this, title, subtitle);
|
||||
resizeDropArea(width());
|
||||
}
|
||||
_dropArea->showAnimated();
|
||||
e->setDropAction(Qt::CopyAction);
|
||||
e->accept();
|
||||
}
|
||||
|
||||
void CoverWidget::dragLeaveEvent(QDragLeaveEvent *e) {
|
||||
if (_dropArea && !_dropArea->hiding()) {
|
||||
_dropArea->hideAnimated([this](CoverDropArea *area) { dropAreaHidden(area); });
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::dropEvent(QDropEvent *e) {
|
||||
auto mimeData = e->mimeData();
|
||||
|
||||
QImage img;
|
||||
if (mimeData->hasImage()) {
|
||||
img = qvariant_cast<QImage>(mimeData->imageData());
|
||||
} else {
|
||||
const auto &urls = mimeData->urls();
|
||||
if (urls.size() == 1) {
|
||||
auto &url = urls.at(0);
|
||||
if (url.isLocalFile()) {
|
||||
img = App::readImage(Platform::File::UrlToLocal(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_dropArea->hiding()) {
|
||||
_dropArea->hideAnimated([this](CoverDropArea *area) { dropAreaHidden(area); });
|
||||
}
|
||||
e->acceptProposedAction();
|
||||
|
||||
showSetPhotoBox(img);
|
||||
}
|
||||
|
||||
void CoverWidget::paintDivider(Painter &p) {
|
||||
auto dividerHeight = st::profileDividerLeft.height();
|
||||
auto dividerLeft = Adaptive::OneColumn() ? 0 : st::lineWidth;
|
||||
auto divider = rtlrect(dividerLeft, _dividerTop, width() - dividerLeft, dividerHeight, width());
|
||||
p.fillRect(divider, st::profileDividerBg);
|
||||
if (!Adaptive::OneColumn()) {
|
||||
st::profileDividerLeft.paint(p, QPoint(dividerLeft, _dividerTop), width());
|
||||
}
|
||||
auto dividerFillLeft = Adaptive::OneColumn() ? 0 : (st::lineWidth + st::profileDividerLeft.width());
|
||||
auto dividerFillTop = rtlrect(dividerFillLeft, _dividerTop, width() - dividerFillLeft, st::profileDividerTop.height(), width());
|
||||
st::profileDividerTop.fill(p, dividerFillTop);
|
||||
auto dividerFillBottom = rtlrect(dividerFillLeft, _dividerTop + dividerHeight - st::profileDividerBottom.height(), width() - dividerFillLeft, st::profileDividerBottom.height(), width());
|
||||
st::profileDividerBottom.fill(p, dividerFillBottom);
|
||||
}
|
||||
|
||||
void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != _peer) {
|
||||
return;
|
||||
}
|
||||
if ((update.flags & ButtonsUpdateFlags) != 0) {
|
||||
refreshButtons();
|
||||
}
|
||||
if (update.flags & UpdateFlag::NameChanged) {
|
||||
refreshNameText();
|
||||
}
|
||||
if (update.flags & UpdateFlag::PhotoChanged) {
|
||||
validatePhoto();
|
||||
}
|
||||
if (update.flags & (UpdateFlag::UserOnlineChanged | UpdateFlag::MembersChanged)) {
|
||||
refreshStatusText();
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::refreshNameText() {
|
||||
_name->setText(App::peerName(_peer));
|
||||
refreshNameGeometry(width());
|
||||
}
|
||||
|
||||
void CoverWidget::refreshStatusText() {
|
||||
if (Messenger::Instance().isPhotoUpdating(_peer->id)) {
|
||||
_statusText = lang(lng_settings_uploading_photo);
|
||||
_statusTextIsOnline = false;
|
||||
if (!_cancelPhotoUpload) {
|
||||
_cancelPhotoUpload.create(this, lang(lng_cancel), st::defaultLinkButton);
|
||||
connect(_cancelPhotoUpload, SIGNAL(clicked()), this, SLOT(onCancelPhotoUpload()));
|
||||
_cancelPhotoUpload->show();
|
||||
_cancelPhotoUpload->moveToLeft(_statusPosition.x() + st::profileStatusFont->width(_statusText) + st::profileStatusFont->spacew, _statusPosition.y());
|
||||
}
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
_cancelPhotoUpload.destroy();
|
||||
auto currentTime = unixtime();
|
||||
if (_peerUser) {
|
||||
_statusText = App::onlineText(_peerUser, currentTime, true);
|
||||
_statusTextIsOnline = App::onlineColorUse(_peerUser, currentTime);
|
||||
} else if (_peerChat && _peerChat->amIn()) {
|
||||
auto fullCount = qMax(_peerChat->count, _peerChat->participants.size());
|
||||
if (_onlineCount > 0 && _onlineCount <= fullCount) {
|
||||
auto membersCount = lng_chat_status_members(lt_count, fullCount);
|
||||
auto onlineCount = lng_chat_status_online(lt_count, _onlineCount);
|
||||
_statusText = lng_chat_status_members_online(lt_members_count, membersCount, lt_online_count, onlineCount);
|
||||
} else if (_peerChat->count > 0) {
|
||||
_statusText = lng_chat_status_members(lt_count, _peerChat->count);
|
||||
} else {
|
||||
_statusText = lang(lng_group_status);
|
||||
}
|
||||
} else if (_peerChannel) {
|
||||
auto fullCount = _peerChannel->membersCount();
|
||||
if (_onlineCount > 0 && _onlineCount <= fullCount) {
|
||||
auto membersCount = lng_chat_status_members(lt_count, fullCount);
|
||||
auto onlineCount = lng_chat_status_online(lt_count, _onlineCount);
|
||||
_statusText = lng_chat_status_members_online(lt_members_count, membersCount, lt_online_count, onlineCount);
|
||||
} else if (fullCount > 0) {
|
||||
_statusText = lng_chat_status_members(lt_count, fullCount);
|
||||
} else {
|
||||
_statusText = lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status);
|
||||
}
|
||||
} else {
|
||||
_statusText = lang(lng_chat_status_unaccessible);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void CoverWidget::refreshButtons() {
|
||||
clearButtons();
|
||||
if (_peerUser) {
|
||||
setUserButtons();
|
||||
} else if (_peerChat) {
|
||||
setChatButtons();
|
||||
} else if (_peerMegagroup) {
|
||||
setMegagroupButtons();
|
||||
} else if (_peerChannel) {
|
||||
setChannelButtons();
|
||||
}
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
void CoverWidget::setUserButtons() {
|
||||
addButton(langFactory(lng_profile_send_message), SLOT(onSendMessage()));
|
||||
if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) {
|
||||
addButton(langFactory(lng_profile_invite_to_group), SLOT(onAddBotToGroup()), &st::profileAddMemberButton);
|
||||
} else if (_peerUser->canShareThisContact()) {
|
||||
addButton(langFactory(lng_profile_share_contact), SLOT(onShareContact()));
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::setChatButtons() {
|
||||
if (_peerChat->canEdit()) {
|
||||
addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||
addButton(langFactory(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton);
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::setMegagroupButtons() {
|
||||
if (_peerMegagroup->amIn()) {
|
||||
if (canEditPhoto()) {
|
||||
addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||
}
|
||||
} else {
|
||||
addButton(langFactory(lng_profile_join_channel), SLOT(onJoin()));
|
||||
}
|
||||
if (_peerMegagroup->canAddMembers()) {
|
||||
addButton(langFactory(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton);
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::setChannelButtons() {
|
||||
if (canEditPhoto()) {
|
||||
addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||
} else if (_peerChannel->amIn()) {
|
||||
addButton(langFactory(lng_profile_view_channel), SLOT(onViewChannel()));
|
||||
} else {
|
||||
addButton(langFactory(lng_profile_join_channel), SLOT(onJoin()));
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::clearButtons() {
|
||||
auto buttons = base::take(_buttons);
|
||||
for_const (auto button, buttons) {
|
||||
delete button.widget;
|
||||
delete button.replacement;
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::addButton(base::lambda<QString()> textFactory, const char *slot, const style::RoundButton *replacementStyle) {
|
||||
auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton;
|
||||
auto button = new Ui::RoundButton(this, std::move(textFactory), buttonStyle);
|
||||
connect(button, SIGNAL(clicked()), this, slot);
|
||||
button->show();
|
||||
|
||||
auto replacement = replacementStyle ? new Ui::RoundButton(this, base::lambda<QString()>(), *replacementStyle) : nullptr;
|
||||
if (replacement) {
|
||||
connect(replacement, SIGNAL(clicked()), this, slot);
|
||||
replacement->hide();
|
||||
}
|
||||
|
||||
_buttons.push_back({ button, replacement });
|
||||
}
|
||||
|
||||
void CoverWidget::onOnlineCountUpdated(int onlineCount) {
|
||||
_onlineCount = onlineCount;
|
||||
refreshStatusText();
|
||||
}
|
||||
|
||||
void CoverWidget::onSendMessage() {
|
||||
App::wnd()->controller()->showPeerHistory(
|
||||
_peer,
|
||||
Window::SectionShow::Way::Forward);
|
||||
}
|
||||
|
||||
void CoverWidget::onShareContact() {
|
||||
App::main()->shareContactLayer(_peerUser);
|
||||
}
|
||||
|
||||
void CoverWidget::onSetPhoto() {
|
||||
App::CallDelayed(st::profilePrimaryButton.ripple.hideDuration, this, [this] {
|
||||
auto imgExtensions = cImgExtensions();
|
||||
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + FileDialog::AllFilesFilter();
|
||||
FileDialog::GetOpenPath(lang(lng_choose_image), filter, base::lambda_guarded(this, [this](const FileDialog::OpenResult &result) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QImage img;
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
img = App::readImage(result.remoteContent);
|
||||
} else {
|
||||
img = App::readImage(result.paths.front());
|
||||
}
|
||||
|
||||
showSetPhotoBox(img);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void CoverWidget::showSetPhotoBox(const QImage &img) {
|
||||
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
|
||||
Ui::show(Box<InformBox>(lang(lng_bad_photo)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto box = Ui::show(Box<PhotoCropBox>(img, _peer));
|
||||
subscribe(box->boxClosing, [this] { onPhotoUploadStatusChanged(); });
|
||||
}
|
||||
|
||||
void CoverWidget::onPhotoUploadStatusChanged(PeerId peerId) {
|
||||
if (!peerId || peerId == _peer->id) {
|
||||
refreshStatusText();
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::onAddMember() {
|
||||
if (_peerChat) {
|
||||
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
|
||||
Ui::show(Box<ConvertToSupergroupBox>(_peerChat));
|
||||
} else {
|
||||
AddParticipantsBoxController::Start(_peerChat);
|
||||
}
|
||||
} else if (_peerChannel && _peerChannel->mgInfo) {
|
||||
auto &participants = _peerChannel->mgInfo->lastParticipants;
|
||||
AddParticipantsBoxController::Start(_peerChannel, { participants.cbegin(), participants.cend() });
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::onAddBotToGroup() {
|
||||
if (_peerUser && _peerUser->botInfo) {
|
||||
AddBotToGroupBoxController::Start(_peerUser);
|
||||
}
|
||||
}
|
||||
|
||||
void CoverWidget::onJoin() {
|
||||
if (!_peerChannel) return;
|
||||
|
||||
Auth().api().joinChannel(_peerChannel);
|
||||
}
|
||||
|
||||
void CoverWidget::onViewChannel() {
|
||||
Ui::showPeerHistory(_peer, ShowAtUnreadMsgId);
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "base/observer.h"
|
||||
|
||||
namespace style {
|
||||
struct RoundButton;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
class RoundButton;
|
||||
class LinkButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class BackButton;
|
||||
class UserpicButton;
|
||||
class CoverDropArea;
|
||||
|
||||
class CoverWidget final : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CoverWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
void showFinished();
|
||||
|
||||
// Profile fixed top bar should use this flag to decide
|
||||
// if it shows "Share contact" button or not.
|
||||
// It should show it only if it is hidden in the cover.
|
||||
bool shareContactButtonShown() const;
|
||||
|
||||
public slots:
|
||||
void onOnlineCountUpdated(int onlineCount);
|
||||
|
||||
private slots:
|
||||
void onPhotoShow();
|
||||
void onPhotoUploadStatusChanged(PeerId peerId = 0);
|
||||
void onCancelPhotoUpload();
|
||||
|
||||
void onSendMessage();
|
||||
void onShareContact();
|
||||
void onSetPhoto();
|
||||
void onAddMember();
|
||||
void onAddBotToGroup();
|
||||
void onJoin();
|
||||
void onViewChannel();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void dragEnterEvent(QDragEnterEvent *e) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *e) override;
|
||||
void dropEvent(QDropEvent *e) override;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
void refreshLang();
|
||||
|
||||
// Observed notifications.
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
// Counts userpic button left offset for a new widget width.
|
||||
int countPhotoLeft(int newWidth) const;
|
||||
PhotoData *validatePhoto() const;
|
||||
|
||||
void refreshNameGeometry(int newWidth);
|
||||
void moveAndToggleButtons(int newWidth);
|
||||
void refreshNameText();
|
||||
void refreshStatusText();
|
||||
|
||||
void refreshButtons();
|
||||
void setUserButtons();
|
||||
void setChatButtons();
|
||||
void setMegagroupButtons();
|
||||
void setChannelButtons();
|
||||
|
||||
void clearButtons();
|
||||
void addButton(base::lambda<QString()> textFactory, const char *slot, const style::RoundButton *replacementStyle = nullptr);
|
||||
|
||||
void paintDivider(Painter &p);
|
||||
|
||||
bool canEditPhoto() const;
|
||||
void showSetPhotoBox(const QImage &img);
|
||||
void resizeDropArea(int newWidth);
|
||||
void dropAreaHidden(CoverDropArea *dropArea);
|
||||
bool mimeDataHasImage(const QMimeData *mimeData) const;
|
||||
|
||||
PeerData *_peer;
|
||||
UserData *_peerUser;
|
||||
ChatData *_peerChat;
|
||||
ChannelData *_peerChannel;
|
||||
ChannelData *_peerMegagroup;
|
||||
|
||||
object_ptr<UserpicButton> _userpicButton;
|
||||
object_ptr<CoverDropArea> _dropArea = { nullptr };
|
||||
|
||||
object_ptr<Ui::FlatLabel> _name;
|
||||
object_ptr<Ui::LinkButton> _cancelPhotoUpload = { nullptr };
|
||||
|
||||
QPoint _statusPosition;
|
||||
QString _statusText;
|
||||
bool _statusTextIsOnline = false;
|
||||
|
||||
struct Button {
|
||||
Ui::RoundButton *widget;
|
||||
Ui::RoundButton *replacement;
|
||||
};
|
||||
QList<Button> _buttons;
|
||||
|
||||
int _photoLeft = 0; // Caching countPhotoLeft() result.
|
||||
int _dividerTop = 0;
|
||||
|
||||
int _onlineCount = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,262 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_fixed_bar.h"
|
||||
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "observer_peer.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "profile/profile_back_button.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
namespace Profile {
|
||||
namespace {
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact
|
||||
| UpdateFlag::UserIsContact
|
||||
| UpdateFlag::ChatCanEdit
|
||||
| UpdateFlag::ChannelRightsChanged;
|
||||
|
||||
} // namespace
|
||||
|
||||
FixedBar::FixedBar(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||
, _peer(peer)
|
||||
, _peerUser(peer->asUser())
|
||||
, _peerChat(peer->asChat())
|
||||
, _peerChannel(peer->asChannel())
|
||||
, _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr)
|
||||
, _backButton(this, lang(lng_menu_back)) {
|
||||
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
||||
|
||||
_backButton->moveToLeft(0, 0);
|
||||
connect(_backButton, SIGNAL(clicked()), this, SLOT(onBack()));
|
||||
|
||||
auto observeEvents = ButtonsUpdateFlags
|
||||
| UpdateFlag::MigrationChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdate(update);
|
||||
}));
|
||||
|
||||
refreshRightActions();
|
||||
}
|
||||
|
||||
void FixedBar::notifyPeerUpdate(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != _peer) {
|
||||
return;
|
||||
}
|
||||
if ((update.flags & ButtonsUpdateFlags) != 0) {
|
||||
refreshRightActions();
|
||||
}
|
||||
if (update.flags & UpdateFlag::MigrationChanged) {
|
||||
if (_peerChat && _peerChat->migrateTo()) {
|
||||
auto channel = _peerChat->migrateTo();
|
||||
onBack();
|
||||
Ui::showPeerProfile(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::refreshRightActions() {
|
||||
_currentAction = 0;
|
||||
if (_peerUser) {
|
||||
setUserActions();
|
||||
} else if (_peerChat) {
|
||||
setChatActions();
|
||||
} else if (_peerMegagroup) {
|
||||
setMegagroupActions();
|
||||
} else if (_peerChannel) {
|
||||
setChannelActions();
|
||||
}
|
||||
while (_rightActions.size() > _currentAction) {
|
||||
delete _rightActions.back().button;
|
||||
_rightActions.pop_back();
|
||||
}
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
void FixedBar::setUserActions() {
|
||||
if (_peerUser->canShareThisContact()) {
|
||||
addRightAction(RightActionType::ShareContact, langFactory(lng_profile_top_bar_share_contact), SLOT(onShareContact()));
|
||||
}
|
||||
if (_peerUser->isContact()) {
|
||||
addRightAction(RightActionType::EditContact, langFactory(lng_profile_edit_contact), SLOT(onEditContact()));
|
||||
addRightAction(RightActionType::DeleteContact, langFactory(lng_profile_delete_contact), SLOT(onDeleteContact()));
|
||||
} else if (_peerUser->canAddContact()) {
|
||||
addRightAction(RightActionType::AddContact, langFactory(lng_profile_add_contact), SLOT(onAddContact()));
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::setChatActions() {
|
||||
if (_peerChat->canEdit()) {
|
||||
addRightAction(RightActionType::EditGroup, langFactory(lng_profile_edit_contact), SLOT(onEditGroup()));
|
||||
}
|
||||
addRightAction(RightActionType::LeaveGroup, langFactory(lng_profile_delete_and_exit), SLOT(onLeaveGroup()));
|
||||
}
|
||||
|
||||
void FixedBar::setMegagroupActions() {
|
||||
if (_peerMegagroup->canEditInformation()) {
|
||||
addRightAction(RightActionType::EditChannel, langFactory(lng_profile_edit_contact), SLOT(onEditChannel()));
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::setChannelActions() {
|
||||
if (_peerChannel->canEditInformation()) {
|
||||
addRightAction(RightActionType::EditChannel, langFactory(lng_profile_edit_contact), SLOT(onEditChannel()));
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::addRightAction(RightActionType type, base::lambda<QString()> textFactory, const char *slot) {
|
||||
if (_rightActions.size() > _currentAction) {
|
||||
if (_rightActions.at(_currentAction).type == type) {
|
||||
++_currentAction;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Assert(_rightActions.size() == _currentAction);
|
||||
_rightActions.push_back(RightAction());
|
||||
}
|
||||
_rightActions[_currentAction].type = type;
|
||||
delete _rightActions[_currentAction].button;
|
||||
_rightActions[_currentAction].button = new Ui::RoundButton(this, std::move(textFactory), st::profileFixedBarButton);
|
||||
connect(_rightActions[_currentAction].button, SIGNAL(clicked()), this, slot);
|
||||
bool showButton = !_animatingMode && (type != RightActionType::ShareContact || !_hideShareContactButton);
|
||||
_rightActions[_currentAction].button->setVisible(showButton);
|
||||
++_currentAction;
|
||||
}
|
||||
|
||||
void FixedBar::onBack() {
|
||||
App::main()->showBackFromStack(Window::SectionShow());
|
||||
}
|
||||
|
||||
void FixedBar::onEditChannel() {
|
||||
Ui::show(Box<EditChannelBox>(_peerMegagroup ? _peerMegagroup : _peerChannel));
|
||||
}
|
||||
|
||||
void FixedBar::onEditGroup() {
|
||||
Ui::show(Box<EditNameTitleBox>(_peerChat));
|
||||
}
|
||||
|
||||
void FixedBar::onAddContact() {
|
||||
auto firstName = _peerUser->firstName;
|
||||
auto lastName = _peerUser->lastName;
|
||||
auto phone = _peerUser->phone().isEmpty() ? App::phoneFromSharedContact(peerToUser(_peer->id)) : _peerUser->phone();
|
||||
Ui::show(Box<AddContactBox>(firstName, lastName, phone));
|
||||
}
|
||||
|
||||
void FixedBar::onEditContact() {
|
||||
Ui::show(Box<AddContactBox>(_peerUser));
|
||||
}
|
||||
|
||||
void FixedBar::onShareContact() {
|
||||
App::main()->shareContactLayer(_peerUser);
|
||||
}
|
||||
|
||||
void FixedBar::onDeleteContact() {
|
||||
auto text = lng_sure_delete_contact(lt_contact, App::peerName(_peerUser));
|
||||
Ui::show(Box<ConfirmBox>(text, lang(lng_box_delete), base::lambda_guarded(this, [this] {
|
||||
Ui::showChatsList();
|
||||
Ui::hideLayer();
|
||||
MTP::send(MTPcontacts_DeleteContact(_peerUser->inputUser), App::main()->rpcDone(&MainWidget::deletedContact, _peerUser));
|
||||
})));
|
||||
}
|
||||
|
||||
void FixedBar::onLeaveGroup() {
|
||||
auto text = lng_sure_delete_and_exit(lt_group, App::peerName(_peerChat));
|
||||
Ui::show(Box<ConfirmBox>(text, lang(lng_box_leave), st::attentionBoxButton, base::lambda_guarded(this, [this] {
|
||||
Ui::showChatsList();
|
||||
Ui::hideLayer();
|
||||
App::main()->deleteAndExit(_peerChat);
|
||||
})));
|
||||
}
|
||||
|
||||
int FixedBar::resizeGetHeight(int newWidth) {
|
||||
int newHeight = 0;
|
||||
|
||||
updateButtonsGeometry(newWidth);
|
||||
|
||||
_backButton->resizeToWidth(newWidth);
|
||||
_backButton->moveToLeft(0, 0);
|
||||
newHeight += _backButton->height();
|
||||
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void FixedBar::updateButtonsGeometry(int newWidth) {
|
||||
int buttonLeft = newWidth;
|
||||
for (auto i = _rightActions.cend(), b = _rightActions.cbegin(); i != b;) {
|
||||
--i;
|
||||
buttonLeft -= i->button->width();
|
||||
i->button->moveToLeft(buttonLeft, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::refreshLang() {
|
||||
InvokeQueued(this, [this] { updateButtonsGeometry(width()); });
|
||||
}
|
||||
|
||||
void FixedBar::setAnimatingMode(bool enabled) {
|
||||
if (_animatingMode != enabled) {
|
||||
_animatingMode = enabled;
|
||||
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
|
||||
if (_animatingMode) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||
hideChildren();
|
||||
} else {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
showChildren();
|
||||
if (_hideShareContactButton) {
|
||||
applyHideShareContactButton();
|
||||
}
|
||||
}
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::setHideShareContactButton(bool hideButton) {
|
||||
_hideShareContactButton = hideButton;
|
||||
if (!_animatingMode) {
|
||||
applyHideShareContactButton();
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::applyHideShareContactButton() {
|
||||
for_const (auto &action, _rightActions) {
|
||||
if (action.type == RightActionType::ShareContact) {
|
||||
action.button->setVisible(!_hideShareContactButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FixedBar::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
onBack();
|
||||
} else {
|
||||
TWidget::mousePressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "base/observer.h"
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Ui {
|
||||
class RoundButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class BackButton;
|
||||
|
||||
class FixedBar final : public TWidget, private base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FixedBar(QWidget *parent, PeerData *peer);
|
||||
|
||||
// When animating mode is enabled the content is hidden and the
|
||||
// whole fixed bar acts like a back button.
|
||||
void setAnimatingMode(bool enabled);
|
||||
|
||||
// The "Share contact" button should be hidden if it is shown in the profile cover.
|
||||
void setHideShareContactButton(bool hideButton);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
public slots:
|
||||
void onBack();
|
||||
|
||||
private slots:
|
||||
void onEditChannel();
|
||||
void onEditGroup();
|
||||
void onAddContact();
|
||||
void onEditContact();
|
||||
void onShareContact();
|
||||
void onDeleteContact();
|
||||
void onLeaveGroup();
|
||||
|
||||
private:
|
||||
void refreshLang();
|
||||
void updateButtonsGeometry(int newWidth);
|
||||
void notifyPeerUpdate(const Notify::PeerUpdate &update);
|
||||
|
||||
void refreshRightActions();
|
||||
void setUserActions();
|
||||
void setChatActions();
|
||||
void setMegagroupActions();
|
||||
void setChannelActions();
|
||||
|
||||
enum class RightActionType {
|
||||
None,
|
||||
EditChannel,
|
||||
EditGroup,
|
||||
LeaveGroup,
|
||||
AddContact,
|
||||
EditContact,
|
||||
DeleteContact,
|
||||
ShareContact,
|
||||
};
|
||||
|
||||
void addRightAction(RightActionType type, base::lambda<QString()> textFactory, const char *slot);
|
||||
void applyHideShareContactButton();
|
||||
|
||||
PeerData *_peer;
|
||||
UserData *_peerUser;
|
||||
ChatData *_peerChat;
|
||||
ChannelData *_peerChannel;
|
||||
ChannelData *_peerMegagroup;
|
||||
|
||||
object_ptr<BackButton> _backButton;
|
||||
|
||||
int _currentAction = 0;
|
||||
struct RightAction {
|
||||
RightActionType type = RightActionType::None;
|
||||
Ui::RoundButton *button = nullptr;
|
||||
};
|
||||
QList<RightAction> _rightActions;
|
||||
|
||||
bool _animatingMode = false;
|
||||
bool _hideShareContactButton = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,272 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_inner_widget.h"
|
||||
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "profile/profile_cover.h"
|
||||
#include "profile/profile_block_info.h"
|
||||
#include "profile/profile_block_settings.h"
|
||||
#include "profile/profile_block_invite_link.h"
|
||||
#include "profile/profile_block_shared_media.h"
|
||||
#include "profile/profile_block_actions.h"
|
||||
#include "profile/profile_block_channel_members.h"
|
||||
#include "profile/profile_block_group_members.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
InnerWidget::InnerWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||
, _peer(peer)
|
||||
, _cover(this, peer) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
createBlocks();
|
||||
}
|
||||
|
||||
void InnerWidget::createBlocks() {
|
||||
auto user = _peer->asUser();
|
||||
auto chat = _peer->asChat();
|
||||
auto channel = _peer->asChannel();
|
||||
auto megagroup = _peer->isMegagroup() ? channel : nullptr;
|
||||
if (user || channel || megagroup) {
|
||||
_blocks.push_back({ new InfoWidget(this, _peer), RectPart::Right });
|
||||
}
|
||||
_blocks.push_back({ new SettingsWidget(this, _peer), RectPart::Right });
|
||||
if (chat || channel || megagroup) {
|
||||
_blocks.push_back({ new InviteLinkWidget(this, _peer), RectPart::Right });
|
||||
}
|
||||
_blocks.push_back({ new SharedMediaWidget(this, _peer), RectPart::Right });
|
||||
if (channel && !megagroup) {
|
||||
_blocks.push_back({ new ChannelMembersWidget(this, _peer), RectPart::Right });
|
||||
}
|
||||
_blocks.push_back({ new ActionsWidget(this, _peer), RectPart::Right });
|
||||
if (chat || megagroup) {
|
||||
auto membersWidget = new GroupMembersWidget(this, _peer);
|
||||
connect(membersWidget, SIGNAL(onlineCountUpdated(int)), _cover, SLOT(onOnlineCountUpdated(int)));
|
||||
_cover->onOnlineCountUpdated(membersWidget->onlineCount());
|
||||
_blocks.push_back({ membersWidget, RectPart::Left });
|
||||
}
|
||||
for_const (auto &blockData, _blocks) {
|
||||
connect(blockData.block, SIGNAL(heightUpdated()), this, SLOT(onBlockHeightUpdated()));
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::visibleTopBottomUpdated(int visibleTop, int visibleBottom) {
|
||||
_visibleTop = visibleTop;
|
||||
_visibleBottom = visibleBottom;
|
||||
|
||||
int notDisplayedAtBottom = height() - _visibleBottom;
|
||||
if (notDisplayedAtBottom > 0) {
|
||||
decreaseAdditionalHeight(notDisplayedAtBottom);
|
||||
}
|
||||
|
||||
for_const (auto &blockData, _blocks) {
|
||||
int blockY = blockData.block->y();
|
||||
blockData.block->setVisibleTopBottom(visibleTop - blockY, visibleBottom - blockY);
|
||||
}
|
||||
}
|
||||
|
||||
bool InnerWidget::shareContactButtonShown() const {
|
||||
return _cover->shareContactButtonShown();
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
|
||||
for_const (auto &blockData, _blocks) {
|
||||
blockData.block->saveState(memento);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
|
||||
for_const (auto &blockData, _blocks) {
|
||||
blockData.block->restoreState(memento);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::showFinished() {
|
||||
_cover->showFinished();
|
||||
for_const (auto &blockData, _blocks) {
|
||||
blockData.block->showFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::decreaseAdditionalHeight(int removeHeight) {
|
||||
resizeToWidth(width(), height() - removeHeight);
|
||||
}
|
||||
|
||||
void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
p.fillRect(e->rect(), st::profileBg);
|
||||
|
||||
if (_mode == Mode::TwoColumn) {
|
||||
int leftHeight = countBlocksHeight(RectPart::Left);
|
||||
int rightHeight = countBlocksHeight(RectPart::Right);
|
||||
int shadowHeight = rightHeight;// qMin(leftHeight, rightHeight);
|
||||
|
||||
int shadowLeft = _blocksLeft + _leftColumnWidth + _columnDivider;
|
||||
int shadowTop = _blocksTop + st::profileBlockMarginTop;
|
||||
p.fillRect(rtlrect(shadowLeft, shadowTop, st::lineWidth, shadowHeight - st::profileBlockMarginTop, width()), st::shadowFg);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
|
||||
emit cancelled();
|
||||
}
|
||||
}
|
||||
|
||||
int InnerWidget::countBlocksHeight(RectPart countSide) const {
|
||||
int result = 0;
|
||||
for_const (auto &blockData, _blocks) {
|
||||
if (blockData.side != countSide || blockData.block->isHidden()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += blockData.block->height();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int InnerWidget::countBlocksLeft(int newWidth) const {
|
||||
int result = st::profileBlockLeftMin;
|
||||
result += (newWidth - st::columnMinimalWidthMain) / 2;
|
||||
return qMin(result, st::profileBlockLeftMax);
|
||||
}
|
||||
|
||||
InnerWidget::Mode InnerWidget::countBlocksMode(int newWidth) const {
|
||||
bool hasLeftWidget = false, hasRightWidget = false;
|
||||
for_const (auto &blockData, _blocks) {
|
||||
if (!blockData.block->isHidden()) {
|
||||
if (blockData.side == RectPart::Left) {
|
||||
hasLeftWidget = true;
|
||||
} else {
|
||||
hasRightWidget = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasLeftWidget || !hasRightWidget) {
|
||||
return Mode::OneColumn;
|
||||
}
|
||||
|
||||
int availWidth = newWidth - _blocksLeft;
|
||||
if (availWidth >= st::profileBlockWideWidthMin + _columnDivider + st::profileBlockNarrowWidthMin) {
|
||||
return Mode::TwoColumn;
|
||||
}
|
||||
return Mode::OneColumn;
|
||||
}
|
||||
|
||||
int InnerWidget::countLeftColumnWidth(int newWidth) const {
|
||||
int result = st::profileBlockWideWidthMin;
|
||||
|
||||
int availWidth = newWidth - _blocksLeft;
|
||||
int additionalWidth = (availWidth - st::profileBlockWideWidthMin - _columnDivider - st::profileBlockNarrowWidthMin);
|
||||
if (additionalWidth > 0) {
|
||||
result += (additionalWidth / 2);
|
||||
accumulate_min(result, st::profileBlockWideWidthMax);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void InnerWidget::refreshBlocksPositions() {
|
||||
auto layoutBlocks = [this](RectPart layoutSide, int left) {
|
||||
int top = _blocksTop;
|
||||
for_const (auto &blockData, _blocks) {
|
||||
if (_mode == Mode::TwoColumn && blockData.side != layoutSide) {
|
||||
continue;
|
||||
}
|
||||
if (blockData.block->isHidden()) {
|
||||
continue;
|
||||
}
|
||||
blockData.block->moveToLeft(left, top);
|
||||
blockData.block->setVisibleTopBottom(_visibleTop - top, _visibleBottom - top);
|
||||
|
||||
top += blockData.block->height();
|
||||
}
|
||||
};
|
||||
layoutBlocks(RectPart::Left, _blocksLeft);
|
||||
if (_mode == Mode::TwoColumn) {
|
||||
layoutBlocks(RectPart::Right, _blocksLeft + _leftColumnWidth + _columnDivider);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::resizeBlocks(int newWidth) {
|
||||
for_const (auto &blockData, _blocks) {
|
||||
int blockWidth = newWidth - _blocksLeft;
|
||||
if (_mode == Mode::OneColumn) {
|
||||
blockWidth -= _blocksLeft;
|
||||
} else {
|
||||
if (blockData.side == RectPart::Left) {
|
||||
blockWidth = _leftColumnWidth;
|
||||
} else {
|
||||
blockWidth -= _leftColumnWidth + _columnDivider;
|
||||
}
|
||||
}
|
||||
blockData.block->resizeToWidth(blockWidth);
|
||||
}
|
||||
}
|
||||
|
||||
int InnerWidget::resizeGetHeight(int newWidth) {
|
||||
_cover->resizeToWidth(newWidth);
|
||||
|
||||
_blocksTop = _cover->y() + _cover->height() + st::profileBlocksTop;
|
||||
_blocksLeft = countBlocksLeft(newWidth);
|
||||
_columnDivider = st::profileMemberPaddingLeft;
|
||||
_mode = countBlocksMode(newWidth);
|
||||
_leftColumnWidth = countLeftColumnWidth(newWidth);
|
||||
resizeBlocks(newWidth);
|
||||
|
||||
refreshBlocksPositions();
|
||||
|
||||
update();
|
||||
auto naturalHeight = countHeight();
|
||||
|
||||
_addedHeight = qMax(_minHeight - naturalHeight, 0);
|
||||
return naturalHeight + _addedHeight;
|
||||
}
|
||||
|
||||
int InnerWidget::countHeight() const {
|
||||
auto newHeight = _cover->height();
|
||||
auto leftHeight = countBlocksHeight(RectPart::Left);
|
||||
auto rightHeight = countBlocksHeight(RectPart::Right);
|
||||
|
||||
auto blocksHeight = (_mode == Mode::OneColumn) ? (leftHeight + rightHeight) : qMax(leftHeight, rightHeight);
|
||||
newHeight += st::profileBlocksTop + blocksHeight + st::profileBlocksBottom;
|
||||
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void InnerWidget::onBlockHeightUpdated() {
|
||||
refreshBlocksPositions();
|
||||
|
||||
int naturalHeight = countHeight();
|
||||
int notDisplayedAtBottom = naturalHeight - _visibleBottom;
|
||||
if (notDisplayedAtBottom < 0) {
|
||||
_addedHeight = -notDisplayedAtBottom;
|
||||
} else {
|
||||
_addedHeight = 0;
|
||||
}
|
||||
if (naturalHeight + _addedHeight != height()) {
|
||||
resize(width(), naturalHeight + _addedHeight);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
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 Profile {
|
||||
|
||||
class CoverWidget;
|
||||
class BlockWidget;
|
||||
class SectionMemento;
|
||||
|
||||
class InnerWidget final : public TWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InnerWidget(QWidget *parent, PeerData *peer);
|
||||
|
||||
PeerData *peer() const {
|
||||
return _peer;
|
||||
}
|
||||
|
||||
void resizeToWidth(int newWidth, int minHeight) {
|
||||
_minHeight = minHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
// Profile fixed top bar should use this flag to decide
|
||||
// if it shows "Share contact" button or not.
|
||||
// It should show it only if it is hidden in the cover.
|
||||
bool shareContactButtonShown() const;
|
||||
|
||||
void saveState(not_null<SectionMemento*> memento);
|
||||
void restoreState(not_null<SectionMemento*> memento);
|
||||
|
||||
void showFinished();
|
||||
|
||||
signals:
|
||||
void cancelled();
|
||||
|
||||
private slots:
|
||||
void onBlockHeightUpdated();
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
void createBlocks();
|
||||
|
||||
// Counts the natural widget height after resizing of child widgets.
|
||||
int countHeight() const;
|
||||
|
||||
enum class Mode {
|
||||
OneColumn,
|
||||
TwoColumn,
|
||||
};
|
||||
int countBlocksLeft(int newWidth) const;
|
||||
Mode countBlocksMode(int newWidth) const;
|
||||
int countLeftColumnWidth(int newWidth) const;
|
||||
int countBlocksHeight(RectPart countSide) const;
|
||||
void resizeBlocks(int newWidth);
|
||||
void refreshBlocksPositions();
|
||||
|
||||
// Sometimes height of this widget is larger than it is required
|
||||
// so that it is allowed to scroll down to the desired position.
|
||||
// When resizing with scroll moving up the additional height may be decreased.
|
||||
void decreaseAdditionalHeight(int removeHeight);
|
||||
|
||||
PeerData *_peer;
|
||||
|
||||
// Height that we added to the natural height so that it is allowed
|
||||
// to scroll down to the desired position.
|
||||
int _addedHeight = 0;
|
||||
int _minHeight = 0;
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
|
||||
object_ptr<CoverWidget> _cover;
|
||||
|
||||
int _blocksLeft = 0; // Caching countBlocksLeft() result.
|
||||
int _blocksTop = 0;
|
||||
int _columnDivider = 0;
|
||||
int _leftColumnWidth = 0; // Caching countLeftColumnWidth() result.
|
||||
struct Block {
|
||||
BlockWidget *block;
|
||||
RectPart side;
|
||||
};
|
||||
QList<Block> _blocks;
|
||||
|
||||
Mode _mode = Mode::OneColumn;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_section_memento.h"
|
||||
|
||||
#include "profile/profile_widget.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
object_ptr<Window::SectionWidget> SectionMemento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) {
|
||||
auto result = object_ptr<Widget>(parent, controller, _peer);
|
||||
result->setInternalState(geometry, this);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "window/section_memento.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class Widget;
|
||||
|
||||
class SectionMemento : public Window::SectionMemento {
|
||||
public:
|
||||
SectionMemento(PeerData *peer) : _peer(peer) {
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) override;
|
||||
|
||||
PeerData *getPeer() const {
|
||||
return _peer;
|
||||
}
|
||||
void setScrollTop(int scrollTop) {
|
||||
_scrollTop = scrollTop;
|
||||
}
|
||||
int getScrollTop() const {
|
||||
return _scrollTop;
|
||||
}
|
||||
|
||||
private:
|
||||
PeerData *_peer;
|
||||
int _scrollTop = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Window
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_userpic_button.h"
|
||||
|
||||
#include "styles/style_profile.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
UserpicButton::UserpicButton(QWidget *parent, not_null<PeerData*> peer, int size) : AbstractButton(parent)
|
||||
, _size(size ? size : st::profilePhotoSize)
|
||||
, _peer(peer) {
|
||||
resize(_size, _size);
|
||||
|
||||
processPeerPhoto();
|
||||
_notShownYet = _waiting;
|
||||
if (!_waiting) {
|
||||
_userpic = prepareUserpicPixmap();
|
||||
}
|
||||
|
||||
auto observeEvents = Notify::PeerUpdate::Flag::PhotoChanged;
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
|
||||
notifyPeerUpdated(update);
|
||||
}));
|
||||
subscribe(Auth().downloaderTaskFinished(), [this] {
|
||||
if (_waiting && _peer->userpicLoaded()) {
|
||||
_waiting = false;
|
||||
startNewPhotoShowing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UserpicButton::showFinished() {
|
||||
if (_notShownYet) {
|
||||
_notShownYet = false;
|
||||
if (!_waiting) {
|
||||
_a_appearance.finish();
|
||||
_a_appearance.start([this] { update(); }, 0, 1, st::profilePhotoDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UserpicButton::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
if (_a_appearance.animating(getms())) {
|
||||
p.drawPixmap(0, 0, _oldUserpic);
|
||||
p.setOpacity(_a_appearance.current());
|
||||
}
|
||||
p.drawPixmap(0, 0, _userpic);
|
||||
}
|
||||
|
||||
void UserpicButton::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||
if (update.peer != _peer) {
|
||||
return;
|
||||
}
|
||||
|
||||
processNewPeerPhoto();
|
||||
this->update();
|
||||
}
|
||||
|
||||
void UserpicButton::processPeerPhoto() {
|
||||
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
|
||||
setCursor(hasPhoto ? style::cur_pointer : style::cur_default);
|
||||
_waiting = !_peer->userpicLoaded();
|
||||
if (_waiting) {
|
||||
_peer->loadUserpic(true);
|
||||
}
|
||||
}
|
||||
|
||||
void UserpicButton::processNewPeerPhoto() {
|
||||
processPeerPhoto();
|
||||
if (!_waiting) {
|
||||
startNewPhotoShowing();
|
||||
}
|
||||
}
|
||||
|
||||
void UserpicButton::startNewPhotoShowing() {
|
||||
_oldUserpic = myGrab(this);
|
||||
_userpic = prepareUserpicPixmap();
|
||||
|
||||
if (_notShownYet) {
|
||||
return;
|
||||
}
|
||||
|
||||
_a_appearance.finish();
|
||||
_a_appearance.start([this] { update(); }, 0, 1, st::profilePhotoDuration);
|
||||
update();
|
||||
}
|
||||
|
||||
QPixmap UserpicButton::prepareUserpicPixmap() const {
|
||||
auto retina = cIntRetinaFactor();
|
||||
auto size = width() * retina;
|
||||
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
|
||||
image.setDevicePixelRatio(cRetinaFactor());
|
||||
image.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&image);
|
||||
_peer->paintUserpic(p, 0, 0, width());
|
||||
}
|
||||
return App::pixmapFromImageInPlace(std::move(image));
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "ui/abstract_button.h"
|
||||
#include "base/observer.h"
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
} // namespace Notify
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class UserpicButton : public Ui::AbstractButton, private base::Subscriber {
|
||||
public:
|
||||
UserpicButton(QWidget *parent, not_null<PeerData*> peer, int size = 0);
|
||||
|
||||
// If at the first moment the _userpic was not loaded,
|
||||
// we need to show it animated after the profile is fully shown.
|
||||
void showFinished();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
private:
|
||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||
|
||||
void processPeerPhoto();
|
||||
void processNewPeerPhoto();
|
||||
void startNewPhotoShowing();
|
||||
QPixmap prepareUserpicPixmap() const;
|
||||
|
||||
bool _notShownYet;
|
||||
|
||||
int _size = 0;
|
||||
not_null<PeerData*> _peer;
|
||||
bool _waiting = false;
|
||||
QPixmap _userpic, _oldUserpic;
|
||||
Animation _a_appearance;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
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 "profile/profile_widget.h"
|
||||
|
||||
#include "styles/style_settings.h"
|
||||
#include "profile/profile_fixed_bar.h"
|
||||
#include "profile/profile_inner_widget.h"
|
||||
#include "profile/profile_section_memento.h"
|
||||
#include "mainwindow.h"
|
||||
#include "application.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
Widget::Widget(QWidget *parent, not_null<Window::Controller*> controller, PeerData *peer) : Window::SectionWidget(parent, controller)
|
||||
, _scroll(this, st::settingsScroll)
|
||||
, _fixedBar(this, peer)
|
||||
, _fixedBarShadow(this) {
|
||||
_fixedBar->move(0, 0);
|
||||
_fixedBar->resizeToWidth(width());
|
||||
_fixedBar->show();
|
||||
|
||||
_fixedBarShadow->hide(anim::type::instant);
|
||||
_fixedBarShadow->raise();
|
||||
updateAdaptiveLayout();
|
||||
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
|
||||
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, peer));
|
||||
_scroll->move(0, _fixedBar->height());
|
||||
_scroll->show();
|
||||
|
||||
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack()));
|
||||
}
|
||||
|
||||
void Widget::updateAdaptiveLayout() {
|
||||
_fixedBarShadow->moveToLeft(Adaptive::OneColumn() ? 0 : st::lineWidth, _fixedBar->height());
|
||||
}
|
||||
|
||||
PeerData *Widget::peer() const {
|
||||
return _inner->peer();
|
||||
}
|
||||
|
||||
bool Widget::hasTopBarShadow() const {
|
||||
return !_fixedBarShadow->isHidden() && !_fixedBarShadow->animating();
|
||||
}
|
||||
|
||||
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||
if (params.withTopBarShadow || !_scroll->scrollTop()) _fixedBarShadow->hide(anim::type::instant);
|
||||
auto result = myGrab(this);
|
||||
if (params.withTopBarShadow) _fixedBarShadow->show(anim::type::instant);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Widget::doSetInnerFocus() {
|
||||
_inner->setFocus();
|
||||
}
|
||||
|
||||
bool Widget::showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) {
|
||||
if (auto profileMemento = dynamic_cast<SectionMemento*>(memento.get())) {
|
||||
if (profileMemento->getPeer() == peer()) {
|
||||
restoreState(profileMemento);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento*> memento) {
|
||||
setGeometry(geometry);
|
||||
myEnsureResized(this);
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||
auto result = std::make_unique<SectionMemento>(peer());
|
||||
saveState(result.get());
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
void Widget::saveState(not_null<SectionMemento*> memento) {
|
||||
memento->setScrollTop(_scroll->scrollTop());
|
||||
_inner->saveState(memento);
|
||||
}
|
||||
|
||||
void Widget::restoreState(not_null<SectionMemento*> memento) {
|
||||
_inner->restoreState(memento);
|
||||
auto scrollTop = memento->getScrollTop();
|
||||
_scroll->scrollToY(scrollTop);
|
||||
updateScrollState();
|
||||
_fixedBarShadow->finishAnimating();
|
||||
}
|
||||
|
||||
void Widget::resizeEvent(QResizeEvent *e) {
|
||||
if (!width() || !height()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newScrollTop = _scroll->scrollTop() + topDelta();
|
||||
_fixedBar->resizeToWidth(width());
|
||||
|
||||
QSize scrollSize(width(), height() - _fixedBar->height());
|
||||
if (_scroll->size() != scrollSize) {
|
||||
_scroll->resize(scrollSize);
|
||||
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
|
||||
}
|
||||
_fixedBar->setHideShareContactButton(_inner->shareContactButtonShown());
|
||||
|
||||
if (!_scroll->isHidden()) {
|
||||
if (topDelta()) {
|
||||
_scroll->scrollToY(newScrollTop);
|
||||
}
|
||||
updateScrollState();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::updateScrollState() {
|
||||
auto scrollTop = _scroll->scrollTop();
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
_fixedBarShadow->toggle(scrollTop > 0, anim::type::normal);
|
||||
}
|
||||
|
||||
void Widget::onScroll() {
|
||||
updateScrollState();
|
||||
}
|
||||
|
||||
void Widget::showAnimatedHook(
|
||||
const Window::SectionSlideParams ¶ms) {
|
||||
_fixedBar->setAnimatingMode(true);
|
||||
}
|
||||
|
||||
void Widget::showFinishedHook() {
|
||||
_fixedBar->setAnimatingMode(false);
|
||||
if (!_scroll->scrollTop()) {
|
||||
_fixedBarShadow->hide(anim::type::instant);
|
||||
}
|
||||
_inner->showFinished();
|
||||
}
|
||||
|
||||
bool Widget::wheelEventFromFloatPlayer(QEvent *e) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect Widget::rectForFloatPlayer() const {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
} // namespace Profile
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "window/section_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
class FadeShadow;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class SectionMemento;
|
||||
class InnerWidget;
|
||||
class FixedBar;
|
||||
class Widget final : public Window::SectionWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget(QWidget *parent, not_null<Window::Controller*> controller, PeerData *peer);
|
||||
|
||||
PeerData *peer() const;
|
||||
PeerData *activePeer() const override {
|
||||
return peer();
|
||||
}
|
||||
|
||||
bool hasTopBarShadow() const override;
|
||||
|
||||
QPixmap grabForShowAnimation(const Window::SectionSlideParams ¶ms) override;
|
||||
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
void setInternalState(const QRect &geometry, not_null<SectionMemento*> memento);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e) override;
|
||||
QRect rectForFloatPlayer() const override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
void showAnimatedHook(
|
||||
const Window::SectionSlideParams ¶ms) override;
|
||||
void showFinishedHook() override;
|
||||
void doSetInnerFocus() override;
|
||||
|
||||
private slots:
|
||||
void onScroll();
|
||||
|
||||
private:
|
||||
void updateScrollState();
|
||||
void updateAdaptiveLayout();
|
||||
void saveState(not_null<SectionMemento*> memento);
|
||||
void restoreState(not_null<SectionMemento*> memento);
|
||||
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<InnerWidget> _inner;
|
||||
object_ptr<FixedBar> _fixedBar;
|
||||
object_ptr<Ui::FadeShadow> _fixedBarShadow;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
|
@ -24,6 +24,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include <rpl/lifetime.h>
|
||||
#include <rpl/details/callable.h>
|
||||
|
||||
// GCC 7.2 can't handle not type-erased consumers.
|
||||
// It eats up 4GB RAM + 16GB swap on the unittest and dies.
|
||||
// Clang and Visual C++ both handle it without such problems.
|
||||
#if defined _DEBUG || defined COMPILER_GCC
|
||||
#define RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
#endif // _DEBUG || COMPILER_GCC
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
|
@ -609,9 +616,6 @@ inline bool operator>=(
|
|||
return !(a < b);
|
||||
}
|
||||
|
||||
// GCC 7.2 can't handle not type-erased consumers.
|
||||
// It eats up 4GB RAM + 16GB swap on the unittest and dies.
|
||||
// Clang and Visual C++ both handle it without such problems.
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
|
@ -622,11 +626,11 @@ template <
|
|||
details::is_callable_v<OnNext, Value> &&
|
||||
details::is_callable_v<OnError, Error> &&
|
||||
details::is_callable_v<OnDone>>>
|
||||
#ifdef COMPILER_GCC
|
||||
#ifdef RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
inline consumer<Value, Error> make_consumer(
|
||||
#else // COMPILER_GCC
|
||||
#else // RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
inline auto make_consumer(
|
||||
#endif // COMPILER_GCC
|
||||
#endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
OnNext &&next,
|
||||
OnError &&error,
|
||||
OnDone &&done) {
|
||||
|
|
|
@ -26,6 +26,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include <rpl/details/superset_type.h>
|
||||
#include <rpl/details/callable.h>
|
||||
|
||||
#if defined _DEBUG
|
||||
#define RPL_PRODUCER_TYPE_ERASED_ALWAYS
|
||||
#endif // _DEBUG
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
|
@ -450,7 +454,11 @@ template <
|
|||
details::const_ref_consumer<Value, Error>())),
|
||||
lifetime>>>
|
||||
inline auto make_producer(Generator &&generator)
|
||||
#ifdef RPL_PRODUCER_TYPE_ERASED_ALWAYS
|
||||
-> producer<Value, Error> {
|
||||
#else // RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
-> producer<Value, Error, std::decay_t<Generator>> {
|
||||
#endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
return std::forward<Generator>(generator);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,10 +72,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "base/assertion.h"
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#ifndef _DEBUG
|
||||
#include <rpl/rpl.h>
|
||||
#endif // _DEBUG
|
||||
|
||||
#include "base/variant.h"
|
||||
#include "base/optional.h"
|
||||
|
|
|
@ -118,4 +118,16 @@ rpl::producer<SharedMediaResult> SharedMedia::query(SharedMediaQuery &&query) co
|
|||
};
|
||||
}
|
||||
|
||||
rpl::producer<SharedMediaSliceUpdate> SharedMedia::sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
|
||||
rpl::producer<SharedMediaRemoveOne> SharedMedia::oneRemoved() const {
|
||||
return _oneRemoved.events();
|
||||
}
|
||||
|
||||
rpl::producer<SharedMediaRemoveAll> SharedMedia::allRemoved() const {
|
||||
return _allRemoved.events();
|
||||
}
|
||||
|
||||
} // namespace Storage
|
||||
|
|
|
@ -194,16 +194,9 @@ public:
|
|||
void remove(SharedMediaRemoveAll &&query);
|
||||
|
||||
rpl::producer<SharedMediaResult> query(SharedMediaQuery &&query) const;
|
||||
|
||||
rpl::producer<SharedMediaSliceUpdate> sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
rpl::producer<SharedMediaRemoveOne> oneRemoved() const {
|
||||
return _oneRemoved.events();
|
||||
}
|
||||
rpl::producer<SharedMediaRemoveAll> allRemoved() const {
|
||||
return _allRemoved.events();
|
||||
}
|
||||
rpl::producer<SharedMediaSliceUpdate> sliceUpdated() const;
|
||||
rpl::producer<SharedMediaRemoveOne> oneRemoved() const;
|
||||
rpl::producer<SharedMediaRemoveAll> allRemoved() const;
|
||||
|
||||
private:
|
||||
using Lists = std::array<SparseIdsList, kSharedMediaTypeCount>;
|
||||
|
|
|
@ -197,6 +197,10 @@ rpl::producer<SparseIdsListResult> SparseIdsList::query(
|
|||
};
|
||||
}
|
||||
|
||||
rpl::producer<SparseIdsSliceUpdate> SparseIdsList::sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
|
||||
SparseIdsListResult SparseIdsList::queryFromSlice(
|
||||
const SparseIdsListQuery &query,
|
||||
const Slice &slice) const {
|
||||
|
|
|
@ -62,10 +62,7 @@ public:
|
|||
void removeOne(MsgId messageId);
|
||||
void removeAll();
|
||||
rpl::producer<SparseIdsListResult> query(SparseIdsListQuery &&query) const;
|
||||
|
||||
rpl::producer<SparseIdsSliceUpdate> sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
rpl::producer<SparseIdsSliceUpdate> sliceUpdated() const;
|
||||
|
||||
private:
|
||||
struct Slice {
|
||||
|
|
|
@ -114,8 +114,15 @@ rpl::producer<UserPhotosResult> UserPhotos::List::query(
|
|||
};
|
||||
}
|
||||
|
||||
std::map<UserId, UserPhotos::List>::iterator
|
||||
UserPhotos::enforceLists(UserId user) {
|
||||
auto UserPhotos::List::sliceUpdated() const -> rpl::producer<SliceUpdate> {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
|
||||
rpl::producer<UserPhotosSliceUpdate> UserPhotos::sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
|
||||
std::map<UserId, UserPhotos::List>::iterator UserPhotos::enforceLists(UserId user) {
|
||||
auto result = _lists.find(user);
|
||||
if (result != _lists.end()) {
|
||||
return result;
|
||||
|
|
|
@ -144,10 +144,7 @@ public:
|
|||
void remove(UserPhotosRemoveAfter &&query);
|
||||
|
||||
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
||||
|
||||
rpl::producer<UserPhotosSliceUpdate> sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
rpl::producer<UserPhotosSliceUpdate> sliceUpdated() const;
|
||||
|
||||
private:
|
||||
class List {
|
||||
|
@ -164,9 +161,7 @@ private:
|
|||
const std::deque<PhotoId> *photoIds = nullptr;
|
||||
base::optional<int> count;
|
||||
};
|
||||
rpl::producer<SliceUpdate> sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
rpl::producer<SliceUpdate> sliceUpdated() const;
|
||||
|
||||
private:
|
||||
void sendUpdate();
|
||||
|
|
134
Telegram/SourceFiles/ui/rp_widget.cpp
Normal file
134
Telegram/SourceFiles/ui/rp_widget.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
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 "ui/rp_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
rpl::producer<QRect> RpWidgetMethods::geometryValue() const {
|
||||
auto &stream = eventStreams().geometry;
|
||||
return stream.events_starting_with_copy(callGetGeometry());
|
||||
}
|
||||
|
||||
rpl::producer<QSize> RpWidgetMethods::sizeValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.size(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<int> RpWidgetMethods::heightValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.height(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<int> RpWidgetMethods::widthValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.width(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<QPoint> RpWidgetMethods::positionValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.topLeft(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<int> RpWidgetMethods::leftValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.left(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<int> RpWidgetMethods::topValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.top(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<int> RpWidgetMethods::desiredHeightValue() const {
|
||||
return heightValue();
|
||||
}
|
||||
|
||||
rpl::producer<bool> RpWidgetMethods::shownValue() const {
|
||||
auto &stream = eventStreams().shown;
|
||||
return stream.events_starting_with(!callIsHidden());
|
||||
}
|
||||
|
||||
rpl::producer<QRect> RpWidgetMethods::paintRequest() const {
|
||||
return eventStreams().paint.events();
|
||||
}
|
||||
|
||||
rpl::producer<> RpWidgetMethods::alive() const {
|
||||
return eventStreams().alive.events();
|
||||
}
|
||||
|
||||
rpl::lifetime &RpWidgetMethods::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
bool RpWidgetMethods::handleEvent(QEvent *event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::Move:
|
||||
case QEvent::Resize:
|
||||
if (auto streams = _eventStreams.get()) {
|
||||
auto that = callCreateWeak();
|
||||
streams->geometry.fire_copy(callGetGeometry());
|
||||
if (!that) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case QEvent::Paint:
|
||||
if (auto streams = _eventStreams.get()) {
|
||||
auto that = callCreateWeak();
|
||||
streams->paint.fire_copy(
|
||||
static_cast<QPaintEvent*>(event)->rect());
|
||||
if (!that) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return eventHook(event);
|
||||
}
|
||||
|
||||
RpWidgetMethods::Initer::Initer(QWidget *parent) {
|
||||
parent->setGeometry(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void RpWidgetMethods::visibilityChangedHook(bool wasVisible, bool nowVisible) {
|
||||
if (nowVisible != wasVisible) {
|
||||
if (auto streams = _eventStreams.get()) {
|
||||
streams->shown.fire_copy(nowVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto RpWidgetMethods::eventStreams() const -> EventStreams& {
|
||||
if (!_eventStreams) {
|
||||
_eventStreams = std::make_unique<EventStreams>();
|
||||
}
|
||||
return *_eventStreams;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
|
@ -56,9 +56,7 @@ inline Widget *CreateChild(
|
|||
Parent *parent,
|
||||
Args &&...args) {
|
||||
Expects(parent != nullptr);
|
||||
return base::make_unique_q<Widget>(
|
||||
parent,
|
||||
std::forward<Args>(args)...).release();
|
||||
return new Widget(parent, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
|
@ -76,141 +74,102 @@ using RpWidgetParent = std::conditional_t<
|
|||
TWidgetHelper<Widget>>;
|
||||
|
||||
template <typename Widget>
|
||||
class RpWidgetWrap : public RpWidgetParent<Widget> {
|
||||
using Parent = RpWidgetParent<Widget>;
|
||||
class RpWidgetWrap;
|
||||
|
||||
class RpWidgetMethods {
|
||||
public:
|
||||
using Parent::Parent;
|
||||
|
||||
auto geometryValue() const {
|
||||
auto &stream = eventStreams().geometry;
|
||||
return stream.events_starting_with_copy(this->geometry());
|
||||
}
|
||||
auto sizeValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.size(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
auto heightValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.height(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
auto widthValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.width(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
auto positionValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.topLeft(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
auto leftValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.left(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
auto topValue() const {
|
||||
return geometryValue()
|
||||
| rpl::map([](QRect &&value) { return value.top(); })
|
||||
| rpl::distinct_until_changed();
|
||||
}
|
||||
virtual rpl::producer<int> desiredHeightValue() const {
|
||||
return heightValue();
|
||||
}
|
||||
auto shownValue() const {
|
||||
auto &stream = eventStreams().shown;
|
||||
return stream.events_starting_with(!this->isHidden());
|
||||
}
|
||||
|
||||
auto paintRequest() const {
|
||||
return eventStreams().paint.events();
|
||||
}
|
||||
|
||||
auto alive() const {
|
||||
return eventStreams().alive.events();
|
||||
}
|
||||
|
||||
void setVisible(bool visible) final override {
|
||||
auto wasVisible = !this->isHidden();
|
||||
Parent::setVisible(visible);
|
||||
auto nowVisible = !this->isHidden();
|
||||
if (nowVisible != wasVisible) {
|
||||
if (auto streams = _eventStreams.get()) {
|
||||
streams->shown.fire_copy(nowVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
rpl::producer<QRect> geometryValue() const;
|
||||
rpl::producer<QSize> sizeValue() const;
|
||||
rpl::producer<int> heightValue() const;
|
||||
rpl::producer<int> widthValue() const;
|
||||
rpl::producer<QPoint> positionValue() const;
|
||||
rpl::producer<int> leftValue() const;
|
||||
rpl::producer<int> topValue() const;
|
||||
virtual rpl::producer<int> desiredHeightValue() const;
|
||||
rpl::producer<bool> shownValue() const;
|
||||
rpl::producer<QRect> paintRequest() const;
|
||||
rpl::producer<> alive() const;
|
||||
|
||||
template <typename Error, typename Generator>
|
||||
void showOn(rpl::producer<bool, Error, Generator> &&shown) {
|
||||
std::move(shown)
|
||||
| rpl::start_with_next([this](bool visible) {
|
||||
this->setVisible(visible);
|
||||
callSetVisible(visible);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
rpl::lifetime &lifetime() {
|
||||
return _lifetime.data;
|
||||
}
|
||||
rpl::lifetime &lifetime();
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) final override {
|
||||
switch (event->type()) {
|
||||
case QEvent::Move:
|
||||
case QEvent::Resize:
|
||||
if (auto streams = _eventStreams.get()) {
|
||||
auto that = weak(this);
|
||||
streams->geometry.fire_copy(this->geometry());
|
||||
if (!that) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case QEvent::Paint:
|
||||
if (auto streams = _eventStreams.get()) {
|
||||
auto that = weak(this);
|
||||
streams->paint.fire_copy(
|
||||
static_cast<QPaintEvent*>(event)->rect());
|
||||
if (!that) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return eventHook(event);
|
||||
}
|
||||
virtual bool eventHook(QEvent *event) {
|
||||
return Parent::event(event);
|
||||
}
|
||||
bool handleEvent(QEvent *event);
|
||||
virtual bool eventHook(QEvent *event) = 0;
|
||||
|
||||
private:
|
||||
template <typename Widget>
|
||||
friend class RpWidgetWrap;
|
||||
|
||||
struct EventStreams {
|
||||
rpl::event_stream<QRect> geometry;
|
||||
rpl::event_stream<QRect> paint;
|
||||
rpl::event_stream<bool> shown;
|
||||
rpl::event_stream<> alive;
|
||||
};
|
||||
struct LifetimeHolder {
|
||||
LifetimeHolder(QWidget *parent) {
|
||||
parent->setGeometry(0, 0, 0, 0);
|
||||
}
|
||||
rpl::lifetime data;
|
||||
struct Initer {
|
||||
Initer(QWidget *parent);
|
||||
};
|
||||
|
||||
EventStreams &eventStreams() const {
|
||||
if (!_eventStreams) {
|
||||
_eventStreams = std::make_unique<EventStreams>();
|
||||
}
|
||||
return *_eventStreams;
|
||||
}
|
||||
virtual void callSetVisible(bool visible) = 0;
|
||||
virtual QPointer<QObject> callCreateWeak() = 0;
|
||||
virtual QRect callGetGeometry() const = 0;
|
||||
virtual bool callIsHidden() const = 0;
|
||||
|
||||
void visibilityChangedHook(bool wasVisible, bool nowVisible);
|
||||
EventStreams &eventStreams() const;
|
||||
|
||||
mutable std::unique_ptr<EventStreams> _eventStreams;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
LifetimeHolder _lifetime = { this };
|
||||
};
|
||||
|
||||
template <typename Widget>
|
||||
class RpWidgetWrap
|
||||
: public RpWidgetParent<Widget>
|
||||
, public RpWidgetMethods {
|
||||
using Self = RpWidgetWrap<Widget>;
|
||||
using Parent = RpWidgetParent<Widget>;
|
||||
|
||||
public:
|
||||
using Parent::Parent;
|
||||
|
||||
void setVisible(bool visible) final override {
|
||||
auto wasVisible = !this->isHidden();
|
||||
Parent::setVisible(visible);
|
||||
visibilityChangedHook(wasVisible, !this->isHidden());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) final override {
|
||||
return handleEvent(event);
|
||||
}
|
||||
bool eventHook(QEvent *event) override {
|
||||
return Parent::event(event);
|
||||
}
|
||||
|
||||
private:
|
||||
void callSetVisible(bool visible) override {
|
||||
Self::setVisible(visible);
|
||||
}
|
||||
QPointer<QObject> callCreateWeak() override {
|
||||
return QPointer<QObject>((QObject*)this);
|
||||
}
|
||||
QRect callGetGeometry() const override {
|
||||
return this->geometry();
|
||||
}
|
||||
bool callIsHidden() const override {
|
||||
return this->isHidden();
|
||||
}
|
||||
|
||||
Initer _initer = { this };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -97,6 +97,18 @@ base::unique_qptr<Ui::RpWidget> SearchFieldController::createRowView(
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
QString SearchFieldController::query() const {
|
||||
return _query.current();
|
||||
}
|
||||
|
||||
rpl::producer<QString> SearchFieldController::queryValue() const {
|
||||
return _query.value();
|
||||
}
|
||||
|
||||
rpl::producer<QString> SearchFieldController::queryChanges() const {
|
||||
return _query.changes();
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::InputField> SearchFieldController::createField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st) {
|
||||
|
|
|
@ -45,15 +45,9 @@ public:
|
|||
QWidget *parent,
|
||||
const style::SearchFieldRow &st);
|
||||
|
||||
QString query() const {
|
||||
return _query.current();
|
||||
}
|
||||
rpl::producer<QString> queryValue() const {
|
||||
return _query.value();
|
||||
}
|
||||
rpl::producer<QString> queryChanges() const {
|
||||
return _query.changes();
|
||||
}
|
||||
QString query() const;
|
||||
rpl::producer<QString> queryValue() const;
|
||||
rpl::producer<QString> queryChanges() const;
|
||||
|
||||
rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
|
|
|
@ -98,8 +98,8 @@ protected:
|
|||
return heightNoMargins();
|
||||
}
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override {
|
||||
int visibleTop,
|
||||
int visibleBottom) override {
|
||||
setChildVisibleTopBottom(
|
||||
wrapped(),
|
||||
visibleTop,
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
<(src_loc)/data/data_game.h
|
||||
<(src_loc)/data/data_peer.cpp
|
||||
<(src_loc)/data/data_peer.h
|
||||
<(src_loc)/data/data_peer_values.cpp
|
||||
<(src_loc)/data/data_peer_values.h
|
||||
<(src_loc)/data/data_photo.cpp
|
||||
<(src_loc)/data/data_photo.h
|
||||
|
@ -444,42 +445,16 @@
|
|||
<(src_loc)/platform/platform_window_title.h
|
||||
<(src_loc)/profile/profile_back_button.cpp
|
||||
<(src_loc)/profile/profile_back_button.h
|
||||
<(src_loc)/profile/profile_block_actions.cpp
|
||||
<(src_loc)/profile/profile_block_actions.h
|
||||
<(src_loc)/profile/profile_block_channel_members.cpp
|
||||
<(src_loc)/profile/profile_block_channel_members.h
|
||||
<(src_loc)/profile/profile_block_info.cpp
|
||||
<(src_loc)/profile/profile_block_info.h
|
||||
<(src_loc)/profile/profile_block_invite_link.cpp
|
||||
<(src_loc)/profile/profile_block_invite_link.h
|
||||
<(src_loc)/profile/profile_block_group_members.cpp
|
||||
<(src_loc)/profile/profile_block_group_members.h
|
||||
<(src_loc)/profile/profile_block_peer_list.cpp
|
||||
<(src_loc)/profile/profile_block_peer_list.h
|
||||
<(src_loc)/profile/profile_block_settings.cpp
|
||||
<(src_loc)/profile/profile_block_settings.h
|
||||
<(src_loc)/profile/profile_block_shared_media.cpp
|
||||
<(src_loc)/profile/profile_block_shared_media.h
|
||||
<(src_loc)/profile/profile_block_widget.cpp
|
||||
<(src_loc)/profile/profile_block_widget.h
|
||||
<(src_loc)/profile/profile_channel_controllers.cpp
|
||||
<(src_loc)/profile/profile_channel_controllers.h
|
||||
<(src_loc)/profile/profile_common_groups_section.cpp
|
||||
<(src_loc)/profile/profile_common_groups_section.h
|
||||
<(src_loc)/profile/profile_cover_drop_area.cpp
|
||||
<(src_loc)/profile/profile_cover_drop_area.h
|
||||
<(src_loc)/profile/profile_cover.cpp
|
||||
<(src_loc)/profile/profile_cover.h
|
||||
<(src_loc)/profile/profile_fixed_bar.cpp
|
||||
<(src_loc)/profile/profile_fixed_bar.h
|
||||
<(src_loc)/profile/profile_inner_widget.cpp
|
||||
<(src_loc)/profile/profile_inner_widget.h
|
||||
<(src_loc)/profile/profile_section_memento.cpp
|
||||
<(src_loc)/profile/profile_section_memento.h
|
||||
<(src_loc)/profile/profile_userpic_button.cpp
|
||||
<(src_loc)/profile/profile_userpic_button.h
|
||||
<(src_loc)/profile/profile_widget.cpp
|
||||
<(src_loc)/profile/profile_widget.h
|
||||
<(src_loc)/settings/settings_advanced_widget.cpp
|
||||
<(src_loc)/settings/settings_advanced_widget.h
|
||||
<(src_loc)/settings/settings_background_widget.cpp
|
||||
|
@ -617,6 +592,7 @@
|
|||
<(src_loc)/ui/images.cpp
|
||||
<(src_loc)/ui/images.h
|
||||
<(src_loc)/ui/resize_area.h
|
||||
<(src_loc)/ui/rp_widget.cpp
|
||||
<(src_loc)/ui/rp_widget.h
|
||||
<(src_loc)/ui/search_field_controller.cpp
|
||||
<(src_loc)/ui/search_field_controller.h
|
||||
|
|
Loading…
Add table
Reference in a new issue