diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings
index 3c0d0b6e0..f4255ae1b 100644
--- a/Telegram/Resources/lang.strings
+++ b/Telegram/Resources/lang.strings
@@ -161,7 +161,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 "lng_dlg_filter" = "Search";
 "lng_dlg_new_group_name" = "Group name";
 "lng_dlg_new_channel_name" = "Channel name";
-"lng_dlg_create_group" = "Create";
 "lng_no_contacts" = "You have no contacts";
 "lng_no_chats" = "Your chats will be here";
 "lng_contacts_loading" = "Loading..";
@@ -378,7 +377,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 "lng_profile_invite_to_group" = "Add to Group";
 "lng_profile_delete_contact" = "Delete";
 "lng_profile_set_group_photo" = "Set Photo";
-"lng_profile_add_participant" = "Add Member";
+"lng_profile_add_participant" = "Add Members";
 "lng_profile_delete_and_exit" = "Leave";
 "lng_profile_kick" = "Kick";
 "lng_profile_sure_kick" = "Kick {user} from the group?";
@@ -402,9 +401,28 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 "lng_participant_invite" = "Invite";
 "lng_create_new_group" = "New Group";
 "lng_create_new_channel" = "New Channel";
+"lng_create_group_back" = "Back";
 "lng_create_group_next" = "Next";
+"lng_create_group_create" = "Create";
 "lng_create_group_title" = "New Group";
+"lng_create_group_about" = "Groups have up to 200 members and are good for smaller communities";
 "lng_create_channel_title" = "New Channel";
+"lng_create_channel_about" = "Channels have unlimited number of members and are good for connecting with large audiences";
+"lng_create_public_channel_title" = "Public Channel";
+"lng_create_public_channel_about" = "Everyone will be able to join your channel";
+"lng_create_private_channel_title" = "Private Channel";
+"lng_create_private_channel_about" = "Only those who have the invitation link will be able to join your channel";
+"lng_create_group_save" = "Save";
+"lng_create_group_skip" = "Skip";
+
+"lng_create_channel_link_invalid" = "This link is invalid";
+"lng_create_channel_link_occupied" = "This link is already occupied";
+"lng_create_channel_link_too_short" = "This link is too short";
+"lng_create_channel_link_bad_symbols" = "This link has bad symbols";
+"lng_create_channel_link_available" = "This link is available";
+
+"lng_create_group_crop" = "Select square area for group photo";
+"lng_create_channel_crop" = "Select square area for channel photo";
 
 "lng_failed_add_participant" = "Could not add user. Try again later.";
 "lng_failed_add_not_mutual" = "Sorry, if a person left a group, only a\nmutual contact can bring them back\n(they need to have your phone\nnumber, and you need theirs).";
@@ -435,6 +453,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 "lng_action_changed_title_channel" = "Channel name was changed to «{title}»";
 "lng_action_created_chat" = "{from} created group «{title}»";
 "lng_action_created_channel" = "Channel «{title}» created";
+"lng_action_comments_disabled" = "Comments were disabled";
+"lng_action_comments_enabled" = "Comments were enabled";
 
 "lng_group_invite_bad_link" = "This invite link is broken\nor has expired.";
 "lng_group_invite_want_join" = "Do you want to join the group «{title}»?";
@@ -509,6 +529,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 
 "lng_send_button" = "Send";
 "lng_message_ph" = "Write a message..";
+"lng_comment_ph" = "Write a comment..";
+"lng_broadcast_ph" = "Broadcast a message..";
 "lng_record_cancel" = "Release outside this field to cancel";
 "lng_empty_history" = "";
 "lng_willbe_history" = "Please select a chat to start messaging";
@@ -627,7 +649,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 "lng_contacts_header" = "Contacts";
 "lng_contact_not_joined" = "Unfortunately {name} did not join Telegram yet, but you can send your friend an invitation.\n\nWe will notify you about any of your contacts who is joining Telegram.";
 "lng_try_other_contact" = "Try other";
-"lng_contacts_done" = "Cancel";
+"lng_contacts_done" = "Close";
+"lng_create_group_link" = "Link";
+"lng_create_group_photo" = "Set Photo";
+"lng_create_group_description" = "Description (optional)";
 
 "lng_drag_images_here" = "Drop images here";
 "lng_drag_photos_here" = "Drop photos here";
diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt
index 51ed821c8..eaf0af65a 100644
--- a/Telegram/Resources/style.txt
+++ b/Telegram/Resources/style.txt
@@ -1380,15 +1380,57 @@ contactsFilter: flatInput(dlgFilter) {
 	textMrg: margins(34px, 3px, 5px, 4px);
 	imgPos: point(6px, 7px);
 }
-newGroupName: flatInput(inpDefGray) {
-	width: 340px;
-	height: 44px;
-	font: font(15px);
-	textMrg: margins(12px, 5px, 12px, 5px);
-}
 inpCountry: flatInput(contactsFilter) {
 }
+newGroupName: flatInput(inpDefGray) {
+	width: 340px;
+	height: 42px;
+	font: font(15px);
+	textMrg: margins(12px, 4px, 12px, 4px);
+}
+newGroupLink: flatInput(inpDefFlat) {
+	width: 340px;
+	height: 42px;
+	font: font(15px);
+	textMrg: margins(12px, 4px, 12px, 4px);
+	bgColor: transparent;
+	bgActive: transparent;
+	borderWidth: 2px;
+	borderColor: #f2f2f2;
+	borderActive: #80cff9;
+	borderError: #ed8080;
+	phColor: #828282;
+	phFocusColor: #949494;
+}
+newGroupLimitFg: #a4a4a4;
+newGroupAboutFg: #808080;
 newGroupNamePadding: margins(12px, 15px, 12px, 13px);
+newGroupPadding: margins(26px, 28px, 26px, 24px);
+newGroupSkip: 15px;
+newGroupLinkPadding: margins(26px, 27px, 26px, 27px);
+newGroupLinkTop: -3px;
+newGroupLinkFont: font(16px);
+newGroupPhoto: flatButton(btnDefNext, btnDefBig) {
+	width: 199px;
+	height: 42px;
+
+	textTop: 9px;
+	overTextTop: 9px;
+	downTextTop: 10px;
+
+	font: font(17px);
+	overFont: font(17px);
+}
+newGroupPhotoSize: 96px;
+newGroupPhotoSkip: 18px;
+newGroupDescriptionSkip: 28px;
+newGroupDescription: flatTextarea(taDefFlat) {
+	font: font(15px);
+	bgColor: transparent;
+	phColor: #828282;
+	phFocusColor: #949494;
+}
+newGroupDescriptionPadding: margins(5px, 6px, 5px, 6px);
 
 connectionSkip: 20px;
 inpConnectionHost: flatInput(inpDefGray) {
@@ -1546,10 +1588,6 @@ dragPadding: margins(20px, 10px, 20px, 10px);
 
 dragHeight: 72px;
 
-selectedFont: font(fsize);
-selectedColor: #777;
-selectedTop: 14px;
-
 downloadSkip: 20px;
 inpDownloadDir: flatInput(inpDefGray) {
 	font: font(fsize);
diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h
index 630a36cb8..ece1a89b4 100644
--- a/Telegram/SourceFiles/boxes/addcontactbox.h
+++ b/Telegram/SourceFiles/boxes/addcontactbox.h
@@ -30,6 +30,10 @@ public:
 	void paintEvent(QPaintEvent *e);
 	void resizeEvent(QResizeEvent *e);
 
+	void setInnerFocus() {
+		_firstInput.setFocus();
+	}
+
 public slots:
 
 	void onSend();
diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp
index 35343e2a8..7341fad73 100644
--- a/Telegram/SourceFiles/boxes/backgroundbox.cpp
+++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp
@@ -172,7 +172,7 @@ void BackgroundInner::resizeEvent(QResizeEvent *e) {
 }
 
 BackgroundBox::BackgroundBox() : ItemListBox(st::boxScroll), _inner(),
-_close(this, lang(lng_contacts_done), st::contactsClose) {
+_close(this, lang(lng_cancel), st::contactsClose) {
 
 	init(&_inner, _close.height(), st::boxFont->height + st::newGroupNamePadding.top() + st::newGroupNamePadding.bottom());
 
diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp
index f6cb0da1f..913a3da0b 100644
--- a/Telegram/SourceFiles/boxes/contactsbox.cpp
+++ b/Telegram/SourceFiles/boxes/contactsbox.cpp
@@ -23,9 +23,38 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "mainwidget.h"
 #include "window.h"
 
+#include "application.h"
+
+#include "gui/filedialog.h"
+#include "photocropbox.h"
+
 #include "confirmbox.h"
 
-ContactsInner::ContactsInner(bool creatingChat) : _chat(0), _bot(0), _creatingChat(creatingChat), _addToChat(0),
+ContactsInner::ContactsInner(CreatingGroupType creating) : _chat(0), _channel(0), _bot(0), _creating(creating), _addToChat(0),
+_contacts(&App::main()->contactsList()),
+_sel(0),
+_filteredSel(-1),
+_mouseSel(false),
+_selCount(1),
+_searching(false),
+_byUsernameSel(-1),
+_addContactLnk(this, lang(lng_add_contact_button)) {
+	init();
+}
+
+ContactsInner::ContactsInner(ChannelData *channel) : _chat(0), _channel(channel), _bot(0), _creating(CreatingGroupChannel), _addToChat(0),
+_contacts(&App::main()->contactsList()),
+_sel(0),
+_filteredSel(-1),
+_mouseSel(false),
+_selCount(1),
+_searching(false),
+_byUsernameSel(-1),
+_addContactLnk(this, lang(lng_add_contact_button)) {
+	init();
+}
+
+ContactsInner::ContactsInner(ChatData *chat) : _chat(chat), _channel(0), _bot(0), _creating(CreatingGroupNone), _addToChat(0),
 _contacts(&App::main()->contactsList()),
 _sel(0),
 _filteredSel(-1),
@@ -37,19 +66,7 @@ _addContactLnk(this, lang(lng_add_contact_button)) {
 	init();
 }
 
-ContactsInner::ContactsInner(ChatData *chat) : _chat(chat), _bot(0), _creatingChat(false), _addToChat(0),
-_contacts(&App::main()->contactsList()),
-_sel(0),
-_filteredSel(-1),
-_mouseSel(false),
-_selCount(0),
-_searching(false),
-_byUsernameSel(-1),
-_addContactLnk(this, lang(lng_add_contact_button)) {
-	init();
-}
-
-ContactsInner::ContactsInner(UserData *bot) : _chat(0), _bot(bot), _creatingChat(false), _addToChat(0),
+ContactsInner::ContactsInner(UserData *bot) : _chat(0), _channel(0), _bot(bot), _creating(CreatingGroupNone), _addToChat(0),
 _contacts(new DialogsIndexed(DialogsSortByAdd)),
 _sel(0),
 _filteredSel(-1),
@@ -92,14 +109,12 @@ void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &old
 
 void ContactsInner::onAddBot() {
 	if (_bot->botInfo && !_bot->botInfo->startGroupToken.isEmpty()) {
-		uint64 randomId = MTP::nonce<uint64>();
-		MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToChat->inputChat, MTP_long(randomId), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot));
-
-		App::wnd()->hideLayer();
-		App::main()->showPeerHistory(_addToChat->id, ShowAtUnreadMsgId);
+		MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToChat->inputChat, MTP_long(MTP::nonce<uint64>()), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot));
 	} else {
 		App::main()->addParticipants(_addToChat, QVector<UserData*>(1, _bot));
 	}
+	App::wnd()->hideLayer();
+	App::main()->showPeerHistory(_addToChat->id, ShowAtUnreadMsgId);
 }
 
 void ContactsInner::peerUpdated(PeerData *peer) {
@@ -179,7 +194,7 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) {
 		ContactsData::const_iterator i = _contactsData.constFind(peer);
 		if (i == _contactsData.cend()) {
 			_contactsData.insert(peer, data = new ContactData());
-			data->inchat = (_chat && peer->isUser()) ? _chat->participants.contains(peer->asUser()) : false;
+			data->inchat = (_chat && peer->isUser()) ? _chat->participants.contains(peer->asUser()) : ((_creating == CreatingGroupGroup || _channel) ? (peer == App::self()) : false);
 			data->check = _checkedContacts.contains(peer);
 			data->name.setText(st::profileListNameFont, peer->name, _textNameOptions);
 			if (peer->isUser()) {
@@ -207,7 +222,7 @@ void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data,
 
 	UserData *user = peer->asUser();
 
-	if (data->inchat || data->check || _selCount + (_chat ? _chat->count : 0) >= cMaxGroupCount()) {
+	if (data->inchat || data->check || selectedCount() >= cMaxGroupCount()) {
 		sel = false;
 	}
 
@@ -222,10 +237,10 @@ void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data,
 	} else {
 		p.setPen(st::profileListNameColor->p);
 	}
-	int32 iconw = (_chat || _creatingChat) ? st::profileCheckRect.pxWidth() : st::contactsImg.pxWidth();
+	int32 iconw = (_chat || _creating != CreatingGroupNone) ? st::profileCheckRect.pxWidth() : st::contactsImg.pxWidth();
 	data->name.drawElided(p, left + st::profileListPhotoSize + st::participantDelta, st::profileListNameTop, width() - left - st::profileListPhotoSize - st::profileListPadding.width() - st::participantDelta - st::scrollDef.width - iconw);
 
-	if (_chat || _creatingChat) {
+	if (_chat || _creating !=CreatingGroupNone) {
 		if (sel || data->check) {
 			p.drawPixmap(QPoint(width() - st::profileCheckRect.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::profileCheckRect.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), (data->check ? st::profileCheckActiveRect : st::profileCheckRect));
 		}
@@ -397,7 +412,7 @@ void ContactsInner::mousePressEvent(QMouseEvent *e) {
 }
 
 void ContactsInner::chooseParticipant() {
-	if (_chat || _creatingChat) {
+	if (_chat || _creating != CreatingGroupNone) {
 		_time = unixtime();
 		int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
 		if (_filter.isEmpty()) {
@@ -476,15 +491,21 @@ void ContactsInner::changeCheckState(DialogRow *row) {
 }
 
 void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) {
+	int32 cnt = _selCount;
 	if (data->check) {
 		data->check = false;
 		_checkedContacts.remove(peer);
 		--_selCount;
-	} else if (_selCount + (_chat ? _chat->count : 0) < cMaxGroupCount()) {
+	} else if (selectedCount() < cMaxGroupCount()) {
 		data->check = true;
 		_checkedContacts.insert(peer, true);
 		++_selCount;
 	}
+	if (cnt != _selCount) emit chosenChanged();
+}
+
+int32 ContactsInner::selectedCount() const {
+	return _selCount + ((_chat && _chat->count >= 0) ? _chat->count : 0);
 }
 
 void ContactsInner::updateSel() {
@@ -695,11 +716,12 @@ void ContactsInner::peopleReceived(const QString &query, const QVector<MTPPeer>
 			PeerData *p = App::peer(peerId);
 			if (!p) continue;
 
-			if ((!p->isUser() || p->asUser()->botInfo && p->asUser()->botInfo->cantJoinGroups) && (_chat || _creatingChat)) continue; // skip bot's that can't be invited to groups
+			if ((!p->isUser() || p->asUser()->botInfo && p->asUser()->botInfo->cantJoinGroups) && (_chat || _creating != CreatingGroupNone)) continue; // skip bot's that can't be invited to groups
+			if (p->isUser() && p->asUser()->botInfo && _creating == CreatingGroupChannel) continue; // skip bots in channels
 
 			ContactData *d = new ContactData();
 			_byUsernameDatas.push_back(d);
-			d->inchat = _chat ? _chat->participants.contains(p->asUser()) : false;
+			d->inchat = _chat ? _chat->participants.contains(p->asUser()) : ((_creating == CreatingGroupGroup || _channel) ? (p == App::self()) : false);
 			d->check = _checkedContacts.contains(p);
 			d->name.setText(st::profileListNameFont, p->name, _textNameOptions);
 			d->online = '@' + p->userName();
@@ -741,12 +763,16 @@ ChatData *ContactsInner::chat() const {
 	return _chat;
 }
 
+ChannelData *ContactsInner::channel() const {
+	return _channel;
+}
+
 UserData *ContactsInner::bot() const {
 	return _bot;
 }
 
-bool ContactsInner::creatingChat() const {
-	return _creatingChat;
+CreatingGroupType ContactsInner::creating() const {
+	return _creating;
 }
 
 ContactsInner::~ContactsInner() {
@@ -946,51 +972,66 @@ PeerData *ContactsInner::selectedUser() {
 	return 0;
 }
 
-ContactsBox::ContactsBox(bool creatingChat) : ItemListBox(st::boxNoTopScroll), _inner(creatingChat),
+ContactsBox::ContactsBox() : ItemListBox(st::boxNoTopScroll), _inner(CreatingGroupNone),
 _addContact(this, lang(lng_add_contact_button), st::contactsAdd),
-_createChannel(this, lang(lng_create_channel_button), st::contactsAdd),
 _filter(this, st::contactsFilter, lang(lng_participant_filter)),
 _next(this, lang(lng_create_group_next), st::btnSelectDone),
-_cancel(this, lang(lng_contacts_done), creatingChat ? st::btnSelectCancel : st::contactsClose), _creatingChannel(false) {
+_cancel(this, lang(lng_contacts_done), st::contactsClose),
+_creationRequestId(0) {
+	init();
+}
+
+ContactsBox::ContactsBox(const QString &name, const QImage &photo) : ItemListBox(st::boxNoTopScroll), _inner(CreatingGroupGroup),
+_addContact(this, lang(lng_add_contact_button), st::contactsAdd),
+_filter(this, st::contactsFilter, lang(lng_participant_filter)),
+_next(this, lang(lng_create_group_create), st::btnSelectDone),
+_cancel(this, lang(lng_create_group_back), st::btnSelectCancel),
+_creationRequestId(0), _creationName(name), _creationPhoto(photo) {
+	init();
+}
+
+ContactsBox::ContactsBox(ChannelData *channel) : ItemListBox(st::boxNoTopScroll), _inner(channel),
+_addContact(this, lang(lng_add_contact_button), st::contactsAdd),
+_filter(this, st::contactsFilter, lang(lng_participant_filter)),
+_next(this, lang(lng_participant_invite), st::btnSelectDone),
+_cancel(this, lang(lng_create_group_skip), st::btnSelectCancel),
+_creationRequestId(0) {
 	init();
 }
 
 ContactsBox::ContactsBox(ChatData *chat) : ItemListBox(st::boxNoTopScroll), _inner(chat),
 _addContact(this, lang(lng_add_contact_button), st::contactsAdd),
-_createChannel(this, lang(lng_create_channel_button), st::contactsAdd),
 _filter(this, st::contactsFilter, lang(lng_participant_filter)),
 _next(this, lang(lng_participant_invite), st::btnSelectDone),
-_cancel(this, lang(lng_cancel), st::btnSelectCancel), _creatingChannel(false) {
+_cancel(this, lang(lng_cancel), st::btnSelectCancel),
+_creationRequestId(0) {
 	init();
 }
 
 ContactsBox::ContactsBox(UserData *bot) : ItemListBox(st::boxNoTopScroll), _inner(bot),
 _addContact(this, lang(lng_add_contact_button), st::contactsAdd),
-_createChannel(this, lang(lng_create_channel_button), st::contactsAdd),
 _filter(this, st::contactsFilter, lang(lng_participant_filter)),
 _next(this, lang(lng_create_group_next), st::btnSelectDone),
-_cancel(this, lang(lng_cancel), st::contactsClose), _creatingChannel(false) {
+_cancel(this, lang(lng_cancel), st::contactsClose),
+_creationRequestId(0) {
 	init();
 }
 
 void ContactsBox::init() {
 	ItemListBox::init(&_inner, _cancel.height(), st::contactsAdd.height + st::newGroupNamePadding.top() + _filter.height() + st::newGroupNamePadding.bottom());
 
+	connect(&_inner, SIGNAL(chosenChanged()), this, SLOT(update()));
 	if (_inner.chat()) {
 		_addContact.hide();
-		_createChannel.hide();
-	} else if (_inner.creatingChat()) {
+	} else if (_inner.creating() != CreatingGroupNone) {
 		_addContact.hide();
-		_createChannel.show();
-		connect(&_createChannel, SIGNAL(clicked()), this, SLOT(onCreateChannel()));
 	} else {
 		connect(&_addContact, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
-		_createChannel.hide();
 	}
-	if (_inner.chat()) {
+	if (_inner.chat() || _inner.channel()) {
 		connect(&_next, SIGNAL(clicked()), this, SLOT(onInvite()));
-	} else if (_inner.creatingChat()) {
-		connect(&_next, SIGNAL(clicked()), this, SLOT(onNext()));
+	} else if (_inner.creating() != CreatingGroupNone) {
+		connect(&_next, SIGNAL(clicked()), this, SLOT(onCreate()));
 	} else {
 		_next.hide();
 	}
@@ -998,7 +1039,6 @@ void ContactsBox::init() {
 	connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSel()));
 	connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
 	connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
-	connect(&_filter, SIGNAL(cancelled()), this, SLOT(onClose()));
 	connect(&_inner, SIGNAL(mustScrollTo(int, int)), &_scroll, SLOT(scrollToY(int, int)));
 	connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll()));
 	connect(&_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername()));
@@ -1079,7 +1119,6 @@ bool ContactsBox::peopleFailed(const RPCError &error, mtpRequestId req) {
 void ContactsBox::hideAll() {
 	ItemListBox::hideAll();
 	_addContact.hide();
-	_createChannel.hide();
 	_filter.hide();
 	_next.hide();
 	_cancel.hide();
@@ -1091,11 +1130,9 @@ void ContactsBox::showAll() {
 	if (_inner.chat()) {
 		_next.show();
 		_addContact.hide();
-		_createChannel.hide();
-	} else if (_inner.creatingChat()) {
+	} else if (_inner.creating() != CreatingGroupNone) {
 		_next.show();
 		_addContact.hide();
-		_createChannel.show();
 	} else {
 		_next.hide();
 		if (_inner.bot()) {
@@ -1103,7 +1140,6 @@ void ContactsBox::showAll() {
 		} else {
 			_addContact.show();
 		}
-		_createChannel.hide();
 	}
 	_cancel.show();
 }
@@ -1140,8 +1176,12 @@ void ContactsBox::paintEvent(QPaintEvent *e) {
 	Painter p(this);
 	if (paint(p)) return;
 
-	if (_inner.chat() || _inner.creatingChat()) {
-		paintTitle(p, lang(_inner.chat() ? lng_profile_add_participant : (_creatingChannel ? lng_create_new_channel : lng_create_new_group)), true);
+	if (_inner.chat() || _inner.creating() != CreatingGroupNone) {
+		QString title(lang(lng_profile_add_participant));
+		paintTitle(p, title, true);
+
+		p.setPen(st::newGroupLimitFg);
+		p.drawTextLeft(st::boxTitlePos.x() + st::boxTitleFont->m.width(title) + st::addContactDelta, st::boxTitlePos.y(), width(), QString("%1 / %2").arg(_inner.selectedCount()).arg(cMaxGroupCount()));
 
 		// paint button sep
 		p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
@@ -1155,13 +1195,18 @@ void ContactsBox::paintEvent(QPaintEvent *e) {
 void ContactsBox::resizeEvent(QResizeEvent *e) {
 	ItemListBox::resizeEvent(e);
 	_addContact.move(width() - _addContact.width(), 0);
-	_createChannel.move(width() - _createChannel.width(), 0);
 	_filter.move(st::newGroupNamePadding.left(), _addContact.height() + st::newGroupNamePadding.top());
 	_inner.resize(width(), _inner.height());
 	_next.move(width() - _next.width(), height() - _next.height());
 	_cancel.move(0, height() - _cancel.height());
 }
 
+void ContactsBox::closePressed() {
+	if (_inner.channel()) {
+		App::main()->showPeerHistory(_inner.channel()->id, ShowAtTheEndMsgId);
+	}
+}
+
 void ContactsBox::onFilterUpdate() {
 	_scroll.scrollToY(0);
 	_inner.updateFilter(_filter.text());
@@ -1171,130 +1216,52 @@ void ContactsBox::onAdd() {
 	App::wnd()->replaceLayer(new AddContactBox());
 }
 
-void ContactsBox::onCreateChannel() {
-	_creatingChannel = !_creatingChannel;
-	_createChannel.setText(lang(_creatingChannel ? lng_create_group_button : lng_create_channel_button));
-	_createChannel.move(width() - _createChannel.width(), 0);
-	update();
-}
-
 void ContactsBox::onInvite() {
 	QVector<UserData*> users(_inner.selected());
 	if (users.isEmpty()) {
 		_filter.setFocus();
+		_filter.notaBene();
 		return;
 	}
 
-	App::main()->addParticipants(_inner.chat(), users);
+	App::main()->addParticipants(_inner.chat() ? (PeerData*)_inner.chat() : _inner.channel(), users);
+	if (_inner.chat()) {
+		App::wnd()->hideLayer();
+		App::main()->showPeerHistory(_inner.chat()->id, ShowAtTheEndMsgId);
+	} else {
+		onClose();
+	}
 }
 
-void ContactsBox::onNext() {
+void ContactsBox::onCreate() {
+	if (_creationRequestId) return;
+
+	//if (_inner.creating() == CreatingGroupChannel) { // tmp
+	//	ChannelData *channel = App::channelLoaded(10449997);
+	//	if (channel) {
+	//		App::wnd()->hideLayer(true);
+	//		App::wnd()->showLayer(new SetupChannelBox(channel), true);
+	//	}
+	//	return;
+	//}
+
 	MTPVector<MTPInputUser> users(MTP_vector<MTPInputUser>(_inner.selectedInputs()));
 	const QVector<MTPInputUser> &v(users.c_vector().v);
-	if (v.isEmpty()) {
+	if (v.isEmpty() || (v.size() == 1 && v.at(0).type() == mtpc_inputUserSelf)) {
 		_filter.setFocus();
 		_filter.notaBene();
-	} else if (v.size() == 1) {
-		App::main()->showPeerHistory(_inner.selectedUser()->id, ShowAtUnreadMsgId);
-	} else {
-		App::wnd()->replaceLayer(new CreateGroupBox(users, _creatingChannel));
+		return;
 	}
+	_creationRequestId = MTP::send(MTPmessages_CreateChat(MTP_vector<MTPInputUser>(v), MTP_string(_creationName)), rpcDone(&ContactsBox::creationDone), rpcFail(&ContactsBox::creationFail));
 }
 
 void ContactsBox::onScroll() {
 	_inner.loadProfilePhotos(_scroll.scrollTop());
 }
 
-CreateGroupBox::CreateGroupBox(const MTPVector<MTPInputUser> &users, bool creatingChannel) : AbstractBox(), _users(users),
-_creatingChannel(creatingChannel),
-_createRequestId(0),
-_name(this, st::newGroupName, lang(_creatingChannel ? lng_dlg_new_channel_name : lng_dlg_new_group_name)),
-_create(this, lang(lng_dlg_create_group), st::btnSelectDone),
-_cancel(this, lang(lng_cancel), st::btnSelectCancel) {
-
-	setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + _name.height() + st::addContactPadding.bottom() + _create.height());
-
-	connect(&_create, SIGNAL(clicked()), this, SLOT(onCreate()));
-	connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
-
-	prepare();
-}
-
-void CreateGroupBox::hideAll() {
-	_name.hide();
-	_cancel.hide();
-	_create.hide();
-}
-
-void CreateGroupBox::showAll() {
-	_name.show();
-	_cancel.show();
-	_create.show();
-}
-
-void CreateGroupBox::showDone() {
-	_name.setFocus();
-}
-
-void CreateGroupBox::keyPressEvent(QKeyEvent *e) {
-	if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
-		if (_name.hasFocus()) {
-			if (_name.text().trimmed().isEmpty()) {
-				_name.setFocus();
-				_name.notaBene();
-			} else {
-				onCreate();
-			}
-		}
-	} else {
-		AbstractBox::keyPressEvent(e);
-	}
-}
-
-void CreateGroupBox::paintEvent(QPaintEvent *e) {
-	Painter p(this);
-	if (paint(p)) return;
-
-	paintTitle(p, lang(_creatingChannel ? lng_create_channel_title : lng_create_group_title), true);
-
-	// paint shadow
-	p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
-
-	// paint button sep
-	p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
-}
-
-void CreateGroupBox::resizeEvent(QResizeEvent *e) {
-	_name.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _name.height());
-
-	int32 buttonTop = _name.y() + _name.height() + st::addContactPadding.bottom();
-	_cancel.move(0, buttonTop);
-	_create.move(width() - _create.width(), buttonTop);
-}
-
-void CreateGroupBox::onCreate() {
-	if (_createRequestId) return;
-
-	QString name = _name.text();
-	if (name.isEmpty()) {
-		_name.setFocus();
-		_name.notaBene();
-		return;
-	}
-
-	_create.setDisabled(true);
-	_name.setDisabled(true);
-	if (_creatingChannel) {
-		_createRequestId = MTP::send(MTPmessages_CreateChannel(MTP_int(MTPmessages_CreateChannel_flag_broadcast), MTP_string(_name.text()), MTP_string(""), _users), rpcDone(&CreateGroupBox::created), rpcFail(&CreateGroupBox::failed));
-	} else {
-		_createRequestId = MTP::send(MTPmessages_CreateChat(_users, MTP_string(_name.text())), rpcDone(&CreateGroupBox::created), rpcFail(&CreateGroupBox::failed));
-	}
-}
-
-void CreateGroupBox::created(const MTPUpdates &updates) {
+PeerData *chatOrChannelCreated(const MTPUpdates &updates, const QImage &photo) {
 	App::main()->sentUpdatesReceived(updates);
 
-	App::wnd()->hideLayer();
 	const QVector<MTPChat> *v = 0;
 	switch (updates.type()) {
 	case mtpc_updates: v = &updates.c_updates().vchats.c_vector().v; break;
@@ -1311,26 +1278,706 @@ void CreateGroupBox::created(const MTPUpdates &updates) {
 	} break;
 	}
 	if (v && !v->isEmpty() && v->front().type() == mtpc_chat) {
-		App::main()->showPeerHistory(peerFromChat(v->front().c_chat().vid.v), ShowAtUnreadMsgId);
+		ChatData *chat = App::chat(v->front().c_chat().vid.v);
+		if (chat) {
+			if (!photo.isNull()) {
+				App::app()->uploadProfilePhoto(photo, chat->id);
+			}
+			return chat;
+		}
 	} else if (v && !v->isEmpty() && v->front().type() == mtpc_channel) {
-		App::main()->showPeerHistory(peerFromChannel(v->front().c_channel().vid.v), ShowAtUnreadMsgId);
+		ChannelData *channel = App::channel(v->front().c_channel().vid.v);
+		if (channel) {
+			if (!photo.isNull()) {
+				App::app()->uploadProfilePhoto(photo, channel->id);
+			}
+			return channel;
+		}
+	}
+
+	return 0;
+}
+
+void ContactsBox::creationDone(const MTPUpdates &updates) {
+	App::wnd()->hideLayer();
+
+	PeerData *peer = chatOrChannelCreated(updates, _creationPhoto);
+	if (peer) {
+		App::main()->showPeerHistory(peer->id, ShowAtUnreadMsgId);
 	}
 }
 
-bool CreateGroupBox::failed(const RPCError &error) {
+bool ContactsBox::creationFail(const RPCError &error) {
 	if (mtpIsFlood(error)) return false;
 
-	_createRequestId = 0;
+	_creationRequestId = 0;
 	if (error.type() == "NO_CHAT_TITLE") {
-		_name.setFocus();
+		emit closed();
 		return true;
 	} else if (error.type() == "USERS_TOO_FEW") {
-		emit closed();
+		_filter.setFocus();
+		_filter.notaBene();
 		return true;
 	} else if (error.type() == "PEER_FLOOD") {
-		emit closed();
-		App::wnd()->showLayer(new ConfirmBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))), true);
+		App::wnd()->replaceLayer(new ConfirmBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))));
 		return true;
 	}
 	return false;
 }
+
+NewGroupBox::NewGroupBox() : AbstractBox(),
+_group(this, qsl("group_type"), 0, lang(lng_create_group_title), true),
+_channel(this, qsl("group_type"), 1, lang(lng_create_channel_title)),
+_aboutGroupWidth(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft),
+_aboutGroup(st::normalFont, lang(lng_create_group_about), _defaultOptions, _aboutGroupWidth),
+_aboutChannel(st::normalFont, lang(lng_create_channel_about), _defaultOptions, _aboutGroupWidth),
+_next(this, lang(lng_create_group_next), st::btnSelectDone),
+_cancel(this, lang(lng_cancel), st::btnSelectCancel) {
+	_aboutGroupHeight = _aboutGroup.countHeight(_aboutGroupWidth);
+	setMaxHeight(st::newGroupPadding.top() + _group.height() + _aboutGroupHeight + st::newGroupSkip + _channel.height() + _aboutChannel.countHeight(_aboutGroupWidth) + st::newGroupPadding.bottom() + _next.height());
+
+	connect(&_next, SIGNAL(clicked()), this, SLOT(onNext()));
+	connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
+
+	prepare();
+}
+
+void NewGroupBox::hideAll() {
+	_group.hide();
+	_channel.hide();
+	_cancel.hide();
+	_next.hide();
+}
+
+void NewGroupBox::showAll() {
+	_group.show();
+	_channel.show();
+	_cancel.show();
+	_next.show();
+}
+
+void NewGroupBox::showDone() {
+	setFocus();
+}
+
+void NewGroupBox::keyPressEvent(QKeyEvent *e) {
+	if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
+		onNext();
+	} else {
+		AbstractBox::keyPressEvent(e);
+	}
+}
+
+void NewGroupBox::paintEvent(QPaintEvent *e) {
+	Painter p(this);
+	if (paint(p)) return;
+
+	p.setPen(st::newGroupAboutFg->p);
+
+	QRect aboutGroup(st::newGroupPadding.left() + st::rbDefFlat.textLeft, _group.y() + _group.height(), width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft, _aboutGroupHeight);
+	if (rtl()) aboutGroup.setX(width() - aboutGroup.x() - aboutGroup.width());
+	_aboutGroup.draw(p, aboutGroup.x(), aboutGroup.y(), aboutGroup.width());
+
+	QRect aboutChannel(st::newGroupPadding.left() + st::rbDefFlat.textLeft, _channel.y() + _channel.height(), width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft, _aboutGroupHeight);
+	if (rtl()) aboutChannel.setX(width() - aboutChannel.x() - aboutChannel.width());
+	_aboutChannel.draw(p, aboutChannel.x(), aboutChannel.y(), aboutChannel.width());
+
+	// paint shadow
+	p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
+
+	// paint button sep
+	p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
+}
+
+void NewGroupBox::resizeEvent(QResizeEvent *e) {
+	_group.moveToLeft(st::newGroupPadding.left(), st::newGroupPadding.top(), width());
+	_channel.moveToLeft(st::newGroupPadding.left(), _group.y() + _group.height() + _aboutGroupHeight + st::newGroupSkip, width());
+
+	int32 buttonTop = height() - st::btnSelectCancel.height;
+	_cancel.moveToLeft(0, buttonTop, width());
+	_next.moveToRight(0, buttonTop, width());
+}
+
+void NewGroupBox::onNext() {
+	App::wnd()->replaceLayer(new GroupInfoBox(_group.checked() ? CreatingGroupGroup : CreatingGroupChannel));
+}
+
+GroupInfoBox::GroupInfoBox(CreatingGroupType creating) : AbstractBox(),
+_creating(creating),
+a_photoOver(0, 0),
+a_photo(animFunc(this, &GroupInfoBox::photoAnimStep)),
+_photoOver(false),
+_descriptionOver(false),
+a_descriptionBg(st::newGroupName.bgColor->c, st::newGroupName.bgColor->c),
+a_descriptionBorder(st::newGroupName.borderColor->c, st::newGroupName.borderColor->c),
+a_description(animFunc(this, &GroupInfoBox::descriptionAnimStep)),
+_name(this, st::newGroupName, lang(_creating == CreatingGroupChannel ? lng_dlg_new_channel_name : lng_dlg_new_group_name)),
+_photo(this, lang(lng_create_group_photo), st::newGroupPhoto),
+_description(this, st::newGroupDescription, lang(lng_create_group_description)),
+_next(this, lang(lng_create_group_next), st::btnSelectDone),
+_cancel(this, lang(lng_create_group_back), st::btnSelectCancel),
+_creationRequestId(0), _createdChannel(0) {
+
+	setMouseTracking(true);
+
+	_description.resize(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::newGroupDescriptionPadding.left() - st::newGroupDescriptionPadding.right(), _name.height() - st::newGroupDescriptionPadding.top() - st::newGroupDescriptionPadding.bottom());
+	_description.setMinHeight(_description.height());
+	_description.setMaxHeight(3 * _description.height() + 2 * st::newGroupDescriptionPadding.top() + 2 * st::newGroupDescriptionPadding.bottom());
+
+	updateMaxHeight();
+	connect(&_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
+	connect(&_description, SIGNAL(submitted(bool)), this, SLOT(onNext()));
+	connect(&_description, SIGNAL(cancelled()), this, SLOT(onClose()));
+
+	connect(&_photo, SIGNAL(clicked()), this, SLOT(onPhoto()));
+
+	connect(&_next, SIGNAL(clicked()), this, SLOT(onNext()));
+	connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
+
+	prepare();
+}
+
+void GroupInfoBox::hideAll() {
+	_name.hide();
+	_photo.hide();
+	_description.hide();
+	_cancel.hide();
+	_next.hide();
+}
+
+void GroupInfoBox::showAll() {
+	_name.show();
+	_photo.show();
+	if (_creating == CreatingGroupChannel) {
+		_description.show();
+	} else {
+		_description.hide();
+	}
+	_cancel.show();
+	_next.show();
+}
+
+void GroupInfoBox::showDone() {
+	_name.setFocus();
+}
+
+void GroupInfoBox::keyPressEvent(QKeyEvent *e) {
+	if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
+		if (_name.hasFocus()) {
+			if (_name.text().trimmed().isEmpty()) {
+				_name.setFocus();
+				_name.notaBene();
+			} else if (_description.isHidden()) {
+				onNext();
+			} else {
+				_description.setFocus();
+			}
+		}
+	} else {
+		AbstractBox::keyPressEvent(e);
+	}
+}
+
+void GroupInfoBox::paintEvent(QPaintEvent *e) {
+	Painter p(this);
+	if (paint(p)) return;
+
+	QRect phRect(photoRect());
+	if (phRect.intersects(e->rect())) {
+		if (_photoSmall.isNull()) {
+			int32 s = st::newGroupPhotoSize * cIntRetinaFactor();
+			QRect ph(st::setPhotoImg), overph(st::setOverPhotoImg);
+			if (a_photoOver.current() < 1) {
+				p.drawPixmapLeft(phRect.topLeft(), width(), App::sprite(), QRect(ph.x() + (ph.width() - s) / 2, ph.y() + (ph.height() - s) / 2, s, s));
+			}
+			if (a_photoOver.current() > 0) {
+				p.setOpacity(a_photoOver.current());
+				p.drawPixmapLeft(phRect.topLeft(), width(), App::sprite(), QRect(overph.x() + (overph.width() - s) / 2, overph.y() + (overph.height() - s) / 2, s, s));
+				p.setOpacity(1);
+			}
+		} else {
+			p.drawPixmap(st::newGroupPadding.left(), st::newGroupPadding.left(), _photoSmall);
+		}
+		if (phRect.contains(e->rect())) {
+			return;
+		}
+	}
+	QRect descRect(descriptionRect());
+	if (_creating == CreatingGroupChannel && descRect.intersects(e->rect())) {
+		p.fillRect(descRect, a_descriptionBg.current());
+		if (st::newGroupName.borderWidth) {
+			QBrush b(a_descriptionBorder.current());
+			p.fillRect(descRect.x(), descRect.y(), descRect.width() - st::newGroupName.borderWidth, st::newGroupName.borderWidth, b);
+			p.fillRect(descRect.x() + descRect.width() - st::newGroupName.borderWidth, descRect.y(), st::newGroupName.borderWidth, descRect.height() - st::newGroupName.borderWidth, b);
+			p.fillRect(descRect.x() + st::newGroupName.borderWidth, descRect.y() + descRect.height() - st::newGroupName.borderWidth, descRect.width() - st::newGroupName.borderWidth, st::newGroupName.borderWidth, b);
+			p.fillRect(descRect.x(), descRect.y() + st::newGroupName.borderWidth, st::newGroupName.borderWidth, descRect.height() - st::newGroupName.borderWidth, b);
+		}
+		if (descRect.contains(e->rect())) {
+			return;
+		}
+	}
+
+	// paint shadow
+	p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
+
+	// paint button sep
+	p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
+}
+
+void GroupInfoBox::resizeEvent(QResizeEvent *e) {
+	int32 nameLeft = st::newGroupPhotoSize + st::newGroupPhotoSkip;
+	_name.resize(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - nameLeft, _name.height());
+	_name.moveToLeft(st::newGroupPadding.left() + nameLeft, st::newGroupPadding.top(), width());
+	_photo.moveToLeft(_name.x(), _name.y() + st::newGroupPhotoSize - _photo.height(), width());
+
+	_description.moveToLeft(st::newGroupPadding.left() + st::newGroupDescriptionPadding.left(), _photo.y() + _photo.height() + st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top(), width());
+	_description.installEventFilter(this);
+
+	int32 buttonTop = (_creating == CreatingGroupChannel) ? (_description.y() + _description.height() + st::newGroupDescriptionPadding.bottom()) : (_photo.y() + _photo.height());
+	buttonTop += st::newGroupPadding.bottom();
+	_cancel.move(0, buttonTop);
+	_next.move(width() - _next.width(), buttonTop);
+}
+
+void GroupInfoBox::mouseMoveEvent(QMouseEvent *e) {
+	updateSelected(e->globalPos());
+}
+
+void GroupInfoBox::updateSelected(const QPoint &cursorGlobalPosition) {
+	QPoint p(mapFromGlobal(cursorGlobalPosition));
+
+	bool photoOver = photoRect().contains(p);
+	if (photoOver != _photoOver) {
+		_photoOver = photoOver;
+		if (_photoSmall.isNull()) {
+			a_photoOver.start(_photoOver ? 1 : 0);
+			a_photo.start();
+		}
+	}
+
+	bool descriptionOver = _photoOver ? false : descriptionRect().contains(p);
+	if (descriptionOver != _descriptionOver) {
+		_descriptionOver = descriptionOver;
+	}
+
+	setCursor(_photoOver ? style::cur_pointer : (_descriptionOver ? style::cur_text : style::cur_default));
+}
+
+void GroupInfoBox::mousePressEvent(QMouseEvent *e) {
+	mouseMoveEvent(e);
+	if (_photoOver) {
+		onPhoto();
+	} else if (_descriptionOver) {
+		_description.setFocus();
+	}
+}
+
+void GroupInfoBox::leaveEvent(QEvent *e) {
+	updateSelected(QCursor::pos());
+}
+
+bool GroupInfoBox::descriptionAnimStep(float64 ms) {
+	float dt = ms / st::newGroupName.phDuration;
+	bool res = true;
+	if (dt >= 1) {
+		res = false;
+		a_descriptionBg.finish();
+		a_descriptionBorder.finish();
+	} else {
+		a_descriptionBg.update(dt, st::newGroupName.phColorFunc);
+		a_descriptionBorder.update(dt, st::newGroupName.phColorFunc);
+	}
+	update(descriptionRect());
+	return res;
+}
+
+bool GroupInfoBox::photoAnimStep(float64 ms) {
+	float64 dt = ms / st::setPhotoDuration;
+	bool res = true;
+	if (dt >= 1) {
+		res = false;
+		a_photoOver.finish();
+	} else {
+		a_photoOver.update(dt, anim::linear);
+	}
+	update(photoRect());
+	return res;
+}
+
+bool GroupInfoBox::eventFilter(QObject *obj, QEvent *e) {
+	if (obj == &_description) {
+		if (e->type() == QEvent::FocusIn) {
+			a_descriptionBorder.start(st::newGroupName.borderActive->c);
+			a_descriptionBg.start(st::newGroupName.bgActive->c);
+			a_description.start();
+		} else if (e->type() == QEvent::FocusOut) {
+			a_descriptionBorder.start(st::newGroupName.borderColor->c);
+			a_descriptionBg.start(st::newGroupName.bgColor->c);
+			a_description.start();
+		}
+	}
+	return AbstractBox::eventFilter(obj, e);
+}
+
+void GroupInfoBox::onNext() {
+	if (_creationRequestId) return;
+
+	QString name = _name.text().trimmed();
+	if (name.isEmpty()) {
+		_name.setFocus();
+		_name.notaBene();
+		return;
+	}
+	if (_creating == CreatingGroupGroup) {
+		App::wnd()->replaceLayer(new ContactsBox(name, _photoBig));
+	} else {
+		_creationRequestId = MTP::send(MTPmessages_CreateChannel(MTP_int(MTPmessages_CreateChannel_flag_broadcast), MTP_string(name), MTP_string(_description.getLastText().trimmed()), MTP_vector<MTPInputUser>(0)), rpcDone(&GroupInfoBox::creationDone), rpcFail(&GroupInfoBox::creationFail));
+	}
+}
+
+void GroupInfoBox::creationDone(const MTPUpdates &updates) {
+	PeerData *result = chatOrChannelCreated(updates, _photoBig);
+	if (!result || !result->isChannel()) {
+		onClose();
+	} else {
+		_createdChannel = result->asChannel();
+		_creationRequestId = MTP::send(MTPmessages_ExportChatInvite(_createdChannel->inputChat), rpcDone(&GroupInfoBox::exportDone));
+	}
+}
+
+bool GroupInfoBox::creationFail(const RPCError &error) {
+	if (mtpIsFlood(error)) return false;
+
+	_creationRequestId = 0;
+	if (error.type() == "NO_CHAT_TITLE") {
+		_name.setFocus();
+		_name.notaBene();
+		return true;
+	} else if (error.type() == "PEER_FLOOD") {
+		App::wnd()->replaceLayer(new ConfirmBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))));
+		return true;
+	}
+	return false;
+}
+
+void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
+	_creationRequestId = 0;
+	if (result.type() == mtpc_chatInviteExported) {
+		_createdChannel->invitationUrl = qs(result.c_chatInviteExported().vlink);
+	}
+	App::wnd()->hideLayer(true);
+	App::wnd()->showLayer(new SetupChannelBox(_createdChannel), true);
+}
+
+void GroupInfoBox::onDescriptionResized() {
+	updateMaxHeight();
+	update();
+}
+
+QRect GroupInfoBox::descriptionRect() const {
+	return rtlrect(_description.x() - st::newGroupDescriptionPadding.left(), _description.y() - st::newGroupDescriptionPadding.top(), _description.width() + st::newGroupDescriptionPadding.left() + st::newGroupDescriptionPadding.right(), _description.height() + st::newGroupDescriptionPadding.top() + st::newGroupDescriptionPadding.bottom(), width());
+}
+
+QRect GroupInfoBox::photoRect() const {
+	return rtlrect(st::newGroupPadding.left(), st::newGroupPadding.top(), st::newGroupPhotoSize, st::newGroupPhotoSize, width());
+}
+
+void GroupInfoBox::updateMaxHeight() {
+	int32 h = st::newGroupPadding.top() + st::newGroupPhotoSize + st::newGroupPadding.bottom() + _next.height();
+	if (_creating == CreatingGroupChannel) {
+		h += st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top() + _description.height() + st::newGroupDescriptionPadding.bottom();
+	}
+	setMaxHeight(h);
+}
+
+void GroupInfoBox::onPhoto() {
+	QStringList imgExtensions(cImgExtensions());
+	QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;All files (*.*)"));
+
+	QImage img;
+	QString file;
+	QByteArray remoteContent;
+	if (filedialogGetOpenFile(file, remoteContent, lang(lng_choose_images), filter)) {
+		if (!remoteContent.isEmpty()) {
+			img = App::readImage(remoteContent);
+		} else {
+			if (!file.isEmpty()) {
+				img = App::readImage(file);
+			}
+		}
+	} else {
+		return;
+	}
+
+	if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
+		return;
+	}
+	PhotoCropBox *box = new PhotoCropBox(img, (_creating == CreatingGroupChannel) ? peerFromChannel(0) : peerFromChat(0), false);
+	connect(box, SIGNAL(ready(const QImage&)), this, SLOT(onPhotoReady(const QImage&)));
+	App::wnd()->replaceLayer(box);
+}
+
+void GroupInfoBox::onPhotoReady(const QImage &img) {
+	_photoBig = img;
+	_photoSmall = QPixmap::fromImage(img.scaled(st::newGroupPhotoSize * cIntRetinaFactor(), st::newGroupPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
+	_photoSmall.setDevicePixelRatio(cRetinaFactor());
+}
+
+SetupChannelBox::SetupChannelBox(ChannelData *channel) : AbstractBox(),
+_channel(channel),
+_public(this, qsl("channel_privacy"), 0, lang(lng_create_public_channel_title), true),
+_private(this, qsl("channel_privacy"), 1, lang(lng_create_private_channel_title)),
+_aboutPublicWidth(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft),
+_aboutPublic(st::normalFont, lang(lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth),
+_aboutPrivate(st::normalFont, lang(lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth),
+_linkPlaceholder(qsl("telegram.me/")),
+_link(this, st::newGroupLink, QString()),
+_save(this, lang(lng_create_group_save), st::btnSelectDone),
+_skip(this, lang(lng_create_group_skip), st::btnSelectCancel),
+_saveRequestId(0), _checkRequestId(0) {
+	_link.setTextMargin(style::margins(st::newGroupLink.textMrg.left() + st::newGroupLink.font->m.width(_linkPlaceholder), st::newGroupLink.textMrg.top(), st::newGroupLink.textMrg.right(), st::newGroupLink.textMrg.bottom()));
+
+	_aboutPublicHeight = _aboutPublic.countHeight(_aboutPublicWidth);
+	setMaxHeight(st::newGroupPadding.top() + _public.height() + _aboutPublicHeight + st::newGroupSkip + _private.height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupPadding.bottom() + st::newGroupLinkPadding.top() + _link.height() + st::newGroupLinkPadding.bottom() + _save.height());
+
+	connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));
+	connect(&_skip, SIGNAL(clicked()), this, SLOT(onClose()));
+
+	connect(&_link, SIGNAL(changed()), this, SLOT(onChange()));
+
+	_checkTimer.setSingleShot(true);
+	connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck()));
+
+	connect(&_public, SIGNAL(changed()), this, SLOT(onPrivacyChange()));
+	connect(&_private, SIGNAL(changed()), this, SLOT(onPrivacyChange()));
+
+	prepare();
+}
+
+void SetupChannelBox::hideAll() {
+	_link.hide();
+	_save.hide();
+	_skip.hide();
+}
+
+void SetupChannelBox::showAll() {
+	if (_public.checked()) {
+		_link.show();
+	} else {
+		_link.hide();
+	}
+	_save.show();
+	_skip.show();
+}
+
+void SetupChannelBox::showDone() {
+	_link.setFocus();
+}
+
+void SetupChannelBox::keyPressEvent(QKeyEvent *e) {
+	if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
+		if (_link.hasFocus()) {
+			if (_link.text().trimmed().isEmpty()) {
+				_link.setFocus();
+				_link.notaBene();
+			} else {
+				onSave();
+			}
+		}
+	} else {
+		AbstractBox::keyPressEvent(e);
+	}
+}
+
+void SetupChannelBox::paintEvent(QPaintEvent *e) {
+	Painter p(this);
+	if (paint(p)) return;
+
+	p.setPen(st::newGroupAboutFg);
+
+	QRect aboutPublic(st::newGroupPadding.left() + st::rbDefFlat.textLeft, _public.y() + _public.height(), width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft, _aboutPublicHeight);
+	if (rtl()) aboutPublic.setX(width() - aboutPublic.x() - aboutPublic.width());
+	_aboutPublic.draw(p, aboutPublic.x(), aboutPublic.y(), aboutPublic.width());
+
+	QRect aboutPrivate(st::newGroupPadding.left() + st::rbDefFlat.textLeft, _private.y() + _private.height(), width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft, _aboutPublicHeight);
+	if (rtl()) aboutPrivate.setX(width() - aboutPrivate.x() - aboutPrivate.width());
+	_aboutPrivate.draw(p, aboutPrivate.x(), aboutPrivate.y(), aboutPrivate.width());
+
+	p.setPen(st::black);
+	p.setFont(st::newGroupLinkFont);
+	p.drawTextLeft(st::newGroupPadding.left(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop, width(), lang(lng_create_group_link));
+
+	if (!_link.isHidden()) {
+		p.setFont(st::newGroupLink.font);
+		p.setPen(st::newGroupLink.phColor);
+		p.drawText(QRect(_link.x() + st::newGroupLink.textMrg.left(), _link.y() + st::newGroupLink.textMrg.top(), _link.width(), _link.height() - st::newGroupLink.textMrg.top() - st::newGroupLink.textMrg.bottom()), _linkPlaceholder, style::al_left);
+
+		if (!_errorText.isEmpty()) {
+			p.setPen(st::setErrColor->p);
+			p.setFont(st::setErrFont->f);
+			p.drawTextRight(st::newGroupPadding.right(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::setErrFont->ascent, width(), _errorText);
+		} else if (!_goodText.isEmpty()) {
+			p.setPen(st::setGoodColor->p);
+			p.setFont(st::setErrFont->f);
+			p.drawTextRight(st::newGroupPadding.right(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::setErrFont->ascent, width(), _goodText);
+		}
+	}
+
+	// paint shadow
+	p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
+
+	// paint button sep
+	p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
+}
+
+void SetupChannelBox::resizeEvent(QResizeEvent *e) {
+	_public.moveToLeft(st::newGroupPadding.left(), st::newGroupPadding.top(), width());
+	_private.moveToLeft(st::newGroupPadding.left(), _public.y() + _public.height() + _aboutPublicHeight + st::newGroupSkip, width());
+
+	_link.setGeometry(st::newGroupLinkPadding.left(), st::newGroupPadding.top() + _public.height() + _aboutPublicHeight + st::newGroupSkip + _private.height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupPadding.bottom() + st::newGroupLinkPadding.top(), width() - st::newGroupPadding.left() - st::newGroupPadding.right(), _link.height());
+
+	int32 buttonTop = _link.y() + _link.height() + st::newGroupLinkPadding.bottom();
+	_skip.moveToLeft(0, buttonTop, width());
+	_save.moveToRight(0, buttonTop, width());
+}
+
+void SetupChannelBox::closePressed() {
+	App::wnd()->showLayer(new ContactsBox(_channel), true);
+}
+
+void SetupChannelBox::onSave() {
+	if (!_public.checked()) {
+		onClose();
+	}
+
+	if (_saveRequestId) return;
+
+	QString link = _link.text().trimmed();
+	if (link.isEmpty()) {
+		_link.setFocus();
+		_link.notaBene();
+		return;
+	}
+
+	_sentUsername = link;
+	_saveRequestId = MTP::send(MTPmessages_UpdateChannelUsername(_channel->inputChat, MTP_string(_sentUsername)), rpcDone(&SetupChannelBox::onUpdateDone), rpcFail(&SetupChannelBox::onUpdateFail));
+}
+
+void SetupChannelBox::onChange() {
+	QString name = _link.text().trimmed();
+	if (name.isEmpty()) {
+		if (!_errorText.isEmpty() || !_goodText.isEmpty()) {
+			_errorText = _goodText = QString();
+			update();
+		}
+		_checkTimer.stop();
+	} else {
+		int32 i, len = name.size();
+		for (int32 i = 0; i < len; ++i) {
+			QChar ch = name.at(i);
+			if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '_' && (ch != '@' || i > 0)) {
+				if (_errorText != lang(lng_create_channel_link_bad_symbols)) {
+					_errorText = lang(lng_create_channel_link_bad_symbols);
+					update();
+				}
+				_checkTimer.stop();
+				return;
+			}
+		}
+		if (name.size() < MinUsernameLength) {
+			if (_errorText != lang(lng_create_channel_link_too_short)) {
+				_errorText = lang(lng_create_channel_link_too_short);
+				update();
+			}
+			_checkTimer.stop();
+		} else {
+			if (!_errorText.isEmpty() || !_goodText.isEmpty()) {
+				_errorText = _goodText = QString();
+				update();
+			}
+			_checkTimer.start(UsernameCheckTimeout);
+		}
+	}
+}
+
+void SetupChannelBox::onCheck() {
+	if (_checkRequestId) {
+		MTP::cancel(_checkRequestId);
+	}
+	QString link = _link.text().trimmed();
+	if (link.size() >= MinUsernameLength) {
+		_checkUsername = link;
+		_checkRequestId = MTP::send(MTPmessages_CheckChannelUsername(_channel->inputChat, MTP_string(link)), rpcDone(&SetupChannelBox::onCheckDone), rpcFail(&SetupChannelBox::onCheckFail));
+	}
+}
+
+void SetupChannelBox::onPrivacyChange() {
+	if (_public.checked()) {
+		_link.show();
+		_link.setFocus();
+	} else {
+		_link.hide();
+	}
+	update();
+}
+
+void SetupChannelBox::onUpdateDone(const MTPBool &result) {
+	_channel->setName(textOneLine(_channel->name), _sentUsername);
+	onClose();
+}
+
+bool SetupChannelBox::onUpdateFail(const RPCError &error) {
+	if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
+
+	_saveRequestId = 0;
+	QString err(error.type());
+	if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == _channel->username) {
+		_channel->setName(textOneLine(_channel->name), textOneLine(_sentUsername));
+		onClose();
+		return true;
+	} else if (err == "USERNAME_INVALID") {
+		_link.setFocus();
+		_link.notaBene();
+		_errorText = lang(lng_create_channel_link_invalid);
+		update();
+		return true;
+	} else if (err == "USERNAME_OCCUPIED" || err == "USERNAMES_UNAVAILABLE") {
+		_link.setFocus();
+		_link.notaBene();
+		_errorText = lang(lng_create_channel_link_occupied);
+		update();
+		return true;
+	}
+	_link.setFocus();
+	return true;
+}
+
+void SetupChannelBox::onCheckDone(const MTPBool &result) {
+	_checkRequestId = 0;
+	QString newError = (result.v || _checkUsername == _channel->username) ? QString() : lang(lng_create_channel_link_occupied);
+	QString newGood = newError.isEmpty() ? lang(lng_create_channel_link_available) : QString();
+	if (_errorText != newError || _goodText != newGood) {
+		_errorText = newError;
+		_goodText = newGood;
+		update();
+	}
+}
+
+bool SetupChannelBox::onCheckFail(const RPCError &error) {
+	if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
+
+	_checkRequestId = 0;
+	QString err(error.type());
+	if (err == "USERNAME_INVALID") {
+		_errorText = lang(lng_create_channel_link_invalid);
+		update();
+		return true;
+	} else if (err == "USERNAME_OCCUPIED" && _checkUsername != _channel->username) {
+		_errorText = lang(lng_create_channel_link_occupied);
+		update();
+		return true;
+	}
+	_goodText = QString();
+	_link.setFocus();
+	return true;
+}
diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h
index 276b7a562..95ce9edcd 100644
--- a/Telegram/SourceFiles/boxes/contactsbox.h
+++ b/Telegram/SourceFiles/boxes/contactsbox.h
@@ -19,6 +19,12 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 
 #include "abstractbox.h"
 
+enum CreatingGroupType {
+	CreatingGroupNone,
+	CreatingGroupGroup,
+	CreatingGroupChannel,
+};
+
 class ContactsInner : public QWidget, public RPCSender {
 	Q_OBJECT
 
@@ -28,7 +34,8 @@ private:
 
 public:
 
-	ContactsInner(bool creatingChat);
+	ContactsInner(CreatingGroupType creating = CreatingGroupNone);
+	ContactsInner(ChannelData *channel);
 	ContactsInner(ChatData *chat);
 	ContactsInner(UserData *bot);
 	void init();
@@ -60,8 +67,11 @@ public:
 	void refresh();
 
 	ChatData *chat() const;
+	ChannelData *channel() const;
 	UserData *bot() const;
-	bool creatingChat() const;
+	CreatingGroupType creating() const;
+
+	int32 selectedCount() const;
 
 	~ContactsInner();
 
@@ -70,6 +80,7 @@ signals:
 	void mustScrollTo(int ymin, int ymax);
 	void selectAllQuery();
 	void searchByUsername();
+	void chosenChanged();
 
 public slots:
 
@@ -84,8 +95,9 @@ public slots:
 private:
 
 	ChatData *_chat;
+	ChannelData *_channel;
 	UserData *_bot;
-	bool _creatingChat;
+	CreatingGroupType _creating;
 
 	ChatData *_addToChat;
 	
@@ -133,22 +145,29 @@ class ContactsBox : public ItemListBox, public RPCSender {
 
 public:
 
-	ContactsBox(bool creatingChat = false);
+	ContactsBox();
+	ContactsBox(const QString &name, const QImage &photo); // group creation
+	ContactsBox(ChannelData *channel); // channel setup
 	ContactsBox(ChatData *chat);
 	ContactsBox(UserData *bot);
 	void keyPressEvent(QKeyEvent *e);
 	void paintEvent(QPaintEvent *e);
 	void resizeEvent(QResizeEvent *e);
 
+	void closePressed();
+
+	void setInnerFocus() {
+		_filter.setFocus();
+	}
+
 public slots:
 
 	void onFilterUpdate();
 	void onScroll();
 
 	void onAdd();
-	void onCreateChannel();
 	void onInvite();
-	void onNext();
+	void onCreate();
 
 	bool onSearchByUsername(bool searchCache = false);
 	void onNeedSearchByUsername();
@@ -164,7 +183,7 @@ private:
 	void init();
 
 	ContactsInner _inner;
-	FlatButton _addContact, _createChannel;
+	FlatButton _addContact;
 	FlatInput _filter;
 
 	FlatButton _next, _cancel;
@@ -172,8 +191,6 @@ private:
 	void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req);
 	bool peopleFailed(const RPCError &error, mtpRequestId req);
 
-	bool _creatingChannel;
-
 	QTimer _searchTimer;
 	QString _peopleQuery;
 	bool _peopleFull;
@@ -184,21 +201,29 @@ private:
 
 	typedef QMap<mtpRequestId, QString> PeopleQueries;
 	PeopleQueries _peopleQueries;
+
+	// group creation
+	int32 _creationRequestId;
+	QString _creationName;
+	QImage _creationPhoto;
+
+	void creationDone(const MTPUpdates &updates);
+	bool creationFail(const RPCError &e);
 };
 
-class CreateGroupBox : public AbstractBox, public RPCSender {
+class NewGroupBox : public AbstractBox {
 	Q_OBJECT
 
 public:
 
-	CreateGroupBox(const MTPVector<MTPInputUser> &users, bool creatingChannel);
+	NewGroupBox();
 	void keyPressEvent(QKeyEvent *e);
 	void paintEvent(QPaintEvent *e);
 	void resizeEvent(QResizeEvent *e);
 
 public slots:
 
-	void onCreate();
+	void onNext();
 
 protected:
 
@@ -208,14 +233,130 @@ protected:
 
 private:
 
-	void created(const MTPUpdates &updates);
-	bool failed(const RPCError &e);
+	FlatRadiobutton _group, _channel;
+	int32 _aboutGroupWidth, _aboutGroupHeight;
+	Text _aboutGroup, _aboutChannel;
+	FlatButton _next, _cancel;
 
-	MTPVector<MTPInputUser> _users;
-	bool _creatingChannel;
+};
 
-	int32 _createRequestId;
+class GroupInfoBox : public AbstractBox, public RPCSender {
+	Q_OBJECT
+
+public:
+
+	GroupInfoBox(CreatingGroupType creating);
+	void keyPressEvent(QKeyEvent *e);
+	void paintEvent(QPaintEvent *e);
+	void resizeEvent(QResizeEvent *e);
+	void mouseMoveEvent(QMouseEvent *e);
+	void mousePressEvent(QMouseEvent *e);
+	void leaveEvent(QEvent *e);
+
+	bool eventFilter(QObject *obj, QEvent *e);
+
+	bool descriptionAnimStep(float64 ms);
+	bool photoAnimStep(float64 ms);
+
+	void setInnerFocus() {
+		_name.setFocus();
+	}
+
+public slots:
+
+	void onPhoto();
+	void onPhotoReady(const QImage &img);
+
+	void onNext();
+	void onDescriptionResized();
+
+protected:
+
+	void hideAll();
+	void showAll();
+	void showDone();
+
+private:
+
+	QRect descriptionRect() const;
+	QRect photoRect() const;
+
+	void updateMaxHeight();
+	void updateSelected(const QPoint &cursorGlobalPosition);
+	CreatingGroupType _creating;
+
+	anim::fvalue a_photoOver;
+	Animation a_photo;
+	bool _photoOver, _descriptionOver;
+
+	anim::cvalue a_descriptionBg, a_descriptionBorder;
+	Animation a_description;
 
 	FlatInput _name;
-	FlatButton _create, _cancel;
+	FlatButton _photo;
+	FlatTextarea _description;
+	QImage _photoBig;
+	QPixmap _photoSmall;
+	FlatButton _next, _cancel;
+
+	// channel creation
+	int32 _creationRequestId;
+	ChannelData *_createdChannel;
+
+	void creationDone(const MTPUpdates &updates);
+	bool creationFail(const RPCError &e);
+	void exportDone(const MTPExportedChatInvite &result);
+};
+
+class SetupChannelBox : public AbstractBox, public RPCSender {
+	Q_OBJECT
+
+public:
+
+	SetupChannelBox(ChannelData *channel);
+	void keyPressEvent(QKeyEvent *e);
+	void paintEvent(QPaintEvent *e);
+	void resizeEvent(QResizeEvent *e);
+
+	void closePressed();
+
+	void setInnerFocus() {
+		_link.setFocus();
+	}
+
+public slots:
+
+	void onSave();
+	void onChange();
+	void onCheck();
+
+	void onPrivacyChange();
+
+protected:
+
+	void hideAll();
+	void showAll();
+	void showDone();
+
+private:
+
+	ChannelData *_channel;
+
+	FlatRadiobutton _public, _private;
+	int32 _aboutPublicWidth, _aboutPublicHeight;
+	Text _aboutPublic, _aboutPrivate;
+	QString _linkPlaceholder;
+	UsernameInput _link;
+	FlatButton _save, _skip;
+
+	void onUpdateDone(const MTPBool &result);
+	bool onUpdateFail(const RPCError &error);
+
+	void onCheckDone(const MTPBool &result);
+	bool onCheckFail(const RPCError &error);
+
+	mtpRequestId _saveRequestId, _checkRequestId;
+	QString _sentUsername, _checkUsername, _errorText, _goodText;
+
+	QTimer _checkTimer;
 };
diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp
index ea0db6d56..c3f15771b 100644
--- a/Telegram/SourceFiles/boxes/photocropbox.cpp
+++ b/Telegram/SourceFiles/boxes/photocropbox.cpp
@@ -24,14 +24,22 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "photocropbox.h"
 #include "fileuploader.h"
 
-PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : _downState(0),
+PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer, bool upload) : _downState(0),
 	_sendButton(this, lang(lng_settings_save), st::btnSelectDone),
 	_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
     _img(img), _peerId(peer) {
 
+	if (peerIsChannel(_peerId)) {
+		_title = lang(lng_create_channel_crop);
+	} else if (peerIsChat(_peerId)) {
+		_title = lang(lng_create_group_crop);
+	} else {
+		_title = lang(lng_settings_crop_profile);
+	}
+
 	connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend()));
 	connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
-	if (_peerId) {
+	if (_peerId && upload) {
 		connect(this, SIGNAL(ready(const QImage &)), this, SLOT(onReady(const QImage &)));
 	}
 
@@ -209,7 +217,7 @@ void PhotoCropBox::paintEvent(QPaintEvent *e) {
 	// paint button sep
 	p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
 
-	paintGrayTitle(p, lang(lng_settings_crop_profile));
+	paintGrayTitle(p, _title);
 
 	p.translate(_thumbx, _thumby);
 	p.drawPixmap(0, 0, _thumb);
@@ -269,11 +277,11 @@ void PhotoCropBox::onSend() {
 	}
 
 	emit ready(tosend);
+	onClose();
 }
 
 void PhotoCropBox::onReady(const QImage &tosend) {
 	App::app()->uploadProfilePhoto(tosend, _peerId);
-	emit closed();
 }
 
 void PhotoCropBox::hideAll() {
diff --git a/Telegram/SourceFiles/boxes/photocropbox.h b/Telegram/SourceFiles/boxes/photocropbox.h
index 4afda343c..ce0c0f45d 100644
--- a/Telegram/SourceFiles/boxes/photocropbox.h
+++ b/Telegram/SourceFiles/boxes/photocropbox.h
@@ -24,7 +24,7 @@ class PhotoCropBox : public AbstractBox {
 
 public:
 
-	PhotoCropBox(const QImage &img, const PeerId &peer);
+	PhotoCropBox(const QImage &img, const PeerId &peer, bool upload = true);
 	void keyPressEvent(QKeyEvent *e);
 	void paintEvent(QPaintEvent *e);
 	void resizeEvent(QResizeEvent *e);
@@ -50,6 +50,7 @@ protected:
 
 private:
 
+	QString _title;
 	int32 _downState;
 	int32 _thumbx, _thumby, _thumbw, _thumbh;
 	int32 _cropx, _cropy, _cropw;
diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp
index 60b49eb50..3e7815b39 100644
--- a/Telegram/SourceFiles/boxes/usernamebox.cpp
+++ b/Telegram/SourceFiles/boxes/usernamebox.cpp
@@ -23,34 +23,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "mainwidget.h"
 #include "window.h"
 
-UsernameInput::UsernameInput(QWidget *parent, const style::flatInput &st, const QString &ph, const QString &val) : FlatInput(parent, st, ph, val) {
-}
-
-void UsernameInput::correctValue(QKeyEvent *e, const QString &was) {
-	QString oldText(text()), newText;
-	int32 newPos = cursorPosition(), from, len = oldText.size();
-	for (from = 0; from < len; ++from) {
-		if (!oldText.at(from).isSpace()) {
-			break;
-		}
-		if (newPos > 0) --newPos;
-	}
-	len -= from;
-	if (len > MaxUsernameLength) len = MaxUsernameLength + (oldText.at(from) == '@' ? 1 : 0);
-	for (int32 to = from + len; to > from;) {
-		--to;
-		if (!oldText.at(to).isSpace()) {
-			break;
-		}
-		--len;
-	}
-	newText = oldText.mid(from, len);
-	if (newText != oldText) {
-		setText(newText);
-		setCursorPosition(newPos);
-	}
-}
-
 UsernameBox::UsernameBox() :
 _saveButton(this, lang(lng_settings_save), st::usernameDone),
 _cancelButton(this, lang(lng_cancel), st::usernameCancel),
@@ -146,7 +118,7 @@ void UsernameBox::onCheck() {
 	}
 	QString name = getName();
 	if (name.size() >= MinUsernameLength) {
-		_checkUsername = getName();
+		_checkUsername = name;
 		_checkRequest = MTP::send(MTPaccount_CheckUsername(MTP_string(name)), rpcDone(&UsernameBox::onCheckDone), rpcFail(&UsernameBox::onCheckFail));
 	}
 }
@@ -197,9 +169,9 @@ bool UsernameBox::onUpdateFail(const RPCError &error) {
 	if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
 
 	_saveRequest = 0;
-	QString err(error.type()), name = getName();
+	QString err(error.type());
 	if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == App::self()->username) {
-		App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(name));
+		App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(_sentUsername));
 		emit closed();
 		return true;
 	} else if (err == "USERNAME_INVALID") {
diff --git a/Telegram/SourceFiles/boxes/usernamebox.h b/Telegram/SourceFiles/boxes/usernamebox.h
index 73ea1a88e..481ca17d8 100644
--- a/Telegram/SourceFiles/boxes/usernamebox.h
+++ b/Telegram/SourceFiles/boxes/usernamebox.h
@@ -19,17 +19,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 
 #include "abstractbox.h"
 
-class UsernameInput : public FlatInput {
-public:
-
-	UsernameInput(QWidget *parent, const style::flatInput &st, const QString &ph = QString(), const QString &val = QString());
-
-protected:
-
-	void correctValue(QKeyEvent *e, const QString &was);
-
-};
-
 class UsernameBox : public AbstractBox, public RPCSender {
 	Q_OBJECT
 
diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp
index b4fd697eb..0b2709988 100644
--- a/Telegram/SourceFiles/dialogswidget.cpp
+++ b/Telegram/SourceFiles/dialogswidget.cpp
@@ -2203,7 +2203,7 @@ void DialogsWidget::onAddContact() {
 }
 
 void DialogsWidget::onNewGroup() {
-	App::wnd()->showLayer(new ContactsBox(true));
+	App::wnd()->showLayer(new NewGroupBox());
 }
 
 bool DialogsWidget::onCancelSearch() {
diff --git a/Telegram/SourceFiles/gui/flatinput.cpp b/Telegram/SourceFiles/gui/flatinput.cpp
index 1d6f6e03f..357765fba 100644
--- a/Telegram/SourceFiles/gui/flatinput.cpp
+++ b/Telegram/SourceFiles/gui/flatinput.cpp
@@ -74,6 +74,10 @@ void FlatInput::customUpDown(bool custom) {
 	_customUpDown = custom;
 }
 
+void FlatInput::setTextMargin(const QMargins &mrg) {
+	_st.textMrg = mrg;
+}
+
 void FlatInput::onTouchTimer() {
 	_touchRightButton = true;
 }
@@ -383,6 +387,34 @@ void CountryCodeInput::correctValue(QKeyEvent *e, const QString &was) {
 	}
 }
 
+UsernameInput::UsernameInput(QWidget *parent, const style::flatInput &st, const QString &ph, const QString &val) : FlatInput(parent, st, ph, val) {
+}
+
+void UsernameInput::correctValue(QKeyEvent *e, const QString &was) {
+	QString oldText(text()), newText;
+	int32 newPos = cursorPosition(), from, len = oldText.size();
+	for (from = 0; from < len; ++from) {
+		if (!oldText.at(from).isSpace()) {
+			break;
+		}
+		if (newPos > 0) --newPos;
+	}
+	len -= from;
+	if (len > MaxUsernameLength) len = MaxUsernameLength + (oldText.at(from) == '@' ? 1 : 0);
+	for (int32 to = from + len; to > from;) {
+		--to;
+		if (!oldText.at(to).isSpace()) {
+			break;
+		}
+		--len;
+	}
+	newText = oldText.mid(from, len);
+	if (newText != oldText) {
+		setText(newText);
+		setCursorPosition(newPos);
+	}
+}
+
 InputField::InputField(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : TWidget(parent),
 _inner(this, val),
 _oldtext(val),
diff --git a/Telegram/SourceFiles/gui/flatinput.h b/Telegram/SourceFiles/gui/flatinput.h
index 56ea566f0..94438b230 100644
--- a/Telegram/SourceFiles/gui/flatinput.h
+++ b/Telegram/SourceFiles/gui/flatinput.h
@@ -23,6 +23,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 
 class FlatInput : public QLineEdit, public Animated {
 	Q_OBJECT
+	T_WIDGET
 
 public:
 
@@ -56,6 +57,8 @@ public:
 		return text();
 	}
 
+	void setTextMargin(const QMargins &mrg);
+
 public slots:
 
 	void onTextChange(const QString &text);
@@ -131,6 +134,18 @@ private:
 
 };
 
+
+class UsernameInput : public FlatInput {
+public:
+
+	UsernameInput(QWidget *parent, const style::flatInput &st, const QString &ph = QString(), const QString &val = QString());
+
+protected:
+
+	void correctValue(QKeyEvent *e, const QString &was);
+
+};
+
 class InputField : public TWidget {
 	Q_OBJECT
 
diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp
index 0e3a6a72d..59f395175 100644
--- a/Telegram/SourceFiles/gui/flattextarea.cpp
+++ b/Telegram/SourceFiles/gui/flattextarea.cpp
@@ -22,10 +22,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "window.h"
 
 FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(v, parent),
-	_ph(pholder), _oldtext(v), _phVisible(!v.length()),
-    a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
-	_st(st), _undoAvailable(false), _redoAvailable(false), _inDrop(false), _fakeMargin(0),
-    _touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmojis(false) {
+_minHeight(-1), _maxHeight(-1), _ctrlEnterSubmit(true),
+_ph(pholder), _oldtext(v), _phVisible(!v.length()),
+a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
+_st(st), _undoAvailable(false), _redoAvailable(false), _inDrop(false), _fakeMargin(0),
+_touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmojis(false) {
 	setAcceptRichText(false);
 	resize(_st.width, _st.font->height);
 	
@@ -64,6 +65,31 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const
 	if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
 }
 
+void FlatTextarea::setMinHeight(int32 minHeight) {
+	_minHeight = minHeight;
+	heightAutoupdated();
+}
+
+void FlatTextarea::setMaxHeight(int32 maxHeight) {
+	_maxHeight = maxHeight;
+	heightAutoupdated();
+}
+
+bool FlatTextarea::heightAutoupdated() {
+	if (_minHeight < 0 || _maxHeight < 0) return false;
+	int newh = ceil(document()->size().height()) + 2 * fakeMargin();
+	if (newh > _maxHeight) {
+		newh = _maxHeight;
+	} else if (newh < _minHeight) {
+		newh = _minHeight;
+	}
+	if (height() != newh) {
+		resize(width(), newh);
+		return true;
+	}
+	return false;
+}
+
 void FlatTextarea::onTouchTimer() {
 	_touchRightButton = true;
 }
@@ -555,6 +581,12 @@ QVariant FlatTextarea::loadResource(int type, const QUrl &name) {
 	return QVariant();
 }
 
+void FlatTextarea::checkContentHeight() {
+	if (heightAutoupdated()) {
+		emit resized();
+	}
+}
+
 void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
 	int32 emojiPosition = 0, emojiLen = 0;
 	const EmojiData *emoji = 0;
@@ -693,6 +725,7 @@ void FlatTextarea::onDocumentContentsChanged() {
 	if (_oldtext != curText) {
 		_oldtext = curText;
 		emit changed();
+		checkContentHeight();
 	}
 	updatePlaceholder();
 	if (App::wnd()) App::wnd()->updateGlobalMenu();
@@ -753,10 +786,14 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const {
 	return result;
 }
 
+void FlatTextarea::setCtrlEnterSubmit(bool ctrlEnterSubmit) {
+	_ctrlEnterSubmit = ctrlEnterSubmit;
+}
+
 void FlatTextarea::keyPressEvent(QKeyEvent *e) {
 	bool shift = e->modifiers().testFlag(Qt::ShiftModifier);
 	bool macmeta = (cPlatform() == dbipMac) && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier);
-	bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()) || (ctrl && shift);
+	bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && _ctrlEnterSubmit) || (!ctrl && !shift && !_ctrlEnterSubmit) || (ctrl && shift);
 	bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return);
 
 	if (macmeta && e->key() == Qt::Key_Backspace) {
diff --git a/Telegram/SourceFiles/gui/flattextarea.h b/Telegram/SourceFiles/gui/flattextarea.h
index 408f3ed16..a57fcbb66 100644
--- a/Telegram/SourceFiles/gui/flattextarea.h
+++ b/Telegram/SourceFiles/gui/flattextarea.h
@@ -23,6 +23,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 
 class FlatTextarea : public QTextEdit, public Animated {
 	Q_OBJECT
+	T_WIDGET
 
 public:
 
@@ -38,6 +39,9 @@ public:
 	void mousePressEvent(QMouseEvent *e);
 	void dropEvent(QDropEvent *e);
 
+	void setMinHeight(int32 minHeight);
+	void setMaxHeight(int32 maxHeight);
+
 	const QString &getLastText() const;
 	void updatePlaceholder();
 
@@ -64,6 +68,7 @@ public:
 	void insertFromMimeData(const QMimeData *source);
 
 	QMimeData *createMimeDataFromSelection() const;
+	void setCtrlEnterSubmit(bool ctrlEnterSubmit);
 
 public slots:
 
@@ -79,6 +84,7 @@ public slots:
 
 signals:
 
+	void resized();
 	void changed();
 	void submitted(bool ctrlShiftEnter);
 	void cancelled();
@@ -89,29 +95,19 @@ signals:
 protected:
 
 	void insertEmoji(EmojiPtr emoji, QTextCursor c);
-	TWidget *tparent() {
-		return qobject_cast<TWidget*>(parentWidget());
-	}
-	const TWidget *tparent() const {
-		return qobject_cast<const TWidget*>(parentWidget());
-	}
-	void enterEvent(QEvent *e) {
-		TWidget *p(tparent());
-		if (p) p->leaveToChildEvent(e);
-		return QTextEdit::enterEvent(e);
-	}
-	void leaveEvent(QEvent *e) {
-		TWidget *p(tparent());
-		if (p) p->enterFromChildEvent(e);
-		return QTextEdit::leaveEvent(e);
-	}
 
 	QVariant loadResource(int type, const QUrl &name);
 
+	void checkContentHeight();
+
 private:
 
 	void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const;
 	void processDocumentContentsChange(int position, int charsAdded);
+	bool heightAutoupdated();
+
+	int32 _minHeight, _maxHeight; // < 0 - no autosize
+	bool _ctrlEnterSubmit;
 
 	QString _ph, _phelided, _oldtext;
 	bool _phVisible;
diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h
index b79f824e4..15b1890fe 100644
--- a/Telegram/SourceFiles/gui/twidget.h
+++ b/Telegram/SourceFiles/gui/twidget.h
@@ -26,6 +26,37 @@ public:
 	explicit Painter(QPaintDevice *device) : QPainter(device) {
 	}
 
+	void setFont(const style::font &font) {
+		QPainter::setFont(font->f);
+	}
+	void setFont(const QFont &font) {
+		QPainter::setFont(font);
+	}
+	void setBrush(const style::color &color) {
+		QPainter::setBrush(color->b);
+	}
+	void setBrush(const QColor &color) {
+		QPainter::setBrush(color);
+	}
+	void setBrush(const QBrush &brush) {
+		QPainter::setBrush(brush);
+	}
+	void setBrush(Qt::BrushStyle style) {
+		QPainter::setBrush(style);
+	}
+	void setPen(const style::color &color) {
+		QPainter::setPen(color->p);
+	}
+	void setPen(const QPen &pen) {
+		QPainter::setPen(pen);
+	}
+	void setPen(const QColor &color) {
+		QPainter::setPen(color);
+	}
+	void setPen(Qt::PenStyle style) {
+		QPainter::setPen(style);
+	}
+
 	void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) {
 		QFontMetrics m(fontMetrics());
 		if (rtl() && textWidth < 0) textWidth = m.width(text);
@@ -113,63 +144,62 @@ public:
 	}
 };
 
+#define T_WIDGET public: \
+TWidget *tparent() { \
+return qobject_cast<TWidget*>(parentWidget()); \
+} \
+const TWidget *tparent() const { \
+	return qobject_cast<const TWidget*>(parentWidget()); \
+} \
+virtual void leaveToChildEvent(QEvent *e) { /* e -- from enterEvent() of child TWidget */ \
+} \
+virtual void enterFromChildEvent(QEvent *e) { /* e -- from leaveEvent() of child TWidget */ \
+} \
+void moveToLeft(int x, int y, int outerw) { \
+	move(rtl() ? (outerw - x - width()) : x, y); \
+} \
+void moveToRight(int x, int y, int outerw) { \
+	move(rtl() ? x : (outerw - x - width()), y); \
+} \
+QPoint myrtlpoint(int x, int y) const { \
+	return rtlpoint(x, y, width()); \
+} \
+QPoint myrtlpoint(const QPoint p) const { \
+	return rtlpoint(p, width()); \
+} \
+QRect myrtlrect(int x, int y, int w, int h) const { \
+	return rtlrect(x, y, w, h, width()); \
+} \
+QRect myrtlrect(const QRect &r) { \
+	return rtlrect(r, width()); \
+} \
+void rtlupdate(const QRect &r) { \
+	update(myrtlrect(r)); \
+} \
+protected: \
+void enterEvent(QEvent *e) { \
+	TWidget *p(tparent()); \
+	if (p) p->leaveToChildEvent(e); \
+	return QWidget::enterEvent(e); \
+} \
+void leaveEvent(QEvent *e) { \
+	TWidget *p(tparent()); \
+	if (p) p->enterFromChildEvent(e); \
+	return QWidget::leaveEvent(e); \
+}
+
 class TWidget : public QWidget {
 	Q_OBJECT
+	T_WIDGET
 
 public:
 
 	TWidget(QWidget *parent = 0) : QWidget(parent) {
 	}
-	TWidget *tparent() {
-		return qobject_cast<TWidget*>(parentWidget());
-	}
-	const TWidget *tparent() const {
-		return qobject_cast<const TWidget*>(parentWidget());
-	}
-
-	virtual void leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget
-	}
-	virtual void enterFromChildEvent(QEvent *e) { // e -- from leaveEvent() of child TWidget
-	}
-
-	void moveToLeft(int x, int y, int outerw) {
-		move(rtl() ? (outerw - x - width()) : x, y);
-	}
-	void moveToRight(int x, int y, int outerw) {
-		move(rtl() ? x : (outerw - x - width()), y);
-	}
-	QPoint myrtlpoint(int x, int y) const {
-		return rtlpoint(x, y, width());
-	}
-	QPoint myrtlpoint(const QPoint p) const {
-		return rtlpoint(p, width());
-	}
-	QRect myrtlrect(int x, int y, int w, int h) const {
-		return rtlrect(x, y, w, h, width());
-	}
-	QRect myrtlrect(const QRect &r) {
-		return rtlrect(r, width());
-	}
-	void rtlupdate(const QRect &r) {
-		update(myrtlrect(r));
-	}
 	bool event(QEvent *e) {
 		return QWidget::event(e);
 	}
 
-protected:
-
-	void enterEvent(QEvent *e) {
-		TWidget *p(tparent());
-		if (p) p->leaveToChildEvent(e);
-		return QWidget::enterEvent(e);
-	}
-	void leaveEvent(QEvent *e) {
-		TWidget *p(tparent());
-		if (p) p->enterFromChildEvent(e);
-		return QWidget::leaveEvent(e);
-	}
-
 private:
 
 };
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index 5598f5058..d8b937726 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -1461,7 +1461,7 @@ bool History::isReadyFor(MsgId msgId, bool check) const {
 void History::getReadyFor(MsgId msgId) {
 	if (!isReadyFor(msgId, true)) {
 		clear(true);
-		newLoaded = (msgId == ShowAtTheEndMsgId) || (lastMsg && !lastMsg->detached());
+		newLoaded = (msgId == ShowAtTheEndMsgId);
 		oldLoaded = false;
 		lastWidth = 0;
 		lastShowAtMsgId = msgId;
@@ -6386,6 +6386,11 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) {
 		text = lng_action_created_channel(lt_title, textClean(qs(d.vtitle)));
 	} break;
 
+	case mtpc_messageActionChannelToggleComments: {
+		const MTPDmessageActionChannelToggleComments &d(action.c_messageActionChannelToggleComments());
+		text = lang(d.venabled.v ? lng_action_comments_enabled : lng_action_comments_disabled);
+	} break;
+
 	case mtpc_messageActionChatDeletePhoto: {
 		text = fromChannel() ? lang(lng_action_removed_photo_channel) : lng_action_removed_photo(lt_from, from);
 	} break;
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index 161e2324d..80481f5ab 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -695,7 +695,7 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt
 			uint32 sel = _selected.cbegin().value();
 			if (sel != FullItemSel && (sel & 0xFFFF) == ((sel >> 16) & 0xFFFF)) {
 				_selected.clear();
-				App::wnd()->setInnerFocus();
+				if (App::wnd()) App::wnd()->setInnerFocus();
 			}
 		}
 	}
@@ -1577,21 +1577,9 @@ void HistoryList::onParentGeometryChanged() {
 	}
 }
 
-MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st, const QString &ph, const QString &val) : FlatTextarea(history, st, ph, val), history(history), _maxHeight(st::maxFieldHeight) {
-	connect(this, SIGNAL(changed()), this, SLOT(onChange()));
-}
-
-void MessageField::setMaxHeight(int32 maxHeight) {
-	_maxHeight = maxHeight;
-	int newh = ceil(document()->size().height()) + 2 * fakeMargin(), minh = st::btnSend.height - 2 * st::sendPadding;
-	if (newh > _maxHeight) {
-		newh = _maxHeight;
-	} else if (newh < minh) {
-		newh = minh;
-	}
-	if (height() != newh) {
-		resize(width(), newh);
-	}
+MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st, const QString &ph, const QString &val) : FlatTextarea(history, st, ph, val), history(history) {
+	setMinHeight(st::btnSend.height - 2 * st::sendPadding);
+	setMaxHeight(st::maxFieldHeight);
 }
 
 bool MessageField::hasSendText() const {
@@ -1605,20 +1593,6 @@ bool MessageField::hasSendText() const {
 	return false;
 }
 
-void MessageField::onChange() {
-	int newh = ceil(document()->size().height()) + 2 * fakeMargin(), minh = st::btnSend.height - 2 * st::sendPadding;
-	if (newh > _maxHeight) {
-		newh = _maxHeight;
-	} else if (newh < minh) {
-		newh = minh;
-	}
-	
-	if (height() != newh) {
-		resize(width(), newh);
-		emit resized();
-	}
-}
-
 void MessageField::onEmojiInsert(EmojiPtr emoji) {
 	insertEmoji(emoji, textCursor());
 }
@@ -1632,7 +1606,7 @@ void MessageField::dropEvent(QDropEvent *e) {
 
 void MessageField::resizeEvent(QResizeEvent *e) {
 	FlatTextarea::resizeEvent(e);
-	onChange();
+	checkContentHeight();
 }
 
 bool MessageField::canInsertFromMimeData(const QMimeData *source) const {
@@ -2396,6 +2370,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
 	_attachMention.hide();
 	connect(&_attachMention, SIGNAL(chosen(QString)), this, SLOT(onMentionHashtagOrBotCommandInsert(QString)));
 	_field.installEventFilter(&_attachMention);
+	_field.setCtrlEnterSubmit(cCtrlEnter());
 
 	_field.hide();
 	_field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _send.height() - 2 * st::sendPadding);
@@ -2580,7 +2555,7 @@ void HistoryWidget::sendActionDone(const MTPBool &result, mtpRequestId req) {
 
 void HistoryWidget::activate() {
 	if (_history) updateListSize(0, true);
-	setInnerFocus();
+	if (App::wnd()) App::wnd()->setInnerFocus();
 }
 
 void HistoryWidget::setInnerFocus() {
@@ -2935,7 +2910,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) {
 		
 		if (_history->draftToId > 0 || !_history->draft.isEmpty()) {
 			setFieldText(_history->draft);
-			setInnerFocus();
+			_field.setFocus();
 			_history->draftCursor.applyTo(_field, &_synthedTextUpdate);
 			_replyToId = readyToForward() ? 0 : _history->draftToId;
 			if (_history->draftPreviewCancelled) {
@@ -2944,7 +2919,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) {
 		} else {
 			Local::MessageDraft draft = Local::readDraft(_peer->id);
 			setFieldText(draft.text);
-			setInnerFocus();
+			_field.setFocus();
 			if (!draft.text.isEmpty()) {
 				MessageCursor cur = Local::readDraftPositions(_peer->id);
 				cur.applyTo(_field, &_synthedTextUpdate);
@@ -2969,6 +2944,8 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) {
 		doneShow();
 	}
 
+	if (App::wnd()) App::wnd()->setInnerFocus();
+
 	emit peerShown(_peer);
 	App::main()->topBar()->update();
 	update();
@@ -3000,6 +2977,10 @@ void HistoryWidget::updateAfterDrag() {
 	if (_list) _list->dragActionUpdate(QCursor::pos());
 }
 
+void HistoryWidget::ctrlEnterSubmitUpdated() {
+	_field.setCtrlEnterSubmit(cCtrlEnter());
+}
+
 void HistoryWidget::updateReportSpamStatus() {
 	if (!_peer || (_peer->isUser() && (peerToUser(_peer->id) == MTP::authedId() || isNotificationsUser(_peer->id) || isServiceUser(_peer->id) || _peer->asUser()->botInfo))) {
 		_reportSpamStatus = dbiprsNoButton;
@@ -3447,7 +3428,7 @@ void HistoryWidget::loadMessagesDown() {
 	MsgId max = _history->maxMsgId();
 	if (!max) return;
 
-	int32 loadCount = MessagesPerPage, offset = -loadCount - 1;
+	int32 loadCount = MessagesPerPage, offset = -loadCount;
 	if (_peer->isChannel()) {
 		_preloadDownRequest = MTP::send(MTPmessages_GetImportantHistory(_peer->input, MTP_int(max + 1), MTP_int(offset), MTP_int(loadCount), MTP_int(0), MTP_int(0)), rpcDone(&HistoryWidget::messagesReceived, _peer), rpcFail(&HistoryWidget::messagesFailed));
 	} else {
@@ -3745,8 +3726,10 @@ void HistoryWidget::doneShow() {
 	updateControlsVisibility();
 	updateListSize(0, true);
 	onListScroll();
-	if (App::wnd()) App::wnd()->checkHistoryActivation();
-	App::wnd()->setInnerFocus();
+	if (App::wnd()) {
+		App::wnd()->checkHistoryActivation();
+		App::wnd()->setInnerFocus();
+	}
 }
 
 void HistoryWidget::animStop() {
diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h
index 0f8739bf2..0ccc2bb0d 100644
--- a/Telegram/SourceFiles/historywidget.h
+++ b/Telegram/SourceFiles/historywidget.h
@@ -190,7 +190,6 @@ public:
 	void insertFromMimeData(const QMimeData *source);
 
 	void focusInEvent(QFocusEvent *e);
-	void setMaxHeight(int32 maxHeight);
 
 	bool hasSendText() const;
 
@@ -206,17 +205,14 @@ public:
 
 public slots:
 
-	void onChange();
 	void onEmojiInsert(EmojiPtr emoji);
 
 signals:
 
-	void resized();
 	void focused();
 
 private:
 	HistoryWidget *history;
-	int32 _maxHeight;
 
 };
 
@@ -520,6 +516,9 @@ public:
 	void updateToEndVisibility();
 
 	void updateAfterDrag();
+	void ctrlEnterSubmitUpdated();
+
+	void setInnerFocus();
 
 	~HistoryWidget();
 
@@ -585,7 +584,6 @@ public slots:
 	void onPhotoFailed(quint64 id);
 
 	void activate();
-	void setInnerFocus();
 	void onMentionHashtagOrBotCommandInsert(QString str);
 	void onTextChange();
 
diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp
index d29977362..cc4bcdde1 100644
--- a/Telegram/SourceFiles/intro/introsignup.cpp
+++ b/Telegram/SourceFiles/intro/introsignup.cpp
@@ -207,8 +207,8 @@ void IntroSignup::onCheckRequest() {
 
 void IntroSignup::onPhotoReady(const QImage &img) {
 	_photoBig = img;
-	_photoSmall = QPixmap::fromImage(img.scaled(st::introPhotoSize, st::introPhotoSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
-	App::wnd()->hideLayer();
+	_photoSmall = QPixmap::fromImage(img.scaled(st::introPhotoSize * cIntRetinaFactor(), st::introPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
+	_photoSmall.setDevicePixelRatio(cRetinaFactor());
 }
 
 void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) {
diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp
index 1e0b92f88..69795b3ad 100644
--- a/Telegram/SourceFiles/layerwidget.cpp
+++ b/Telegram/SourceFiles/layerwidget.cpp
@@ -24,7 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "mainwidget.h"
 #include "gui/filedialog.h"
 
-BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : QWidget(parent), w(w), _hidden(0),
+BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : QWidget(parent), w(w),
 aBackground(0), aBackgroundFunc(anim::easeOutCirc), hiding(false), shadow(st::boxShadow) {
 	w->setParent(this);
 	setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
@@ -72,13 +72,13 @@ void BackgroundWidget::onClose() {
 }
 
 bool BackgroundWidget::onInnerClose() {
-	if (!_hidden) {
+	if (_hidden.isEmpty()) {
 		onClose();
 		return true;
 	}
 	w->deleteLater();
-	w = _hidden;
-	_hidden = 0;
+	w = _hidden.back();
+	_hidden.pop_back();
 	w->show();
 	resizeEvent(0);
 	w->animStep(1);
@@ -87,13 +87,24 @@ bool BackgroundWidget::onInnerClose() {
 }
 
 void BackgroundWidget::startHide() {
-	if (App::main()) App::main()->setInnerFocus();
+	if (hiding) return;
 	hiding = true;
+	if (App::wnd()) App::wnd()->setInnerFocus();
 	aBackground.start(0);
 	anim::start(this);
 	w->startHide();
 }
 
+bool BackgroundWidget::canSetFocus() const {
+	return w && !hiding;
+}
+
+void BackgroundWidget::setInnerFocus() {
+	if (w) {
+		w->setInnerFocus();
+	}
+}
+
 void BackgroundWidget::resizeEvent(QResizeEvent *e) {
 	w->parentResized();
 }
@@ -103,9 +114,8 @@ void BackgroundWidget::updateWideMode() {
 }
 
 void BackgroundWidget::replaceInner(LayeredWidget *n) {
-	if (_hidden) _hidden->deleteLater();
-	_hidden = w;
-	_hidden->hide();
+	_hidden.push_back(w);
+	w->hide();
 	w = n;
 	w->setParent(this);
 	connect(w, SIGNAL(closed()), this, SLOT(onInnerClose()));
@@ -139,13 +149,18 @@ void BackgroundWidget::boxDestroyed(QObject *obj) {
 	if (obj == w) {
 		if (App::wnd()) App::wnd()->layerFinishedHide(this);
 		w = 0;
-	} else if (_hidden == obj) {
-		_hidden = 0;
+	} else {
+		int32 index = _hidden.indexOf(static_cast<LayeredWidget*>(obj));
+		if (index >= 0) {
+			_hidden.removeAt(index);
+		}
 	}
 }
 
 BackgroundWidget::~BackgroundWidget() {
 	if (App::wnd()) App::wnd()->noBox(this);
 	w->deleteLater();
-	if (_hidden) _hidden->deleteLater();
+	for (HiddenLayers::const_iterator i = _hidden.cbegin(), e = _hidden.cend(); i != e; ++i) {
+		(*i)->deleteLater();
+	}
 }
diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h
index e5b6267cb..0d4b57a71 100644
--- a/Telegram/SourceFiles/layerwidget.h
+++ b/Telegram/SourceFiles/layerwidget.h
@@ -30,6 +30,10 @@ public:
 	virtual void startHide() {
 	}
 
+	virtual void setInnerFocus() {
+		setFocus();
+	}
+
 	virtual void resizeEvent(QResizeEvent *e) {
 		emit resized();
 	}
@@ -71,6 +75,9 @@ public:
 
 	bool animStep(float64 ms);
 
+	bool canSetFocus() const;
+	void setInnerFocus();
+
 	~BackgroundWidget();
 
 public slots:
@@ -83,7 +90,9 @@ private:
 
 	void startHide();
 
-	LayeredWidget *w, *_hidden;
+	LayeredWidget *w;
+	typedef QList<LayeredWidget*> HiddenLayers;
+	HiddenLayers _hidden;
 	anim::fvalue aBackground;
 	anim::transition aBackgroundFunc;
 	bool hiding;
diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp
index e977faa8e..d40303dcf 100644
--- a/Telegram/SourceFiles/localstorage.cpp
+++ b/Telegram/SourceFiles/localstorage.cpp
@@ -958,6 +958,7 @@ namespace {
 			if (!_checkStreamStatus(stream)) return false;
 
 			cSetCtrlEnter(v == dbiskCtrlEnter);
+			if (App::main()) App::main()->ctrlEnterSubmitUpdated();
 		} break;
 
 		case dbiCatsAndDogs: { // deprecated
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 119d77bd6..e4644ba7c 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -831,12 +831,14 @@ void MainWidget::removeContact(UserData *user) {
 	dialogs.removeContact(user);
 }
 
-void MainWidget::addParticipants(ChatData *chat, const QVector<UserData*> &users) {
+void MainWidget::addParticipants(PeerData *chatOrChannel, const QVector<UserData*> &users) {
 	for (QVector<UserData*>::const_iterator i = users.cbegin(), e = users.cend(); i != e; ++i) {
-		MTP::send(MTPmessages_AddChatUser(chat->inputChat, (*i)->inputUser, MTP_int(ForwardOnAdd)), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::addParticipantFail, *i), 0, 5);
+		if (chatOrChannel->isChat()) {
+			MTP::send(MTPmessages_AddChatUser(chatOrChannel->asChat()->inputChat, (*i)->inputUser, MTP_int(ForwardOnAdd)), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::addParticipantFail, *i), 0, 5);
+		} else if (chatOrChannel->isChannel()) {
+			MTP::send(MTPmessages_AddChatUser(chatOrChannel->asChannel()->inputChat, (*i)->inputUser, MTP_int(0)), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::addParticipantFail, *i), 0, 5);
+		}
 	}
-	App::wnd()->hideLayer();
-	showPeerHistory(chat->id, ShowAtTheEndMsgId);
 }
 
 bool MainWidget::addParticipantFail(UserData *user, const RPCError &error) {
@@ -971,7 +973,7 @@ void MainWidget::onResendAsDocument() {
 			item->destroy();
 		}
 	}
-	App::wnd()->layerHidden();
+	App::wnd()->hideLayer(true);
 }
 
 void MainWidget::onCancelResend() {
@@ -2087,7 +2089,7 @@ void MainWidget::setInnerFocus() {
 	} else if (profile) {
 		profile->setFocus();
 	} else {
-		history.activate();
+		history.setInnerFocus();
 	}
 }
 
@@ -2121,6 +2123,10 @@ void MainWidget::updateAfterDrag() {
 	}
 }
 
+void MainWidget::ctrlEnterSubmitUpdated() {
+	history.ctrlEnterSubmitUpdated();
+}
+
 void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back) {
 	if (!back && (!peerId || (_stack.size() == 1 && _stack[0]->type() == HistoryStackItem && _stack[0]->peer->id == peerId))) {
 		back = true;
@@ -2197,8 +2203,8 @@ void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back)
 			if (history.isHidden()) history.show();
 			if (!animCache.isNull()) {
 				history.animShow(animCache, animTopBarCache, back);
-			} else {
-				QTimer::singleShot(0, this, SLOT(setInnerFocus()));
+			} else if (App::wnd()) {
+				QTimer::singleShot(0, App::wnd(), SLOT(setInnerFocus()));
 			}
 		}
 	}
@@ -2381,7 +2387,7 @@ void MainWidget::showBackFromStack() {
 	if (selectingPeer()) return;
 	if (_stack.isEmpty()) {
 		showDialogs();
-		QTimer::singleShot(0, this, SLOT(setInnerFocus()));
+		if (App::wnd()) QTimer::singleShot(0, App::wnd(), SLOT(setInnerFocus()));
 		return;
 	}
 	StackItem *item = _stack.back();
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 07f2e40b9..dc38cd774 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -291,7 +291,7 @@ public:
 	void clearHistory(PeerData *peer);
 	void removeContact(UserData *user);
 
-	void addParticipants(ChatData *chat, const QVector<UserData*> &users);
+	void addParticipants(PeerData *chatOrChannel, const QVector<UserData*> &users);
 	bool addParticipantFail(UserData *user, const RPCError &e);
 
 	void kickParticipant(ChatData *chat, UserData *user);
@@ -385,6 +385,9 @@ public:
 	void feedUpdate(const MTPUpdate &update);
 	void updateAfterDrag();
 
+	void ctrlEnterSubmitUpdated();
+	void setInnerFocus();
+
 	~MainWidget();
 
 signals:
@@ -414,7 +417,6 @@ public slots:
 	void documentPlayProgress(const SongMsgId &songId);
 	void hidePlayer();
 
-	void setInnerFocus();
 	void dialogsCancelled();
 
 	void onParentResize(const QSize &newSize);
diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp
index 9b2cf778a..55bc78225 100644
--- a/Telegram/SourceFiles/mediaview.cpp
+++ b/Telegram/SourceFiles/mediaview.cpp
@@ -412,7 +412,9 @@ void MediaView::showSaveMsgFile() {
 }
 
 void MediaView::close() {
-	if (App::wnd()) App::wnd()->layerHidden();
+	if (App::wnd()) {
+		App::wnd()->hideLayer(true);
+	}
 }
 
 void MediaView::activateControls() {
diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp
index e6aa6ec56..d2b49fffe 100644
--- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp
+++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp
@@ -1671,6 +1671,19 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
 				}
 			break;
 
+			case mtpc_messageActionChannelToggleComments:
+				if (stage) {
+					to.add(",\n").addSpaces(lev);
+				} else {
+					to.add("{ messageActionChannelToggleComments");
+					to.add("\n").addSpaces(lev);
+				}
+				switch (stage) {
+				case 0: to.add("  enabled: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
+				}
+			break;
+
 			case mtpc_dialog:
 				if (stage) {
 					to.add(",\n").addSpaces(lev);
@@ -4728,6 +4741,69 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
 				}
 			break;
 
+			case mtpc_channelParticipantModerator:
+				if (stage) {
+					to.add(",\n").addSpaces(lev);
+				} else {
+					to.add("{ channelParticipantModerator");
+					to.add("\n").addSpaces(lev);
+				}
+				switch (stage) {
+				case 0: to.add("  user_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 1: to.add("  inviter_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 2: to.add("  date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
+				}
+			break;
+
+			case mtpc_channelParticipantPublisher:
+				if (stage) {
+					to.add(",\n").addSpaces(lev);
+				} else {
+					to.add("{ channelParticipantPublisher");
+					to.add("\n").addSpaces(lev);
+				}
+				switch (stage) {
+				case 0: to.add("  user_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 1: to.add("  inviter_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 2: to.add("  date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
+				}
+			break;
+
+			case mtpc_channelParticipantCreator:
+				if (stage) {
+					to.add(",\n").addSpaces(lev);
+				} else {
+					to.add("{ channelParticipantCreator");
+					to.add("\n").addSpaces(lev);
+				}
+				switch (stage) {
+				case 0: to.add("  user_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
+				}
+			break;
+
+			case mtpc_channelParticipantsRecent:
+				to.add("{ channelParticipantsRecent }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
+			break;
+
+			case mtpc_channelParticipantsAdmins:
+				to.add("{ channelParticipantsAdmins }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
+			break;
+
+			case mtpc_channelRoleEmpty:
+				to.add("{ channelRoleEmpty }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
+			break;
+
+			case mtpc_channelRoleModerator:
+				to.add("{ channelRoleModerator }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
+			break;
+
+			case mtpc_channelRolePublisher:
+				to.add("{ channelRolePublisher }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
+			break;
+
 			case mtpc_messages_channelParticipants:
 				if (stage) {
 					to.add(",\n").addSpaces(lev);
@@ -5257,6 +5333,21 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
 				}
 			break;
 
+			case mtpc_messages_editChatAdmin:
+				if (stage) {
+					to.add(",\n").addSpaces(lev);
+				} else {
+					to.add("{ messages_editChatAdmin");
+					to.add("\n").addSpaces(lev);
+				}
+				switch (stage) {
+				case 0: to.add("  chat_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 1: to.add("  user_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 2: to.add("  role: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
+				}
+			break;
+
 			case mtpc_messages_checkChannelUsername:
 				if (stage) {
 					to.add(",\n").addSpaces(lev);
@@ -6211,6 +6302,20 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
 				}
 			break;
 
+			case mtpc_messages_toggleChannelComments:
+				if (stage) {
+					to.add(",\n").addSpaces(lev);
+				} else {
+					to.add("{ messages_toggleChannelComments");
+					to.add("\n").addSpaces(lev);
+				}
+				switch (stage) {
+				case 0: to.add("  chat_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 1: to.add("  enabled: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
+				}
+			break;
+
 			case mtpc_messages_getChats:
 				if (stage) {
 					to.add(",\n").addSpaces(lev);
@@ -6441,8 +6546,9 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
 				}
 				switch (stage) {
 				case 0: to.add("  chat_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-				case 1: to.add("  offset: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-				case 2: to.add("  limit: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 1: to.add("  filter: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 2: to.add("  offset: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+				case 3: to.add("  limit: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 				default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 				}
 			break;
diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h
index 92b0133b0..ac89fcebd 100644
--- a/Telegram/SourceFiles/mtproto/mtpScheme.h
+++ b/Telegram/SourceFiles/mtproto/mtpScheme.h
@@ -167,6 +167,7 @@ enum {
 	mtpc_messageActionChatDeleteUser = 0xb2ae9b0c,
 	mtpc_messageActionChatJoinedByLink = 0xf89cf5e8,
 	mtpc_messageActionChannelCreate = 0x95d2ac92,
+	mtpc_messageActionChannelToggleComments = 0xf2863903,
 	mtpc_dialog = 0xc1dd804a,
 	mtpc_dialogChannel = 0x5b8496b2,
 	mtpc_photoEmpty = 0x2331b22d,
@@ -412,6 +413,14 @@ enum {
 	mtpc_channelMessagesFilterCollapsed = 0xfa01232e,
 	mtpc_contacts_resolvedPeer = 0x7f077ad9,
 	mtpc_channelParticipant = 0x506116ea,
+	mtpc_channelParticipantModerator = 0x91057fef,
+	mtpc_channelParticipantPublisher = 0x375d616,
+	mtpc_channelParticipantCreator = 0xe3e2e1f9,
+	mtpc_channelParticipantsRecent = 0xde3f3c79,
+	mtpc_channelParticipantsAdmins = 0xb4608969,
+	mtpc_channelRoleEmpty = 0xb285a0c6,
+	mtpc_channelRoleModerator = 0x9618d975,
+	mtpc_channelRolePublisher = 0x515b5530,
 	mtpc_messages_channelParticipants = 0xd6891de1,
 	mtpc_invokeAfterMsg = 0xcb9f372d,
 	mtpc_invokeAfterMsgs = 0x3dc4b4f0,
@@ -519,12 +528,14 @@ enum {
 	mtpc_messages_getImportantHistory = 0x24af43a5,
 	mtpc_messages_readChannelHistory = 0x36a1210e,
 	mtpc_messages_createChannel = 0x7f44d2c3,
+	mtpc_messages_toggleChannelComments = 0xb405b8af,
 	mtpc_messages_deleteChannelMessages = 0x9995a84f,
 	mtpc_messages_getChannelMessages = 0x5f46b265,
 	mtpc_messages_incrementMessagesViews = 0x91ffd479,
 	mtpc_messages_getMessagesViews = 0x9abfbbe1,
 	mtpc_messages_editChatAbout = 0x8a969b93,
-	mtpc_messages_getChannelParticipants = 0x4a771976,
+	mtpc_messages_getChannelParticipants = 0x38a1db31,
+	mtpc_messages_editChatAdmin = 0x62394070,
 	mtpc_messages_checkChannelUsername = 0xe6d2d8f4,
 	mtpc_messages_updateChannelUsername = 0xce2e9587,
 	mtpc_updates_getState = 0xedd4882a,
@@ -744,6 +755,7 @@ class MTPDmessageActionChatAddUser;
 class MTPDmessageActionChatDeleteUser;
 class MTPDmessageActionChatJoinedByLink;
 class MTPDmessageActionChannelCreate;
+class MTPDmessageActionChannelToggleComments;
 
 class MTPdialog;
 class MTPDdialog;
@@ -1131,6 +1143,13 @@ class MTPDcontacts_resolvedPeer;
 
 class MTPchannelParticipant;
 class MTPDchannelParticipant;
+class MTPDchannelParticipantModerator;
+class MTPDchannelParticipantPublisher;
+class MTPDchannelParticipantCreator;
+
+class MTPchannelParticipantsFilter;
+
+class MTPchannelParticipantRole;
 
 class MTPmessages_channelParticipants;
 class MTPDmessages_channelParticipants;
@@ -1286,6 +1305,8 @@ typedef MTPBoxed<MTPupdates_channelDifference> MTPupdates_ChannelDifference;
 typedef MTPBoxed<MTPchannelMessagesFilter> MTPChannelMessagesFilter;
 typedef MTPBoxed<MTPcontacts_resolvedPeer> MTPcontacts_ResolvedPeer;
 typedef MTPBoxed<MTPchannelParticipant> MTPChannelParticipant;
+typedef MTPBoxed<MTPchannelParticipantsFilter> MTPChannelParticipantsFilter;
+typedef MTPBoxed<MTPchannelParticipantRole> MTPChannelParticipantRole;
 typedef MTPBoxed<MTPmessages_channelParticipants> MTPmessages_ChannelParticipants;
 
 // Type classes definitions
@@ -3702,6 +3723,18 @@ public:
 		return *(const MTPDmessageActionChannelCreate*)data;
 	}
 
+	MTPDmessageActionChannelToggleComments &_messageActionChannelToggleComments() {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_messageActionChannelToggleComments) throw mtpErrorWrongTypeId(_type, mtpc_messageActionChannelToggleComments);
+		split();
+		return *(MTPDmessageActionChannelToggleComments*)data;
+	}
+	const MTPDmessageActionChannelToggleComments &c_messageActionChannelToggleComments() const {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_messageActionChannelToggleComments) throw mtpErrorWrongTypeId(_type, mtpc_messageActionChannelToggleComments);
+		return *(const MTPDmessageActionChannelToggleComments*)data;
+	}
+
 	uint32 innerLength() const;
 	mtpTypeId type() const;
 	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
@@ -3718,6 +3751,7 @@ private:
 	explicit MTPmessageAction(MTPDmessageActionChatDeleteUser *_data);
 	explicit MTPmessageAction(MTPDmessageActionChatJoinedByLink *_data);
 	explicit MTPmessageAction(MTPDmessageActionChannelCreate *_data);
+	explicit MTPmessageAction(MTPDmessageActionChannelToggleComments *_data);
 
 	friend MTPmessageAction MTP_messageActionEmpty();
 	friend MTPmessageAction MTP_messageActionChatCreate(const MTPstring &_title, const MTPVector<MTPint> &_users);
@@ -3728,6 +3762,7 @@ private:
 	friend MTPmessageAction MTP_messageActionChatDeleteUser(MTPint _user_id);
 	friend MTPmessageAction MTP_messageActionChatJoinedByLink(MTPint _inviter_id);
 	friend MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title);
+	friend MTPmessageAction MTP_messageActionChannelToggleComments(MTPBool _enabled);
 
 	mtpTypeId _type;
 };
@@ -8392,35 +8427,134 @@ typedef MTPBoxed<MTPcontacts_resolvedPeer> MTPcontacts_ResolvedPeer;
 
 class MTPchannelParticipant : private mtpDataOwner {
 public:
-	MTPchannelParticipant();
-	MTPchannelParticipant(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channelParticipant) : mtpDataOwner(0) {
+	MTPchannelParticipant() : mtpDataOwner(0), _type(0) {
+	}
+	MTPchannelParticipant(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : mtpDataOwner(0), _type(0) {
 		read(from, end, cons);
 	}
 
 	MTPDchannelParticipant &_channelParticipant() {
 		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipant) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipant);
 		split();
 		return *(MTPDchannelParticipant*)data;
 	}
 	const MTPDchannelParticipant &c_channelParticipant() const {
 		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipant) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipant);
 		return *(const MTPDchannelParticipant*)data;
 	}
 
+	MTPDchannelParticipantModerator &_channelParticipantModerator() {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipantModerator) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantModerator);
+		split();
+		return *(MTPDchannelParticipantModerator*)data;
+	}
+	const MTPDchannelParticipantModerator &c_channelParticipantModerator() const {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipantModerator) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantModerator);
+		return *(const MTPDchannelParticipantModerator*)data;
+	}
+
+	MTPDchannelParticipantPublisher &_channelParticipantPublisher() {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipantPublisher) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantPublisher);
+		split();
+		return *(MTPDchannelParticipantPublisher*)data;
+	}
+	const MTPDchannelParticipantPublisher &c_channelParticipantPublisher() const {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipantPublisher) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantPublisher);
+		return *(const MTPDchannelParticipantPublisher*)data;
+	}
+
+	MTPDchannelParticipantCreator &_channelParticipantCreator() {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipantCreator) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantCreator);
+		split();
+		return *(MTPDchannelParticipantCreator*)data;
+	}
+	const MTPDchannelParticipantCreator &c_channelParticipantCreator() const {
+		if (!data) throw mtpErrorUninitialized();
+		if (_type != mtpc_channelParticipantCreator) throw mtpErrorWrongTypeId(_type, mtpc_channelParticipantCreator);
+		return *(const MTPDchannelParticipantCreator*)data;
+	}
+
 	uint32 innerLength() const;
 	mtpTypeId type() const;
-	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channelParticipant);
+	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
 	void write(mtpBuffer &to) const;
 
 	typedef void ResponseType;
 
 private:
+	explicit MTPchannelParticipant(mtpTypeId type);
 	explicit MTPchannelParticipant(MTPDchannelParticipant *_data);
+	explicit MTPchannelParticipant(MTPDchannelParticipantModerator *_data);
+	explicit MTPchannelParticipant(MTPDchannelParticipantPublisher *_data);
+	explicit MTPchannelParticipant(MTPDchannelParticipantCreator *_data);
 
 	friend MTPchannelParticipant MTP_channelParticipant(MTPint _user_id, MTPint _inviter_id, MTPint _date);
+	friend MTPchannelParticipant MTP_channelParticipantModerator(MTPint _user_id, MTPint _inviter_id, MTPint _date);
+	friend MTPchannelParticipant MTP_channelParticipantPublisher(MTPint _user_id, MTPint _inviter_id, MTPint _date);
+	friend MTPchannelParticipant MTP_channelParticipantCreator(MTPint _user_id);
+
+	mtpTypeId _type;
 };
 typedef MTPBoxed<MTPchannelParticipant> MTPChannelParticipant;
 
+class MTPchannelParticipantsFilter {
+public:
+	MTPchannelParticipantsFilter() : _type(0) {
+	}
+	MTPchannelParticipantsFilter(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : _type(0) {
+		read(from, end, cons);
+	}
+
+	uint32 innerLength() const;
+	mtpTypeId type() const;
+	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
+	void write(mtpBuffer &to) const;
+
+	typedef void ResponseType;
+
+private:
+	explicit MTPchannelParticipantsFilter(mtpTypeId type);
+
+	friend MTPchannelParticipantsFilter MTP_channelParticipantsRecent();
+	friend MTPchannelParticipantsFilter MTP_channelParticipantsAdmins();
+
+	mtpTypeId _type;
+};
+typedef MTPBoxed<MTPchannelParticipantsFilter> MTPChannelParticipantsFilter;
+
+class MTPchannelParticipantRole {
+public:
+	MTPchannelParticipantRole() : _type(0) {
+	}
+	MTPchannelParticipantRole(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : _type(0) {
+		read(from, end, cons);
+	}
+
+	uint32 innerLength() const;
+	mtpTypeId type() const;
+	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
+	void write(mtpBuffer &to) const;
+
+	typedef void ResponseType;
+
+private:
+	explicit MTPchannelParticipantRole(mtpTypeId type);
+
+	friend MTPchannelParticipantRole MTP_channelRoleEmpty();
+	friend MTPchannelParticipantRole MTP_channelRoleModerator();
+	friend MTPchannelParticipantRole MTP_channelRolePublisher();
+
+	mtpTypeId _type;
+};
+typedef MTPBoxed<MTPchannelParticipantRole> MTPChannelParticipantRole;
+
 class MTPmessages_channelParticipants : private mtpDataOwner {
 public:
 	MTPmessages_channelParticipants();
@@ -9705,6 +9839,16 @@ public:
 	MTPstring vtitle;
 };
 
+class MTPDmessageActionChannelToggleComments : public mtpDataImpl<MTPDmessageActionChannelToggleComments> {
+public:
+	MTPDmessageActionChannelToggleComments() {
+	}
+	MTPDmessageActionChannelToggleComments(MTPBool _enabled) : venabled(_enabled) {
+	}
+
+	MTPBool venabled;
+};
+
 class MTPDdialog : public mtpDataImpl<MTPDdialog> {
 public:
 	MTPDdialog() {
@@ -12065,6 +12209,40 @@ public:
 	MTPint vdate;
 };
 
+class MTPDchannelParticipantModerator : public mtpDataImpl<MTPDchannelParticipantModerator> {
+public:
+	MTPDchannelParticipantModerator() {
+	}
+	MTPDchannelParticipantModerator(MTPint _user_id, MTPint _inviter_id, MTPint _date) : vuser_id(_user_id), vinviter_id(_inviter_id), vdate(_date) {
+	}
+
+	MTPint vuser_id;
+	MTPint vinviter_id;
+	MTPint vdate;
+};
+
+class MTPDchannelParticipantPublisher : public mtpDataImpl<MTPDchannelParticipantPublisher> {
+public:
+	MTPDchannelParticipantPublisher() {
+	}
+	MTPDchannelParticipantPublisher(MTPint _user_id, MTPint _inviter_id, MTPint _date) : vuser_id(_user_id), vinviter_id(_inviter_id), vdate(_date) {
+	}
+
+	MTPint vuser_id;
+	MTPint vinviter_id;
+	MTPint vdate;
+};
+
+class MTPDchannelParticipantCreator : public mtpDataImpl<MTPDchannelParticipantCreator> {
+public:
+	MTPDchannelParticipantCreator() {
+	}
+	MTPDchannelParticipantCreator(MTPint _user_id) : vuser_id(_user_id) {
+	}
+
+	MTPint vuser_id;
+};
+
 class MTPDmessages_channelParticipants : public mtpDataImpl<MTPDmessages_channelParticipants> {
 public:
 	MTPDmessages_channelParticipants() {
@@ -16889,6 +17067,48 @@ public:
 	}
 };
 
+class MTPmessages_toggleChannelComments { // RPC method 'messages.toggleChannelComments'
+public:
+	MTPInputChat vchat_id;
+	MTPBool venabled;
+
+	MTPmessages_toggleChannelComments() {
+	}
+	MTPmessages_toggleChannelComments(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_toggleChannelComments) {
+		read(from, end, cons);
+	}
+	MTPmessages_toggleChannelComments(const MTPInputChat &_chat_id, MTPBool _enabled) : vchat_id(_chat_id), venabled(_enabled) {
+	}
+
+	uint32 innerLength() const {
+		return vchat_id.innerLength() + venabled.innerLength();
+	}
+	mtpTypeId type() const {
+		return mtpc_messages_toggleChannelComments;
+	}
+	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_toggleChannelComments) {
+		vchat_id.read(from, end);
+		venabled.read(from, end);
+	}
+	void write(mtpBuffer &to) const {
+		vchat_id.write(to);
+		venabled.write(to);
+	}
+
+	typedef MTPUpdates ResponseType;
+};
+class MTPmessages_ToggleChannelComments : public MTPBoxed<MTPmessages_toggleChannelComments> {
+public:
+	MTPmessages_ToggleChannelComments() {
+	}
+	MTPmessages_ToggleChannelComments(const MTPmessages_toggleChannelComments &v) : MTPBoxed<MTPmessages_toggleChannelComments>(v) {
+	}
+	MTPmessages_ToggleChannelComments(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_toggleChannelComments>(from, end, cons) {
+	}
+	MTPmessages_ToggleChannelComments(const MTPInputChat &_chat_id, MTPBool _enabled) : MTPBoxed<MTPmessages_toggleChannelComments>(MTPmessages_toggleChannelComments(_chat_id, _enabled)) {
+	}
+};
+
 class MTPmessages_deleteChannelMessages { // RPC method 'messages.deleteChannelMessages'
 public:
 	MTPInputPeer vpeer;
@@ -17102,6 +17322,7 @@ public:
 class MTPmessages_getChannelParticipants { // RPC method 'messages.getChannelParticipants'
 public:
 	MTPInputChat vchat_id;
+	MTPChannelParticipantsFilter vfilter;
 	MTPint voffset;
 	MTPint vlimit;
 
@@ -17110,22 +17331,24 @@ public:
 	MTPmessages_getChannelParticipants(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getChannelParticipants) {
 		read(from, end, cons);
 	}
-	MTPmessages_getChannelParticipants(const MTPInputChat &_chat_id, MTPint _offset, MTPint _limit) : vchat_id(_chat_id), voffset(_offset), vlimit(_limit) {
+	MTPmessages_getChannelParticipants(const MTPInputChat &_chat_id, const MTPChannelParticipantsFilter &_filter, MTPint _offset, MTPint _limit) : vchat_id(_chat_id), vfilter(_filter), voffset(_offset), vlimit(_limit) {
 	}
 
 	uint32 innerLength() const {
-		return vchat_id.innerLength() + voffset.innerLength() + vlimit.innerLength();
+		return vchat_id.innerLength() + vfilter.innerLength() + voffset.innerLength() + vlimit.innerLength();
 	}
 	mtpTypeId type() const {
 		return mtpc_messages_getChannelParticipants;
 	}
 	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getChannelParticipants) {
 		vchat_id.read(from, end);
+		vfilter.read(from, end);
 		voffset.read(from, end);
 		vlimit.read(from, end);
 	}
 	void write(mtpBuffer &to) const {
 		vchat_id.write(to);
+		vfilter.write(to);
 		voffset.write(to);
 		vlimit.write(to);
 	}
@@ -17140,7 +17363,52 @@ public:
 	}
 	MTPmessages_GetChannelParticipants(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_getChannelParticipants>(from, end, cons) {
 	}
-	MTPmessages_GetChannelParticipants(const MTPInputChat &_chat_id, MTPint _offset, MTPint _limit) : MTPBoxed<MTPmessages_getChannelParticipants>(MTPmessages_getChannelParticipants(_chat_id, _offset, _limit)) {
+	MTPmessages_GetChannelParticipants(const MTPInputChat &_chat_id, const MTPChannelParticipantsFilter &_filter, MTPint _offset, MTPint _limit) : MTPBoxed<MTPmessages_getChannelParticipants>(MTPmessages_getChannelParticipants(_chat_id, _filter, _offset, _limit)) {
+	}
+};
+
+class MTPmessages_editChatAdmin { // RPC method 'messages.editChatAdmin'
+public:
+	MTPInputChat vchat_id;
+	MTPInputUser vuser_id;
+	MTPChannelParticipantRole vrole;
+
+	MTPmessages_editChatAdmin() {
+	}
+	MTPmessages_editChatAdmin(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_editChatAdmin) {
+		read(from, end, cons);
+	}
+	MTPmessages_editChatAdmin(const MTPInputChat &_chat_id, const MTPInputUser &_user_id, const MTPChannelParticipantRole &_role) : vchat_id(_chat_id), vuser_id(_user_id), vrole(_role) {
+	}
+
+	uint32 innerLength() const {
+		return vchat_id.innerLength() + vuser_id.innerLength() + vrole.innerLength();
+	}
+	mtpTypeId type() const {
+		return mtpc_messages_editChatAdmin;
+	}
+	void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_editChatAdmin) {
+		vchat_id.read(from, end);
+		vuser_id.read(from, end);
+		vrole.read(from, end);
+	}
+	void write(mtpBuffer &to) const {
+		vchat_id.write(to);
+		vuser_id.write(to);
+		vrole.write(to);
+	}
+
+	typedef MTPBool ResponseType;
+};
+class MTPmessages_EditChatAdmin : public MTPBoxed<MTPmessages_editChatAdmin> {
+public:
+	MTPmessages_EditChatAdmin() {
+	}
+	MTPmessages_EditChatAdmin(const MTPmessages_editChatAdmin &v) : MTPBoxed<MTPmessages_editChatAdmin>(v) {
+	}
+	MTPmessages_EditChatAdmin(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_editChatAdmin>(from, end, cons) {
+	}
+	MTPmessages_EditChatAdmin(const MTPInputChat &_chat_id, const MTPInputUser &_user_id, const MTPChannelParticipantRole &_role) : MTPBoxed<MTPmessages_editChatAdmin>(MTPmessages_editChatAdmin(_chat_id, _user_id, _role)) {
 	}
 };
 
@@ -21118,6 +21386,10 @@ inline uint32 MTPmessageAction::innerLength() const {
 			const MTPDmessageActionChannelCreate &v(c_messageActionChannelCreate());
 			return v.vtitle.innerLength();
 		}
+		case mtpc_messageActionChannelToggleComments: {
+			const MTPDmessageActionChannelToggleComments &v(c_messageActionChannelToggleComments());
+			return v.venabled.innerLength();
+		}
 	}
 	return 0;
 }
@@ -21166,6 +21438,11 @@ inline void MTPmessageAction::read(const mtpPrime *&from, const mtpPrime *end, m
 			MTPDmessageActionChannelCreate &v(_messageActionChannelCreate());
 			v.vtitle.read(from, end);
 		} break;
+		case mtpc_messageActionChannelToggleComments: _type = cons; {
+			if (!data) setData(new MTPDmessageActionChannelToggleComments());
+			MTPDmessageActionChannelToggleComments &v(_messageActionChannelToggleComments());
+			v.venabled.read(from, end);
+		} break;
 		default: throw mtpErrorUnexpected(cons, "MTPmessageAction");
 	}
 }
@@ -21200,6 +21477,10 @@ inline void MTPmessageAction::write(mtpBuffer &to) const {
 			const MTPDmessageActionChannelCreate &v(c_messageActionChannelCreate());
 			v.vtitle.write(to);
 		} break;
+		case mtpc_messageActionChannelToggleComments: {
+			const MTPDmessageActionChannelToggleComments &v(c_messageActionChannelToggleComments());
+			v.venabled.write(to);
+		} break;
 	}
 }
 inline MTPmessageAction::MTPmessageAction(mtpTypeId type) : mtpDataOwner(0), _type(type) {
@@ -21213,6 +21494,7 @@ inline MTPmessageAction::MTPmessageAction(mtpTypeId type) : mtpDataOwner(0), _ty
 		case mtpc_messageActionChatDeleteUser: setData(new MTPDmessageActionChatDeleteUser()); break;
 		case mtpc_messageActionChatJoinedByLink: setData(new MTPDmessageActionChatJoinedByLink()); break;
 		case mtpc_messageActionChannelCreate: setData(new MTPDmessageActionChannelCreate()); break;
+		case mtpc_messageActionChannelToggleComments: setData(new MTPDmessageActionChannelToggleComments()); break;
 		default: throw mtpErrorBadTypeId(type, "MTPmessageAction");
 	}
 }
@@ -21230,6 +21512,8 @@ inline MTPmessageAction::MTPmessageAction(MTPDmessageActionChatJoinedByLink *_da
 }
 inline MTPmessageAction::MTPmessageAction(MTPDmessageActionChannelCreate *_data) : mtpDataOwner(_data), _type(mtpc_messageActionChannelCreate) {
 }
+inline MTPmessageAction::MTPmessageAction(MTPDmessageActionChannelToggleComments *_data) : mtpDataOwner(_data), _type(mtpc_messageActionChannelToggleComments) {
+}
 inline MTPmessageAction MTP_messageActionEmpty() {
 	return MTPmessageAction(mtpc_messageActionEmpty);
 }
@@ -21257,6 +21541,9 @@ inline MTPmessageAction MTP_messageActionChatJoinedByLink(MTPint _inviter_id) {
 inline MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title) {
 	return MTPmessageAction(new MTPDmessageActionChannelCreate(_title));
 }
+inline MTPmessageAction MTP_messageActionChannelToggleComments(MTPBool _enabled) {
+	return MTPmessageAction(new MTPDmessageActionChannelToggleComments(_enabled));
+}
 
 inline uint32 MTPdialog::innerLength() const {
 	switch (_type) {
@@ -27515,36 +27802,187 @@ inline MTPcontacts_resolvedPeer MTP_contacts_resolvedPeer(const MTPPeer &_peer,
 	return MTPcontacts_resolvedPeer(new MTPDcontacts_resolvedPeer(_peer, _chats, _users));
 }
 
-inline MTPchannelParticipant::MTPchannelParticipant() : mtpDataOwner(new MTPDchannelParticipant()) {
-}
-
 inline uint32 MTPchannelParticipant::innerLength() const {
-	const MTPDchannelParticipant &v(c_channelParticipant());
-	return v.vuser_id.innerLength() + v.vinviter_id.innerLength() + v.vdate.innerLength();
+	switch (_type) {
+		case mtpc_channelParticipant: {
+			const MTPDchannelParticipant &v(c_channelParticipant());
+			return v.vuser_id.innerLength() + v.vinviter_id.innerLength() + v.vdate.innerLength();
+		}
+		case mtpc_channelParticipantModerator: {
+			const MTPDchannelParticipantModerator &v(c_channelParticipantModerator());
+			return v.vuser_id.innerLength() + v.vinviter_id.innerLength() + v.vdate.innerLength();
+		}
+		case mtpc_channelParticipantPublisher: {
+			const MTPDchannelParticipantPublisher &v(c_channelParticipantPublisher());
+			return v.vuser_id.innerLength() + v.vinviter_id.innerLength() + v.vdate.innerLength();
+		}
+		case mtpc_channelParticipantCreator: {
+			const MTPDchannelParticipantCreator &v(c_channelParticipantCreator());
+			return v.vuser_id.innerLength();
+		}
+	}
+	return 0;
 }
 inline mtpTypeId MTPchannelParticipant::type() const {
-	return mtpc_channelParticipant;
+	if (!_type) throw mtpErrorUninitialized();
+	return _type;
 }
 inline void MTPchannelParticipant::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
-	if (cons != mtpc_channelParticipant) throw mtpErrorUnexpected(cons, "MTPchannelParticipant");
-
-	if (!data) setData(new MTPDchannelParticipant());
-	MTPDchannelParticipant &v(_channelParticipant());
-	v.vuser_id.read(from, end);
-	v.vinviter_id.read(from, end);
-	v.vdate.read(from, end);
+	if (cons != _type) setData(0);
+	switch (cons) {
+		case mtpc_channelParticipant: _type = cons; {
+			if (!data) setData(new MTPDchannelParticipant());
+			MTPDchannelParticipant &v(_channelParticipant());
+			v.vuser_id.read(from, end);
+			v.vinviter_id.read(from, end);
+			v.vdate.read(from, end);
+		} break;
+		case mtpc_channelParticipantModerator: _type = cons; {
+			if (!data) setData(new MTPDchannelParticipantModerator());
+			MTPDchannelParticipantModerator &v(_channelParticipantModerator());
+			v.vuser_id.read(from, end);
+			v.vinviter_id.read(from, end);
+			v.vdate.read(from, end);
+		} break;
+		case mtpc_channelParticipantPublisher: _type = cons; {
+			if (!data) setData(new MTPDchannelParticipantPublisher());
+			MTPDchannelParticipantPublisher &v(_channelParticipantPublisher());
+			v.vuser_id.read(from, end);
+			v.vinviter_id.read(from, end);
+			v.vdate.read(from, end);
+		} break;
+		case mtpc_channelParticipantCreator: _type = cons; {
+			if (!data) setData(new MTPDchannelParticipantCreator());
+			MTPDchannelParticipantCreator &v(_channelParticipantCreator());
+			v.vuser_id.read(from, end);
+		} break;
+		default: throw mtpErrorUnexpected(cons, "MTPchannelParticipant");
+	}
 }
 inline void MTPchannelParticipant::write(mtpBuffer &to) const {
-	const MTPDchannelParticipant &v(c_channelParticipant());
-	v.vuser_id.write(to);
-	v.vinviter_id.write(to);
-	v.vdate.write(to);
+	switch (_type) {
+		case mtpc_channelParticipant: {
+			const MTPDchannelParticipant &v(c_channelParticipant());
+			v.vuser_id.write(to);
+			v.vinviter_id.write(to);
+			v.vdate.write(to);
+		} break;
+		case mtpc_channelParticipantModerator: {
+			const MTPDchannelParticipantModerator &v(c_channelParticipantModerator());
+			v.vuser_id.write(to);
+			v.vinviter_id.write(to);
+			v.vdate.write(to);
+		} break;
+		case mtpc_channelParticipantPublisher: {
+			const MTPDchannelParticipantPublisher &v(c_channelParticipantPublisher());
+			v.vuser_id.write(to);
+			v.vinviter_id.write(to);
+			v.vdate.write(to);
+		} break;
+		case mtpc_channelParticipantCreator: {
+			const MTPDchannelParticipantCreator &v(c_channelParticipantCreator());
+			v.vuser_id.write(to);
+		} break;
+	}
 }
-inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipant *_data) : mtpDataOwner(_data) {
+inline MTPchannelParticipant::MTPchannelParticipant(mtpTypeId type) : mtpDataOwner(0), _type(type) {
+	switch (type) {
+		case mtpc_channelParticipant: setData(new MTPDchannelParticipant()); break;
+		case mtpc_channelParticipantModerator: setData(new MTPDchannelParticipantModerator()); break;
+		case mtpc_channelParticipantPublisher: setData(new MTPDchannelParticipantPublisher()); break;
+		case mtpc_channelParticipantCreator: setData(new MTPDchannelParticipantCreator()); break;
+		default: throw mtpErrorBadTypeId(type, "MTPchannelParticipant");
+	}
+}
+inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipant *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipant) {
+}
+inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantModerator *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantModerator) {
+}
+inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantPublisher *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantPublisher) {
+}
+inline MTPchannelParticipant::MTPchannelParticipant(MTPDchannelParticipantCreator *_data) : mtpDataOwner(_data), _type(mtpc_channelParticipantCreator) {
 }
 inline MTPchannelParticipant MTP_channelParticipant(MTPint _user_id, MTPint _inviter_id, MTPint _date) {
 	return MTPchannelParticipant(new MTPDchannelParticipant(_user_id, _inviter_id, _date));
 }
+inline MTPchannelParticipant MTP_channelParticipantModerator(MTPint _user_id, MTPint _inviter_id, MTPint _date) {
+	return MTPchannelParticipant(new MTPDchannelParticipantModerator(_user_id, _inviter_id, _date));
+}
+inline MTPchannelParticipant MTP_channelParticipantPublisher(MTPint _user_id, MTPint _inviter_id, MTPint _date) {
+	return MTPchannelParticipant(new MTPDchannelParticipantPublisher(_user_id, _inviter_id, _date));
+}
+inline MTPchannelParticipant MTP_channelParticipantCreator(MTPint _user_id) {
+	return MTPchannelParticipant(new MTPDchannelParticipantCreator(_user_id));
+}
+
+inline uint32 MTPchannelParticipantsFilter::innerLength() const {
+	return 0;
+}
+inline mtpTypeId MTPchannelParticipantsFilter::type() const {
+	if (!_type) throw mtpErrorUninitialized();
+	return _type;
+}
+inline void MTPchannelParticipantsFilter::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
+	switch (cons) {
+		case mtpc_channelParticipantsRecent: _type = cons; break;
+		case mtpc_channelParticipantsAdmins: _type = cons; break;
+		default: throw mtpErrorUnexpected(cons, "MTPchannelParticipantsFilter");
+	}
+}
+inline void MTPchannelParticipantsFilter::write(mtpBuffer &to) const {
+	switch (_type) {
+	}
+}
+inline MTPchannelParticipantsFilter::MTPchannelParticipantsFilter(mtpTypeId type) : _type(type) {
+	switch (type) {
+		case mtpc_channelParticipantsRecent: break;
+		case mtpc_channelParticipantsAdmins: break;
+		default: throw mtpErrorBadTypeId(type, "MTPchannelParticipantsFilter");
+	}
+}
+inline MTPchannelParticipantsFilter MTP_channelParticipantsRecent() {
+	return MTPchannelParticipantsFilter(mtpc_channelParticipantsRecent);
+}
+inline MTPchannelParticipantsFilter MTP_channelParticipantsAdmins() {
+	return MTPchannelParticipantsFilter(mtpc_channelParticipantsAdmins);
+}
+
+inline uint32 MTPchannelParticipantRole::innerLength() const {
+	return 0;
+}
+inline mtpTypeId MTPchannelParticipantRole::type() const {
+	if (!_type) throw mtpErrorUninitialized();
+	return _type;
+}
+inline void MTPchannelParticipantRole::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
+	switch (cons) {
+		case mtpc_channelRoleEmpty: _type = cons; break;
+		case mtpc_channelRoleModerator: _type = cons; break;
+		case mtpc_channelRolePublisher: _type = cons; break;
+		default: throw mtpErrorUnexpected(cons, "MTPchannelParticipantRole");
+	}
+}
+inline void MTPchannelParticipantRole::write(mtpBuffer &to) const {
+	switch (_type) {
+	}
+}
+inline MTPchannelParticipantRole::MTPchannelParticipantRole(mtpTypeId type) : _type(type) {
+	switch (type) {
+		case mtpc_channelRoleEmpty: break;
+		case mtpc_channelRoleModerator: break;
+		case mtpc_channelRolePublisher: break;
+		default: throw mtpErrorBadTypeId(type, "MTPchannelParticipantRole");
+	}
+}
+inline MTPchannelParticipantRole MTP_channelRoleEmpty() {
+	return MTPchannelParticipantRole(mtpc_channelRoleEmpty);
+}
+inline MTPchannelParticipantRole MTP_channelRoleModerator() {
+	return MTPchannelParticipantRole(mtpc_channelRoleModerator);
+}
+inline MTPchannelParticipantRole MTP_channelRolePublisher() {
+	return MTPchannelParticipantRole(mtpc_channelRolePublisher);
+}
 
 inline MTPmessages_channelParticipants::MTPmessages_channelParticipants() : mtpDataOwner(new MTPDmessages_channelParticipants()) {
 }
diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl
index ff463de04..9236eb9c8 100644
--- a/Telegram/SourceFiles/mtproto/scheme.tl
+++ b/Telegram/SourceFiles/mtproto/scheme.tl
@@ -257,6 +257,7 @@ messageActionChatAddUser#5e3cfc4b user_id:int = MessageAction;
 messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction;
 messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction;
 messageActionChannelCreate#95d2ac92 title:string = MessageAction;
+messageActionChannelToggleComments#f2863903 enabled:Bool = MessageAction;
 
 dialog#c1dd804a peer:Peer top_message:int read_inbox_max_id:int unread_count:int notify_settings:PeerNotifySettings = Dialog;
 dialogChannel#5b8496b2 peer:Peer top_message:int top_important_message:int read_inbox_max_id:int unread_count:int unread_important_count:int notify_settings:PeerNotifySettings pts:int = Dialog;
@@ -602,6 +603,16 @@ channelMessagesFilterCollapsed#fa01232e = ChannelMessagesFilter;
 contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;
 
 channelParticipant#506116ea user_id:int inviter_id:int date:int = ChannelParticipant;
+channelParticipantModerator#91057fef user_id:int inviter_id:int date:int = ChannelParticipant;
+channelParticipantPublisher#375d616 user_id:int inviter_id:int date:int = ChannelParticipant;
+channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant;
+
+channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
+channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter;
+
+channelRoleEmpty#b285a0c6 = ChannelParticipantRole;
+channelRoleModerator#9618d975 = ChannelParticipantRole;
+channelRolePublisher#515b5530 = ChannelParticipantRole;
 
 messages.channelParticipants#d6891de1 count:int participants:Vector<ChannelParticipant> users:Vector<User> = messages.ChannelParticipants;
 
@@ -718,12 +729,14 @@ messages.getChannelDialogs#92689583 offset:int limit:int = messages.Dialogs;
 messages.getImportantHistory#24af43a5 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
 messages.readChannelHistory#36a1210e peer:InputPeer max_id:int = Bool;
 messages.createChannel#7f44d2c3 flags:# title:string about:string users:Vector<InputUser> = Updates;
+messages.toggleChannelComments#b405b8af chat_id:InputChat enabled:Bool = Updates;
 messages.deleteChannelMessages#9995a84f peer:InputPeer id:Vector<int> = messages.AffectedMessages;
 messages.getChannelMessages#5f46b265 peer:InputPeer id:Vector<int> = messages.Messages;
 messages.incrementMessagesViews#91ffd479 peer:InputPeer id:Vector<int> = Bool;
 messages.getMessagesViews#9abfbbe1 peer:InputPeer id:Vector<int> = Vector<int>;
 messages.editChatAbout#8a969b93 chat_id:InputChat about:string = Bool;
-messages.getChannelParticipants#4a771976 chat_id:InputChat offset:int limit:int = messages.ChannelParticipants;
+messages.getChannelParticipants#38a1db31 chat_id:InputChat filter:ChannelParticipantsFilter offset:int limit:int = messages.ChannelParticipants;
+messages.editChatAdmin#62394070 chat_id:InputChat user_id:InputUser role:ChannelParticipantRole = Bool;
 messages.checkChannelUsername#e6d2d8f4 chat_id:InputChat username:string = Bool;
 messages.updateChannelUsername#ce2e9587 chat_id:InputChat username:string = Bool;
 
diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp
index dbb2f19e2..0c4bbd629 100644
--- a/Telegram/SourceFiles/passcodewidget.cpp
+++ b/Telegram/SourceFiles/passcodewidget.cpp
@@ -139,7 +139,7 @@ bool PasscodeWidget::animStep(float64 ms) {
 		_animCache = _bgAnimCache = QPixmap();
 
 		showAll();
-		setInnerFocus();
+		if (App::wnd()) App::wnd()->setInnerFocus();
 	} else {
 		a_bgCoord.update(dt1, st::introHideFunc);
 		a_bgAlpha.update(dt1, st::introAlphaHideFunc);
diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp
index 2859c9067..cf0ff373c 100644
--- a/Telegram/SourceFiles/pspecific_linux.cpp
+++ b/Telegram/SourceFiles/pspecific_linux.cpp
@@ -1142,7 +1142,7 @@ void psOpenFile(const QString &name, bool openWith) {
 }
 
 void psShowInFolder(const QString &name) {
-    App::wnd()->layerHidden();
+    App::wnd()->hideLayer(true);
     system((qsl("xdg-open ") + escapeShell(QFileInfo(name).absoluteDir().absolutePath())).toUtf8().constData());
 }
 
diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp
index 6863af5ea..dedd5c33f 100644
--- a/Telegram/SourceFiles/pspecific_mac.cpp
+++ b/Telegram/SourceFiles/pspecific_mac.cpp
@@ -65,7 +65,7 @@ void MacPrivate::notifyClicked(unsigned long long peer, int msgid) {
 
     App::wnd()->showFromTray();
 	if (App::passcoded()) {
-		App::wnd()->passcodeWidget()->setInnerFocus();
+		App::wnd()->setInnerFocus();
 		App::wnd()->notifyClear();
 	} else {
 		App::wnd()->hideSettings();
diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp
index a4039296d..51feeda62 100644
--- a/Telegram/SourceFiles/pspecific_wnd.cpp
+++ b/Telegram/SourceFiles/pspecific_wnd.cpp
@@ -2592,7 +2592,7 @@ public:
 
 			App::wnd()->showFromTray();
 			if (App::passcoded()) {
-				App::wnd()->passcodeWidget()->setInnerFocus();
+				App::wnd()->setInnerFocus();
 				App::wnd()->notifyClear();
 			} else {
 				App::wnd()->hideSettings();
diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp
index 41a1ed9a6..e96490a3a 100644
--- a/Telegram/SourceFiles/settingswidget.cpp
+++ b/Telegram/SourceFiles/settingswidget.cpp
@@ -1523,6 +1523,7 @@ void SettingsInner::onViewEmojis() {
 void SettingsInner::onEnterSend() {
 	if (_enterSend.checked()) {
 		cSetCtrlEnter(false);
+		if (App::main()) App::main()->ctrlEnterSubmitUpdated();
 		Local::writeUserSettings();
 	}
 }
@@ -1530,6 +1531,7 @@ void SettingsInner::onEnterSend() {
 void SettingsInner::onCtrlEnterSend() {
 	if (_ctrlEnterSend.checked()) {
 		cSetCtrlEnter(true);
+		if (App::main()) App::main()->ctrlEnterSubmitUpdated();
 		Local::writeUserSettings();
 	}
 }
diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp
index 4a4841b90..c8a315278 100644
--- a/Telegram/SourceFiles/structs.cpp
+++ b/Telegram/SourceFiles/structs.cpp
@@ -96,7 +96,19 @@ PeerData::PeerData(const PeerId &id) : id(id), lnk(new PeerLink(this))
 }
 
 void PeerData::updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername) {
-	if (name == newName && (!isUser() || (asUser()->nameOrPhone == newNameOrPhone && asUser()->username == newUsername)) && nameVersion > 0) return;
+	if (name == newName && nameVersion > 0) {
+		if (isUser()) {
+			if (asUser()->nameOrPhone == newNameOrPhone && asUser()->username == newUsername) {
+				return;
+			}
+		} else if (isChannel()) {
+			if (asChannel()->username == newUsername) {
+				return;
+			}
+		} else if (isChat()) {
+			return;
+		}
+	}
 
 	++nameVersion;
 	name = newName;
diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp
index 5c90d402f..bfa319ccd 100644
--- a/Telegram/SourceFiles/window.cpp
+++ b/Telegram/SourceFiles/window.cpp
@@ -286,7 +286,7 @@ void NotifyWindow::mousePressEvent(QMouseEvent *e) {
 	} else if (history) {
 		App::wnd()->showFromTray();
 		if (App::passcoded()) {
-			App::wnd()->passcodeWidget()->setInnerFocus();
+			App::wnd()->setInnerFocus();
 			App::wnd()->notifyClear();
 		} else {
 			App::wnd()->hideSettings();
@@ -461,7 +461,7 @@ QWidget *Window::filedialogParent() {
 }
 
 void Window::clearWidgets() {
-	layerHidden();
+	hideLayer(true);
 	if (_passcode) {
 		_passcode->hide();
 		_passcode->deleteLater();
@@ -528,7 +528,7 @@ void Window::setupPasscode(bool anim) {
 	if (anim) {
 		_passcode->animShow(bg);
 	} else {
-		_passcode->setInnerFocus();
+		setInnerFocus();
 	}
 	_shouldLockAt = 0;
 	notifyUpdateAll();
@@ -746,34 +746,42 @@ void Window::showPhoto(const PhotoLink *lnk, HistoryItem *item) {
 }
 
 void Window::showPhoto(PhotoData *photo, HistoryItem *item) {
-	layerHidden();
+	hideLayer(true);
 	_mediaView->showPhoto(photo, item);
 	_mediaView->activateWindow();
 	_mediaView->setFocus();
 }
 
 void Window::showPhoto(PhotoData *photo, PeerData *peer) {
-	layerHidden();
+	hideLayer(true);
 	_mediaView->showPhoto(photo, peer);
 	_mediaView->activateWindow();
 	_mediaView->setFocus();
 }
 
 void Window::showDocument(DocumentData *doc, HistoryItem *item) {
-	layerHidden();
+	hideLayer(true);
 	_mediaView->showDocument(doc, item);
 	_mediaView->activateWindow();
 	_mediaView->setFocus();
 }
 
 void Window::showLayer(LayeredWidget *w, bool fast) {
-	layerHidden();
+	hideLayer(true);
 	layerBG = new BackgroundWidget(this, w);
 	if (fast) {
 		layerBG->showFast();
 	}
 }
 
+void Window::replaceLayer(LayeredWidget *w) {
+	if (layerBG) {
+		layerBG->replaceInner(w);
+	} else {
+		layerBG = new BackgroundWidget(this, w);
+	}
+}
+
 void Window::showConnecting(const QString &text, const QString &reconnect) {
 	if (_connecting) {
 		_connecting->set(text, reconnect);
@@ -798,19 +806,12 @@ void Window::hideConnecting() {
 	if (settings) settings->update();
 }
 
-void Window::replaceLayer(LayeredWidget *w) {
-	if (layerBG) {
-		layerBG->replaceInner(w);
-	} else {
-		layerBG = new BackgroundWidget(this, w);
-	}
-}
-
 void Window::hideLayer(bool fast) {
 	if (layerBG) {
 		layerBG->onClose();
 		if (fast) {
 			layerBG->hide();
+			layerBG->deleteLater();
 			layerBG = 0;
 		}
 	}
@@ -854,7 +855,9 @@ void Window::hideMediaview() {
 }
 
 void Window::setInnerFocus() {
-	if (_passcode) {
+	if (layerBG && layerBG->canSetFocus()) {
+		layerBG->setInnerFocus();
+	} else if (_passcode) {
 		_passcode->setInnerFocus();
 	} else if (settings) {
 		settings->setInnerFocus();