mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Redesign edit account settings page.
This commit is contained in:
parent
a2ac9e10e2
commit
f31be7784b
13 changed files with 613 additions and 552 deletions
Binary file not shown.
Before Width: | Height: | Size: 318 B |
Binary file not shown.
Before Width: | Height: | Size: 647 B |
Binary file not shown.
Before Width: | Height: | Size: 788 B |
Binary file not shown.
Before Width: | Height: | Size: 992 B |
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "boxes/peers/edit_peer_permissions_box.h"
|
#include "boxes/peers/edit_peer_permissions_box.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
|
||||||
namespace Info {
|
namespace Info {
|
||||||
namespace Profile {
|
namespace Profile {
|
||||||
|
|
|
@ -38,37 +38,55 @@ inline auto ToSingleLine() {
|
||||||
rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
|
rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
|
||||||
not_null<PeerData*> peer);
|
not_null<PeerData*> peer);
|
||||||
|
|
||||||
rpl::producer<TextWithEntities> NameValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<TextWithEntities> NameValue(
|
||||||
rpl::producer<TextWithEntities> PhoneValue(not_null<UserData*> user);
|
not_null<PeerData*> peer);
|
||||||
rpl::producer<TextWithEntities> PhoneOrHiddenValue(not_null<UserData*> user);
|
[[nodiscard]] rpl::producer<TextWithEntities> PhoneValue(
|
||||||
rpl::producer<TextWithEntities> UsernameValue(not_null<UserData*> user);
|
not_null<UserData*> user);
|
||||||
|
[[nodiscard]] rpl::producer<TextWithEntities> PhoneOrHiddenValue(
|
||||||
|
not_null<UserData*> user);
|
||||||
|
[[nodiscard]] rpl::producer<TextWithEntities> UsernameValue(
|
||||||
|
not_null<UserData*> user);
|
||||||
[[nodiscard]] TextWithEntities AboutWithEntities(
|
[[nodiscard]] TextWithEntities AboutWithEntities(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const QString &value);
|
const QString &value);
|
||||||
rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<TextWithEntities> AboutValue(
|
||||||
rpl::producer<QString> LinkValue(not_null<PeerData*> peer);
|
not_null<PeerData*> peer);
|
||||||
rpl::producer<const ChannelLocation*> LocationValue(
|
[[nodiscard]] rpl::producer<QString> LinkValue(not_null<PeerData*> peer);
|
||||||
|
[[nodiscard]] rpl::producer<const ChannelLocation*> LocationValue(
|
||||||
not_null<ChannelData*> channel);
|
not_null<ChannelData*> channel);
|
||||||
rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<bool> NotificationsEnabledValue(
|
||||||
rpl::producer<bool> IsContactValue(not_null<UserData*> user);
|
not_null<PeerData*> peer);
|
||||||
rpl::producer<bool> CanInviteBotToGroupValue(not_null<UserData*> user);
|
[[nodiscard]] rpl::producer<bool> IsContactValue(not_null<UserData*> user);
|
||||||
rpl::producer<bool> CanShareContactValue(not_null<UserData*> user);
|
[[nodiscard]] rpl::producer<bool> CanInviteBotToGroupValue(
|
||||||
rpl::producer<bool> CanAddContactValue(not_null<UserData*> user);
|
not_null<UserData*> user);
|
||||||
rpl::producer<bool> AmInChannelValue(not_null<ChannelData*> channel);
|
[[nodiscard]] rpl::producer<bool> CanShareContactValue(
|
||||||
rpl::producer<int> MembersCountValue(not_null<PeerData*> peer);
|
not_null<UserData*> user);
|
||||||
rpl::producer<int> PendingRequestsCountValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<bool> CanAddContactValue(
|
||||||
rpl::producer<int> AdminsCountValue(not_null<PeerData*> peer);
|
not_null<UserData*> user);
|
||||||
rpl::producer<int> RestrictionsCountValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<bool> AmInChannelValue(
|
||||||
rpl::producer<int> RestrictedCountValue(not_null<ChannelData*> channel);
|
not_null<ChannelData*> channel);
|
||||||
rpl::producer<int> KickedCountValue(not_null<ChannelData*> channel);
|
[[nodiscard]] rpl::producer<int> MembersCountValue(not_null<PeerData*> peer);
|
||||||
rpl::producer<int> SharedMediaCountValue(
|
[[nodiscard]] rpl::producer<int> PendingRequestsCountValue(
|
||||||
|
not_null<PeerData*> peer);
|
||||||
|
[[nodiscard]] rpl::producer<int> AdminsCountValue(not_null<PeerData*> peer);
|
||||||
|
[[nodiscard]] rpl::producer<int> RestrictionsCountValue(
|
||||||
|
not_null<PeerData*> peer);
|
||||||
|
[[nodiscard]] rpl::producer<int> RestrictedCountValue(
|
||||||
|
not_null<ChannelData*> channel);
|
||||||
|
[[nodiscard]] rpl::producer<int> KickedCountValue(
|
||||||
|
not_null<ChannelData*> channel);
|
||||||
|
[[nodiscard]] rpl::producer<int> SharedMediaCountValue(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
PeerData *migrated,
|
PeerData *migrated,
|
||||||
Storage::SharedMediaType type);
|
Storage::SharedMediaType type);
|
||||||
rpl::producer<int> CommonGroupsCountValue(not_null<UserData*> user);
|
[[nodiscard]] rpl::producer<int> CommonGroupsCountValue(
|
||||||
rpl::producer<bool> CanAddMemberValue(not_null<PeerData*> peer);
|
not_null<UserData*> user);
|
||||||
rpl::producer<int> FullReactionsCountValue(not_null<Main::Session*> peer);
|
[[nodiscard]] rpl::producer<bool> CanAddMemberValue(
|
||||||
rpl::producer<int> AllowedReactionsCountValue(not_null<PeerData*> peer);
|
not_null<PeerData*> peer);
|
||||||
|
[[nodiscard]] rpl::producer<int> FullReactionsCountValue(
|
||||||
|
not_null<Main::Session*> peer);
|
||||||
|
[[nodiscard]] rpl::producer<int> AllowedReactionsCountValue(
|
||||||
|
not_null<PeerData*> peer);
|
||||||
|
|
||||||
enum class Badge {
|
enum class Badge {
|
||||||
None,
|
None,
|
||||||
|
@ -76,7 +94,7 @@ enum class Badge {
|
||||||
Scam,
|
Scam,
|
||||||
Fake,
|
Fake,
|
||||||
};
|
};
|
||||||
rpl::producer<Badge> BadgeValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<Badge> BadgeValue(not_null<PeerData*> peer);
|
||||||
|
|
||||||
} // namespace Profile
|
} // namespace Profile
|
||||||
} // namespace Info
|
} // namespace Info
|
||||||
|
|
|
@ -90,8 +90,6 @@ settingsIconPosition: icon {{ "settings/position", settingsIconFg }};
|
||||||
settingsIconPin: icon {{ "settings/pin", settingsIconFg }};
|
settingsIconPin: icon {{ "settings/pin", settingsIconFg }};
|
||||||
settingsIconDownload: icon {{ "settings/download", settingsIconFg }};
|
settingsIconDownload: icon {{ "settings/download", settingsIconFg }};
|
||||||
|
|
||||||
settingsSetPhotoSkip: 7px;
|
|
||||||
|
|
||||||
settingsCheckbox: Checkbox(defaultBoxCheckbox) {
|
settingsCheckbox: Checkbox(defaultBoxCheckbox) {
|
||||||
textPosition: point(15px, 1px);
|
textPosition: point(15px, 1px);
|
||||||
}
|
}
|
||||||
|
@ -138,35 +136,16 @@ settingsCloudPasswordLabel: FlatLabel(defaultFlatLabel) {
|
||||||
}
|
}
|
||||||
settingsCloudPasswordLabelPadding: margins(22px, 8px, 10px, 8px);
|
settingsCloudPasswordLabelPadding: margins(22px, 8px, 10px, 8px);
|
||||||
|
|
||||||
settingsInfoPhotoHeight: 175px;
|
settingsInfoPhotoHeight: 161px;
|
||||||
settingsInfoPhotoSize: 84px;
|
settingsInfoPhotoSize: 100px;
|
||||||
settingsInfoPhoto: UserpicButton(defaultUserpicButton) {
|
settingsInfoPhoto: UserpicButton(defaultUserpicButton) {
|
||||||
size: size(settingsInfoPhotoSize, settingsInfoPhotoSize);
|
size: size(settingsInfoPhotoSize, settingsInfoPhotoSize);
|
||||||
photoSize: settingsInfoPhotoSize;
|
photoSize: settingsInfoPhotoSize;
|
||||||
}
|
}
|
||||||
settingsInfoPhotoTop: 17px;
|
settingsInfoPhotoTop: 0px;
|
||||||
settingsInfoPhotoSkip: 16px;
|
settingsInfoPhotoSkip: 7px;
|
||||||
settingsInfoPhotoSet: defaultActiveButton;
|
settingsInfoNameSkip: -1px;
|
||||||
|
|
||||||
settingsInfoRow: SettingsButton(settingsButtonNoIcon) {
|
|
||||||
height: 62px;
|
|
||||||
padding: margins(0px, 0px, 0px, 0px);
|
|
||||||
}
|
|
||||||
settingsInfoIconPosition: point(22px, 18px);
|
|
||||||
settingsInfoValue: FlatLabel(defaultFlatLabel) {
|
|
||||||
textFg: windowFg;
|
|
||||||
style: boxTextStyle;
|
|
||||||
maxHeight: 20px;
|
|
||||||
}
|
|
||||||
settingsInfoValuePosition: point(78px, 14px);
|
|
||||||
settingsInfoAbout: FlatLabel(settingsInfoValue) {
|
|
||||||
textFg: windowSubTextFg;
|
|
||||||
style: defaultTextStyle;
|
|
||||||
}
|
|
||||||
settingsInfoAboutPosition: point(78px, 34px);
|
|
||||||
settingsInfoRightSkip: 60px;
|
|
||||||
settingsInfoEditRight: 14px;
|
|
||||||
settingsInfoEditIconOver: icon {{ "settings/settings_edit", menuIconFgOver }};
|
|
||||||
settingsBio: InputField(defaultInputField) {
|
settingsBio: InputField(defaultInputField) {
|
||||||
textBg: transparent;
|
textBg: transparent;
|
||||||
textMargins: margins(0px, 7px, 0px, 13px);
|
textMargins: margins(0px, 7px, 0px, 13px);
|
||||||
|
@ -178,21 +157,31 @@ settingsBio: InputField(defaultInputField) {
|
||||||
placeholderScale: 0.;
|
placeholderScale: 0.;
|
||||||
placeholderFont: normalFont;
|
placeholderFont: normalFont;
|
||||||
|
|
||||||
|
border: 0px;
|
||||||
|
borderActive: 0px;
|
||||||
|
|
||||||
heightMin: 32px;
|
heightMin: 32px;
|
||||||
|
|
||||||
font: boxTextFont;
|
font: boxTextFont;
|
||||||
}
|
}
|
||||||
settingsInfoAfterSkip: 14px;
|
settingsInfoAfterSkip: 14px;
|
||||||
settingsInfoName: icon {{ "settings/settings_name", menuIconFg }};
|
|
||||||
settingsInfoPhone: icon {{ "settings/settings_phone_number", menuIconFg }};
|
|
||||||
settingsInfoUsername: icon {{ "settings/settings_username", menuIconFg }};
|
|
||||||
|
|
||||||
settingsBioMargins: margins(20px, 6px, 20px, 6px);
|
settingsBioMargins: margins(22px, 6px, 22px, 4px);
|
||||||
settingsBioCountdown: FlatLabel(defaultFlatLabel) {
|
settingsBioCountdown: FlatLabel(defaultFlatLabel) {
|
||||||
style: boxTextStyle;
|
style: boxTextStyle;
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
settingsBioLabelPadding: margins(22px, 11px, 22px, 0px);
|
|
||||||
|
settingsCoverName: FlatLabel(defaultFlatLabel) {
|
||||||
|
style: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(17px semibold);
|
||||||
|
linkFont: font(17px semibold);
|
||||||
|
linkFontOver: font(17px semibold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settingsCoverStatus: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
|
||||||
settingsPrivacyEditLabelPadding: margins(22px, 11px, 22px, 11px);
|
settingsPrivacyEditLabelPadding: margins(22px, 11px, 22px, 11px);
|
||||||
|
|
||||||
|
|
|
@ -10,31 +10,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "editor/photo_editor_layer_widget.h"
|
#include "editor/photo_editor_layer_widget.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/wrap/vertical_layout_reorder.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/box_content_divider.h"
|
#include "ui/widgets/box_content_divider.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||||
#include "boxes/add_contact_box.h"
|
#include "boxes/add_contact_box.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
|
||||||
#include "boxes/change_phone_box.h"
|
#include "boxes/change_phone_box.h"
|
||||||
#include "boxes/username_box.h"
|
#include "boxes/username_box.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "data/data_peer_values.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "dialogs/ui/dialogs_layout.h"
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_account.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "main/main_domain.h"
|
||||||
|
#include "mtproto/mtproto_dc_options.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
#include "window/window_peer_menu.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "api/api_peer_photo.h"
|
#include "api/api_peer_photo.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_settings.h"
|
#include "styles/style_settings.h"
|
||||||
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
@ -44,11 +57,65 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kSaveBioTimeout = 1000;
|
constexpr auto kSaveBioTimeout = 1000;
|
||||||
|
|
||||||
|
class AccountsList final {
|
||||||
|
public:
|
||||||
|
AccountsList(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> currentAccountActivations() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::SlideWrap<Ui::SettingsButton>*> setupAdd();
|
||||||
|
void rebuild();
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
const not_null<Ui::VerticalLayout*> _outer;
|
||||||
|
int _outerIndex = 0;
|
||||||
|
|
||||||
|
Ui::SlideWrap<Ui::SettingsButton> *_addAccount = nullptr;
|
||||||
|
base::flat_map<
|
||||||
|
not_null<Main::Account*>,
|
||||||
|
base::unique_qptr<Ui::SettingsButton>> _watched;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _contextMenu;
|
||||||
|
std::unique_ptr<Ui::VerticalLayoutReorder> _reorder;
|
||||||
|
int _reordering = 0;
|
||||||
|
|
||||||
|
rpl::event_stream<> _currentAccountActivations;
|
||||||
|
|
||||||
|
base::binary_guard _accountSwitchGuard;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<TextWithEntities> StatusValue(
|
||||||
|
not_null<UserData*> user) {
|
||||||
|
return [=](auto consumer) {
|
||||||
|
auto lifetime = rpl::lifetime();
|
||||||
|
const auto timer = lifetime.make_state<base::Timer>();
|
||||||
|
const auto push = [=] {
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
consumer.put_next(Data::OnlineTextActive(user, now)
|
||||||
|
? Ui::Text::Link(Data::OnlineText(user, now))
|
||||||
|
: Ui::Text::WithEntities(Data::OnlineText(user, now)));
|
||||||
|
timer->callOnce(Data::OnlineChangeTimeout(user, now));
|
||||||
|
};
|
||||||
|
timer->setCallback(push);
|
||||||
|
user->session().changes().peerFlagsValue(
|
||||||
|
user,
|
||||||
|
Data::PeerUpdate::Flag::OnlineStatus
|
||||||
|
) | rpl::start_with_next(push, lifetime);
|
||||||
|
return lifetime;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void SetupPhoto(
|
void SetupPhoto(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<UserData*> self) {
|
not_null<UserData*> self) {
|
||||||
const auto wrap = container->add(object_ptr<Ui::BoxContentDivider>(
|
const auto wrap = container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
container,
|
container,
|
||||||
st::settingsInfoPhotoHeight));
|
st::settingsInfoPhotoHeight));
|
||||||
const auto photo = Ui::CreateChild<Ui::UserpicButton>(
|
const auto photo = Ui::CreateChild<Ui::UserpicButton>(
|
||||||
|
@ -57,33 +124,34 @@ void SetupPhoto(
|
||||||
self,
|
self,
|
||||||
Ui::UserpicButton::Role::OpenPhoto,
|
Ui::UserpicButton::Role::OpenPhoto,
|
||||||
st::settingsInfoPhoto);
|
st::settingsInfoPhoto);
|
||||||
const auto upload = Ui::CreateChild<Ui::RoundButton>(
|
|
||||||
|
const auto name = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
wrap,
|
wrap,
|
||||||
tr::lng_settings_upload(),
|
Info::Profile::NameValue(self),
|
||||||
st::settingsInfoPhotoSet);
|
st::settingsCoverName);
|
||||||
upload->setFullRadius(true);
|
const auto status = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
upload->addClickHandler([=] {
|
wrap,
|
||||||
auto callback = [=](QImage &&image) {
|
StatusValue(self),
|
||||||
self->session().api().peerPhoto().upload(self, std::move(image));
|
st::settingsCoverStatus);
|
||||||
};
|
|
||||||
Editor::PrepareProfilePhotoFromFile(
|
|
||||||
upload,
|
|
||||||
&controller->window(),
|
|
||||||
std::move(callback));
|
|
||||||
});
|
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
wrap->widthValue(),
|
wrap->widthValue(),
|
||||||
photo->widthValue(),
|
photo->widthValue(),
|
||||||
upload->widthValue()
|
name->widthValue(),
|
||||||
) | rpl::start_with_next([=](int max, int photoWidth, int uploadWidth) {
|
status->widthValue()
|
||||||
|
) | rpl::start_with_next([=](
|
||||||
|
int max,
|
||||||
|
int photoWidth,
|
||||||
|
int nameWidth,
|
||||||
|
int statusWidth) {
|
||||||
photo->moveToLeft(
|
photo->moveToLeft(
|
||||||
(max - photoWidth) / 2,
|
(max - photoWidth) / 2,
|
||||||
st::settingsInfoPhotoTop);
|
st::settingsInfoPhotoTop);
|
||||||
upload->moveToLeft(
|
name->moveToLeft(
|
||||||
(max - uploadWidth) / 2,
|
(max - nameWidth) / 2,
|
||||||
(st::settingsInfoPhotoTop
|
(photo->y() + photo->height() + st::settingsInfoPhotoSkip));
|
||||||
+ photo->height()
|
status->moveToLeft(
|
||||||
+ st::settingsInfoPhotoSkip));
|
(max - statusWidth) / 2,
|
||||||
|
(name->y() + name->height() + st::settingsInfoNameSkip));
|
||||||
}, photo->lifetime());
|
}, photo->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,12 +173,13 @@ void AddRow(
|
||||||
rpl::producer<TextWithEntities> value,
|
rpl::producer<TextWithEntities> value,
|
||||||
const QString ©Button,
|
const QString ©Button,
|
||||||
Fn<void()> edit,
|
Fn<void()> edit,
|
||||||
const style::icon &icon) {
|
IconDescriptor &&descriptor) {
|
||||||
const auto wrap = AddButton(
|
const auto wrap = AddButtonWithLabel(
|
||||||
container,
|
container,
|
||||||
rpl::single(QString()),
|
std::move(label),
|
||||||
st::settingsInfoRow,
|
std::move(value) | rpl::map([](const auto &t) { return t.text; }),
|
||||||
{ .icon = &icon });
|
st::settingsButton,
|
||||||
|
std::move(descriptor));
|
||||||
const auto forcopy = Ui::CreateChild<QString>(wrap.get());
|
const auto forcopy = Ui::CreateChild<QString>(wrap.get());
|
||||||
wrap->setAcceptBoth();
|
wrap->setAcceptBoth();
|
||||||
wrap->clicks(
|
wrap->clicks(
|
||||||
|
@ -136,62 +205,6 @@ void AddRow(
|
||||||
}) | rpl::start_with_next([=](const TextWithEntities &text) {
|
}) | rpl::start_with_next([=](const TextWithEntities &text) {
|
||||||
*forcopy = text.text;
|
*forcopy = text.text;
|
||||||
}, wrap->lifetime());
|
}, wrap->lifetime());
|
||||||
const auto text = Ui::CreateChild<Ui::FlatLabel>(
|
|
||||||
wrap.get(),
|
|
||||||
std::move(value),
|
|
||||||
st::settingsInfoValue);
|
|
||||||
text->setClickHandlerFilter([=](auto&&...) {
|
|
||||||
edit();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
base::duplicate(
|
|
||||||
existing
|
|
||||||
) | rpl::start_with_next([=](bool existing) {
|
|
||||||
wrap->setDisabled(!existing);
|
|
||||||
text->setAttribute(Qt::WA_TransparentForMouseEvents, existing);
|
|
||||||
text->setSelectable(existing);
|
|
||||||
text->setDoubleClickSelectsParagraph(existing);
|
|
||||||
}, text->lifetime());
|
|
||||||
|
|
||||||
const auto about = Ui::CreateChild<Ui::FlatLabel>(
|
|
||||||
wrap.get(),
|
|
||||||
std::move(label),
|
|
||||||
st::settingsInfoAbout);
|
|
||||||
about->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
|
|
||||||
const auto button = Ui::CreateChild<Ui::RpWidget>(wrap.get());
|
|
||||||
button->resize(st::settingsInfoEditIconOver.size());
|
|
||||||
button->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
button->paintRequest(
|
|
||||||
) | rpl::filter([=] {
|
|
||||||
return (wrap->isOver() || wrap->isDown()) && !wrap->isDisabled();
|
|
||||||
}) | rpl::start_with_next([=](QRect clip) {
|
|
||||||
Painter p(button);
|
|
||||||
st::settingsInfoEditIconOver.paint(p, QPoint(), button->width());
|
|
||||||
}, button->lifetime());
|
|
||||||
|
|
||||||
wrap->sizeValue(
|
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
|
||||||
const auto width = size.width();
|
|
||||||
text->resizeToWidth(width
|
|
||||||
- st::settingsInfoValuePosition.x()
|
|
||||||
- st::settingsInfoRightSkip);
|
|
||||||
text->moveToLeft(
|
|
||||||
st::settingsInfoValuePosition.x(),
|
|
||||||
st::settingsInfoValuePosition.y(),
|
|
||||||
width);
|
|
||||||
about->resizeToWidth(width
|
|
||||||
- st::settingsInfoAboutPosition.x()
|
|
||||||
- st::settingsInfoRightSkip);
|
|
||||||
about->moveToLeft(
|
|
||||||
st::settingsInfoAboutPosition.x(),
|
|
||||||
st::settingsInfoAboutPosition.y(),
|
|
||||||
width);
|
|
||||||
button->moveToRight(
|
|
||||||
st::settingsInfoEditRight,
|
|
||||||
(size.height() - button->height()) / 2,
|
|
||||||
width);
|
|
||||||
}, wrap->lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupRows(
|
void SetupRows(
|
||||||
|
@ -208,7 +221,7 @@ void SetupRows(
|
||||||
Info::Profile::NameValue(self),
|
Info::Profile::NameValue(self),
|
||||||
tr::lng_profile_copy_fullname(tr::now),
|
tr::lng_profile_copy_fullname(tr::now),
|
||||||
[=] { controller->show(Box<EditNameBox>(self)); },
|
[=] { controller->show(Box<EditNameBox>(self)); },
|
||||||
st::settingsInfoName);
|
{ &st::settingsIconUser, kIconLightBlue });
|
||||||
|
|
||||||
AddRow(
|
AddRow(
|
||||||
container,
|
container,
|
||||||
|
@ -216,7 +229,7 @@ void SetupRows(
|
||||||
Info::Profile::PhoneValue(self),
|
Info::Profile::PhoneValue(self),
|
||||||
tr::lng_profile_copy_phone(tr::now),
|
tr::lng_profile_copy_phone(tr::now),
|
||||||
[=] { controller->show(Box<ChangePhoneBox>(controller)); },
|
[=] { controller->show(Box<ChangePhoneBox>(controller)); },
|
||||||
st::settingsInfoPhone);
|
{ &st::settingsIconCalls, kIconGreen });
|
||||||
|
|
||||||
auto username = Info::Profile::UsernameValue(self);
|
auto username = Info::Profile::UsernameValue(self);
|
||||||
auto empty = base::duplicate(
|
auto empty = base::duplicate(
|
||||||
|
@ -251,17 +264,14 @@ void SetupRows(
|
||||||
std::move(value),
|
std::move(value),
|
||||||
tr::lng_context_copy_mention(tr::now),
|
tr::lng_context_copy_mention(tr::now),
|
||||||
[=] { controller->show(Box<UsernameBox>(session)); },
|
[=] { controller->show(Box<UsernameBox>(session)); },
|
||||||
st::settingsInfoUsername);
|
{ &st::settingsIconArchive, kIconLightOrange });
|
||||||
|
|
||||||
AddSkip(container, st::settingsInfoAfterSkip);
|
AddSkip(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupBio(
|
void SetupBio(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<UserData*> self) {
|
not_null<UserData*> self) {
|
||||||
AddDivider(container);
|
|
||||||
AddSkip(container);
|
|
||||||
|
|
||||||
const auto bioStyle = [] {
|
const auto bioStyle = [] {
|
||||||
auto result = st::settingsBio;
|
auto result = st::settingsBio;
|
||||||
result.textMargins.setRight(st::boxTextFont->spacew
|
result.textMargins.setRight(st::boxTextFont->spacew
|
||||||
|
@ -372,14 +382,345 @@ void SetupBio(
|
||||||
&self->session());
|
&self->session());
|
||||||
updated();
|
updated();
|
||||||
|
|
||||||
container->add(
|
AddDividerText(container, tr::lng_settings_about_bio());
|
||||||
object_ptr<Ui::FlatLabel>(
|
}
|
||||||
container,
|
|
||||||
tr::lng_settings_about_bio(),
|
|
||||||
st::boxDividerLabel),
|
|
||||||
st::settingsBioLabelPadding);
|
|
||||||
|
|
||||||
|
void SetupAccountsWrap(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
|
AddDivider(container);
|
||||||
|
AddSkip(container);
|
||||||
|
|
||||||
|
SetupAccounts(container, controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsAltShift(Qt::KeyboardModifiers modifiers) {
|
||||||
|
return (modifiers & Qt::ShiftModifier) && (modifiers & Qt::AltModifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::SettingsButton> MakeAccountButton(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> window,
|
||||||
|
not_null<Main::Account*> account,
|
||||||
|
Fn<void()> callback) {
|
||||||
|
const auto active = (account == &Core::App().activeAccount());
|
||||||
|
const auto session = &account->session();
|
||||||
|
const auto user = session->user();
|
||||||
|
|
||||||
|
auto text = rpl::single(
|
||||||
|
user->name
|
||||||
|
) | rpl::then(session->changes().realtimeNameUpdates(
|
||||||
|
user
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return user->name;
|
||||||
|
}));
|
||||||
|
auto result = object_ptr<Ui::SettingsButton>(
|
||||||
|
parent,
|
||||||
|
std::move(text),
|
||||||
|
st::mainMenuAddAccountButton);
|
||||||
|
const auto raw = result.data();
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
State(QWidget *parent) : userpic(parent) {
|
||||||
|
userpic.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui::RpWidget userpic;
|
||||||
|
std::shared_ptr<Data::CloudImageView> view;
|
||||||
|
base::unique_qptr<Ui::PopupMenu> menu;
|
||||||
|
};
|
||||||
|
const auto state = raw->lifetime().make_state<State>(raw);
|
||||||
|
|
||||||
|
if (!active) {
|
||||||
|
AddUnreadBadge(raw, rpl::single(rpl::empty) | rpl::then(
|
||||||
|
session->data().unreadBadgeChanges()
|
||||||
|
) | rpl::map([=] {
|
||||||
|
auto &owner = session->data();
|
||||||
|
return UnreadBadge{
|
||||||
|
owner.unreadBadge(),
|
||||||
|
owner.unreadBadgeMuted(),
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto userpicSkip = 2 * st::mainMenuAccountLine + st::lineWidth;
|
||||||
|
const auto userpicSize = st::mainMenuAccountSize
|
||||||
|
+ userpicSkip * 2;
|
||||||
|
raw->heightValue(
|
||||||
|
) | rpl::start_with_next([=](int height) {
|
||||||
|
const auto left = st::mainMenuAddAccountButton.iconLeft
|
||||||
|
+ (st::mainMenuAddAccount.width() - userpicSize) / 2;
|
||||||
|
const auto top = (height - userpicSize) / 2;
|
||||||
|
state->userpic.setGeometry(left, top, userpicSize, userpicSize);
|
||||||
|
}, state->userpic.lifetime());
|
||||||
|
|
||||||
|
state->userpic.paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = Painter(&state->userpic);
|
||||||
|
const auto size = st::mainMenuAccountSize;
|
||||||
|
const auto line = st::mainMenuAccountLine;
|
||||||
|
const auto skip = 2 * line + st::lineWidth;
|
||||||
|
const auto full = size + skip * 2;
|
||||||
|
user->paintUserpicLeft(p, state->view, skip, skip, full, size);
|
||||||
|
if (active) {
|
||||||
|
const auto shift = st::lineWidth + (line * 0.5);
|
||||||
|
const auto diameter = full - 2 * shift;
|
||||||
|
const auto rect = QRectF(shift, shift, diameter, diameter);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
auto pen = st::windowBgActive->p; // The same as '+' in add.
|
||||||
|
pen.setWidthF(line);
|
||||||
|
p.setPen(pen);
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
p.drawEllipse(rect);
|
||||||
|
}
|
||||||
|
}, state->userpic.lifetime());
|
||||||
|
|
||||||
|
raw->setAcceptBoth(true);
|
||||||
|
raw->clicks(
|
||||||
|
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
||||||
|
if (which == Qt::LeftButton) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
} else if (which != Qt::RightButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto addAction = [&](
|
||||||
|
const QString &text,
|
||||||
|
Fn<void()> callback,
|
||||||
|
const style::icon *icon) {
|
||||||
|
return state->menu->addAction(
|
||||||
|
text,
|
||||||
|
crl::guard(raw, std::move(callback)),
|
||||||
|
icon);
|
||||||
|
};
|
||||||
|
if (!state->menu && IsAltShift(raw->clickModifiers())) {
|
||||||
|
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
raw,
|
||||||
|
st::popupMenuWithIcons);
|
||||||
|
Window::MenuAddMarkAsReadAllChatsAction(window, addAction);
|
||||||
|
state->menu->popup(QCursor::pos());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (&session->account() == &Core::App().activeAccount()
|
||||||
|
|| state->menu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
raw,
|
||||||
|
st::popupMenuWithIcons);
|
||||||
|
addAction(tr::lng_menu_activate(tr::now), [=] {
|
||||||
|
Core::App().domain().activate(&session->account());
|
||||||
|
}, &st::menuIconProfile);
|
||||||
|
addAction(tr::lng_settings_logout(tr::now), [=] {
|
||||||
|
const auto callback = [=](Fn<void()> &&close) {
|
||||||
|
close();
|
||||||
|
Core::App().logoutWithChecks(&session->account());
|
||||||
|
};
|
||||||
|
window->show(
|
||||||
|
Ui::MakeConfirmBox({
|
||||||
|
.text = tr::lng_sure_logout(),
|
||||||
|
.confirmed = crl::guard(session, callback),
|
||||||
|
.confirmText = tr::lng_settings_logout(),
|
||||||
|
.confirmStyle = &st::attentionBoxButton,
|
||||||
|
}),
|
||||||
|
Ui::LayerOption::CloseOther);
|
||||||
|
}, &st::menuIconLeave);
|
||||||
|
state->menu->popup(QCursor::pos());
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<not_null<Main::Account*>> OrderedAccounts() {
|
||||||
|
using namespace Main;
|
||||||
|
|
||||||
|
const auto order = Core::App().settings().accountsOrder();
|
||||||
|
auto accounts = ranges::views::all(
|
||||||
|
Core::App().domain().accounts()
|
||||||
|
) | ranges::views::transform([](const Domain::AccountWithIndex &a) {
|
||||||
|
return not_null{ a.account.get() };
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
ranges::stable_sort(accounts, [&](
|
||||||
|
not_null<Account*> a,
|
||||||
|
not_null<Account*> b) {
|
||||||
|
const auto aIt = a->sessionExists()
|
||||||
|
? ranges::find(order, a->session().uniqueId())
|
||||||
|
: end(order);
|
||||||
|
const auto bIt = b->sessionExists()
|
||||||
|
? ranges::find(order, b->session().uniqueId())
|
||||||
|
: end(order);
|
||||||
|
return aIt < bIt;
|
||||||
|
});
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountsList::AccountsList(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller)
|
||||||
|
: _controller(controller)
|
||||||
|
, _outer(container)
|
||||||
|
, _outerIndex(container->count()) {
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> AccountsList::currentAccountActivations() const {
|
||||||
|
return _currentAccountActivations.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountsList::setup() {
|
||||||
|
_addAccount = setupAdd();
|
||||||
|
|
||||||
|
rpl::single(rpl::empty) | rpl::then(
|
||||||
|
Core::App().domain().accountsChanges()
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto &list = Core::App().domain().accounts();
|
||||||
|
const auto exists = [&](not_null<Main::Account*> account) {
|
||||||
|
for (const auto &[index, existing] : list) {
|
||||||
|
if (account == existing.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
for (auto i = _watched.begin(); i != _watched.end();) {
|
||||||
|
if (!exists(i->first)) {
|
||||||
|
i = _watched.erase(i);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &[index, account] : list) {
|
||||||
|
if (_watched.emplace(account.get()).second) {
|
||||||
|
account->sessionChanges(
|
||||||
|
) | rpl::start_with_next([=](Main::Session *session) {
|
||||||
|
rebuild();
|
||||||
|
}, _outer->lifetime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rebuild();
|
||||||
|
}, _outer->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
not_null<Ui::SlideWrap<Ui::SettingsButton>*> AccountsList::setupAdd() {
|
||||||
|
const auto result = _outer->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||||
|
_outer.get(),
|
||||||
|
CreateButton(
|
||||||
|
_outer.get(),
|
||||||
|
tr::lng_menu_add_account(),
|
||||||
|
st::mainMenuAddAccountButton,
|
||||||
|
{
|
||||||
|
&st::mainMenuAddAccount,
|
||||||
|
0,
|
||||||
|
IconType::Round,
|
||||||
|
&st::windowBgActive
|
||||||
|
})))->setDuration(0);
|
||||||
|
const auto button = result->entity();
|
||||||
|
|
||||||
|
const auto add = [=](MTP::Environment environment) {
|
||||||
|
Core::App().preventOrInvoke([=] {
|
||||||
|
Core::App().domain().addActivated(environment);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
button->setAcceptBoth(true);
|
||||||
|
button->clicks(
|
||||||
|
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
||||||
|
if (which == Qt::LeftButton) {
|
||||||
|
add(MTP::Environment::Production);
|
||||||
|
return;
|
||||||
|
} else if (which != Qt::RightButton
|
||||||
|
|| !IsAltShift(button->clickModifiers())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_contextMenu = base::make_unique_q<Ui::PopupMenu>(_outer);
|
||||||
|
_contextMenu->addAction("Production Server", [=] {
|
||||||
|
add(MTP::Environment::Production);
|
||||||
|
});
|
||||||
|
_contextMenu->addAction("Test Server", [=] {
|
||||||
|
add(MTP::Environment::Test);
|
||||||
|
});
|
||||||
|
_contextMenu->popup(QCursor::pos());
|
||||||
|
}, button->lifetime());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountsList::rebuild() {
|
||||||
|
const auto inner = _outer->insert(
|
||||||
|
_outerIndex,
|
||||||
|
object_ptr<Ui::VerticalLayout>(_outer.get()));
|
||||||
|
|
||||||
|
_reorder = std::make_unique<Ui::VerticalLayoutReorder>(inner);
|
||||||
|
_reorder->updates(
|
||||||
|
) | rpl::start_with_next([=](Ui::VerticalLayoutReorder::Single data) {
|
||||||
|
using State = Ui::VerticalLayoutReorder::State;
|
||||||
|
if (data.state == State::Started) {
|
||||||
|
++_reordering;
|
||||||
|
} else {
|
||||||
|
Ui::PostponeCall(inner, [=] {
|
||||||
|
--_reordering;
|
||||||
|
});
|
||||||
|
if (data.state == State::Applied) {
|
||||||
|
std::vector<uint64> order;
|
||||||
|
order.reserve(inner->count());
|
||||||
|
for (auto i = 0; i < inner->count(); i++) {
|
||||||
|
for (const auto &[account, button] : _watched) {
|
||||||
|
if (button.get() == inner->widgetAt(i)) {
|
||||||
|
order.push_back(account->session().uniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Core::App().settings().setAccountsOrder(order);
|
||||||
|
Core::App().saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, inner->lifetime());
|
||||||
|
|
||||||
|
const auto list = OrderedAccounts();
|
||||||
|
for (const auto &account : list) {
|
||||||
|
auto i = _watched.find(account);
|
||||||
|
Assert(i != _watched.end());
|
||||||
|
|
||||||
|
auto &button = i->second;
|
||||||
|
if (!account->sessionExists() || list.size() == 1) {
|
||||||
|
button = nullptr;
|
||||||
|
} else if (!button) {
|
||||||
|
auto callback = [=] {
|
||||||
|
if (_reordering) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (account == &Core::App().domain().active()) {
|
||||||
|
_currentAccountActivations.fire({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto activate = [=, guard = _accountSwitchGuard.make_guard()]{
|
||||||
|
if (guard) {
|
||||||
|
_reorder->finishReordering();
|
||||||
|
Core::App().domain().maybeActivate(account);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
base::call_delayed(
|
||||||
|
st::defaultRippleAnimation.hideDuration,
|
||||||
|
account,
|
||||||
|
std::move(activate));
|
||||||
|
};
|
||||||
|
button.reset(inner->add(MakeAccountButton(
|
||||||
|
inner,
|
||||||
|
_controller,
|
||||||
|
account,
|
||||||
|
std::move(callback))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inner->resizeToWidth(_outer->width());
|
||||||
|
|
||||||
|
_addAccount->toggle(
|
||||||
|
(inner->count() < Main::Domain::kMaxAccounts),
|
||||||
|
anim::type::instant);
|
||||||
|
|
||||||
|
_reorder->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -397,10 +738,91 @@ void Information::setupContent(
|
||||||
|
|
||||||
const auto self = controller->session().user();
|
const auto self = controller->session().user();
|
||||||
SetupPhoto(content, controller, self);
|
SetupPhoto(content, controller, self);
|
||||||
SetupRows(content, controller, self);
|
|
||||||
SetupBio(content, self);
|
SetupBio(content, self);
|
||||||
|
SetupRows(content, controller, self);
|
||||||
|
SetupAccountsWrap(content, controller);
|
||||||
|
|
||||||
Ui::ResizeFitChild(this, content);
|
Ui::ResizeFitChild(this, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AccountsEvents SetupAccounts(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
|
const auto list = container->lifetime().make_state<AccountsList>(
|
||||||
|
container,
|
||||||
|
controller);
|
||||||
|
return {
|
||||||
|
.currentAccountActivations = list->currentAccountActivations(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialogs::Ui::UnreadBadgeStyle BadgeStyle() {
|
||||||
|
auto result = Dialogs::Ui::UnreadBadgeStyle();
|
||||||
|
result.font = st::mainMenuBadgeFont;
|
||||||
|
result.size = st::mainMenuBadgeSize;
|
||||||
|
result.sizeId = Dialogs::Ui::UnreadBadgeInMainMenu;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUnreadBadge(
|
||||||
|
not_null<Ui::SettingsButton*> button,
|
||||||
|
rpl::producer<UnreadBadge> value) {
|
||||||
|
struct State {
|
||||||
|
State(QWidget *parent) : widget(parent) {
|
||||||
|
widget.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui::RpWidget widget;
|
||||||
|
Dialogs::Ui::UnreadBadgeStyle st = BadgeStyle();
|
||||||
|
int count = 0;
|
||||||
|
QString string;
|
||||||
|
};
|
||||||
|
const auto state = button->lifetime().make_state<State>(button);
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
value
|
||||||
|
) | rpl::start_with_next([=](UnreadBadge badge) {
|
||||||
|
state->st.muted = badge.muted;
|
||||||
|
state->count = badge.count;
|
||||||
|
if (!state->count) {
|
||||||
|
state->widget.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->string = Lang::FormatCountToShort(state->count).string;
|
||||||
|
state->widget.resize(CountUnreadBadgeSize(state->string, state->st));
|
||||||
|
if (state->widget.isHidden()) {
|
||||||
|
state->widget.show();
|
||||||
|
}
|
||||||
|
}, state->widget.lifetime());
|
||||||
|
|
||||||
|
state->widget.paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = Painter(&state->widget);
|
||||||
|
Dialogs::Ui::PaintUnreadBadge(
|
||||||
|
p,
|
||||||
|
state->string,
|
||||||
|
state->widget.width(),
|
||||||
|
0,
|
||||||
|
state->st);
|
||||||
|
}, state->widget.lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
button->sizeValue(),
|
||||||
|
state->widget.sizeValue(),
|
||||||
|
state->widget.shownValue()
|
||||||
|
) | rpl::start_with_next([=](QSize outer, QSize inner, bool shown) {
|
||||||
|
auto padding = button->st().padding;
|
||||||
|
if (shown) {
|
||||||
|
state->widget.moveToRight(
|
||||||
|
padding.right(),
|
||||||
|
(outer.height() - inner.height()) / 2,
|
||||||
|
outer.width());
|
||||||
|
padding.setRight(padding.right()
|
||||||
|
+ inner.width()
|
||||||
|
+ button->st().style.font->spacew);
|
||||||
|
}
|
||||||
|
button->setPaddingOverride(padding);
|
||||||
|
}, state->widget.lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
|
||||||
|
namespace Dialogs::Ui {
|
||||||
|
struct UnreadBadgeStyle;
|
||||||
|
} // namespace Dialogs::Ui
|
||||||
|
|
||||||
namespace Settings {
|
namespace Settings {
|
||||||
|
|
||||||
class Information : public Section {
|
class Information : public Section {
|
||||||
|
@ -22,4 +26,21 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AccountsEvents {
|
||||||
|
rpl::producer<> currentAccountActivations;
|
||||||
|
};
|
||||||
|
AccountsEvents SetupAccounts(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller);
|
||||||
|
|
||||||
|
[[nodiscard]] Dialogs::Ui::UnreadBadgeStyle BadgeStyle();
|
||||||
|
|
||||||
|
struct UnreadBadge {
|
||||||
|
int count = 0;
|
||||||
|
bool muted = false;
|
||||||
|
};
|
||||||
|
void AddUnreadBadge(
|
||||||
|
not_null<Ui::SettingsButton*> button,
|
||||||
|
rpl::producer<UnreadBadge> value);
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
#include "support/support_templates.h"
|
#include "support/support_templates.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
#include "settings/settings_information.h"
|
||||||
#include "base/qt_signal_producer.h"
|
#include "base/qt_signal_producer.h"
|
||||||
#include "boxes/about_box.h"
|
#include "boxes/about_box.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
@ -66,10 +67,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Window {
|
namespace Window {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] bool IsAltShift(Qt::KeyboardModifiers modifiers) {
|
|
||||||
return (modifiers & Qt::ShiftModifier) && (modifiers & Qt::AltModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowCallsBox(not_null<Window::SessionController*> window) {
|
void ShowCallsBox(not_null<Window::SessionController*> window) {
|
||||||
auto controller = std::make_unique<Calls::BoxController>(window);
|
auto controller = std::make_unique<Calls::BoxController>(window);
|
||||||
const auto initBox = [
|
const auto initBox = [
|
||||||
|
@ -111,234 +108,6 @@ void ShowCallsBox(not_null<Window::SessionController*> window) {
|
||||||
window->show(Box<PeerListBox>(std::move(controller), initBox));
|
window->show(Box<PeerListBox>(std::move(controller), initBox));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<not_null<Main::Account*>> OrderedAccounts() {
|
|
||||||
const auto order = Core::App().settings().accountsOrder();
|
|
||||||
auto accounts = ranges::views::all(
|
|
||||||
Core::App().domain().accounts()
|
|
||||||
) | ranges::views::transform([](const Main::Domain::AccountWithIndex &a) {
|
|
||||||
return not_null{ a.account.get() };
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
ranges::stable_sort(accounts, [&](
|
|
||||||
not_null<Main::Account*> a,
|
|
||||||
not_null<Main::Account*> b) {
|
|
||||||
const auto aIt = a->sessionExists()
|
|
||||||
? ranges::find(order, a->session().uniqueId())
|
|
||||||
: end(order);
|
|
||||||
const auto bIt = b->sessionExists()
|
|
||||||
? ranges::find(order, b->session().uniqueId())
|
|
||||||
: end(order);
|
|
||||||
return aIt < bIt;
|
|
||||||
});
|
|
||||||
return accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnreadBadge {
|
|
||||||
int count = 0;
|
|
||||||
bool muted = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] Dialogs::Ui::UnreadBadgeStyle BadgeStyle() {
|
|
||||||
auto result = Dialogs::Ui::UnreadBadgeStyle();
|
|
||||||
result.font = st::mainMenuBadgeFont;
|
|
||||||
result.size = st::mainMenuBadgeSize;
|
|
||||||
result.sizeId = Dialogs::Ui::UnreadBadgeInMainMenu;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddUnreadBadge(
|
|
||||||
not_null<Ui::SettingsButton*> button,
|
|
||||||
rpl::producer<UnreadBadge> value) {
|
|
||||||
struct State {
|
|
||||||
State(QWidget *parent) : widget(parent) {
|
|
||||||
widget.setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ui::RpWidget widget;
|
|
||||||
Dialogs::Ui::UnreadBadgeStyle st = BadgeStyle();
|
|
||||||
int count = 0;
|
|
||||||
QString string;
|
|
||||||
};
|
|
||||||
const auto state = button->lifetime().make_state<State>(button);
|
|
||||||
|
|
||||||
std::move(
|
|
||||||
value
|
|
||||||
) | rpl::start_with_next([=](UnreadBadge badge) {
|
|
||||||
state->st.muted = badge.muted;
|
|
||||||
state->count = badge.count;
|
|
||||||
if (!state->count) {
|
|
||||||
state->widget.hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state->string = Lang::FormatCountToShort(state->count).string;
|
|
||||||
state->widget.resize(CountUnreadBadgeSize(state->string, state->st));
|
|
||||||
if (state->widget.isHidden()) {
|
|
||||||
state->widget.show();
|
|
||||||
}
|
|
||||||
}, state->widget.lifetime());
|
|
||||||
|
|
||||||
state->widget.paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
auto p = Painter(&state->widget);
|
|
||||||
Dialogs::Ui::PaintUnreadBadge(
|
|
||||||
p,
|
|
||||||
state->string,
|
|
||||||
state->widget.width(),
|
|
||||||
0,
|
|
||||||
state->st);
|
|
||||||
}, state->widget.lifetime());
|
|
||||||
|
|
||||||
rpl::combine(
|
|
||||||
button->sizeValue(),
|
|
||||||
state->widget.sizeValue(),
|
|
||||||
state->widget.shownValue()
|
|
||||||
) | rpl::start_with_next([=](QSize outer, QSize inner, bool shown) {
|
|
||||||
auto padding = button->st().padding;
|
|
||||||
if (shown) {
|
|
||||||
state->widget.moveToRight(
|
|
||||||
padding.right(),
|
|
||||||
(outer.height() - inner.height()) / 2,
|
|
||||||
outer.width());
|
|
||||||
padding.setRight(padding.right()
|
|
||||||
+ inner.width()
|
|
||||||
+ button->st().style.font->spacew);
|
|
||||||
}
|
|
||||||
button->setPaddingOverride(padding);
|
|
||||||
}, state->widget.lifetime());
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::SettingsButton> MakeAccountButton(
|
|
||||||
QWidget *parent,
|
|
||||||
not_null<Window::SessionController*> window,
|
|
||||||
not_null<Main::Account*> account,
|
|
||||||
Fn<void()> callback) {
|
|
||||||
const auto active = (account == &Core::App().activeAccount());
|
|
||||||
const auto session = &account->session();
|
|
||||||
const auto user = session->user();
|
|
||||||
|
|
||||||
auto text = rpl::single(
|
|
||||||
user->name
|
|
||||||
) | rpl::then(session->changes().realtimeNameUpdates(
|
|
||||||
user
|
|
||||||
) | rpl::map([=] {
|
|
||||||
return user->name;
|
|
||||||
}));
|
|
||||||
auto result = object_ptr<Ui::SettingsButton>(
|
|
||||||
parent,
|
|
||||||
std::move(text),
|
|
||||||
st::mainMenuAddAccountButton);
|
|
||||||
const auto raw = result.data();
|
|
||||||
|
|
||||||
struct State {
|
|
||||||
State(QWidget *parent) : userpic(parent) {
|
|
||||||
userpic.setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ui::RpWidget userpic;
|
|
||||||
std::shared_ptr<Data::CloudImageView> view;
|
|
||||||
base::unique_qptr<Ui::PopupMenu> menu;
|
|
||||||
};
|
|
||||||
const auto state = raw->lifetime().make_state<State>(raw);
|
|
||||||
|
|
||||||
if (!active) {
|
|
||||||
AddUnreadBadge(raw, rpl::single(rpl::empty) | rpl::then(
|
|
||||||
session->data().unreadBadgeChanges()
|
|
||||||
) | rpl::map([=] {
|
|
||||||
auto &owner = session->data();
|
|
||||||
return UnreadBadge{
|
|
||||||
owner.unreadBadge(),
|
|
||||||
owner.unreadBadgeMuted(),
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto userpicSkip = 2 * st::mainMenuAccountLine + st::lineWidth;
|
|
||||||
const auto userpicSize = st::mainMenuAccountSize
|
|
||||||
+ userpicSkip * 2;
|
|
||||||
raw->heightValue(
|
|
||||||
) | rpl::start_with_next([=](int height) {
|
|
||||||
const auto left = st::mainMenuAddAccountButton.iconLeft
|
|
||||||
+ (st::mainMenuAddAccount.width() - userpicSize) / 2;
|
|
||||||
const auto top = (height - userpicSize) / 2;
|
|
||||||
state->userpic.setGeometry(left, top, userpicSize, userpicSize);
|
|
||||||
}, state->userpic.lifetime());
|
|
||||||
|
|
||||||
state->userpic.paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
auto p = Painter(&state->userpic);
|
|
||||||
const auto size = st::mainMenuAccountSize;
|
|
||||||
const auto line = st::mainMenuAccountLine;
|
|
||||||
const auto skip = 2 * line + st::lineWidth;
|
|
||||||
const auto full = size + skip * 2;
|
|
||||||
user->paintUserpicLeft(p, state->view, skip, skip, full, size);
|
|
||||||
if (active) {
|
|
||||||
const auto shift = st::lineWidth + (line * 0.5);
|
|
||||||
const auto diameter = full - 2 * shift;
|
|
||||||
const auto rect = QRectF(shift, shift, diameter, diameter);
|
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
|
||||||
auto pen = st::windowBgActive->p; // The same as '+' in add.
|
|
||||||
pen.setWidthF(line);
|
|
||||||
p.setPen(pen);
|
|
||||||
p.setBrush(Qt::NoBrush);
|
|
||||||
p.drawEllipse(rect);
|
|
||||||
}
|
|
||||||
}, state->userpic.lifetime());
|
|
||||||
|
|
||||||
raw->setAcceptBoth(true);
|
|
||||||
raw->clicks(
|
|
||||||
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
|
||||||
if (which == Qt::LeftButton) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
} else if (which != Qt::RightButton) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto addAction = [&](
|
|
||||||
const QString &text,
|
|
||||||
Fn<void()> callback,
|
|
||||||
const style::icon *icon) {
|
|
||||||
return state->menu->addAction(
|
|
||||||
text,
|
|
||||||
crl::guard(raw, std::move(callback)),
|
|
||||||
icon);
|
|
||||||
};
|
|
||||||
if (!state->menu && IsAltShift(raw->clickModifiers())) {
|
|
||||||
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
|
||||||
raw,
|
|
||||||
st::popupMenuWithIcons);
|
|
||||||
MenuAddMarkAsReadAllChatsAction(window, addAction);
|
|
||||||
state->menu->popup(QCursor::pos());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (&session->account() == &Core::App().activeAccount()
|
|
||||||
|| state->menu) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
|
||||||
raw,
|
|
||||||
st::popupMenuWithIcons);
|
|
||||||
addAction(tr::lng_menu_activate(tr::now), [=] {
|
|
||||||
Core::App().domain().activate(&session->account());
|
|
||||||
}, &st::menuIconProfile);
|
|
||||||
addAction(tr::lng_settings_logout(tr::now), [=] {
|
|
||||||
const auto callback = [=](Fn<void()> &&close) {
|
|
||||||
close();
|
|
||||||
Core::App().logoutWithChecks(&session->account());
|
|
||||||
};
|
|
||||||
window->show(
|
|
||||||
Ui::MakeConfirmBox({
|
|
||||||
.text = tr::lng_sure_logout(),
|
|
||||||
.confirmed = crl::guard(session, callback),
|
|
||||||
.confirmText = tr::lng_settings_logout(),
|
|
||||||
.confirmStyle = &st::attentionBoxButton,
|
|
||||||
}),
|
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
}, &st::menuIconLeave);
|
|
||||||
state->menu->popup(QCursor::pos());
|
|
||||||
}, raw->lifetime());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class MainMenu::ToggleAccountsButton final : public Ui::AbstractButton {
|
class MainMenu::ToggleAccountsButton final : public Ui::AbstractButton {
|
||||||
|
@ -462,7 +231,7 @@ void MainMenu::ToggleAccountsButton::paintUnreadBadge(Painter &p) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto st = BadgeStyle();
|
auto st = Settings::BadgeStyle();
|
||||||
const auto right = width()
|
const auto right = width()
|
||||||
- st::mainMenuTogglePosition.x()
|
- st::mainMenuTogglePosition.x()
|
||||||
- st::mainMenuToggleSize * 3;
|
- st::mainMenuToggleSize * 3;
|
||||||
|
@ -484,7 +253,7 @@ void MainMenu::ToggleAccountsButton::validateUnreadBadge() {
|
||||||
}
|
}
|
||||||
_unreadBadge = computeUnreadBadge();
|
_unreadBadge = computeUnreadBadge();
|
||||||
|
|
||||||
auto st = BadgeStyle();
|
auto st = Settings::BadgeStyle();
|
||||||
_rightSkip = base
|
_rightSkip = base
|
||||||
+ Dialogs::Ui::CountUnreadBadgeSize(_unreadBadge, st).width()
|
+ Dialogs::Ui::CountUnreadBadgeSize(_unreadBadge, st).width()
|
||||||
+ 2 * st::mainMenuToggleSize;
|
+ 2 * st::mainMenuToggleSize;
|
||||||
|
@ -739,6 +508,7 @@ void MainMenu::setupArchive() {
|
||||||
return folder && (folder->id() == Data::Folder::kId);
|
return folder && (folder->id() == Data::Folder::kId);
|
||||||
}) | rpl::take(1);
|
}) | rpl::take(1);
|
||||||
|
|
||||||
|
using namespace Settings;
|
||||||
AddUnreadBadge(button, rpl::single(rpl::empty) | rpl::then(std::move(
|
AddUnreadBadge(button, rpl::single(rpl::empty) | rpl::then(std::move(
|
||||||
folderValue
|
folderValue
|
||||||
) | rpl::map([=](not_null<Data::Folder*> folder) {
|
) | rpl::map([=](not_null<Data::Folder*> folder) {
|
||||||
|
@ -780,38 +550,14 @@ void MainMenu::setupAccounts() {
|
||||||
const auto inner = _accounts->entity();
|
const auto inner = _accounts->entity();
|
||||||
|
|
||||||
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
|
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
|
||||||
_addAccount = setupAddAccount(inner);
|
auto events = Settings::SetupAccounts(inner, _controller);
|
||||||
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
|
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
|
||||||
|
|
||||||
rpl::single(rpl::empty) | rpl::then(
|
std::move(
|
||||||
Core::App().domain().accountsChanges()
|
events.currentAccountActivations
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
const auto &list = Core::App().domain().accounts();
|
closeLayer();
|
||||||
const auto exists = [&](not_null<Main::Account*> account) {
|
}, inner->lifetime());
|
||||||
for (const auto &[index, existing] : list) {
|
|
||||||
if (account == existing.get()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
for (auto i = _watched.begin(); i != _watched.end();) {
|
|
||||||
if (!exists(i->first)) {
|
|
||||||
i = _watched.erase(i);
|
|
||||||
} else {
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto &[index, account] : list) {
|
|
||||||
if (_watched.emplace(account.get()).second) {
|
|
||||||
account->sessionChanges(
|
|
||||||
) | rpl::start_with_next([=](Main::Session *session) {
|
|
||||||
rebuildAccounts();
|
|
||||||
}, lifetime());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rebuildAccounts();
|
|
||||||
}, lifetime());
|
|
||||||
|
|
||||||
_accounts->toggleOn(Core::App().settings().mainMenuAccountsShownValue());
|
_accounts->toggleOn(Core::App().settings().mainMenuAccountsShownValue());
|
||||||
_accounts->finishAnimating();
|
_accounts->finishAnimating();
|
||||||
|
@ -819,128 +565,6 @@ void MainMenu::setupAccounts() {
|
||||||
_shadow->setDuration(0)->toggleOn(_accounts->shownValue());
|
_shadow->setDuration(0)->toggleOn(_accounts->shownValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenu::rebuildAccounts() {
|
|
||||||
const auto inner = _accounts->entity()->insert(
|
|
||||||
1, // After skip with the fixed height.
|
|
||||||
object_ptr<Ui::VerticalLayout>(_accounts.get()));
|
|
||||||
|
|
||||||
_reorder = std::make_unique<Ui::VerticalLayoutReorder>(inner);
|
|
||||||
_reorder->updates(
|
|
||||||
) | rpl::start_with_next([=](Ui::VerticalLayoutReorder::Single data) {
|
|
||||||
using State = Ui::VerticalLayoutReorder::State;
|
|
||||||
if (data.state == State::Started) {
|
|
||||||
++_reordering;
|
|
||||||
} else {
|
|
||||||
Ui::PostponeCall(inner, [=] {
|
|
||||||
--_reordering;
|
|
||||||
});
|
|
||||||
if (data.state == State::Applied) {
|
|
||||||
std::vector<uint64> order;
|
|
||||||
order.reserve(inner->count());
|
|
||||||
for (auto i = 0; i < inner->count(); i++) {
|
|
||||||
for (const auto &[account, button] : _watched) {
|
|
||||||
if (button.get() == inner->widgetAt(i)) {
|
|
||||||
order.push_back(account->session().uniqueId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Core::App().settings().setAccountsOrder(order);
|
|
||||||
Core::App().saveSettings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, inner->lifetime());
|
|
||||||
|
|
||||||
for (const auto &account : OrderedAccounts()) {
|
|
||||||
auto i = _watched.find(account);
|
|
||||||
Assert(i != _watched.end());
|
|
||||||
|
|
||||||
auto &button = i->second;
|
|
||||||
if (!account->sessionExists()) {
|
|
||||||
button = nullptr;
|
|
||||||
} else if (!button) {
|
|
||||||
auto callback = [=] {
|
|
||||||
if (_reordering) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (account == &Core::App().domain().active()) {
|
|
||||||
closeLayer();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto activate = [=, guard = _accountSwitchGuard.make_guard()]{
|
|
||||||
if (guard) {
|
|
||||||
_reorder->finishReordering();
|
|
||||||
Core::App().domain().maybeActivate(account);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
base::call_delayed(
|
|
||||||
st::defaultRippleAnimation.hideDuration,
|
|
||||||
account,
|
|
||||||
std::move(activate));
|
|
||||||
};
|
|
||||||
button.reset(inner->add(MakeAccountButton(
|
|
||||||
inner,
|
|
||||||
_controller,
|
|
||||||
account,
|
|
||||||
std::move(callback))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inner->resizeToWidth(_accounts->width());
|
|
||||||
|
|
||||||
_addAccount->toggle(
|
|
||||||
(inner->count() < Main::Domain::kMaxAccounts),
|
|
||||||
anim::type::instant);
|
|
||||||
|
|
||||||
_reorder->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<Ui::SlideWrap<Ui::SettingsButton>*> MainMenu::setupAddAccount(
|
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
|
||||||
using namespace Settings;
|
|
||||||
|
|
||||||
const auto result = container->add(
|
|
||||||
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
|
||||||
container.get(),
|
|
||||||
CreateButton(
|
|
||||||
container.get(),
|
|
||||||
tr::lng_menu_add_account(),
|
|
||||||
st::mainMenuAddAccountButton,
|
|
||||||
{
|
|
||||||
&st::mainMenuAddAccount,
|
|
||||||
0,
|
|
||||||
IconType::Round,
|
|
||||||
&st::windowBgActive
|
|
||||||
})))->setDuration(0);
|
|
||||||
const auto button = result->entity();
|
|
||||||
|
|
||||||
const auto add = [=](MTP::Environment environment) {
|
|
||||||
Core::App().preventOrInvoke([=] {
|
|
||||||
Core::App().domain().addActivated(environment);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
button->setAcceptBoth(true);
|
|
||||||
button->clicks(
|
|
||||||
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
|
||||||
if (which == Qt::LeftButton) {
|
|
||||||
add(MTP::Environment::Production);
|
|
||||||
return;
|
|
||||||
} else if (which != Qt::RightButton
|
|
||||||
|| !IsAltShift(button->clickModifiers())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_contextMenu = base::make_unique_q<Ui::PopupMenu>(this);
|
|
||||||
_contextMenu->addAction("Production Server", [=] {
|
|
||||||
add(MTP::Environment::Production);
|
|
||||||
});
|
|
||||||
_contextMenu->addAction("Test Server", [=] {
|
|
||||||
add(MTP::Environment::Test);
|
|
||||||
});
|
|
||||||
_contextMenu->popup(QCursor::pos());
|
|
||||||
}, button->lifetime());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainMenu::setupAccountsToggle() {
|
void MainMenu::setupAccountsToggle() {
|
||||||
_toggleAccounts->show();
|
_toggleAccounts->show();
|
||||||
_toggleAccounts->setClickedCallback([=] { toggleAccounts(); });
|
_toggleAccounts->setClickedCallback([=] { toggleAccounts(); });
|
||||||
|
|
|
@ -20,7 +20,6 @@ class UserpicButton;
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
class ScrollArea;
|
class ScrollArea;
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
class VerticalLayoutReorder;
|
|
||||||
class RippleButton;
|
class RippleButton;
|
||||||
class PlainShadow;
|
class PlainShadow;
|
||||||
class SettingsButton;
|
class SettingsButton;
|
||||||
|
@ -57,12 +56,8 @@ private:
|
||||||
void setupUserpicButton();
|
void setupUserpicButton();
|
||||||
void setupAccounts();
|
void setupAccounts();
|
||||||
void setupAccountsToggle();
|
void setupAccountsToggle();
|
||||||
[[nodiscard]] auto setupAddAccount(
|
|
||||||
not_null<Ui::VerticalLayout*> container)
|
|
||||||
-> not_null<Ui::SlideWrap<Ui::SettingsButton>*>;
|
|
||||||
void setupArchive();
|
void setupArchive();
|
||||||
void setupMenu();
|
void setupMenu();
|
||||||
void rebuildAccounts();
|
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void updateInnerControlsGeometry();
|
void updateInnerControlsGeometry();
|
||||||
void updatePhone();
|
void updatePhone();
|
||||||
|
@ -75,12 +70,8 @@ private:
|
||||||
object_ptr<ResetScaleButton> _resetScaleButton = { nullptr };
|
object_ptr<ResetScaleButton> _resetScaleButton = { nullptr };
|
||||||
object_ptr<Ui::ScrollArea> _scroll;
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
not_null<Ui::VerticalLayout*> _inner;
|
not_null<Ui::VerticalLayout*> _inner;
|
||||||
base::flat_map<
|
|
||||||
not_null<Main::Account*>,
|
|
||||||
base::unique_qptr<Ui::SettingsButton>> _watched;
|
|
||||||
not_null<Ui::RpWidget*> _topShadowSkip;
|
not_null<Ui::RpWidget*> _topShadowSkip;
|
||||||
not_null<Ui::SlideWrap<Ui::VerticalLayout>*> _accounts;
|
not_null<Ui::SlideWrap<Ui::VerticalLayout>*> _accounts;
|
||||||
Ui::SlideWrap<Ui::SettingsButton> *_addAccount = nullptr;
|
|
||||||
not_null<Ui::SlideWrap<Ui::PlainShadow>*> _shadow;
|
not_null<Ui::SlideWrap<Ui::PlainShadow>*> _shadow;
|
||||||
not_null<Ui::VerticalLayout*> _menu;
|
not_null<Ui::VerticalLayout*> _menu;
|
||||||
not_null<Ui::RpWidget*> _footer;
|
not_null<Ui::RpWidget*> _footer;
|
||||||
|
@ -91,11 +82,6 @@ private:
|
||||||
base::Timer _nightThemeSwitch;
|
base::Timer _nightThemeSwitch;
|
||||||
base::unique_qptr<Ui::PopupMenu> _contextMenu;
|
base::unique_qptr<Ui::PopupMenu> _contextMenu;
|
||||||
|
|
||||||
std::unique_ptr<Ui::VerticalLayoutReorder> _reorder;
|
|
||||||
int _reordering = 0;
|
|
||||||
|
|
||||||
base::binary_guard _accountSwitchGuard;
|
|
||||||
|
|
||||||
QString _phoneText;
|
QString _phoneText;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue