New design of a chat invite link import box with title, photo, users.

This commit is contained in:
John Preston 2016-07-08 19:59:46 +03:00
parent cd36d367ed
commit 66e2fce8d5
11 changed files with 572 additions and 372 deletions

View File

@ -602,6 +602,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "Do you want to join the channel «{title}»?"; "lng_group_invite_want_join_channel" = "Do you want to join the channel «{title}»?";
"lng_group_invite_join" = "Join"; "lng_group_invite_join" = "Join";
"lng_group_invite_members" = "{count:_not_used_|# member|# members}, among them:";
"lng_group_invite_link" = "Invite link:"; "lng_group_invite_link" = "Invite link:";
"lng_group_invite_create" = "Create an invite link"; "lng_group_invite_create" = "Create an invite link";
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; "lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";

View File

@ -383,9 +383,7 @@ namespace {
return (online > now); return (online > now);
} }
UserData *feedUsers(const MTPVector<MTPUser> &users) { UserData *feedUser(const MTPUser &user) {
UserData *result = nullptr;
for_const (auto &user, users.c_vector().v) {
UserData *data = nullptr; UserData *data = nullptr;
bool wasContact = false, minimal = false; bool wasContact = false, minimal = false;
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty(); const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
@ -395,7 +393,7 @@ namespace {
switch (user.type()) { switch (user.type()) {
case mtpc_userEmpty: { case mtpc_userEmpty: {
const auto &d(user.c_userEmpty()); auto &d(user.c_userEmpty());
PeerId peer(peerFromUser(d.vid.v)); PeerId peer(peerFromUser(d.vid.v));
data = App::user(peer); data = App::user(peer);
@ -416,7 +414,7 @@ namespace {
if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact; if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact;
} break; } break;
case mtpc_user: { case mtpc_user: {
const auto &d(user.c_user()); auto &d(user.c_user());
minimal = d.is_min(); minimal = d.is_min();
PeerId peer(peerFromUser(d.vid.v)); PeerId peer(peerFromUser(d.vid.v));
@ -518,7 +516,9 @@ namespace {
} break; } break;
} }
if (!data) continue; if (!data) {
return nullptr;
}
if (minimal) { if (minimal) {
if (data->loadedStatus == PeerData::NotLoaded) { if (data->loadedStatus == PeerData::NotLoaded) {
@ -559,15 +559,21 @@ namespace {
Notify::peerUpdatedDelayed(update); Notify::peerUpdatedDelayed(update);
} }
} }
result = data; return data;
}
UserData *feedUsers(const MTPVector<MTPUser> &users) {
UserData *result = nullptr;
for_const (auto &user, users.c_vector().v) {
if (auto feededUser = feedUser(user)) {
result = feededUser;
}
} }
return result; return result;
} }
PeerData *feedChats(const MTPVector<MTPChat> &chats) { PeerData *feedChat(const MTPChat &chat) {
PeerData *result = nullptr;
for_const (auto &chat, chats.c_vector().v) {
PeerData *data = nullptr; PeerData *data = nullptr;
bool minimal = false; bool minimal = false;
@ -667,7 +673,7 @@ namespace {
if (minimal) { if (minimal) {
data = App::channelLoaded(peerId); data = App::channelLoaded(peerId);
if (!data) { if (!data) {
continue; // minimal is not loaded, need to make getDifference return nullptr; // minimal is not loaded, need to make getDifference
} }
} else { } else {
data = App::channel(peerId); data = App::channel(peerId);
@ -757,7 +763,9 @@ namespace {
} }
} break; } break;
} }
if (!data) continue; if (!data) {
return nullptr;
}
if (minimal) { if (minimal) {
if (data->loadedStatus == PeerData::NotLoaded) { if (data->loadedStatus == PeerData::NotLoaded) {
@ -773,7 +781,15 @@ namespace {
Notify::peerUpdatedDelayed(update); Notify::peerUpdatedDelayed(update);
} }
} }
result = data; return data;
}
PeerData *feedChats(const MTPVector<MTPChat> &chats) {
PeerData *result = nullptr;
for_const (auto &chat, chats.c_vector().v) {
if (auto feededChat = feedChat(chat)) {
result = feededChat;
}
} }
return result; return result;
} }

View File

@ -65,7 +65,9 @@ namespace App {
bool onlineColorUse(UserData *user, TimeId now); bool onlineColorUse(UserData *user, TimeId now);
bool onlineColorUse(TimeId online, TimeId now); bool onlineColorUse(TimeId online, TimeId now);
UserData *feedUser(const MTPUser &user);
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
PeerData *feedChat(const MTPChat &chat);
PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated = true); void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated = true);

View File

@ -0,0 +1,50 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
using "basic.style";
confirmInviteTitle: flatLabel(labelDefFlat) {
font: font(16px semibold);
align: align(center);
width: 320px;
maxHeight: 24px;
textFg: #333333;
}
confirmInviteStatus: flatLabel(labelDefFlat) {
font: font(boxFontSize);
align: align(center);
width: 320px;
maxHeight: 20px;
textFg: windowSubTextFg;
}
confirmInviteTitleTop: 106px;
confirmInvitePhotoSize: 76px;
confirmInvitePhotoTop: 20px;
confirmInviteStatusTop: 136px;
confirmInviteUserHeight: 80px;
confirmInviteUserPhotoSize: 56px;
confirmInviteUserPhotoTop: 166px;
confirmInviteUserName: flatLabel(labelDefFlat) {
font: normalFont;
align: align(center);
width: 66px;
maxHeight: 20px;
}
confirmInviteUserNameTop: 227px;

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "apiwrap.h" #include "apiwrap.h"
#include "application.h" #include "application.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "styles/style_boxes.h"
TextParseOptions _confirmBoxTextOptions = { TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseRichText, // flags TextParseLinks | TextParseMultiline | TextParseRichText, // flags
@ -520,3 +521,86 @@ void KickMemberBox::onConfirm() {
App::api()->kickParticipant(channel, _member); App::api()->kickParticipant(channel, _member);
} }
} }
ConfirmInviteBox::ConfirmInviteBox(const QString &title, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants) : AbstractBox()
, _title(this, st::confirmInviteTitle)
, _status(this, st::confirmInviteStatus)
, _photo(chatDefPhoto(0))
, _participants(participants)
, _join(this, lang(lng_group_invite_join), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
if (_participants.size() > 4) {
_participants.resize(4);
}
_title->setText(title);
QString status;
if (_participants.isEmpty() || _participants.size() >= count) {
status = lng_chat_status_members(lt_count, count);
} else {
status = lng_group_invite_members(lt_count, count);
}
_status->setText(status);
if (photo.type() == mtpc_chatPhoto) {
auto &d = photo.c_chatPhoto();
auto location = App::imageLocation(160, 160, d.vphoto_small);
if (!location.isNull()) {
_photo = ImagePtr(location);
if (!_photo->loaded()) {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
_photo->load();
}
}
}
int h = st::confirmInviteStatusTop + _status->height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _join->height() + st::boxButtonPadding.bottom();
if (!_participants.isEmpty()) {
int skip = (width() - 4 * st::confirmInviteUserPhotoSize) / 5;
int padding = skip / 2;
_userWidth = (st::confirmInviteUserPhotoSize + 2 * padding);
int sumWidth = _participants.size() * _userWidth;
int left = (width() - sumWidth) / 2;
for_const (auto user, _participants) {
auto name = new FlatLabel(this, st::confirmInviteUserName);
name->resizeToWidth(st::confirmInviteUserPhotoSize + padding);
name->setText(user->firstName.isEmpty() ? App::peerName(user) : user->firstName);
name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop);
left += _userWidth;
}
h += st::confirmInviteUserHeight;
}
setMaxHeight(h);
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
connect(_join, SIGNAL(clicked()), App::main(), SLOT(onInviteImport()));
}
void ConfirmInviteBox::resizeEvent(QResizeEvent *e) {
_title->move((width() - _title->width()) / 2, st::confirmInviteTitleTop);
_status->move((width() - _status->width()) / 2, st::confirmInviteStatusTop);
_join->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _join->height());
_cancel->moveToRight(st::boxButtonPadding.right() + _join->width() + st::boxButtonPadding.left(), _join->y());
}
void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
p.drawPixmap((width() - st::confirmInvitePhotoSize) / 2, st::confirmInvitePhotoTop, _photo->pixCircled(st::confirmInvitePhotoSize, st::confirmInvitePhotoSize));
int sumWidth = _participants.size() * _userWidth;
int left = (width() - sumWidth) / 2;
for_const (auto user, _participants) {
user->paintUserpicLeft(p, st::confirmInviteUserPhotoSize, left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, st::confirmInviteUserPhotoTop, width());
left += _userWidth;
}
}
void ConfirmInviteBox::showAll() {
showChildren();
}
void ConfirmInviteBox::hideAll() {
hideChildren();
}

View File

@ -266,3 +266,26 @@ private:
UserData *_member; UserData *_member;
}; };
class ConfirmInviteBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
ConfirmInviteBox(const QString &title, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void showAll();
void hideAll();
private:
ChildWidget<FlatLabel> _title, _status;
ImagePtr _photo;
QVector<UserData*> _participants;
ChildWidget<BoxButton> _join, _cancel;
int _userWidth = 0;
};

View File

@ -530,9 +530,9 @@ void StickersInner::paintRow(Painter &p, int32 index) {
int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::defaultActiveButton.height) / 2; int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::defaultActiveButton.height) / 2;
QRect add(myrtlrect(addx, addy, addw, st::defaultActiveButton.height)); QRect add(myrtlrect(addx, addy, addw, st::defaultActiveButton.height));
App::roundRect(p, add, st::defaultActiveButton.textBgOver, ImageRoundRadius::Small); App::roundRect(p, add, st::defaultActiveButton.textBg, ImageRoundRadius::Small);
p.setFont(st::defaultActiveButton.font); p.setFont(st::defaultActiveButton.font);
p.setPen(st::defaultActiveButton.textFg); p.setPen(st::defaultActiveButton.textFg); // textBgOver / downTextTop
p.drawTextLeft(addx - st::defaultActiveButton.width / 2, addy + st::defaultActiveButton.textTop, width(), _addText, _addWidth); p.drawTextLeft(addx - st::defaultActiveButton.width / 2, addy + st::defaultActiveButton.textTop, width(), _addText, _addWidth);
} }

View File

@ -85,8 +85,8 @@ dialogsTextStyleActive: textStyle(dialogsTextStyle) {
linkFgDown: dialogsTextFgActive; linkFgDown: dialogsTextFgActive;
} }
dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) {
linkFg: #ffd6d6; linkFg: #c6e1f7;
linkFgDown: #ffd6d6; linkFgDown: #c6e1f7;
} }
dialogsNewChatIcon: icon { dialogsNewChatIcon: icon {

View File

@ -3487,11 +3487,22 @@ bool MainWidget::usernameResolveFail(QString name, const RPCError &error) {
void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) { void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) {
switch (invite.type()) { switch (invite.type()) {
case mtpc_chatInvite: { case mtpc_chatInvite: {
const auto &d(invite.c_chatInvite()); auto &d(invite.c_chatInvite());
ConfirmBox *box = new ConfirmBox(((d.is_channel() && !d.is_megagroup()) ? lng_group_invite_want_join_channel : lng_group_invite_want_join)(lt_title, qs(d.vtitle)), lang(lng_group_invite_join)); ((d.is_channel() && !d.is_megagroup()) ? lng_group_invite_want_join_channel : lng_group_invite_want_join)(lt_title, qs(d.vtitle)), lang(lng_group_invite_join);
QVector<UserData*> participants;
if (d.has_participants()) {
auto &v = d.vparticipants.c_vector().v;
participants.reserve(v.size());
for_const (auto &user, v) {
if (auto feededUser = App::feedUser(user)) {
participants.push_back(feededUser);
}
}
}
auto box = std_::make_unique<ConfirmInviteBox>(qs(d.vtitle), d.vphoto, 3, participants);
_inviteHash = hash; _inviteHash = hash;
connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport())); Ui::showLayer(box.release());
Ui::showLayer(box);
} break; } break;
case mtpc_chatInviteAlready: { case mtpc_chatInviteAlready: {

View File

@ -1185,6 +1185,7 @@
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\styles\style_basic.cpp" /> <ClCompile Include="GeneratedFiles\styles\style_basic.cpp" />
<ClCompile Include="GeneratedFiles\styles\style_basic_types.cpp" /> <ClCompile Include="GeneratedFiles\styles\style_basic_types.cpp" />
<ClCompile Include="GeneratedFiles\styles\style_boxes.cpp" />
<ClCompile Include="GeneratedFiles\styles\style_dialogs.cpp" /> <ClCompile Include="GeneratedFiles\styles\style_dialogs.cpp" />
<ClCompile Include="GeneratedFiles\styles\style_history.cpp" /> <ClCompile Include="GeneratedFiles\styles\style_history.cpp" />
<ClCompile Include="GeneratedFiles\styles\style_overview.cpp" /> <ClCompile Include="GeneratedFiles\styles\style_overview.cpp" />
@ -1457,6 +1458,7 @@
<ClInclude Include="GeneratedFiles\numbers.h" /> <ClInclude Include="GeneratedFiles\numbers.h" />
<ClInclude Include="GeneratedFiles\styles\style_basic.h" /> <ClInclude Include="GeneratedFiles\styles\style_basic.h" />
<ClInclude Include="GeneratedFiles\styles\style_basic_types.h" /> <ClInclude Include="GeneratedFiles\styles\style_basic_types.h" />
<ClInclude Include="GeneratedFiles\styles\style_boxes.h" />
<ClInclude Include="GeneratedFiles\styles\style_dialogs.h" /> <ClInclude Include="GeneratedFiles\styles\style_dialogs.h" />
<ClInclude Include="GeneratedFiles\styles\style_history.h" /> <ClInclude Include="GeneratedFiles\styles\style_history.h" />
<ClInclude Include="GeneratedFiles\styles\style_overview.h" /> <ClInclude Include="GeneratedFiles\styles\style_overview.h" />
@ -2876,6 +2878,7 @@
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling style %(Identity)...</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling style %(Identity)...</Message>
</CustomBuild> </CustomBuild>
<CodegenStyleItem Include="Resources\basic_types.style" /> <CodegenStyleItem Include="Resources\basic_types.style" />
<CodegenStyleItem Include="SourceFiles\boxes\boxes.style" />
<CodegenStyleItem Include="SourceFiles\dialogs\dialogs.style" /> <CodegenStyleItem Include="SourceFiles\dialogs\dialogs.style" />
<CodegenStyleItem Include="SourceFiles\history\history.style" /> <CodegenStyleItem Include="SourceFiles\history\history.style" />
<CodegenStyleItem Include="SourceFiles\overview\overview.style" /> <CodegenStyleItem Include="SourceFiles\overview\overview.style" />

View File

@ -1344,6 +1344,9 @@
<ClCompile Include="SourceFiles\platform\linux\linux_libs.cpp"> <ClCompile Include="SourceFiles\platform\linux\linux_libs.cpp">
<Filter>SourceFiles\platform\linux</Filter> <Filter>SourceFiles\platform\linux</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\styles\style_boxes.cpp">
<Filter>GeneratedFiles\styles</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h"> <ClInclude Include="SourceFiles\stdafx.h">
@ -1595,6 +1598,9 @@
<ClInclude Include="SourceFiles\platform\linux\linux_libs.h"> <ClInclude Include="SourceFiles\platform\linux\linux_libs.h">
<Filter>SourceFiles\platform\linux</Filter> <Filter>SourceFiles\platform\linux</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GeneratedFiles\styles\style_boxes.h">
<Filter>GeneratedFiles\styles</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="SourceFiles\application.h"> <CustomBuild Include="SourceFiles\application.h">
@ -1966,6 +1972,9 @@
<CodegenStyleItem Include="SourceFiles\profile\profile.style"> <CodegenStyleItem Include="SourceFiles\profile\profile.style">
<Filter>SourceFiles\profile</Filter> <Filter>SourceFiles\profile</Filter>
</CodegenStyleItem> </CodegenStyleItem>
<CodegenStyleItem Include="SourceFiles\boxes\boxes.style">
<Filter>SourceFiles\boxes</Filter>
</CodegenStyleItem>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="Resources\winrc\Telegram.rc"> <ResourceCompile Include="Resources\winrc\Telegram.rc">