diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index a610e0d1f..319adac0b 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -25,13 +25,6 @@ UserpicButton { uploadIconPosition: point; } -FeedUserpicButton { - size: size; - innerSize: pixels; - innerPosition: point; - innerPart: UserpicButton; -} - countryRowHeight: 36px; countryRowNameFont: semiboldFont; countryRowNameFg: boxTextFg; @@ -69,12 +62,6 @@ defaultUserpicButton: UserpicButton { uploadIcon: defaultUploadUserpicIcon; uploadIconPosition: point(-1px, 1px); } -defaultFeedUserpicButton: FeedUserpicButton { - size: size(76px, 76px); - innerSize: 76px; - innerPosition: point(-1px, -1px); - innerPart: defaultUserpicButton; -} confirmInviteTitle: FlatLabel(defaultFlatLabel) { align: align(center); diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 51c7085fc..1a7b31099 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -149,6 +149,8 @@ struct TopicUpdate { UnreadMentions = (1U << 2), UnreadReactions = (1U << 3), Notifications = (1U << 4), + Title = (1U << 5), + Icon = (1U << 6), LastUsedBit = (1U << 4), }; diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index bfb06ce5f..5ea18d8d2 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -443,6 +443,7 @@ void ForumTopic::applyTitle(const QString &title) { _defaultIcon = QImage(); indexTitleParts(); updateChatListEntry(); + session().changes().topicUpdated(this, UpdateFlag::Title); } DocumentId ForumTopic::iconId() const { @@ -450,19 +451,21 @@ DocumentId ForumTopic::iconId() const { } void ForumTopic::applyIconId(DocumentId iconId) { - if (_iconId != iconId) { - _iconId = iconId; - _icon = iconId - ? owner().customEmojiManager().create( - _iconId, - [=] { updateChatListEntry(); }, - Data::CustomEmojiManager::SizeTag::Normal) - : nullptr; - if (iconId) { - _defaultIcon = QImage(); - } + if (_iconId == iconId) { + return; + } + _iconId = iconId; + _icon = iconId + ? owner().customEmojiManager().create( + _iconId, + [=] { updateChatListEntry(); }, + Data::CustomEmojiManager::SizeTag::Normal) + : nullptr; + if (iconId) { + _defaultIcon = QImage(); } updateChatListEntry(); + session().changes().topicUpdated(this, UpdateFlag::Icon); } int32 ForumTopic::colorId() const { diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 8d7d1542f..18067e816 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -255,31 +255,24 @@ infoTabs: SettingsSlider(defaultTabsSlider) { labelTop: 19px; } +InfoProfileCover { + height: pixels; + photo: UserpicButton; + photoLeft: pixels; + photoTop: pixels; + name: FlatLabel; + nameLeft: pixels; + nameTop: pixels; + status: FlatLabel; + statusLeft: pixels; + statusTop: pixels; + rightSkip: pixels; +} infoProfilePhotoInnerSize: 72px; infoProfilePhotoSize: size( infoProfilePhotoInnerSize, infoProfilePhotoInnerSize); -infoProfilePhoto: UserpicButton(defaultUserpicButton) { - size: infoProfilePhotoSize; - photoSize: infoProfilePhotoInnerSize; -} -infoFeedProfilePhoto: FeedUserpicButton(defaultFeedUserpicButton) { - size: infoProfilePhotoSize; - innerSize: infoProfilePhotoInnerSize; - innerPart: UserpicButton(defaultUserpicButton) { - size: size(35px, 35px); - photoSize: 35px; - } - -} -infoProfilePhotoLeft: 19px; -infoProfilePhotoTop: 18px; -infoProfilePhotoBottom: 18px; - -infoProfileStatusLeft: 109px; -infoProfileStatusRight: 20px; -infoProfileStatusTop: 58px; -infoProfileStatusLabel: FlatLabel(defaultFlatLabel) { +infoProfileStatus: FlatLabel(defaultFlatLabel) { maxHeight: 18px; textFg: windowSubTextFg; style: TextStyle(defaultTextStyle) { @@ -288,25 +281,46 @@ infoProfileStatusLabel: FlatLabel(defaultFlatLabel) { linkFontOver: normalFont; } } -infoProfileMegagroupStatusLabel: FlatLabel(infoProfileStatusLabel) { - style: defaultTextStyle; - palette: TextPalette(defaultTextPalette) { - linkFg: windowSubTextFg; +infoProfileCover: InfoProfileCover { + height: 108px; + photo: UserpicButton(defaultUserpicButton) { + size: infoProfilePhotoSize; + photoSize: infoProfilePhotoInnerSize; } + photoLeft: 19px; + photoTop: 18px; + name: FlatLabel(defaultFlatLabel) { + maxHeight: 24px; + textFg: windowBoldFg; + style: TextStyle(defaultTextStyle) { + font: font(16px semibold); + linkFont: font(16px semibold); + linkFontOver: font(16px semibold underline); + } + } + nameLeft: 109px; + nameTop: 32px; + status: infoProfileStatus; + statusLeft: 109px; + statusTop: 58px; + rightSkip: 20px; +} +infoProfileMegagroupCover: InfoProfileCover(infoProfileCover) { + status: FlatLabel(infoProfileStatus) { + style: defaultTextStyle; + palette: TextPalette(defaultTextPalette) { + linkFg: windowSubTextFg; + } + } +} +infoTopicCover: InfoProfileCover(infoProfileMegagroupCover) { + height: 77px; + nameLeft: 79px; + nameTop: 14px; + statusLeft: 79px; + statusTop: 38px; } -infoProfileNameLeft: infoProfileStatusLeft; -infoProfileNameRight: infoProfileStatusRight; -infoProfileNameTop: 32px; -infoProfileNameLabel: FlatLabel(infoProfileStatusLabel) { - maxHeight: 24px; - textFg: windowBoldFg; - style: TextStyle(defaultTextStyle) { - font: font(16px semibold); - linkFont: font(16px semibold); - linkFontOver: font(16px semibold underline); - } -} infoVerifiedCheckPosition: point(4px, 2px); infoVerifiedCheck: icon { { "profile_verified_star", profileVerifiedCheckBg }, @@ -392,7 +406,7 @@ infoLabeled: FlatLabel(infoLabeledOneLine) { margin: margins(5px, 5px, 5px, 5px); } -infoBlockHeaderLabel: FlatLabel(infoProfileStatusLabel) { +infoBlockHeaderLabel: FlatLabel(infoProfileStatus) { textFg: windowBoldFg; style: TextStyle(defaultTextStyle) { font: semiboldFont; @@ -719,15 +733,6 @@ topBarInfoButton: UserpicButton(defaultUserpicButton) { photoSize: topBarInfoButtonInnerSize; photoPosition: topBarInfoButtonInnerPosition; } -topBarFeedInfoButton: FeedUserpicButton(defaultFeedUserpicButton) { - size: topBarInfoButtonSize; - innerSize: topBarInfoButtonInnerSize; - innerPosition: topBarInfoButtonInnerPosition; - innerPart: UserpicButton(defaultUserpicButton) { - size: size(20px, 20px); - photoSize: 20px; - } -} topBarConnectingPosition: point(2px, 5px); topBarConnectingSkip: 6px; topBarConnectingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index 6212de6eb..01b0d9d5a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_peer.h" #include "data/data_changes.h" +#include "data/data_session.h" +#include "data/data_forum_topic.h" +#include "data/stickers/data_custom_emoji.h" #include "info/profile/info_profile_values.h" #include "info/profile/info_profile_badge.h" #include "info/profile/info_profile_emoji_status_panel.h" @@ -73,16 +76,49 @@ Cover::Cover( : Cover(parent, peer, controller, NameValue(peer)) { } +Cover::Cover( + QWidget *parent, + not_null topic, + not_null controller) +: Cover( + parent, + topic->channel(), + topic, + controller, + TitleValue(topic)) { +} + Cover::Cover( QWidget *parent, not_null peer, not_null controller, rpl::producer title) -: FixedHeightWidget( +: Cover( parent, - st::infoProfilePhotoTop - + st::infoProfilePhoto.size.height() - + st::infoProfilePhotoBottom) + peer, + nullptr, + controller, + std::move(title)) { +} + +[[nodiscard]] const style::InfoProfileCover &CoverStyle( + not_null peer, + Data::ForumTopic *topic) { + return topic + ? st::infoTopicCover + : peer->isMegagroup() + ? st::infoProfileMegagroupCover + : st::infoProfileCover; +} + +Cover::Cover( + QWidget *parent, + not_null peer, + Data::ForumTopic *topic, + not_null controller, + rpl::producer title) +: FixedHeightWidget(parent, CoverStyle(peer, topic).height) +, _st(CoverStyle(peer, topic)) , _controller(controller) , _peer(peer) , _emojiStatusPanel(peer->isSelf() @@ -98,19 +134,22 @@ Cover::Cover( return controller->isGifPausedAtLeastFor( Window::GifPauseReason::Layer); })) -, _userpic( - this, - controller, - _peer, - Ui::UserpicButton::Role::OpenPhoto, - st::infoProfilePhoto) -, _name(this, st::infoProfileNameLabel) -, _status( - this, - _peer->isMegagroup() - ? st::infoProfileMegagroupStatusLabel - : st::infoProfileStatusLabel) +, _userpic(topic + ? nullptr + : object_ptr( + this, + controller, + _peer, + Ui::UserpicButton::Role::OpenPhoto, + _st.photo)) +, _iconView(topic ? object_ptr(this) : nullptr) +, _name(this, _st.name) +, _status(this, _st.status) , _refreshStatusTimer([this] { refreshStatusText(); }) { + if (topic) { + setupIcon(topic); + } + _peer->updateFull(); _name->setSelectable(true); @@ -134,21 +173,56 @@ Cover::Cover( initViewers(std::move(title)); setupChildGeometry(); - _userpic->uploadPhotoRequests( + if (_userpic) { + _userpic->uploadPhotoRequests( + ) | rpl::start_with_next([=] { + _peer->session().api().peerPhoto().upload( + _peer, + _userpic->takeResultImage()); + }, _userpic->lifetime()); + } else { + // #TODO forum icon change on click if possible + } +} + +void Cover::setupIcon(not_null topic) { + const auto tag = Data::CustomEmojiManager::SizeTag::Large; + IconIdValue( + topic + ) | rpl::start_with_next([=](DocumentId id) { + _icon = id + ? topic->owner().customEmojiManager().create( + id, + [=] { _iconView->update(); }, + tag) + : nullptr; + }, lifetime()); + const auto size = Data::FrameSizeFromTag(tag); + _iconView->resize(size, size); + _iconView->paintRequest( ) | rpl::start_with_next([=] { - _peer->session().api().peerPhoto().upload( - _peer, - _userpic->takeResultImage()); - }, _userpic->lifetime()); + auto p = QPainter(_iconView.data()); + if (_icon) { + _icon->paint(p, { + .preview = st::windowBgOver->c, + .now = crl::now(), + .paused = _controller->isGifPausedAtLeastFor( + Window::GifPauseReason::Layer), + }); + } else { + + } + }, _iconView->lifetime()); } void Cover::setupChildGeometry() { widthValue( ) | rpl::start_with_next([this](int newWidth) { - _userpic->moveToLeft( - st::infoProfilePhotoLeft, - st::infoProfilePhotoTop, - newWidth); + if (_userpic) { + _userpic->moveToLeft(_st.photoLeft, _st.photoTop, newWidth); + } else { + _iconView->moveToLeft(_st.photoLeft, _st.photoTop, newWidth); + } refreshNameGeometry(newWidth); refreshStatusGeometry(newWidth); }, lifetime()); @@ -192,6 +266,9 @@ void Cover::initViewers(rpl::producer title) { } void Cover::refreshUploadPhotoOverlay() { + if (!_userpic) { + return; + } _userpic->switchChangePhotoOverlay([&] { if (const auto chat = _peer->asChat()) { return chat->canEditInformation(); @@ -255,31 +332,22 @@ Cover::~Cover() { } void Cover::refreshNameGeometry(int newWidth) { - auto nameLeft = st::infoProfileNameLeft; - auto nameTop = st::infoProfileNameTop; - auto nameWidth = newWidth - - nameLeft - - st::infoProfileNameRight; + auto nameWidth = newWidth - _st.nameLeft - _st.rightSkip; if (const auto widget = _badge->widget()) { nameWidth -= st::infoVerifiedCheckPosition.x() + widget->width(); } _name->resizeToNaturalWidth(nameWidth); - _name->moveToLeft(nameLeft, nameTop, newWidth); - const auto badgeLeft = nameLeft + _name->width(); - const auto badgeTop = nameTop; - const auto badgeBottom = nameTop + _name->height(); + _name->moveToLeft(_st.nameLeft, _st.nameTop, newWidth); + const auto badgeLeft = _st.nameLeft + _name->width(); + const auto badgeTop = _st.nameTop; + const auto badgeBottom = _st.nameTop + _name->height(); _badge->move(badgeLeft, badgeTop, badgeBottom); } void Cover::refreshStatusGeometry(int newWidth) { - auto statusWidth = newWidth - - st::infoProfileStatusLeft - - st::infoProfileStatusRight; + auto statusWidth = newWidth - _st.statusLeft - _st.rightSkip; _status->resizeToWidth(statusWidth); - _status->moveToLeft( - st::infoProfileStatusLeft, - st::infoProfileStatusTop, - newWidth); + _status->moveToLeft(_st.statusLeft, _st.statusTop, newWidth); } } // namespace Info::Profile diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.h b/Telegram/SourceFiles/info/profile/info_profile_cover.h index 5189f53b0..71080fa1d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.h +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.h @@ -25,11 +25,19 @@ namespace Ui::Text { struct CustomEmojiColored; } // namespace Ui::Text +namespace Data { +class ForumTopic; +} // namespace Data + namespace Info { class Controller; class Section; } // namespace Info +namespace style { +struct InfoProfileCover; +} // namespace style + namespace Info::Profile { class EmojiStatusPanel; @@ -41,6 +49,10 @@ public: QWidget *parent, not_null peer, not_null controller); + Cover( + QWidget *parent, + not_null topic, + not_null controller); Cover( QWidget *parent, not_null peer, @@ -55,6 +67,14 @@ public: } private: + Cover( + QWidget *parent, + not_null peer, + Data::ForumTopic *topic, + not_null controller, + rpl::producer title); + + void setupIcon(not_null topic); void setupChildGeometry(); void initViewers(rpl::producer title); void refreshStatusText(); @@ -62,6 +82,8 @@ private: void refreshStatusGeometry(int newWidth); void refreshUploadPhotoOverlay(); + const style::InfoProfileCover &_st; + const not_null _controller; const not_null _peer; const std::unique_ptr _emojiStatusPanel; @@ -69,6 +91,8 @@ private: int _onlineCount = 0; object_ptr _userpic; + object_ptr _iconView; + std::unique_ptr _icon; object_ptr _name = { nullptr }; object_ptr _status = { nullptr }; //object_ptr _dropArea = { nullptr }; diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index e56be98d7..73fbbdd3d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -66,11 +66,15 @@ InnerWidget::InnerWidget( object_ptr InnerWidget::setupContent( not_null parent) { auto result = object_ptr(parent); - _cover = result->add(object_ptr( - result, - _peer, - _controller->parentController(), - _topic ? TitleValue(_topic) : NameValue(_peer))); + _cover = _topic + ? result->add(object_ptr( + result, + _topic, + _controller->parentController())) + : result->add(object_ptr( + result, + _peer, + _controller->parentController())); _cover->showSection( ) | rpl::start_with_next([=](Section section) { _controller->showSection(_topic diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 5d150b7ee..7bbe7e62c 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -83,7 +83,17 @@ rpl::producer NameValue(not_null peer) { } rpl::producer TitleValue(not_null topic) { - return rpl::single(topic->title()); // #TODO forum title changes + return topic->session().changes().topicFlagsValue( + topic, + Data::TopicUpdate::Flag::Title + ) | rpl::map([=] { return topic->title(); }); +} + +rpl::producer IconIdValue(not_null topic) { + return topic->session().changes().topicFlagsValue( + topic, + Data::TopicUpdate::Flag::Icon + ) | rpl::map([=] { return topic->iconId(); }); } rpl::producer PhoneValue(not_null user) { diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index a687c9f6b..98e26b83d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -47,6 +47,8 @@ rpl::producer> MigratedOrMeValue( [[nodiscard]] rpl::producer NameValue(not_null peer); [[nodiscard]] rpl::producer TitleValue( not_null topic); +[[nodiscard]] rpl::producer IconIdValue( + not_null topic); [[nodiscard]] rpl::producer PhoneValue( not_null user); [[nodiscard]] rpl::producer PhoneOrHiddenValue( diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 55aca1303..d893c12fe 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -103,7 +103,7 @@ Cover::Cover( : FixedHeightWidget( parent, st::settingsPhotoTop - + st::infoProfilePhoto.size.height() + + st::infoProfileCover.photo.size.height() + st::settingsPhotoBottom) , _controller(controller) , _user(user) @@ -123,10 +123,10 @@ Cover::Cover( controller, _user, Ui::UserpicButton::Role::OpenPhoto, - st::infoProfilePhoto) -, _name(this, st::infoProfileNameLabel) + st::infoProfileCover.photo) +, _name(this, st::infoProfileCover.name) , _phone(this, st::defaultFlatLabel) -, _username(this, st::infoProfileMegagroupStatusLabel) { +, _username(this, st::infoProfileMegagroupCover.status) { _user->updateFull(); _name->setSelectable(true); @@ -216,7 +216,7 @@ void Cover::refreshNameGeometry(int newWidth) { const auto nameTop = st::settingsNameTop; auto nameWidth = newWidth - nameLeft - - st::infoProfileNameRight; + - st::infoProfileCover.rightSkip; if (const auto width = _badge.widget() ? _badge.widget()->width() : 0) { nameWidth -= st::infoVerifiedCheckPosition.x() + width; } @@ -231,7 +231,9 @@ void Cover::refreshNameGeometry(int newWidth) { void Cover::refreshPhoneGeometry(int newWidth) { const auto phoneLeft = st::settingsPhoneLeft; const auto phoneTop = st::settingsPhoneTop; - const auto phoneWidth = newWidth - phoneLeft - st::infoProfileNameRight; + const auto phoneWidth = newWidth + - phoneLeft + - st::infoProfileCover.rightSkip; _phone->resizeToWidth(phoneWidth); _phone->moveToLeft(phoneLeft, phoneTop, newWidth); } @@ -239,7 +241,7 @@ void Cover::refreshPhoneGeometry(int newWidth) { void Cover::refreshUsernameGeometry(int newWidth) { const auto usernameLeft = st::settingsUsernameLeft; const auto usernameTop = st::settingsUsernameTop; - const auto usernameRight = st::infoProfileNameRight; + const auto usernameRight = st::infoProfileCover.rightSkip; const auto usernameWidth = newWidth - usernameLeft - usernameRight; _username->resizeToWidth(usernameWidth); _username->moveToLeft(usernameLeft, usernameTop, newWidth);