mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-04-01 23:00:58 +00:00
Add EditPeerInfoBox without saving.
This commit is contained in:
parent
3998fad7ef
commit
8ff0120642
@ -825,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||||||
|
|
||||||
"lng_group_invite_create" = "Create an invite link";
|
"lng_group_invite_create" = "Create an invite link";
|
||||||
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
||||||
|
"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link.";
|
||||||
"lng_group_invite_create_new" = "Revoke invite link";
|
"lng_group_invite_create_new" = "Revoke invite link";
|
||||||
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
|
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
|
||||||
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
||||||
|
@ -78,7 +78,7 @@ public:
|
|||||||
Return operator()(OtherArgs &&...args) {
|
Return operator()(OtherArgs &&...args) {
|
||||||
return _guard
|
return _guard
|
||||||
? _callable(std::forward<OtherArgs>(args)...)
|
? _callable(std::forward<OtherArgs>(args)...)
|
||||||
: Return{};
|
: Return();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@ -87,7 +87,7 @@ public:
|
|||||||
Return operator()(OtherArgs &&...args) const {
|
Return operator()(OtherArgs &&...args) const {
|
||||||
return _guard
|
return _guard
|
||||||
? _callable(std::forward<OtherArgs>(args)...)
|
? _callable(std::forward<OtherArgs>(args)...)
|
||||||
: Return{};
|
: Return();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -126,7 +126,7 @@ boxMediumSkip: 20px;
|
|||||||
boxButtonPadding: margins(8px, 12px, 13px, 12px);
|
boxButtonPadding: margins(8px, 12px, 13px, 12px);
|
||||||
boxLayerButtonPadding: margins(8px, 8px, 8px, 8px);
|
boxLayerButtonPadding: margins(8px, 8px, 8px, 8px);
|
||||||
boxLabel: FlatLabel(defaultFlatLabel) {
|
boxLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 285px;
|
minWidth: 285px;
|
||||||
align: align(topleft);
|
align: align(topleft);
|
||||||
style: boxLabelStyle;
|
style: boxLabelStyle;
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ cropMinSize: 20px;
|
|||||||
|
|
||||||
confirmInviteTitle: FlatLabel(defaultFlatLabel) {
|
confirmInviteTitle: FlatLabel(defaultFlatLabel) {
|
||||||
align: align(center);
|
align: align(center);
|
||||||
width: 320px;
|
minWidth: 320px;
|
||||||
maxHeight: 24px;
|
maxHeight: 24px;
|
||||||
textFg: windowBoldFg;
|
textFg: windowBoldFg;
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
@ -170,7 +170,7 @@ confirmInviteTitle: FlatLabel(defaultFlatLabel) {
|
|||||||
}
|
}
|
||||||
confirmInviteStatus: FlatLabel(boxLabel) {
|
confirmInviteStatus: FlatLabel(boxLabel) {
|
||||||
align: align(center);
|
align: align(center);
|
||||||
width: 320px;
|
minWidth: 320px;
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
@ -183,13 +183,13 @@ confirmInviteUserPhotoSize: 56px;
|
|||||||
confirmInviteUserPhotoTop: 166px;
|
confirmInviteUserPhotoTop: 166px;
|
||||||
confirmInviteUserName: FlatLabel(defaultFlatLabel) {
|
confirmInviteUserName: FlatLabel(defaultFlatLabel) {
|
||||||
align: align(center);
|
align: align(center);
|
||||||
width: 66px;
|
minWidth: 66px;
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
}
|
}
|
||||||
confirmInviteUserNameTop: 227px;
|
confirmInviteUserNameTop: 227px;
|
||||||
|
|
||||||
confirmPhoneAboutLabel: FlatLabel(defaultFlatLabel) {
|
confirmPhoneAboutLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 282px;
|
minWidth: 282px;
|
||||||
}
|
}
|
||||||
confirmPhoneCodeField: InputField(defaultInputField) {
|
confirmPhoneCodeField: InputField(defaultInputField) {
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ revokePublicLinkStatusPalette: TextPalette(defaultTextPalette) {
|
|||||||
}
|
}
|
||||||
aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) {
|
aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) {
|
||||||
align: align(topleft);
|
align: align(topleft);
|
||||||
width: 320px;
|
minWidth: 320px;
|
||||||
}
|
}
|
||||||
editBioCountdownLabel: FlatLabel(defaultFlatLabel) {
|
editBioCountdownLabel: FlatLabel(defaultFlatLabel) {
|
||||||
style: boxTextStyle;
|
style: boxTextStyle;
|
||||||
@ -394,7 +394,7 @@ notificationSampleSize: size(64px, 16px);
|
|||||||
|
|
||||||
membersAboutLimitPadding: margins(0px, 12px, 0px, 12px);
|
membersAboutLimitPadding: margins(0px, 12px, 0px, 12px);
|
||||||
membersAbout: FlatLabel(defaultFlatLabel) {
|
membersAbout: FlatLabel(defaultFlatLabel) {
|
||||||
width: 332px;
|
minWidth: 332px;
|
||||||
textFg: membersAboutLimitFg;
|
textFg: membersAboutLimitFg;
|
||||||
align: align(top);
|
align: align(top);
|
||||||
style: boxLabelStyle;
|
style: boxLabelStyle;
|
||||||
@ -490,7 +490,7 @@ aboutVersionLink: LinkButton(defaultLinkButton) {
|
|||||||
aboutTextTop: 34px;
|
aboutTextTop: 34px;
|
||||||
aboutSkip: 14px;
|
aboutSkip: 14px;
|
||||||
aboutLabel: FlatLabel(defaultFlatLabel) {
|
aboutLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 330px;
|
minWidth: 330px;
|
||||||
align: align(topleft);
|
align: align(topleft);
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
lineHeight: 22px;
|
lineHeight: 22px;
|
||||||
@ -601,7 +601,7 @@ editPrivacyOptionMargin: margins(23px, 14px, 21px, 0px);
|
|||||||
editPrivacyPadding: margins(23px, 0px, 21px, 0px);
|
editPrivacyPadding: margins(23px, 0px, 21px, 0px);
|
||||||
editPrivacyWarningPadding: margins(23px, 14px, 21px, 0px);
|
editPrivacyWarningPadding: margins(23px, 14px, 21px, 0px);
|
||||||
editPrivacyTitle: FlatLabel(defaultFlatLabel) {
|
editPrivacyTitle: FlatLabel(defaultFlatLabel) {
|
||||||
width: 320px;
|
minWidth: 320px;
|
||||||
textFg: boxTitleFg;
|
textFg: boxTitleFg;
|
||||||
maxHeight: 56px;
|
maxHeight: 56px;
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
@ -612,7 +612,7 @@ editPrivacyTitle: FlatLabel(defaultFlatLabel) {
|
|||||||
}
|
}
|
||||||
editPrivacyTitlePadding: margins(23px, 20px, 21px, 13px);
|
editPrivacyTitlePadding: margins(23px, 20px, 21px, 13px);
|
||||||
editPrivacyLabel: FlatLabel(defaultFlatLabel) {
|
editPrivacyLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 320px;
|
minWidth: 320px;
|
||||||
textFg: membersAboutLimitFg;
|
textFg: membersAboutLimitFg;
|
||||||
style: defaultTextStyle;
|
style: defaultTextStyle;
|
||||||
}
|
}
|
||||||
@ -624,13 +624,13 @@ changePhoneIcon: icon {
|
|||||||
{ "phone_simcard_to", changePhoneSimcardTo, point(78px, 0px) }
|
{ "phone_simcard_to", changePhoneSimcardTo, point(78px, 0px) }
|
||||||
};
|
};
|
||||||
changePhoneDescription: FlatLabel(boxLabel) {
|
changePhoneDescription: FlatLabel(boxLabel) {
|
||||||
width: 332px;
|
minWidth: 332px;
|
||||||
align: align(top);
|
align: align(top);
|
||||||
}
|
}
|
||||||
changePhoneIconTop: 20px;
|
changePhoneIconTop: 20px;
|
||||||
changePhoneDescriptionTop: 96px;
|
changePhoneDescriptionTop: 96px;
|
||||||
changePhoneLabel: FlatLabel(defaultFlatLabel) {
|
changePhoneLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 275px;
|
minWidth: 275px;
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
changePhoneError: FlatLabel(changePhoneLabel) {
|
changePhoneError: FlatLabel(changePhoneLabel) {
|
||||||
@ -695,7 +695,7 @@ mutePhotoButton: PeerAvatarButton {
|
|||||||
photoSize: 40px;
|
photoSize: 40px;
|
||||||
}
|
}
|
||||||
muteChatTitle: FlatLabel(boxLabel) {
|
muteChatTitle: FlatLabel(boxLabel) {
|
||||||
width: 235px;
|
minWidth: 235px;
|
||||||
maxHeight: 20px; // block word wrap
|
maxHeight: 20px; // block word wrap
|
||||||
style: TextStyle(boxTextStyle) {
|
style: TextStyle(boxTextStyle) {
|
||||||
font: font(boxFontSize semibold);
|
font: font(boxFontSize semibold);
|
||||||
|
970
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
Normal file
970
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
Normal file
@ -0,0 +1,970 @@
|
|||||||
|
/*
|
||||||
|
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 "boxes/peers/edit_peer_info_box.h"
|
||||||
|
|
||||||
|
#include <rpl/range.h>
|
||||||
|
#include <rpl/flatten_latest.h>
|
||||||
|
#include "info/profile/info_profile_button.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/wrap/padding_wrap.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/widgets/input_fields.h"
|
||||||
|
#include "ui/widgets/checkbox.h"
|
||||||
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
|
#include "ui/special_buttons.h"
|
||||||
|
#include "boxes/confirm_box.h"
|
||||||
|
#include "boxes/photo_crop_box.h"
|
||||||
|
#include "boxes/add_contact_box.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "core/file_utilities.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "application.h"
|
||||||
|
#include "auth_session.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_info.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kUsernameCheckTimeout = TimeMs(200);
|
||||||
|
|
||||||
|
class Controller
|
||||||
|
: private MTP::Sender
|
||||||
|
, private base::enable_weak_from_this {
|
||||||
|
public:
|
||||||
|
Controller(
|
||||||
|
not_null<BoxContent*> box,
|
||||||
|
not_null<ChannelData*> channel);
|
||||||
|
|
||||||
|
object_ptr<Ui::VerticalLayout> createContent();
|
||||||
|
void setFocus();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Privacy {
|
||||||
|
Public,
|
||||||
|
Private,
|
||||||
|
};
|
||||||
|
enum class Invites {
|
||||||
|
Everyone,
|
||||||
|
OnlyAdmins,
|
||||||
|
};
|
||||||
|
enum class UsernameState {
|
||||||
|
Normal,
|
||||||
|
TooMany,
|
||||||
|
NotAvailable,
|
||||||
|
};
|
||||||
|
struct Controls {
|
||||||
|
Ui::InputField *title = nullptr;
|
||||||
|
Ui::InputArea *description = nullptr;
|
||||||
|
Ui::NewAvatarButton *photo = nullptr;
|
||||||
|
rpl::lifetime initialPhotoImageWaiting;
|
||||||
|
|
||||||
|
std::shared_ptr<Ui::RadioenumGroup<Privacy>> privacy;
|
||||||
|
Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr;
|
||||||
|
Ui::UsernameInput *username = nullptr;
|
||||||
|
base::unique_qptr<Ui::FlatLabel> usernameResult;
|
||||||
|
const style::FlatLabel *usernameResultStyle = nullptr;
|
||||||
|
|
||||||
|
Ui::SlideWrap<Ui::RpWidget> *createInviteLinkWrap = nullptr;
|
||||||
|
Ui::SlideWrap<Ui::RpWidget> *editInviteLinkWrap = nullptr;
|
||||||
|
Ui::FlatLabel *inviteLink = nullptr;
|
||||||
|
|
||||||
|
std::shared_ptr<Ui::RadioenumGroup<Invites>> invites;
|
||||||
|
Ui::Checkbox *signatures = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
base::lambda<QString()> computeTitle() const;
|
||||||
|
object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createTitleEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createPhotoEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createDescriptionEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createPrivaciesEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createUsernameEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createInviteLinkCreate();
|
||||||
|
object_ptr<Ui::RpWidget> createInviteLinkEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createSignaturesEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createInvitesEdit();
|
||||||
|
object_ptr<Ui::RpWidget> createDeleteButton();
|
||||||
|
|
||||||
|
void refreshInitialPhotoImage();
|
||||||
|
void submitTitle();
|
||||||
|
void submitDescription();
|
||||||
|
void save();
|
||||||
|
void deleteWithConfirmation();
|
||||||
|
void choosePhotoDelayed();
|
||||||
|
void choosePhoto();
|
||||||
|
void suggestPhotoFile(
|
||||||
|
const FileDialog::OpenResult &result);
|
||||||
|
void suggestPhoto(const QImage &image);
|
||||||
|
void privacyChanged(Privacy value);
|
||||||
|
|
||||||
|
void checkUsernameAvailability();
|
||||||
|
void askUsernameRevoke();
|
||||||
|
void usernameChanged();
|
||||||
|
void showUsernameError(rpl::producer<QString> &&error);
|
||||||
|
void showUsernameGood();
|
||||||
|
void showUsernameResult(
|
||||||
|
rpl::producer<QString> &&text,
|
||||||
|
not_null<const style::FlatLabel*> st);
|
||||||
|
|
||||||
|
bool canEditInviteLink() const;
|
||||||
|
bool inviteLinkShown() const;
|
||||||
|
void refreshEditInviteLink();
|
||||||
|
void refreshCreateInviteLink();
|
||||||
|
void createInviteLink();
|
||||||
|
void revokeInviteLink();
|
||||||
|
void exportInviteLink(const QString &confirmation);
|
||||||
|
|
||||||
|
not_null<BoxContent*> _box;
|
||||||
|
not_null<ChannelData*> _channel;
|
||||||
|
bool _isGroup;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::VerticalLayout> _wrap;
|
||||||
|
Controls _controls;
|
||||||
|
base::Timer _checkUsernameTimer;
|
||||||
|
mtpRequestId _checkUsernameRequestId = 0;
|
||||||
|
UsernameState _usernameState = UsernameState::Normal;
|
||||||
|
rpl::event_stream<rpl::producer<QString>> _usernameResultTexts;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Controller::Controller(
|
||||||
|
not_null<BoxContent*> box,
|
||||||
|
not_null<ChannelData*> channel)
|
||||||
|
: _box(box)
|
||||||
|
, _channel(channel)
|
||||||
|
, _isGroup(_channel->isMegagroup())
|
||||||
|
, _checkUsernameTimer([this] { checkUsernameAvailability(); }) {
|
||||||
|
_box->setTitle(computeTitle());
|
||||||
|
_box->addButton(langFactory(lng_settings_save), [this] {
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
_box->addButton(langFactory(lng_cancel), [this] {
|
||||||
|
_box->closeBox();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
base::lambda<QString()> Controller::computeTitle() const {
|
||||||
|
return langFactory(_isGroup
|
||||||
|
? lng_edit_group
|
||||||
|
: lng_edit_channel_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::VerticalLayout> Controller::createContent() {
|
||||||
|
auto result = object_ptr<Ui::VerticalLayout>(_box);
|
||||||
|
_wrap.reset(result.data());
|
||||||
|
_controls = Controls();
|
||||||
|
|
||||||
|
_wrap->add(createPhotoAndTitleEdit());
|
||||||
|
_wrap->add(createDescriptionEdit());
|
||||||
|
_wrap->add(createPrivaciesEdit());
|
||||||
|
_wrap->add(createInviteLinkCreate());
|
||||||
|
_wrap->add(createInviteLinkEdit());
|
||||||
|
_wrap->add(createSignaturesEdit());
|
||||||
|
_wrap->add(createInvitesEdit());
|
||||||
|
_wrap->add(createDeleteButton());
|
||||||
|
|
||||||
|
_wrap->resizeToWidth(st::boxWideWidth);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::setFocus() {
|
||||||
|
if (_controls.title) {
|
||||||
|
_controls.title->setFocusFast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createPhotoAndTitleEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!_channel->canEditInformation()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::RpWidget>(_wrap);
|
||||||
|
auto container = result.data();
|
||||||
|
|
||||||
|
auto photoWrap = Ui::AttachParentChild(
|
||||||
|
container,
|
||||||
|
createPhotoEdit());
|
||||||
|
auto titleEdit = Ui::AttachParentChild(
|
||||||
|
container,
|
||||||
|
createTitleEdit());
|
||||||
|
photoWrap->heightValue()
|
||||||
|
| rpl::start_with_next([container](int height) {
|
||||||
|
container->resize(container->width(), height);
|
||||||
|
}, photoWrap->lifetime());
|
||||||
|
container->widthValue()
|
||||||
|
| rpl::start_with_next([titleEdit](int width) {
|
||||||
|
auto left = st::editPeerPhotoMargins.left()
|
||||||
|
+ st::editPeerPhotoSize;
|
||||||
|
titleEdit->resizeToWidth(width - left);
|
||||||
|
titleEdit->moveToLeft(left, 0, width);
|
||||||
|
}, titleEdit->lifetime());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
using PhotoWrap = Ui::PaddingWrap<Ui::NewAvatarButton>;
|
||||||
|
auto photoWrap = object_ptr<PhotoWrap>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::NewAvatarButton>(
|
||||||
|
_wrap,
|
||||||
|
st::editPeerPhotoSize,
|
||||||
|
st::editPeerPhotoIconPosition),
|
||||||
|
st::editPeerPhotoMargins);
|
||||||
|
_controls.photo = photoWrap->entity();
|
||||||
|
_controls.photo->addClickHandler([this] { choosePhotoDelayed(); });
|
||||||
|
|
||||||
|
_controls.initialPhotoImageWaiting = base::ObservableViewer(
|
||||||
|
Auth().downloaderTaskFinished())
|
||||||
|
| rpl::start_with_next([=] {
|
||||||
|
refreshInitialPhotoImage();
|
||||||
|
});
|
||||||
|
refreshInitialPhotoImage();
|
||||||
|
|
||||||
|
return photoWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::refreshInitialPhotoImage() {
|
||||||
|
if (auto image = _channel->currentUserpic()) {
|
||||||
|
image->load();
|
||||||
|
if (image->loaded()) {
|
||||||
|
_controls.photo->setImage(image->pixNoCache(
|
||||||
|
st::editPeerPhotoSize * cIntRetinaFactor(),
|
||||||
|
st::editPeerPhotoSize * cIntRetinaFactor(),
|
||||||
|
Images::Option::Smooth).toImage());
|
||||||
|
_controls.initialPhotoImageWaiting.destroy();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_controls.initialPhotoImageWaiting.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::InputField>(
|
||||||
|
_wrap,
|
||||||
|
st::defaultInputField,
|
||||||
|
langFactory(_isGroup
|
||||||
|
? lng_dlg_new_group_name
|
||||||
|
: lng_dlg_new_channel_name),
|
||||||
|
_channel->name),
|
||||||
|
st::editPeerTitleMargins);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
result->entity(),
|
||||||
|
&Ui::InputField::submitted,
|
||||||
|
[this] { submitTitle(); });
|
||||||
|
|
||||||
|
_controls.title = result->entity();
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::PaddingWrap<Ui::InputArea>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::InputArea>(
|
||||||
|
_wrap,
|
||||||
|
st::editPeerDescription,
|
||||||
|
langFactory(lng_create_group_description),
|
||||||
|
_channel->about()),
|
||||||
|
st::editPeerDescriptionMargins);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
result->entity(),
|
||||||
|
&Ui::InputArea::submitted,
|
||||||
|
[this] { submitDescription(); });
|
||||||
|
|
||||||
|
_controls.description = result->entity();
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createPrivaciesEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!_channel->canEditUsername()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::VerticalLayout>(_wrap),
|
||||||
|
st::editPeerPrivaciesMargins);
|
||||||
|
auto container = result->entity();
|
||||||
|
|
||||||
|
_controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>(
|
||||||
|
_channel->isPublic() ? Privacy::Public : Privacy::Private);
|
||||||
|
auto addButton = [&](
|
||||||
|
Privacy value,
|
||||||
|
LangKey groupTextKey,
|
||||||
|
LangKey channelTextKey,
|
||||||
|
LangKey groupAboutKey,
|
||||||
|
LangKey channelAboutKey) {
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::editPeerPrivacyTopSkip));
|
||||||
|
container->add(object_ptr<Ui::Radioenum<Privacy>>(
|
||||||
|
container,
|
||||||
|
_controls.privacy,
|
||||||
|
value,
|
||||||
|
lang(_isGroup ? groupTextKey : channelTextKey),
|
||||||
|
st::defaultBoxCheckbox));
|
||||||
|
container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
Lang::Viewer(_isGroup ? groupAboutKey : channelAboutKey),
|
||||||
|
st::editPeerPrivacyLabel),
|
||||||
|
st::editPeerPrivacyLabelMargins));
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::editPeerPrivacyBottomSkip));
|
||||||
|
};
|
||||||
|
addButton(
|
||||||
|
Privacy::Public,
|
||||||
|
lng_create_public_group_title,
|
||||||
|
lng_create_public_channel_title,
|
||||||
|
lng_create_public_group_about,
|
||||||
|
lng_create_public_channel_about);
|
||||||
|
addButton(
|
||||||
|
Privacy::Private,
|
||||||
|
lng_create_private_group_title,
|
||||||
|
lng_create_private_channel_title,
|
||||||
|
lng_create_private_group_about,
|
||||||
|
lng_create_private_channel_about);
|
||||||
|
container->add(createUsernameEdit());
|
||||||
|
|
||||||
|
_controls.privacy->setChangedCallback([this](Privacy value) {
|
||||||
|
privacyChanged(value);
|
||||||
|
});
|
||||||
|
if (!_channel->isPublic()) {
|
||||||
|
checkUsernameAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||||
|
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::VerticalLayout>(_wrap),
|
||||||
|
st::editPeerUsernameMargins);
|
||||||
|
_controls.usernameWrap = result.data();
|
||||||
|
|
||||||
|
auto container = result->entity();
|
||||||
|
container->add(object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
Lang::Viewer(lng_create_group_link),
|
||||||
|
st::editPeerSectionLabel));
|
||||||
|
auto placeholder = container->add(object_ptr<Ui::RpWidget>(
|
||||||
|
container));
|
||||||
|
placeholder->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_controls.username = Ui::AttachParentChild(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::UsernameInput>(
|
||||||
|
container,
|
||||||
|
st::setupChannelLink,
|
||||||
|
base::lambda<QString()>(),
|
||||||
|
_channel->username,
|
||||||
|
true));
|
||||||
|
_controls.username->heightValue()
|
||||||
|
| rpl::start_with_next([placeholder](int height) {
|
||||||
|
placeholder->resize(placeholder->width(), height);
|
||||||
|
}, placeholder->lifetime());
|
||||||
|
placeholder->widthValue()
|
||||||
|
| rpl::start_with_next([this](int width) {
|
||||||
|
_controls.username->resize(
|
||||||
|
width,
|
||||||
|
_controls.username->height());
|
||||||
|
}, placeholder->lifetime());
|
||||||
|
_controls.username->move(placeholder->pos());
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
_controls.username,
|
||||||
|
&Ui::UsernameInput::changed,
|
||||||
|
[this] { usernameChanged(); });
|
||||||
|
|
||||||
|
auto shown = (_controls.privacy->value() == Privacy::Public);
|
||||||
|
result->toggle(shown, anim::type::instant);
|
||||||
|
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::privacyChanged(Privacy value) {
|
||||||
|
auto toggleEditUsername = [&] {
|
||||||
|
_controls.usernameWrap->toggle(
|
||||||
|
(value == Privacy::Public),
|
||||||
|
anim::type::instant);
|
||||||
|
};
|
||||||
|
auto refreshVisibilities = [&] {
|
||||||
|
// First we need to show everything, then hide anything.
|
||||||
|
// Otherwise the scroll position could jump up undesirably.
|
||||||
|
|
||||||
|
if (value == Privacy::Public) {
|
||||||
|
toggleEditUsername();
|
||||||
|
}
|
||||||
|
refreshCreateInviteLink();
|
||||||
|
refreshEditInviteLink();
|
||||||
|
if (value == Privacy::Public) {
|
||||||
|
_controls.usernameResult = nullptr;
|
||||||
|
checkUsernameAvailability();
|
||||||
|
} else {
|
||||||
|
toggleEditUsername();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (value == Privacy::Public) {
|
||||||
|
if (_usernameState == UsernameState::TooMany) {
|
||||||
|
askUsernameRevoke();
|
||||||
|
return;
|
||||||
|
} else if (_usernameState == UsernameState::NotAvailable) {
|
||||||
|
_controls.privacy->setValue(Privacy::Private);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refreshVisibilities();
|
||||||
|
_controls.username->setDisplayFocused(true);
|
||||||
|
_controls.username->setFocus();
|
||||||
|
} else {
|
||||||
|
request(base::take(_checkUsernameRequestId)).cancel();
|
||||||
|
_checkUsernameTimer.cancel();
|
||||||
|
refreshVisibilities();
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::checkUsernameAvailability() {
|
||||||
|
if (!_controls.username) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto initial = (_controls.privacy->value() != Privacy::Public);
|
||||||
|
auto checking = initial
|
||||||
|
? qsl(".bad.")
|
||||||
|
: _controls.username->getLastText().trimmed();
|
||||||
|
if (checking.size() < MinUsernameLength) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_checkUsernameRequestId) {
|
||||||
|
request(_checkUsernameRequestId).cancel();
|
||||||
|
}
|
||||||
|
_checkUsernameRequestId = request(MTPchannels_CheckUsername(
|
||||||
|
_channel->inputChannel,
|
||||||
|
MTP_string(checking)
|
||||||
|
)).done([=](const MTPBool &result) {
|
||||||
|
_checkUsernameRequestId = 0;
|
||||||
|
if (initial) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mtpIsTrue(result) && checking != _channel->username) {
|
||||||
|
showUsernameError(
|
||||||
|
Lang::Viewer(lng_create_channel_link_occupied));
|
||||||
|
} else {
|
||||||
|
showUsernameGood();
|
||||||
|
}
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_checkUsernameRequestId = 0;
|
||||||
|
auto type = error.type();
|
||||||
|
_usernameState = UsernameState::Normal;
|
||||||
|
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
|
||||||
|
_usernameState = UsernameState::NotAvailable;
|
||||||
|
_controls.privacy->setValue(Privacy::Private);
|
||||||
|
} else if (rand() % 10 < 2 || type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
|
||||||
|
_usernameState = UsernameState::TooMany;
|
||||||
|
if (_controls.privacy->value() == Privacy::Public) {
|
||||||
|
askUsernameRevoke();
|
||||||
|
}
|
||||||
|
} else if (initial) {
|
||||||
|
if (_controls.privacy->value() == Privacy::Public) {
|
||||||
|
_controls.usernameResult = nullptr;
|
||||||
|
_controls.username->setFocus();
|
||||||
|
}
|
||||||
|
} else if (type == qstr("USERNAME_INVALID")) {
|
||||||
|
showUsernameError(
|
||||||
|
Lang::Viewer(lng_create_channel_link_invalid));
|
||||||
|
} else if (type == qstr("USERNAME_OCCUPIED")
|
||||||
|
&& checking != _channel->username) {
|
||||||
|
showUsernameError(
|
||||||
|
Lang::Viewer(lng_create_channel_link_occupied));
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::askUsernameRevoke() {
|
||||||
|
_controls.privacy->setValue(Privacy::Private);
|
||||||
|
auto revokeCallback = base::lambda_guarded(this, [this] {
|
||||||
|
_usernameState = UsernameState::Normal;
|
||||||
|
_controls.privacy->setValue(Privacy::Public);
|
||||||
|
checkUsernameAvailability();
|
||||||
|
});
|
||||||
|
Ui::show(
|
||||||
|
Box<RevokePublicLinkBox>(std::move(revokeCallback)),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::usernameChanged() {
|
||||||
|
auto username = _controls.username->getLastText().trimmed();
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
_controls.usernameResult = nullptr;
|
||||||
|
_checkUsernameTimer.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto bad = base::find_if(username, [](QChar ch) {
|
||||||
|
return (ch < 'A' || ch > 'Z')
|
||||||
|
&& (ch < 'a' || ch > 'z')
|
||||||
|
&& (ch < '0' || ch > '9')
|
||||||
|
&& (ch != '_');
|
||||||
|
}) != username.end();
|
||||||
|
if (bad) {
|
||||||
|
showUsernameError(
|
||||||
|
Lang::Viewer(lng_create_channel_link_bad_symbols));
|
||||||
|
} else if (username.size() < MinUsernameLength) {
|
||||||
|
showUsernameError(
|
||||||
|
Lang::Viewer(lng_create_channel_link_too_short));
|
||||||
|
} else {
|
||||||
|
_controls.usernameResult = nullptr;
|
||||||
|
_checkUsernameTimer.callOnce(kUsernameCheckTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::showUsernameError(rpl::producer<QString> &&error) {
|
||||||
|
showUsernameResult(std::move(error), &st::editPeerUsernameError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::showUsernameGood() {
|
||||||
|
showUsernameResult(
|
||||||
|
Lang::Viewer(lng_create_channel_link_available),
|
||||||
|
&st::editPeerUsernameGood);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::showUsernameResult(
|
||||||
|
rpl::producer<QString> &&text,
|
||||||
|
not_null<const style::FlatLabel*> st) {
|
||||||
|
if (!_controls.usernameResult
|
||||||
|
|| _controls.usernameResultStyle != st) {
|
||||||
|
_controls.usernameResultStyle = st;
|
||||||
|
_controls.usernameResult = base::make_unique_q<Ui::FlatLabel>(
|
||||||
|
_controls.usernameWrap,
|
||||||
|
_usernameResultTexts.events() | rpl::flatten_latest(),
|
||||||
|
*st);
|
||||||
|
auto label = _controls.usernameResult.get();
|
||||||
|
label->show();
|
||||||
|
label->widthValue()
|
||||||
|
| rpl::start_with_next([label] {
|
||||||
|
label->moveToRight(
|
||||||
|
st::editPeerUsernamePosition.x(),
|
||||||
|
st::editPeerUsernamePosition.y());
|
||||||
|
}, label->lifetime());
|
||||||
|
}
|
||||||
|
_usernameResultTexts.fire(std::move(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::createInviteLink() {
|
||||||
|
exportInviteLink(lang(_isGroup
|
||||||
|
? lng_group_invite_about
|
||||||
|
: lng_group_invite_about_channel));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::revokeInviteLink() {
|
||||||
|
exportInviteLink(lang(lng_group_invite_about_new));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::exportInviteLink(const QString &confirmation) {
|
||||||
|
auto boxPointer = std::make_shared<QPointer<ConfirmBox>>();
|
||||||
|
auto callback = base::lambda_guarded(this, [=] {
|
||||||
|
if (auto strong = *boxPointer) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
Auth().api().exportInviteLink(_channel);
|
||||||
|
});
|
||||||
|
auto box = Box<ConfirmBox>(
|
||||||
|
confirmation,
|
||||||
|
std::move(callback));
|
||||||
|
*boxPointer = Ui::show(std::move(box), LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Controller::canEditInviteLink() const {
|
||||||
|
if (_channel->canEditUsername()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (!_channel->isPublic() && _channel->canAddMembers());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Controller::inviteLinkShown() const {
|
||||||
|
return !_controls.privacy
|
||||||
|
|| (_controls.privacy->value() == Privacy::Private);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!canEditInviteLink()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::VerticalLayout>(_wrap),
|
||||||
|
st::editPeerInviteLinkMargins);
|
||||||
|
_controls.editInviteLinkWrap = result.data();
|
||||||
|
|
||||||
|
auto container = result->entity();
|
||||||
|
container->add(object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
Lang::Viewer(lng_profile_invite_link_section),
|
||||||
|
st::editPeerSectionLabel));
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::editPeerInviteLinkSkip));
|
||||||
|
|
||||||
|
_controls.inviteLink = container->add(object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
st::editPeerInviteLink));
|
||||||
|
_controls.inviteLink->setSelectable(true);
|
||||||
|
_controls.inviteLink->setContextCopyText(QString());
|
||||||
|
_controls.inviteLink->setBreakEverywhere(true);
|
||||||
|
_controls.inviteLink->setClickHandlerHook([this](auto&&...) {
|
||||||
|
Application::clipboard()->setText(_channel->inviteLink());
|
||||||
|
Ui::Toast::Show(lang(lng_group_invite_copied));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::editPeerInviteLinkSkip));
|
||||||
|
container->add(object_ptr<Ui::LinkButton>(
|
||||||
|
container,
|
||||||
|
lang(lng_group_invite_create_new),
|
||||||
|
st::editPeerInviteLinkButton)
|
||||||
|
)->addClickHandler([this] { revokeInviteLink(); });
|
||||||
|
|
||||||
|
Notify::PeerUpdateValue(
|
||||||
|
_channel,
|
||||||
|
Notify::PeerUpdate::Flag::InviteLinkChanged)
|
||||||
|
| rpl::start_with_next([this] {
|
||||||
|
refreshEditInviteLink();
|
||||||
|
}, _controls.editInviteLinkWrap->lifetime());
|
||||||
|
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::refreshEditInviteLink() {
|
||||||
|
auto link = _channel->inviteLink();
|
||||||
|
auto text = TextWithEntities();
|
||||||
|
if (!link.isEmpty()) {
|
||||||
|
text.text = link;
|
||||||
|
auto remove = qstr("https://");
|
||||||
|
if (text.text.startsWith(remove)) {
|
||||||
|
text.text.remove(0, remove.size());
|
||||||
|
}
|
||||||
|
text.entities.push_back(EntityInText(
|
||||||
|
EntityInTextCustomUrl,
|
||||||
|
0,
|
||||||
|
text.text.size(),
|
||||||
|
link));
|
||||||
|
}
|
||||||
|
_controls.inviteLink->setMarkedText(text);
|
||||||
|
|
||||||
|
// Hack to expand FlatLabel width to naturalWidth again.
|
||||||
|
_controls.editInviteLinkWrap->resizeToWidth(st::boxWideWidth);
|
||||||
|
|
||||||
|
_controls.editInviteLinkWrap->toggle(
|
||||||
|
inviteLinkShown() && !link.isEmpty(),
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!canEditInviteLink()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::SlideWrap<Ui::LinkButton>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::LinkButton>(
|
||||||
|
_wrap,
|
||||||
|
lang(lng_group_invite_create),
|
||||||
|
st::editPeerInviteLinkButton),
|
||||||
|
st::editPeerInviteLinkMargins);
|
||||||
|
result->entity()->addClickHandler([this] {
|
||||||
|
createInviteLink();
|
||||||
|
});
|
||||||
|
_controls.createInviteLinkWrap = result.data();
|
||||||
|
|
||||||
|
Notify::PeerUpdateValue(
|
||||||
|
_channel,
|
||||||
|
Notify::PeerUpdate::Flag::InviteLinkChanged)
|
||||||
|
| rpl::start_with_next([this] {
|
||||||
|
refreshCreateInviteLink();
|
||||||
|
}, _controls.createInviteLinkWrap->lifetime());
|
||||||
|
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::refreshCreateInviteLink() {
|
||||||
|
auto link = _channel->inviteLink();
|
||||||
|
_controls.createInviteLinkWrap->toggle(
|
||||||
|
inviteLinkShown() && link.isEmpty(),
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createSignaturesEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!_channel->canEditInformation()
|
||||||
|
|| _channel->isMegagroup()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto result = object_ptr<Ui::VerticalLayout>(_wrap);
|
||||||
|
auto container = result.data();
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::defaultBoxCheckbox.margin.top()));
|
||||||
|
_controls.signatures = container->add(
|
||||||
|
object_ptr<Ui::PaddingWrap<Ui::Checkbox>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::Checkbox>(
|
||||||
|
container,
|
||||||
|
lang(lng_edit_sign_messages),
|
||||||
|
_channel->addsSignature(),
|
||||||
|
st::defaultBoxCheckbox),
|
||||||
|
st::editPeerSignaturesMargins))->entity();
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::defaultBoxCheckbox.margin.bottom()));
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createInvitesEdit() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!_channel->canEditInformation()
|
||||||
|
|| !_channel->isMegagroup()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::VerticalLayout>(_wrap),
|
||||||
|
st::editPeerInvitesMargins);
|
||||||
|
|
||||||
|
auto container = result->entity();
|
||||||
|
container->add(object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
Lang::Viewer(lng_edit_group_who_invites),
|
||||||
|
st::editPeerSectionLabel));
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::editPeerInvitesTopSkip));
|
||||||
|
|
||||||
|
_controls.invites = std::make_shared<Ui::RadioenumGroup<Invites>>(
|
||||||
|
_channel->anyoneCanAddMembers()
|
||||||
|
? Invites::Everyone
|
||||||
|
: Invites::OnlyAdmins);
|
||||||
|
auto addButton = [&](
|
||||||
|
Invites value,
|
||||||
|
LangKey textKey) {
|
||||||
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
container,
|
||||||
|
st::editPeerInvitesSkip));
|
||||||
|
container->add(object_ptr<Ui::Radioenum<Invites>>(
|
||||||
|
container,
|
||||||
|
_controls.invites,
|
||||||
|
value,
|
||||||
|
lang(textKey),
|
||||||
|
st::defaultBoxCheckbox));
|
||||||
|
};
|
||||||
|
addButton(
|
||||||
|
Invites::Everyone,
|
||||||
|
lng_edit_group_invites_everybody);
|
||||||
|
addButton(
|
||||||
|
Invites::OnlyAdmins,
|
||||||
|
lng_edit_group_invites_only_admins);
|
||||||
|
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> Controller::createDeleteButton() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
if (!_channel->canDelete()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto text = lang(_isGroup
|
||||||
|
? lng_profile_delete_group
|
||||||
|
: lng_profile_delete_channel);
|
||||||
|
auto result = object_ptr<Ui::PaddingWrap<Ui::LinkButton>>(
|
||||||
|
_wrap,
|
||||||
|
object_ptr<Ui::LinkButton>(
|
||||||
|
_wrap,
|
||||||
|
text,
|
||||||
|
st::editPeerDeleteButton),
|
||||||
|
st::editPeerDeleteButtonMargins);
|
||||||
|
result->entity()->addClickHandler([this] {
|
||||||
|
deleteWithConfirmation();
|
||||||
|
});
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::submitTitle() {
|
||||||
|
Expects(_controls.title != nullptr);
|
||||||
|
Expects(_controls.description != nullptr);
|
||||||
|
|
||||||
|
if (_controls.title->getLastText().isEmpty()) {
|
||||||
|
_controls.title->showError();
|
||||||
|
} else {
|
||||||
|
_controls.description->setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::submitDescription() {
|
||||||
|
Expects(_controls.title != nullptr);
|
||||||
|
Expects(_controls.description != nullptr);
|
||||||
|
|
||||||
|
if (_controls.title->getLastText().isEmpty()) {
|
||||||
|
_controls.title->showError();
|
||||||
|
_controls.title->setFocus();
|
||||||
|
} else {
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::save() {
|
||||||
|
Expects(_wrap != nullptr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::deleteWithConfirmation() {
|
||||||
|
auto text = lang(_isGroup
|
||||||
|
? lng_sure_delete_group
|
||||||
|
: lng_sure_delete_channel);
|
||||||
|
auto deleteCallback = [channel = _channel] {
|
||||||
|
Ui::hideLayer();
|
||||||
|
Ui::showChatsList();
|
||||||
|
if (auto chat = channel->migrateFrom()) {
|
||||||
|
App::main()->deleteAndExit(chat);
|
||||||
|
}
|
||||||
|
MTP::send(
|
||||||
|
MTPchannels_DeleteChannel(channel->inputChannel),
|
||||||
|
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
|
||||||
|
App::main()->rpcFail(&MainWidget::deleteChannelFailed));
|
||||||
|
};
|
||||||
|
Ui::show(Box<ConfirmBox>(
|
||||||
|
text,
|
||||||
|
lang(lng_box_delete),
|
||||||
|
st::attentionBoxButton,
|
||||||
|
std::move(deleteCallback)), LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::choosePhotoDelayed() {
|
||||||
|
App::CallDelayed(
|
||||||
|
st::defaultRippleAnimation.hideDuration,
|
||||||
|
this,
|
||||||
|
[this] { choosePhoto(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::choosePhoto() {
|
||||||
|
auto handleChosenPhoto = base::lambda_guarded(
|
||||||
|
_controls.photo,
|
||||||
|
[this](auto &&result) { suggestPhotoFile(result); });
|
||||||
|
|
||||||
|
auto imgExtensions = cImgExtensions();
|
||||||
|
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + FileDialog::AllFilesFilter();
|
||||||
|
FileDialog::GetOpenPath(
|
||||||
|
lang(lng_choose_image),
|
||||||
|
filter,
|
||||||
|
std::move(handleChosenPhoto));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::suggestPhotoFile(
|
||||||
|
const FileDialog::OpenResult &result) {
|
||||||
|
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto image = [&] {
|
||||||
|
if (!result.remoteContent.isEmpty()) {
|
||||||
|
return App::readImage(result.remoteContent);
|
||||||
|
} else if (!result.paths.isEmpty()) {
|
||||||
|
return App::readImage(result.paths.front());
|
||||||
|
}
|
||||||
|
return QImage();
|
||||||
|
}();
|
||||||
|
suggestPhoto(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::suggestPhoto(const QImage &image) {
|
||||||
|
auto badAspect = [](int a, int b) {
|
||||||
|
return (a >= 10 * b);
|
||||||
|
};
|
||||||
|
if (image.isNull()
|
||||||
|
|| badAspect(image.width(), image.height())
|
||||||
|
|| badAspect(image.height(), image.width())) {
|
||||||
|
Ui::show(Box<InformBox>(lang(lng_bad_photo)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto callback = [this](const QImage &cropped) {
|
||||||
|
_controls.photo->setImage(cropped);
|
||||||
|
};
|
||||||
|
auto box = Ui::show(
|
||||||
|
Box<PhotoCropBox>(image, _channel),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
QObject::connect(
|
||||||
|
box,
|
||||||
|
&PhotoCropBox::ready,
|
||||||
|
base::lambda_guarded(_controls.photo, std::move(callback)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
EditPeerInfoBox::EditPeerInfoBox(
|
||||||
|
QWidget*,
|
||||||
|
not_null<ChannelData*> channel)
|
||||||
|
: _channel(channel) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditPeerInfoBox::prepare() {
|
||||||
|
auto controller = std::make_unique<Controller>(this, _channel);
|
||||||
|
_focusRequests.events()
|
||||||
|
| rpl::start_with_next(
|
||||||
|
[c = controller.get()] { c->setFocus(); },
|
||||||
|
lifetime());
|
||||||
|
auto content = controller->createContent();
|
||||||
|
content->heightValue()
|
||||||
|
| rpl::start_with_next([this](int height) {
|
||||||
|
setDimensions(st::boxWideWidth, height);
|
||||||
|
}, content->lifetime());
|
||||||
|
setInnerWidget(object_ptr<Ui::IgnoreMargins>(
|
||||||
|
this,
|
||||||
|
std::move(content)));
|
||||||
|
Ui::AttachAsChild(this, std::move(controller));
|
||||||
|
}
|
41
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h
Normal file
41
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
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 <rpl/event_stream.h>
|
||||||
|
#include "boxes/peers/manage_peer_box.h"
|
||||||
|
|
||||||
|
class EditPeerInfoBox : public BoxContent {
|
||||||
|
public:
|
||||||
|
EditPeerInfoBox(QWidget*, not_null<ChannelData*> channel);
|
||||||
|
|
||||||
|
void setInnerFocus() override {
|
||||||
|
_focusRequests.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void prepare() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<ChannelData*> _channel;
|
||||||
|
rpl::event_stream<> _focusRequests;
|
||||||
|
|
||||||
|
};
|
@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||||||
|
|
||||||
#include <rpl/combine.h>
|
#include <rpl/combine.h>
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "boxes/peers/edit_peer_info_box.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "history/history_admin_log_section.h"
|
#include "history/history_admin_log_section.h"
|
||||||
@ -35,7 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
base::lambda<QString()> ManageBoxTitle(
|
base::lambda<QString()> ManagePeerTitle(
|
||||||
not_null<ChannelData*> channel) {
|
not_null<ChannelData*> channel) {
|
||||||
return langFactory(channel->isMegagroup()
|
return langFactory(channel->isMegagroup()
|
||||||
? lng_manage_group_title
|
? lng_manage_group_title
|
||||||
@ -83,6 +84,7 @@ void AddButtonWithCount(
|
|||||||
button,
|
button,
|
||||||
std::move(count),
|
std::move(count),
|
||||||
st::managePeerButtonLabel);
|
st::managePeerButtonLabel);
|
||||||
|
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
rpl::combine(button->widthValue(), label->widthValue())
|
rpl::combine(button->widthValue(), label->widthValue())
|
||||||
| rpl::start_with_next([label](int outerWidth, int width) {
|
| rpl::start_with_next([label](int outerWidth, int width) {
|
||||||
label->moveToRight(
|
label->moveToRight(
|
||||||
@ -118,9 +120,7 @@ void FillManageBox(
|
|||||||
Lang::Viewer(isGroup
|
Lang::Viewer(isGroup
|
||||||
? lng_manage_group_info
|
? lng_manage_group_info
|
||||||
: lng_manage_channel_info),
|
: lng_manage_channel_info),
|
||||||
[=] {
|
[=] { Ui::show(Box<EditPeerInfoBox>(channel)); },
|
||||||
|
|
||||||
},
|
|
||||||
st::infoIconInformation);
|
st::infoIconInformation);
|
||||||
}
|
}
|
||||||
if (HasRecentActions(channel)) {
|
if (HasRecentActions(channel)) {
|
||||||
@ -195,10 +195,14 @@ ManagePeerBox::ManagePeerBox(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
|
bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
|
||||||
|
// canViewMembers() is removed, because in supergroups you
|
||||||
|
// see them in profile and in channels only admins can see them.
|
||||||
|
|
||||||
// canViewAdmins() is removed, because in supergroups it is
|
// canViewAdmins() is removed, because in supergroups it is
|
||||||
// always true and in channels it is equal to canViewBanned().
|
// always true and in channels it is equal to canViewBanned().
|
||||||
|
|
||||||
return channel->canViewMembers()
|
return false
|
||||||
|
// || channel->canViewMembers()
|
||||||
// || channel->canViewAdmins()
|
// || channel->canViewAdmins()
|
||||||
|| channel->canViewBanned()
|
|| channel->canViewBanned()
|
||||||
|| channel->canEditInformation()
|
|| channel->canEditInformation()
|
||||||
@ -208,7 +212,7 @@ bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
|
|||||||
void ManagePeerBox::prepare() {
|
void ManagePeerBox::prepare() {
|
||||||
_channel->updateFull();
|
_channel->updateFull();
|
||||||
|
|
||||||
setTitle(ManageBoxTitle(_channel));
|
setTitle(ManagePeerTitle(_channel));
|
||||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||||
|
|
||||||
setupContent();
|
setupContent();
|
||||||
|
@ -95,7 +95,7 @@ callMuteRight: 8px;
|
|||||||
|
|
||||||
callNameTop: 15px;
|
callNameTop: 15px;
|
||||||
callName: FlatLabel(defaultFlatLabel) {
|
callName: FlatLabel(defaultFlatLabel) {
|
||||||
width: 260px;
|
minWidth: 260px;
|
||||||
maxHeight: 30px;
|
maxHeight: 30px;
|
||||||
textFg: callNameFg;
|
textFg: callNameFg;
|
||||||
align: align(top);
|
align: align(top);
|
||||||
@ -107,7 +107,7 @@ callName: FlatLabel(defaultFlatLabel) {
|
|||||||
}
|
}
|
||||||
callStatusTop: 46px;
|
callStatusTop: 46px;
|
||||||
callStatus: FlatLabel(defaultFlatLabel) {
|
callStatus: FlatLabel(defaultFlatLabel) {
|
||||||
width: 260px;
|
minWidth: 260px;
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
textFg: callStatusFg;
|
textFg: callStatusFg;
|
||||||
align: align(top);
|
align: align(top);
|
||||||
|
@ -29,7 +29,7 @@ switchPmButton: RoundButton(defaultBoxButton) {
|
|||||||
textTop: 7px;
|
textTop: 7px;
|
||||||
}
|
}
|
||||||
stickersRestrictedLabel: FlatLabel(defaultFlatLabel) {
|
stickersRestrictedLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 320px;
|
minWidth: 320px;
|
||||||
align: align(center);
|
align: align(center);
|
||||||
textFg: noContactsColor;
|
textFg: noContactsColor;
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,6 @@ infoProfileStatusLeft: 109px;
|
|||||||
infoProfileStatusRight: 20px;
|
infoProfileStatusRight: 20px;
|
||||||
infoProfileStatusTop: 58px;
|
infoProfileStatusTop: 58px;
|
||||||
infoProfileStatusLabel: FlatLabel(defaultFlatLabel) {
|
infoProfileStatusLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 0px;
|
|
||||||
maxHeight: 18px;
|
maxHeight: 18px;
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
@ -324,7 +323,6 @@ infoSharedMediaButtonIconPosition: point(20px, 3px);
|
|||||||
infoIconPosition: point(20px, 15px);
|
infoIconPosition: point(20px, 15px);
|
||||||
|
|
||||||
infoLabeledOneLine: FlatLabel(defaultFlatLabel) {
|
infoLabeledOneLine: FlatLabel(defaultFlatLabel) {
|
||||||
width: 0px; // No need to set minWidth in one-line text.
|
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
lineHeight: 19px;
|
lineHeight: 19px;
|
||||||
@ -335,7 +333,7 @@ infoLabel: FlatLabel(infoLabeledOneLine) {
|
|||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
infoLabeled: FlatLabel(infoLabeledOneLine) {
|
infoLabeled: FlatLabel(infoLabeledOneLine) {
|
||||||
width: 180px;
|
minWidth: 180px;
|
||||||
maxHeight: 0px;
|
maxHeight: 0px;
|
||||||
margin: margins(5px, 5px, 5px, 5px);
|
margin: margins(5px, 5px, 5px, 5px);
|
||||||
}
|
}
|
||||||
@ -538,11 +536,58 @@ infoCommonGroupsList: PeerList(infoMembersList) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
managePeerButton: InfoProfileButton(infoProfileButton) {
|
managePeerButton: InfoProfileButton(infoProfileButton) {
|
||||||
padding: margins(76px, 10px, 76px, 8px);
|
padding: margins(76px, 12px, 76px, 10px);
|
||||||
}
|
}
|
||||||
managePeerButtonIconPosition: infoSharedMediaButtonIconPosition;
|
managePeerButtonIconPosition: point(20px, 5px);
|
||||||
managePeerButtonLabel: FlatLabel(defaultFlatLabel) {
|
managePeerButtonLabel: FlatLabel(defaultFlatLabel) {
|
||||||
textFg: windowActiveTextFg;
|
textFg: windowActiveTextFg;
|
||||||
style: semiboldTextStyle;
|
style: semiboldTextStyle;
|
||||||
}
|
}
|
||||||
managePeerButtonLabelPosition: point(25px, 10px);
|
managePeerButtonLabelPosition: point(25px, 10px);
|
||||||
|
|
||||||
|
editPeerDeleteButtonMargins: margins(23px, 16px, 23px, 16px);
|
||||||
|
editPeerDeleteButton: sessionTerminateAllButton;
|
||||||
|
editPeerPhotoSize: newGroupPhotoSize;
|
||||||
|
editPeerPhotoIconPosition: newGroupPhotoIconPosition;
|
||||||
|
editPeerPhotoMargins: margins(23px, 16px, 23px, 8px);
|
||||||
|
editPeerTitle: defaultInputField;
|
||||||
|
editPeerTitleMargins: margins(27px, 21px, 23px, 8px);
|
||||||
|
editPeerDescription: newGroupDescription;
|
||||||
|
editPeerDescriptionMargins: margins(23px, 5px, 23px, 16px);
|
||||||
|
editPeerPrivaciesMargins: margins(23px, 10px, 23px, 0px);
|
||||||
|
editPeerPrivacyTopSkip: 10px;
|
||||||
|
editPeerPrivacyBottomSkip: 16px;
|
||||||
|
editPeerPrivacyLabel: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 263px;
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
editPeerPrivacyLabelMargins: margins(34px, 0px, 0px, 0px);
|
||||||
|
editPeerSectionLabel: FlatLabel(boxTitle) {
|
||||||
|
style: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(15px semibold);
|
||||||
|
linkFont: font(15px semibold);
|
||||||
|
linkFontOver: font(15px semibold underline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editPeerUsername: setupChannelLink;
|
||||||
|
editPeerUsernameSkip: 8px;
|
||||||
|
editPeerInviteLink: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 1px; // for break everywhere
|
||||||
|
style: boxTextStyle;
|
||||||
|
}
|
||||||
|
editPeerInviteLinkButton: boxLinkButton;
|
||||||
|
editPeerUsernameMargins: margins(0px, 10px, 0px, 0px);
|
||||||
|
editPeerUsernameGood: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: boxTextFgGood;
|
||||||
|
style: boxTextStyle;
|
||||||
|
}
|
||||||
|
editPeerUsernameError: FlatLabel(editPeerUsernameGood) {
|
||||||
|
textFg: boxTextFgError;
|
||||||
|
}
|
||||||
|
editPeerUsernamePosition: point(0px, 10px);
|
||||||
|
editPeerInviteLinkSkip: 10px;
|
||||||
|
editPeerInviteLinkMargins: margins(23px, 10px, 14px, 0px);
|
||||||
|
editPeerSignaturesMargins: margins(23px, 16px, 23px, 8px);
|
||||||
|
editPeerInvitesMargins: margins(23px, 18px, 23px, 16px);
|
||||||
|
editPeerInvitesTopSkip: 10px;
|
||||||
|
editPeerInvitesSkip: 10px;
|
||||||
|
@ -235,8 +235,8 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
|
|||||||
NotificationsEnabledValue(peer)
|
NotificationsEnabledValue(peer)
|
||||||
)->addClickHandler([=] {
|
)->addClickHandler([=] {
|
||||||
App::main()->updateNotifySetting(
|
App::main()->updateNotifySetting(
|
||||||
_peer,
|
peer,
|
||||||
_peer->isMuted()
|
peer->isMuted()
|
||||||
? NotifySettingSetNotify
|
? NotifySettingSetNotify
|
||||||
: NotifySettingSetMuted);
|
: NotifySettingSetMuted);
|
||||||
});
|
});
|
||||||
|
@ -37,7 +37,7 @@ profileNameLeft: 26px;
|
|||||||
profileNameTop: 9px;
|
profileNameTop: 9px;
|
||||||
profileNameLabel: FlatLabel(defaultFlatLabel) {
|
profileNameLabel: FlatLabel(defaultFlatLabel) {
|
||||||
margin: margins(10px, 5px, 10px, 5px);
|
margin: margins(10px, 5px, 10px, 5px);
|
||||||
width: 160px;
|
minWidth: 160px;
|
||||||
maxHeight: 24px;
|
maxHeight: 24px;
|
||||||
textFg: windowBoldFg;
|
textFg: windowBoldFg;
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
@ -98,15 +98,15 @@ profileBlockLabel: FlatLabel(defaultFlatLabel) {
|
|||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
profileBlockTextPart: FlatLabel(defaultFlatLabel) {
|
profileBlockTextPart: FlatLabel(defaultFlatLabel) {
|
||||||
width: 180px;
|
minWidth: 180px;
|
||||||
margin: margins(5px, 5px, 5px, 5px);
|
margin: margins(5px, 5px, 5px, 5px);
|
||||||
}
|
}
|
||||||
profileBlockOneLineTextPart: FlatLabel(profileBlockTextPart) {
|
profileBlockOneLineTextPart: FlatLabel(profileBlockTextPart) {
|
||||||
width: 0px; // No need to set minWidth in one-line text.
|
minWidth: 0px; // No need to set minWidth in one-line text.
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
}
|
}
|
||||||
profileBioLabel: FlatLabel(profileBlockOneLineTextPart) {
|
profileBioLabel: FlatLabel(profileBlockOneLineTextPart) {
|
||||||
width: 120px;
|
minWidth: 120px;
|
||||||
maxHeight: 0px;
|
maxHeight: 0px;
|
||||||
}
|
}
|
||||||
profileBlockOneLineSkip: 9px;
|
profileBlockOneLineSkip: 9px;
|
||||||
@ -116,7 +116,7 @@ profileEnableNotificationsTop: 7px;
|
|||||||
profileSettingsBlockSkip: 8px;
|
profileSettingsBlockSkip: 8px;
|
||||||
|
|
||||||
profileInviteLinkText: FlatLabel(profileBlockTextPart) {
|
profileInviteLinkText: FlatLabel(profileBlockTextPart) {
|
||||||
width: 1px; // Required for BreakEverywhere
|
minWidth: 1px; // Required for BreakEverywhere
|
||||||
}
|
}
|
||||||
|
|
||||||
profileLimitReachedSkip: 6px;
|
profileLimitReachedSkip: 6px;
|
||||||
@ -135,7 +135,7 @@ profileMemberCreatorIconOver: icon {{ "profile_admin_star", profileAdminStarFgOv
|
|||||||
profileMemberAdminIcon: icon {{ "profile_admin_star", profileOtherAdminStarFg, point(4px, 3px) }};
|
profileMemberAdminIcon: icon {{ "profile_admin_star", profileOtherAdminStarFg, point(4px, 3px) }};
|
||||||
profileMemberAdminIconOver: icon {{ "profile_admin_star", profileOtherAdminStarFgOver, point(4px, 3px) }};
|
profileMemberAdminIconOver: icon {{ "profile_admin_star", profileOtherAdminStarFgOver, point(4px, 3px) }};
|
||||||
profileLimitReachedLabel: FlatLabel(defaultFlatLabel) {
|
profileLimitReachedLabel: FlatLabel(defaultFlatLabel) {
|
||||||
width: 180px;
|
minWidth: 180px;
|
||||||
margin: margins(profileMemberPaddingLeft, 9px, profileMemberPaddingLeft, 6px);
|
margin: margins(profileMemberPaddingLeft, 9px, profileMemberPaddingLeft, 6px);
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
lineHeight: 19px;
|
lineHeight: 19px;
|
||||||
|
@ -315,8 +315,10 @@ void ParticipantsBoxController::restoreState(
|
|||||||
loadMoreRows();
|
loadMoreRows();
|
||||||
}
|
}
|
||||||
PeerListController::restoreState(std::move(state));
|
PeerListController::restoreState(std::move(state));
|
||||||
if (!_offset) {
|
if (delegate()->peerListFullRowsCount() > 0) {
|
||||||
setDescriptionText(QString());
|
setNonEmptyDescription();
|
||||||
|
} else if (_allLoaded) {
|
||||||
|
setDescriptionText(lang(lng_blocked_list_not_found));
|
||||||
}
|
}
|
||||||
sortByOnline();
|
sortByOnline();
|
||||||
}
|
}
|
||||||
@ -442,11 +444,9 @@ void ParticipantsBoxController::loadMoreRows() {
|
|||||||
_loadRequestId = request(MTPchannels_GetParticipants(_channel->inputChannel, filter(), MTP_int(_offset), MTP_int(perPage))).done([this](const MTPchannels_ChannelParticipants &result) {
|
_loadRequestId = request(MTPchannels_GetParticipants(_channel->inputChannel, filter(), MTP_int(_offset), MTP_int(perPage))).done([this](const MTPchannels_ChannelParticipants &result) {
|
||||||
Expects(result.type() == mtpc_channels_channelParticipants);
|
Expects(result.type() == mtpc_channels_channelParticipants);
|
||||||
|
|
||||||
|
auto firstLoad = !_offset;
|
||||||
_loadRequestId = 0;
|
_loadRequestId = 0;
|
||||||
|
|
||||||
if (!_offset) {
|
|
||||||
setDescriptionText((_role == Role::Restricted) ? lang(lng_group_blocked_list_about) : QString());
|
|
||||||
}
|
|
||||||
auto &participants = result.c_channels_channelParticipants();
|
auto &participants = result.c_channels_channelParticipants();
|
||||||
App::feedUsers(participants.vusers);
|
App::feedUsers(participants.vusers);
|
||||||
|
|
||||||
@ -462,13 +462,26 @@ void ParticipantsBoxController::loadMoreRows() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sortByOnline();
|
if (delegate()->peerListFullRowsCount() > 0) {
|
||||||
|
sortByOnline();
|
||||||
|
if (firstLoad) {
|
||||||
|
setNonEmptyDescription();
|
||||||
|
}
|
||||||
|
} else if (_allLoaded) {
|
||||||
|
setDescriptionText(lang(lng_blocked_list_not_found));
|
||||||
|
}
|
||||||
delegate()->peerListRefreshRows();
|
delegate()->peerListRefreshRows();
|
||||||
}).fail([this](const RPCError &error) {
|
}).fail([this](const RPCError &error) {
|
||||||
_loadRequestId = 0;
|
_loadRequestId = 0;
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticipantsBoxController::setNonEmptyDescription() {
|
||||||
|
setDescriptionText((_role == Role::Kicked)
|
||||||
|
? lang(lng_group_blocked_list_about)
|
||||||
|
: QString());
|
||||||
|
}
|
||||||
|
|
||||||
bool ParticipantsBoxController::feedMegagroupLastParticipants() {
|
bool ParticipantsBoxController::feedMegagroupLastParticipants() {
|
||||||
if ((_role != Role::Members && _role != Role::Profile)
|
if ((_role != Role::Members && _role != Role::Profile)
|
||||||
|| _offset > 0) {
|
|| _offset > 0) {
|
||||||
@ -822,7 +835,8 @@ bool ParticipantsBoxController::removeRow(not_null<UserData*> user) {
|
|||||||
} else {
|
} else {
|
||||||
delegate()->peerListRemoveRow(row);
|
delegate()->peerListRemoveRow(row);
|
||||||
}
|
}
|
||||||
if (!delegate()->peerListFullRowsCount()) {
|
if (_role != Role::Kicked
|
||||||
|
&& !delegate()->peerListFullRowsCount()) {
|
||||||
setDescriptionText(lang(lng_blocked_list_not_found));
|
setDescriptionText(lang(lng_blocked_list_not_found));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -120,6 +120,7 @@ private:
|
|||||||
Role role,
|
Role role,
|
||||||
not_null<Additional*> additional);
|
not_null<Additional*> additional);
|
||||||
|
|
||||||
|
void setNonEmptyDescription();
|
||||||
void setupSortByOnline();
|
void setupSortByOnline();
|
||||||
void setupListChangeViewers();
|
void setupListChangeViewers();
|
||||||
void sortByOnlineDelayed();
|
void sortByOnlineDelayed();
|
||||||
|
@ -49,7 +49,7 @@ settingsNameLeft: 26px;
|
|||||||
settingsNameTop: 9px;
|
settingsNameTop: 9px;
|
||||||
settingsNameLabel: FlatLabel(defaultFlatLabel) {
|
settingsNameLabel: FlatLabel(defaultFlatLabel) {
|
||||||
margin: margins(10px, 5px, 10px, 5px);
|
margin: margins(10px, 5px, 10px, 5px);
|
||||||
width: 160px;
|
minWidth: 160px;
|
||||||
maxHeight: 24px;
|
maxHeight: 24px;
|
||||||
textFg: windowBoldFg;
|
textFg: windowBoldFg;
|
||||||
style: TextStyle(defaultTextStyle) {
|
style: TextStyle(defaultTextStyle) {
|
||||||
@ -98,12 +98,12 @@ settingsBlockLabel: FlatLabel(settingsPrimaryLabel) {
|
|||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
settingsBlockOneLineTextPart: FlatLabel(settingsPrimaryLabel) {
|
settingsBlockOneLineTextPart: FlatLabel(settingsPrimaryLabel) {
|
||||||
width: 0px; // No need to set minWidth in one-line text.
|
minWidth: 0px; // No need to set minWidth in one-line text.
|
||||||
margin: margins(5px, 5px, 5px, 5px);
|
margin: margins(5px, 5px, 5px, 5px);
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
}
|
}
|
||||||
settingsBioValue: FlatLabel(settingsBlockOneLineTextPart) {
|
settingsBioValue: FlatLabel(settingsBlockOneLineTextPart) {
|
||||||
width: 120px;
|
minWidth: 120px;
|
||||||
maxHeight: 0px;
|
maxHeight: 0px;
|
||||||
}
|
}
|
||||||
settingsSubSkip: 4px;
|
settingsSubSkip: 4px;
|
||||||
|
@ -168,6 +168,10 @@ public:
|
|||||||
|
|
||||||
void setImage(const QImage &image);
|
void setImage(const QImage &image);
|
||||||
|
|
||||||
|
int naturalWidth() const override {
|
||||||
|
return height();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
@ -1744,7 +1744,12 @@ void FlatInput::onTextChange(const QString &text) {
|
|||||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
InputArea::InputArea(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val) : TWidget(parent)
|
InputArea::InputArea(
|
||||||
|
QWidget *parent,
|
||||||
|
const style::InputField &st,
|
||||||
|
base::lambda<QString()> placeholderFactory,
|
||||||
|
const QString &val)
|
||||||
|
: RpWidget(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _inner(this)
|
, _inner(this)
|
||||||
, _oldtext(val)
|
, _oldtext(val)
|
||||||
@ -3272,7 +3277,12 @@ void InputField::setErrorShown(bool error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val) : TWidgetHelper<QLineEdit>(val, parent)
|
MaskedInputField::MaskedInputField(
|
||||||
|
QWidget *parent,
|
||||||
|
const style::InputField &st,
|
||||||
|
base::lambda<QString()> placeholderFactory,
|
||||||
|
const QString &val)
|
||||||
|
: Parent(val, parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _oldtext(val)
|
, _oldtext(val)
|
||||||
, _placeholderFactory(std::move(placeholderFactory)) {
|
, _placeholderFactory(std::move(placeholderFactory)) {
|
||||||
@ -3350,15 +3360,18 @@ void MaskedInputField::onTouchTimer() {
|
|||||||
_touchRightButton = true;
|
_touchRightButton = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaskedInputField::event(QEvent *e) {
|
bool MaskedInputField::eventHook(QEvent *e) {
|
||||||
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
|
auto type = e->type();
|
||||||
QTouchEvent *ev = static_cast<QTouchEvent*>(e);
|
if (type == QEvent::TouchBegin
|
||||||
if (ev->device()->type() == QTouchDevice::TouchScreen) {
|
|| type == QEvent::TouchUpdate
|
||||||
touchEvent(ev);
|
|| type == QEvent::TouchEnd
|
||||||
return QLineEdit::event(e);
|
|| type == QEvent::TouchCancel) {
|
||||||
|
auto event = static_cast<QTouchEvent*>(e);
|
||||||
|
if (event->device()->type() == QTouchDevice::TouchScreen) {
|
||||||
|
touchEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QLineEdit::event(e);
|
return Parent::eventHook(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaskedInputField::touchEvent(QTouchEvent *e) {
|
void MaskedInputField::touchEvent(QTouchEvent *e) {
|
||||||
|
@ -324,11 +324,15 @@ enum class CtrlEnterSubmit {
|
|||||||
Both,
|
Both,
|
||||||
};
|
};
|
||||||
|
|
||||||
class InputArea : public TWidget, private base::Subscriber {
|
class InputArea : public RpWidget, private base::Subscriber {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InputArea(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
|
InputArea(
|
||||||
|
QWidget *parent,
|
||||||
|
const style::InputField &st,
|
||||||
|
base::lambda<QString()> placeholderFactory = base::lambda<QString()>(),
|
||||||
|
const QString &val = QString());
|
||||||
|
|
||||||
void showError();
|
void showError();
|
||||||
|
|
||||||
@ -687,9 +691,12 @@ private:
|
|||||||
bool _correcting = false;
|
bool _correcting = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MaskedInputField : public TWidgetHelper<QLineEdit>, private base::Subscriber {
|
class MaskedInputField
|
||||||
|
: public RpWidgetWrap<QLineEdit>
|
||||||
|
, private base::Subscriber {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
using Parent = RpWidgetWrap<QLineEdit>;
|
||||||
public:
|
public:
|
||||||
MaskedInputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
|
MaskedInputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
|
||||||
|
|
||||||
@ -748,7 +755,7 @@ protected:
|
|||||||
void startBorderAnimation();
|
void startBorderAnimation();
|
||||||
void startPlaceholderAnimation();
|
void startPlaceholderAnimation();
|
||||||
|
|
||||||
bool event(QEvent *e) override;
|
bool eventHook(QEvent *e) override;
|
||||||
void touchEvent(QTouchEvent *e);
|
void touchEvent(QTouchEvent *e);
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void focusInEvent(QFocusEvent *e) override;
|
void focusInEvent(QFocusEvent *e) override;
|
||||||
|
@ -139,7 +139,7 @@ void LabelSimple::paintEvent(QPaintEvent *e) {
|
|||||||
|
|
||||||
FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st)
|
FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _text(st.width ? st.width : QFIXED_MAX)
|
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _contextCopyText(lang(lng_context_copy_text)) {
|
, _contextCopyText(lang(lng_context_copy_text)) {
|
||||||
init();
|
init();
|
||||||
@ -151,7 +151,7 @@ FlatLabel::FlatLabel(
|
|||||||
InitType initType,
|
InitType initType,
|
||||||
const style::FlatLabel &st)
|
const style::FlatLabel &st)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _text(st.width ? st.width : QFIXED_MAX)
|
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _contextCopyText(lang(lng_context_copy_text)) {
|
, _contextCopyText(lang(lng_context_copy_text)) {
|
||||||
if (initType == InitType::Rich) {
|
if (initType == InitType::Rich) {
|
||||||
@ -167,7 +167,7 @@ FlatLabel::FlatLabel(
|
|||||||
rpl::producer<QString> &&text,
|
rpl::producer<QString> &&text,
|
||||||
const style::FlatLabel &st)
|
const style::FlatLabel &st)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _text(st.width ? st.width : QFIXED_MAX)
|
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _contextCopyText(lang(lng_context_copy_text)) {
|
, _contextCopyText(lang(lng_context_copy_text)) {
|
||||||
textUpdated();
|
textUpdated();
|
||||||
@ -182,7 +182,7 @@ FlatLabel::FlatLabel(
|
|||||||
rpl::producer<TextWithEntities> &&text,
|
rpl::producer<TextWithEntities> &&text,
|
||||||
const style::FlatLabel &st)
|
const style::FlatLabel &st)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _text(st.width ? st.width : QFIXED_MAX)
|
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _contextCopyText(lang(lng_context_copy_text)) {
|
, _contextCopyText(lang(lng_context_copy_text)) {
|
||||||
textUpdated();
|
textUpdated();
|
||||||
@ -259,7 +259,7 @@ QMargins FlatLabel::getMargins() const {
|
|||||||
int FlatLabel::countTextWidth() const {
|
int FlatLabel::countTextWidth() const {
|
||||||
return _allowedWidth
|
return _allowedWidth
|
||||||
? _allowedWidth
|
? _allowedWidth
|
||||||
: (_st.width ? _st.width : _text.maxWidth());
|
: (_st.minWidth ? _st.minWidth : _text.maxWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
int FlatLabel::countTextHeight(int textWidth) {
|
int FlatLabel::countTextHeight(int textWidth) {
|
||||||
|
@ -29,7 +29,7 @@ LabelSimple {
|
|||||||
|
|
||||||
FlatLabel {
|
FlatLabel {
|
||||||
margin: margins;
|
margin: margins;
|
||||||
width: pixels;
|
minWidth: pixels;
|
||||||
align: align;
|
align: align;
|
||||||
textFg: color;
|
textFg: color;
|
||||||
maxHeight: pixels;
|
maxHeight: pixels;
|
||||||
@ -522,7 +522,7 @@ defaultLabelSimple: LabelSimple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultFlatLabel: FlatLabel {
|
defaultFlatLabel: FlatLabel {
|
||||||
width: 0px;
|
minWidth: 0px;
|
||||||
maxHeight: 0px;
|
maxHeight: 0px;
|
||||||
align: align(left);
|
align: align(left);
|
||||||
textFg: windowFg;
|
textFg: windowFg;
|
||||||
|
@ -158,4 +158,42 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IgnoreMargins : public Wrap<RpWidget> {
|
||||||
|
using Parent = Wrap<RpWidget>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IgnoreMargins(QWidget *parent, object_ptr<RpWidget> &&child)
|
||||||
|
: Parent(parent, std::move(child)) {
|
||||||
|
if (auto weak = wrapped()) {
|
||||||
|
auto margins = weak->getMargins();
|
||||||
|
resizeToWidth(weak->width()
|
||||||
|
- margins.left()
|
||||||
|
- margins.right());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QMargins getMargins() const override {
|
||||||
|
return QMargins();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int resizeGetHeight(int newWidth) override {
|
||||||
|
if (auto weak = wrapped()) {
|
||||||
|
weak->resizeToWidth(newWidth);
|
||||||
|
weak->moveToLeft(0, 0);
|
||||||
|
return weak->heightNoMargins();
|
||||||
|
}
|
||||||
|
return height();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void wrappedSizeUpdated(QSize size) override {
|
||||||
|
auto margins = wrapped()->getMargins();
|
||||||
|
resize(
|
||||||
|
size.width() - margins.left() - margins.right(),
|
||||||
|
size.height() - margins.top() - margins.bottom());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
<(src_loc)/base/virtual_method.h
|
<(src_loc)/base/virtual_method.h
|
||||||
<(src_loc)/base/weak_unique_ptr.h
|
<(src_loc)/base/weak_unique_ptr.h
|
||||||
<(src_loc)/base/zlib_help.h
|
<(src_loc)/base/zlib_help.h
|
||||||
|
<(src_loc)/boxes/peers/edit_peer_info_box.cpp
|
||||||
|
<(src_loc)/boxes/peers/edit_peer_info_box.h
|
||||||
<(src_loc)/boxes/peers/manage_peer_box.cpp
|
<(src_loc)/boxes/peers/manage_peer_box.cpp
|
||||||
<(src_loc)/boxes/peers/manage_peer_box.h
|
<(src_loc)/boxes/peers/manage_peer_box.h
|
||||||
<(src_loc)/boxes/about_box.cpp
|
<(src_loc)/boxes/about_box.cpp
|
||||||
|
Loading…
Reference in New Issue
Block a user