From 66e2fce8d5cae8622d7ffc86101b91160d113253 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 8 Jul 2016 19:59:46 +0300 Subject: [PATCH] New design of a chat invite link import box with title, photo, users. --- Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/app.cpp | 744 ++++++++++--------- Telegram/SourceFiles/app.h | 2 + Telegram/SourceFiles/boxes/boxes.style | 50 ++ Telegram/SourceFiles/boxes/confirmbox.cpp | 84 +++ Telegram/SourceFiles/boxes/confirmbox.h | 23 + Telegram/SourceFiles/boxes/stickersetbox.cpp | 4 +- Telegram/SourceFiles/dialogs/dialogs.style | 4 +- Telegram/SourceFiles/mainwidget.cpp | 19 +- Telegram/Telegram.vcxproj | 3 + Telegram/Telegram.vcxproj.filters | 9 + 11 files changed, 572 insertions(+), 372 deletions(-) create mode 100644 Telegram/SourceFiles/boxes/boxes.style diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3f3a2dc0b..d05f7de46 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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_join" = "Join"; +"lng_group_invite_members" = "{count:_not_used_|# member|# members}, among them:"; + "lng_group_invite_link" = "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."; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 888a7db71..ee5b159b7 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -383,397 +383,413 @@ namespace { return (online > now); } - UserData *feedUsers(const MTPVector &users) { - UserData *result = nullptr; - for_const (auto &user, users.c_vector().v) { - UserData *data = nullptr; - bool wasContact = false, minimal = false; - const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty(); + UserData *feedUser(const MTPUser &user) { + UserData *data = nullptr; + bool wasContact = false, minimal = false; + const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty(); - Notify::PeerUpdate update; - using UpdateFlag = Notify::PeerUpdate::Flag; + Notify::PeerUpdate update; + using UpdateFlag = Notify::PeerUpdate::Flag; - switch (user.type()) { - case mtpc_userEmpty: { - const auto &d(user.c_userEmpty()); + switch (user.type()) { + case mtpc_userEmpty: { + auto &d(user.c_userEmpty()); - PeerId peer(peerFromUser(d.vid.v)); - data = App::user(peer); - auto canShareThisContact = data->canShareThisContactFast(); - wasContact = data->isContact(); + PeerId peer(peerFromUser(d.vid.v)); + data = App::user(peer); + auto canShareThisContact = data->canShareThisContactFast(); + wasContact = data->isContact(); - data->input = MTP_inputPeerUser(d.vid, MTP_long(0)); - data->inputUser = MTP_inputUser(d.vid, MTP_long(0)); + data->input = MTP_inputPeerUser(d.vid, MTP_long(0)); + data->inputUser = MTP_inputUser(d.vid, MTP_long(0)); + data->setName(lang(lng_deleted), QString(), QString(), QString()); + data->setPhoto(MTP_userProfilePhotoEmpty()); + data->access = UserNoAccess; + data->flags = 0; + data->setBotInfoVersion(-1); + status = &emptyStatus; + data->contact = -1; + + if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact; + if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact; + } break; + case mtpc_user: { + auto &d(user.c_user()); + minimal = d.is_min(); + + PeerId peer(peerFromUser(d.vid.v)); + data = App::user(peer); + auto canShareThisContact = data->canShareThisContactFast(); + wasContact = data->isContact(); + if (!minimal) { + data->flags = d.vflags.v; + if (d.is_self()) { + data->input = MTP_inputPeerSelf(); + data->inputUser = MTP_inputUserSelf(); + } else if (!d.has_access_hash()) { + data->input = MTP_inputPeerUser(d.vid, MTP_long((data->access == UserNoAccess) ? 0 : data->access)); + data->inputUser = MTP_inputUser(d.vid, MTP_long((data->access == UserNoAccess) ? 0 : data->access)); + } else { + data->input = MTP_inputPeerUser(d.vid, d.vaccess_hash); + data->inputUser = MTP_inputUser(d.vid, d.vaccess_hash); + } + if (d.is_restricted()) { + data->setRestrictionReason(extractRestrictionReason(qs(d.vrestriction_reason))); + } else { + data->setRestrictionReason(QString()); + } + } + if (d.is_deleted()) { + if (!data->phone().isEmpty()) { + data->setPhone(QString()); + update.flags |= UpdateFlag::UserPhoneChanged; + } data->setName(lang(lng_deleted), QString(), QString(), QString()); data->setPhoto(MTP_userProfilePhotoEmpty()); data->access = UserNoAccess; - data->flags = 0; - data->setBotInfoVersion(-1); status = &emptyStatus; - data->contact = -1; + } else { + // apply first_name and last_name from minimal user only if we don't have + // local values for first name and last name already, otherwise skip + bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty(); + QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName; + QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName; - if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact; - if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact; - } break; - case mtpc_user: { - const auto &d(user.c_user()); - minimal = d.is_min(); + QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString()); + QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString()); - PeerId peer(peerFromUser(d.vid.v)); - data = App::user(peer); - auto canShareThisContact = data->canShareThisContactFast(); - wasContact = data->isContact(); - if (!minimal) { - data->flags = d.vflags.v; - if (d.is_self()) { - data->input = MTP_inputPeerSelf(); - data->inputUser = MTP_inputUserSelf(); - } else if (!d.has_access_hash()) { - data->input = MTP_inputPeerUser(d.vid, MTP_long((data->access == UserNoAccess) ? 0 : data->access)); - data->inputUser = MTP_inputUser(d.vid, MTP_long((data->access == UserNoAccess) ? 0 : data->access)); - } else { - data->input = MTP_inputPeerUser(d.vid, d.vaccess_hash); - data->inputUser = MTP_inputUser(d.vid, d.vaccess_hash); - } - if (d.is_restricted()) { - data->setRestrictionReason(extractRestrictionReason(qs(d.vrestriction_reason))); - } else { - data->setRestrictionReason(QString()); - } + bool phoneChanged = (data->phone() != phone); + if (phoneChanged) { + data->setPhone(phone); + update.flags |= UpdateFlag::UserPhoneChanged; } - if (d.is_deleted()) { - if (!data->phone().isEmpty()) { - data->setPhone(QString()); - update.flags |= UpdateFlag::UserPhoneChanged; - } - data->setName(lang(lng_deleted), QString(), QString(), QString()); - data->setPhoto(MTP_userProfilePhotoEmpty()); - data->access = UserNoAccess; - status = &emptyStatus; + bool nameChanged = (data->firstName != fname) || (data->lastName != lname); + + bool showPhone = !isServiceUser(data->id) && !d.is_self() && !d.is_contact() && !d.is_mutual_contact(); + bool showPhoneChanged = !isServiceUser(data->id) && !d.is_self() && ((showPhone && data->contact) || (!showPhone && !data->contact)); + if (minimal) { + showPhoneChanged = false; + showPhone = !isServiceUser(data->id) && (data->id != peerFromUser(MTP::authedId())) && !data->contact; + } + + // see also Local::readPeer + + QString pname = (showPhoneChanged || phoneChanged || nameChanged) ? ((showPhone && !phone.isEmpty()) ? formatPhone(phone) : QString()) : data->nameOrPhone; + + if (!minimal && d.is_self() && uname != data->username) { + SignalHandlers::setCrashAnnotation("Username", uname); + } + data->setName(fname, lname, pname, uname); + if (d.has_photo()) { + data->setPhoto(d.vphoto); } else { - // apply first_name and last_name from minimal user only if we don't have - // local values for first name and last name already, otherwise skip - bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty(); - QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName; - QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName; - - QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString()); - QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString()); - - bool phoneChanged = (data->phone() != phone); - if (phoneChanged) { - data->setPhone(phone); - update.flags |= UpdateFlag::UserPhoneChanged; - } - bool nameChanged = (data->firstName != fname) || (data->lastName != lname); - - bool showPhone = !isServiceUser(data->id) && !d.is_self() && !d.is_contact() && !d.is_mutual_contact(); - bool showPhoneChanged = !isServiceUser(data->id) && !d.is_self() && ((showPhone && data->contact) || (!showPhone && !data->contact)); - if (minimal) { - showPhoneChanged = false; - showPhone = !isServiceUser(data->id) && (data->id != peerFromUser(MTP::authedId())) && !data->contact; - } - - // see also Local::readPeer - - QString pname = (showPhoneChanged || phoneChanged || nameChanged) ? ((showPhone && !phone.isEmpty()) ? formatPhone(phone) : QString()) : data->nameOrPhone; - - if (!minimal && d.is_self() && uname != data->username) { - SignalHandlers::setCrashAnnotation("Username", uname); - } - data->setName(fname, lname, pname, uname); - if (d.has_photo()) { - data->setPhoto(d.vphoto); - } else { - data->setPhoto(MTP_userProfilePhotoEmpty()); - } - if (d.has_access_hash()) data->access = d.vaccess_hash.v; - status = d.has_status() ? &d.vstatus : &emptyStatus; + data->setPhoto(MTP_userProfilePhotoEmpty()); } - if (!minimal) { - if (d.has_bot_info_version()) { - data->setBotInfoVersion(d.vbot_info_version.v); - data->botInfo->readsAllHistory = d.is_bot_chat_history(); - if (data->botInfo->cantJoinGroups != d.is_bot_nochats()) { - data->botInfo->cantJoinGroups = d.is_bot_nochats(); - update.flags |= UpdateFlag::BotCanAddToGroups; - } - data->botInfo->inlinePlaceholder = d.has_bot_inline_placeholder() ? '_' + qs(d.vbot_inline_placeholder) : QString(); - } else { - data->setBotInfoVersion(-1); - } - data->contact = (d.is_contact() || d.is_mutual_contact()) ? 1 : (data->phone().isEmpty() ? -1 : 0); - if (data->contact == 1 && cReportSpamStatuses().value(data->id, dbiprsHidden) != dbiprsHidden) { - cRefReportSpamStatuses().insert(data->id, dbiprsHidden); - Local::writeReportSpamStatuses(); - } - if (d.is_self() && ::self != data) { - ::self = data; - if (App::wnd()) App::wnd()->updateGlobalMenu(); + if (d.has_access_hash()) data->access = d.vaccess_hash.v; + status = d.has_status() ? &d.vstatus : &emptyStatus; + } + if (!minimal) { + if (d.has_bot_info_version()) { + data->setBotInfoVersion(d.vbot_info_version.v); + data->botInfo->readsAllHistory = d.is_bot_chat_history(); + if (data->botInfo->cantJoinGroups != d.is_bot_nochats()) { + data->botInfo->cantJoinGroups = d.is_bot_nochats(); + update.flags |= UpdateFlag::BotCanAddToGroups; } + data->botInfo->inlinePlaceholder = d.has_bot_inline_placeholder() ? '_' + qs(d.vbot_inline_placeholder) : QString(); + } else { + data->setBotInfoVersion(-1); } - - if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact; - if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact; - } break; - } - - if (!data) continue; - - if (minimal) { - if (data->loadedStatus == PeerData::NotLoaded) { - data->loadedStatus = PeerData::MinimalLoaded; + data->contact = (d.is_contact() || d.is_mutual_contact()) ? 1 : (data->phone().isEmpty() ? -1 : 0); + if (data->contact == 1 && cReportSpamStatuses().value(data->id, dbiprsHidden) != dbiprsHidden) { + cRefReportSpamStatuses().insert(data->id, dbiprsHidden); + Local::writeReportSpamStatuses(); } - } else if (data->loadedStatus != PeerData::FullLoaded) { - data->loadedStatus = PeerData::FullLoaded; - } - - auto oldOnlineTill = data->onlineTill; - if (status && !minimal) switch (status->type()) { - case mtpc_userStatusEmpty: data->onlineTill = 0; break; - case mtpc_userStatusRecently: - if (data->onlineTill > -10) { // don't modify pseudo-online - data->onlineTill = -2; - } - break; - case mtpc_userStatusLastWeek: data->onlineTill = -3; break; - case mtpc_userStatusLastMonth: data->onlineTill = -4; break; - case mtpc_userStatusOffline: data->onlineTill = status->c_userStatusOffline().vwas_online.v; break; - case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break; - } - if (oldOnlineTill != data->onlineTill) { - update.flags |= UpdateFlag::UserOnlineChanged; - } - - if (data->contact < 0 && !data->phone().isEmpty() && peerToUser(data->id) != MTP::authedId()) { - data->contact = 0; - } - if (App::main()) { - if ((data->contact > 0 && !wasContact) || (wasContact && data->contact < 1)) { - Notify::userIsContactChanged(data); - } - - markPeerUpdated(data); - if (update.flags) { - update.peer = data; - Notify::peerUpdatedDelayed(update); + if (d.is_self() && ::self != data) { + ::self = data; + if (App::wnd()) App::wnd()->updateGlobalMenu(); } } - result = data; + + if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact; + if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact; + } break; + } + + if (!data) { + return nullptr; + } + + if (minimal) { + if (data->loadedStatus == PeerData::NotLoaded) { + data->loadedStatus = PeerData::MinimalLoaded; + } + } else if (data->loadedStatus != PeerData::FullLoaded) { + data->loadedStatus = PeerData::FullLoaded; + } + + auto oldOnlineTill = data->onlineTill; + if (status && !minimal) switch (status->type()) { + case mtpc_userStatusEmpty: data->onlineTill = 0; break; + case mtpc_userStatusRecently: + if (data->onlineTill > -10) { // don't modify pseudo-online + data->onlineTill = -2; + } + break; + case mtpc_userStatusLastWeek: data->onlineTill = -3; break; + case mtpc_userStatusLastMonth: data->onlineTill = -4; break; + case mtpc_userStatusOffline: data->onlineTill = status->c_userStatusOffline().vwas_online.v; break; + case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break; + } + if (oldOnlineTill != data->onlineTill) { + update.flags |= UpdateFlag::UserOnlineChanged; + } + + if (data->contact < 0 && !data->phone().isEmpty() && peerToUser(data->id) != MTP::authedId()) { + data->contact = 0; + } + if (App::main()) { + if ((data->contact > 0 && !wasContact) || (wasContact && data->contact < 1)) { + Notify::userIsContactChanged(data); + } + + markPeerUpdated(data); + if (update.flags) { + update.peer = data; + Notify::peerUpdatedDelayed(update); + } + } + return data; + } + + UserData *feedUsers(const MTPVector &users) { + UserData *result = nullptr; + for_const (auto &user, users.c_vector().v) { + if (auto feededUser = feedUser(user)) { + result = feededUser; + } } return result; } + PeerData *feedChat(const MTPChat &chat) { + PeerData *data = nullptr; + bool minimal = false; + + Notify::PeerUpdate update; + using UpdateFlag = Notify::PeerUpdate::Flag; + + switch (chat.type()) { + case mtpc_chat: { + auto &d(chat.c_chat()); + + data = App::chat(peerFromChat(d.vid.v)); + auto cdata = data->asChat(); + auto canEdit = cdata->canEdit(); + + if (cdata->version < d.vversion.v) { + cdata->version = d.vversion.v; + cdata->invalidateParticipants(); + } + + data->input = MTP_inputPeerChat(d.vid); + cdata->setName(qs(d.vtitle)); + cdata->setPhoto(d.vphoto); + cdata->date = d.vdate.v; + + if (d.has_migrated_to() && d.vmigrated_to.type() == mtpc_inputChannel) { + const auto &c(d.vmigrated_to.c_inputChannel()); + ChannelData *channel = App::channel(peerFromChannel(c.vchannel_id)); + if (!channel->mgInfo) { + channel->flags |= MTPDchannel::Flag::f_megagroup; + channel->flagsUpdated(); + } + if (!channel->access) { + channel->input = MTP_inputPeerChannel(c.vchannel_id, c.vaccess_hash); + channel->inputChannel = d.vmigrated_to; + channel->access = d.vmigrated_to.c_inputChannel().vaccess_hash.v; + } + bool updatedTo = (cdata->migrateToPtr != channel), updatedFrom = (channel->mgInfo->migrateFromPtr != cdata); + if (updatedTo) { + cdata->migrateToPtr = channel; + } + if (updatedFrom) { + channel->mgInfo->migrateFromPtr = cdata; + if (History *h = App::historyLoaded(cdata->id)) { + if (History *hto = App::historyLoaded(channel->id)) { + if (!h->isEmpty()) { + h->clear(true); + } + if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { + App::removeDialog(h); + } + } + } + Notify::migrateUpdated(channel); + update.flags |= UpdateFlag::MigrationChanged; + } + if (updatedTo) { + Notify::migrateUpdated(cdata); + update.flags |= UpdateFlag::MigrationChanged; + } + } + + if (!(cdata->flags & MTPDchat::Flag::f_admins_enabled) && (d.vflags.v & MTPDchat::Flag::f_admins_enabled)) { + cdata->invalidateParticipants(); + } + cdata->flags = d.vflags.v; + + cdata->count = d.vparticipants_count.v; + cdata->isForbidden = false; + if (canEdit != cdata->canEdit()) { + update.flags |= UpdateFlag::ChatCanEdit; + } + } break; + case mtpc_chatForbidden: { + auto &d(chat.c_chatForbidden()); + + data = App::chat(peerFromChat(d.vid.v)); + auto cdata = data->asChat(); + auto canEdit = cdata->canEdit(); + + data->input = MTP_inputPeerChat(d.vid); + cdata->setName(qs(d.vtitle)); + cdata->setPhoto(MTP_chatPhotoEmpty()); + cdata->date = 0; + cdata->count = -1; + cdata->invalidateParticipants(); + cdata->flags = 0; + cdata->isForbidden = true; + if (canEdit != cdata->canEdit()) { + update.flags |= UpdateFlag::ChatCanEdit; + } + } break; + case mtpc_channel: { + auto &d(chat.c_channel()); + + auto peerId = peerFromChannel(d.vid.v); + minimal = d.is_min(); + if (minimal) { + data = App::channelLoaded(peerId); + if (!data) { + return nullptr; // minimal is not loaded, need to make getDifference + } + } else { + data = App::channel(peerId); + data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0)); + } + + auto cdata = data->asChannel(); + auto wasInChannel = cdata->amIn(); + auto canEditPhoto = cdata->canEditPhoto(); + auto canViewAdmins = cdata->canViewAdmins(); + auto canViewMembers = cdata->canViewMembers(); + auto canAddMembers = cdata->canAddMembers(); + auto wasEditor = cdata->amEditor(); + + if (minimal) { + auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy; + cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask); + } else { + cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); + cdata->access = d.vaccess_hash.v; + cdata->date = d.vdate.v; + if (cdata->version < d.vversion.v) { + cdata->version = d.vversion.v; + } + if (d.is_restricted()) { + cdata->setRestrictionReason(extractRestrictionReason(qs(d.vrestriction_reason))); + } else { + cdata->setRestrictionReason(QString()); + } + cdata->flags = d.vflags.v; + } + cdata->flagsUpdated(); + + QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString(); + cdata->setName(qs(d.vtitle), uname); + + cdata->isForbidden = false; + cdata->setPhoto(d.vphoto); + + if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn; + if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto; + if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins; + if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers; + if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers; + if (wasEditor != cdata->amEditor()) { + cdata->selfAdminUpdated(); + update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged); + } + } break; + case mtpc_channelForbidden: { + auto &d(chat.c_channelForbidden()); + + auto peerId = peerFromChannel(d.vid.v); + data = App::channel(peerId); + data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash); + + auto cdata = data->asChannel(); + auto wasInChannel = cdata->amIn(); + auto canEditPhoto = cdata->canEditPhoto(); + auto canViewAdmins = cdata->canViewAdmins(); + auto canViewMembers = cdata->canViewMembers(); + auto canAddMembers = cdata->canAddMembers(); + auto wasEditor = cdata->amEditor(); + + cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); + + auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup); + cdata->flags = (cdata->flags & ~mask) | (mtpCastFlags(d.vflags) & mask); + cdata->flagsUpdated(); + + cdata->setName(qs(d.vtitle), QString()); + + cdata->access = d.vaccess_hash.v; + cdata->setPhoto(MTP_chatPhotoEmpty()); + cdata->date = 0; + cdata->setMembersCount(0); + cdata->isForbidden = true; + + if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn; + if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto; + if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins; + if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers; + if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers; + if (wasEditor != cdata->amEditor()) { + cdata->selfAdminUpdated(); + update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged); + } + } break; + } + if (!data) { + return nullptr; + } + + if (minimal) { + if (data->loadedStatus == PeerData::NotLoaded) { + data->loadedStatus = PeerData::MinimalLoaded; + } + } else if (data->loadedStatus != PeerData::FullLoaded) { + data->loadedStatus = PeerData::FullLoaded; + } + if (App::main()) { + markPeerUpdated(data); + if (update.flags) { + update.peer = data; + Notify::peerUpdatedDelayed(update); + } + } + return data; + } + PeerData *feedChats(const MTPVector &chats) { PeerData *result = nullptr; for_const (auto &chat, chats.c_vector().v) { - PeerData *data = nullptr; - bool minimal = false; - - Notify::PeerUpdate update; - using UpdateFlag = Notify::PeerUpdate::Flag; - - switch (chat.type()) { - case mtpc_chat: { - auto &d(chat.c_chat()); - - data = App::chat(peerFromChat(d.vid.v)); - auto cdata = data->asChat(); - auto canEdit = cdata->canEdit(); - - if (cdata->version < d.vversion.v) { - cdata->version = d.vversion.v; - cdata->invalidateParticipants(); - } - - data->input = MTP_inputPeerChat(d.vid); - cdata->setName(qs(d.vtitle)); - cdata->setPhoto(d.vphoto); - cdata->date = d.vdate.v; - - if (d.has_migrated_to() && d.vmigrated_to.type() == mtpc_inputChannel) { - const auto &c(d.vmigrated_to.c_inputChannel()); - ChannelData *channel = App::channel(peerFromChannel(c.vchannel_id)); - if (!channel->mgInfo) { - channel->flags |= MTPDchannel::Flag::f_megagroup; - channel->flagsUpdated(); - } - if (!channel->access) { - channel->input = MTP_inputPeerChannel(c.vchannel_id, c.vaccess_hash); - channel->inputChannel = d.vmigrated_to; - channel->access = d.vmigrated_to.c_inputChannel().vaccess_hash.v; - } - bool updatedTo = (cdata->migrateToPtr != channel), updatedFrom = (channel->mgInfo->migrateFromPtr != cdata); - if (updatedTo) { - cdata->migrateToPtr = channel; - } - if (updatedFrom) { - channel->mgInfo->migrateFromPtr = cdata; - if (History *h = App::historyLoaded(cdata->id)) { - if (History *hto = App::historyLoaded(channel->id)) { - if (!h->isEmpty()) { - h->clear(true); - } - if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { - App::removeDialog(h); - } - } - } - Notify::migrateUpdated(channel); - update.flags |= UpdateFlag::MigrationChanged; - } - if (updatedTo) { - Notify::migrateUpdated(cdata); - update.flags |= UpdateFlag::MigrationChanged; - } - } - - if (!(cdata->flags & MTPDchat::Flag::f_admins_enabled) && (d.vflags.v & MTPDchat::Flag::f_admins_enabled)) { - cdata->invalidateParticipants(); - } - cdata->flags = d.vflags.v; - - cdata->count = d.vparticipants_count.v; - cdata->isForbidden = false; - if (canEdit != cdata->canEdit()) { - update.flags |= UpdateFlag::ChatCanEdit; - } - } break; - case mtpc_chatForbidden: { - auto &d(chat.c_chatForbidden()); - - data = App::chat(peerFromChat(d.vid.v)); - auto cdata = data->asChat(); - auto canEdit = cdata->canEdit(); - - data->input = MTP_inputPeerChat(d.vid); - cdata->setName(qs(d.vtitle)); - cdata->setPhoto(MTP_chatPhotoEmpty()); - cdata->date = 0; - cdata->count = -1; - cdata->invalidateParticipants(); - cdata->flags = 0; - cdata->isForbidden = true; - if (canEdit != cdata->canEdit()) { - update.flags |= UpdateFlag::ChatCanEdit; - } - } break; - case mtpc_channel: { - auto &d(chat.c_channel()); - - auto peerId = peerFromChannel(d.vid.v); - minimal = d.is_min(); - if (minimal) { - data = App::channelLoaded(peerId); - if (!data) { - continue; // minimal is not loaded, need to make getDifference - } - } else { - data = App::channel(peerId); - data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0)); - } - - auto cdata = data->asChannel(); - auto wasInChannel = cdata->amIn(); - auto canEditPhoto = cdata->canEditPhoto(); - auto canViewAdmins = cdata->canViewAdmins(); - auto canViewMembers = cdata->canViewMembers(); - auto canAddMembers = cdata->canAddMembers(); - auto wasEditor = cdata->amEditor(); - - if (minimal) { - auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy; - cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask); - } else { - cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); - cdata->access = d.vaccess_hash.v; - cdata->date = d.vdate.v; - if (cdata->version < d.vversion.v) { - cdata->version = d.vversion.v; - } - if (d.is_restricted()) { - cdata->setRestrictionReason(extractRestrictionReason(qs(d.vrestriction_reason))); - } else { - cdata->setRestrictionReason(QString()); - } - cdata->flags = d.vflags.v; - } - cdata->flagsUpdated(); - - QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString(); - cdata->setName(qs(d.vtitle), uname); - - cdata->isForbidden = false; - cdata->setPhoto(d.vphoto); - - if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn; - if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto; - if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins; - if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers; - if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers; - if (wasEditor != cdata->amEditor()) { - cdata->selfAdminUpdated(); - update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged); - } - } break; - case mtpc_channelForbidden: { - auto &d(chat.c_channelForbidden()); - - auto peerId = peerFromChannel(d.vid.v); - data = App::channel(peerId); - data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash); - - auto cdata = data->asChannel(); - auto wasInChannel = cdata->amIn(); - auto canEditPhoto = cdata->canEditPhoto(); - auto canViewAdmins = cdata->canViewAdmins(); - auto canViewMembers = cdata->canViewMembers(); - auto canAddMembers = cdata->canAddMembers(); - auto wasEditor = cdata->amEditor(); - - cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); - - auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup); - cdata->flags = (cdata->flags & ~mask) | (mtpCastFlags(d.vflags) & mask); - cdata->flagsUpdated(); - - cdata->setName(qs(d.vtitle), QString()); - - cdata->access = d.vaccess_hash.v; - cdata->setPhoto(MTP_chatPhotoEmpty()); - cdata->date = 0; - cdata->setMembersCount(0); - cdata->isForbidden = true; - - if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn; - if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto; - if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins; - if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers; - if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers; - if (wasEditor != cdata->amEditor()) { - cdata->selfAdminUpdated(); - update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged); - } - } break; + if (auto feededChat = feedChat(chat)) { + result = feededChat; } - if (!data) continue; - - if (minimal) { - if (data->loadedStatus == PeerData::NotLoaded) { - data->loadedStatus = PeerData::MinimalLoaded; - } - } else if (data->loadedStatus != PeerData::FullLoaded) { - data->loadedStatus = PeerData::FullLoaded; - } - if (App::main()) { - markPeerUpdated(data); - if (update.flags) { - update.peer = data; - Notify::peerUpdatedDelayed(update); - } - } - result = data; } return result; } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 0c12a7615..c05ca179d 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -65,7 +65,9 @@ namespace App { bool onlineColorUse(UserData *user, TimeId now); bool onlineColorUse(TimeId online, TimeId now); + UserData *feedUser(const MTPUser &user); UserData *feedUsers(const MTPVector &users); // returns last user + PeerData *feedChat(const MTPChat &chat); PeerData *feedChats(const MTPVector &chats); // returns last chat void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated = true); diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style new file mode 100644 index 000000000..42c691542 --- /dev/null +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -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; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 83ce30643..39c978ed3 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "application.h" #include "core/click_handler_types.h" +#include "styles/style_boxes.h" TextParseOptions _confirmBoxTextOptions = { TextParseLinks | TextParseMultiline | TextParseRichText, // flags @@ -520,3 +521,86 @@ void KickMemberBox::onConfirm() { App::api()->kickParticipant(channel, _member); } } + +ConfirmInviteBox::ConfirmInviteBox(const QString &title, const MTPChatPhoto &photo, int count, const QVector &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(); +} diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 59f27336d..ebad005d5 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -266,3 +266,26 @@ private: UserData *_member; }; + +class ConfirmInviteBox : public AbstractBox, public RPCSender { + Q_OBJECT + +public: + ConfirmInviteBox(const QString &title, const MTPChatPhoto &photo, int count, const QVector &participants); + +protected: + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + + void showAll(); + void hideAll(); + +private: + ChildWidget _title, _status; + ImagePtr _photo; + QVector _participants; + + ChildWidget _join, _cancel; + int _userWidth = 0; + +}; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 78837cc1e..51d52bb82 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -530,9 +530,9 @@ void StickersInner::paintRow(Painter &p, int32 index) { int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::defaultActiveButton.height) / 2; 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.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); } diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 8ca6163bc..ce4681df0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -85,8 +85,8 @@ dialogsTextStyleActive: textStyle(dialogsTextStyle) { linkFgDown: dialogsTextFgActive; } dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { - linkFg: #ffd6d6; - linkFgDown: #ffd6d6; + linkFg: #c6e1f7; + linkFgDown: #c6e1f7; } dialogsNewChatIcon: icon { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 184556342..f4b456789 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3487,11 +3487,22 @@ bool MainWidget::usernameResolveFail(QString name, const RPCError &error) { void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) { switch (invite.type()) { case mtpc_chatInvite: { - const 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)); + auto &d(invite.c_chatInvite()); + ((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 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(qs(d.vtitle), d.vphoto, 3, participants); _inviteHash = hash; - connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport())); - Ui::showLayer(box); + Ui::showLayer(box.release()); } break; case mtpc_chatInviteAlready: { diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 5b8fc9a82..749debce7 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -1185,6 +1185,7 @@ + @@ -1457,6 +1458,7 @@ + @@ -2876,6 +2878,7 @@ Compiling style %(Identity)... + diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 2d1a1f393..227208438 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -1344,6 +1344,9 @@ SourceFiles\platform\linux + + GeneratedFiles\styles + @@ -1595,6 +1598,9 @@ SourceFiles\platform\linux + + GeneratedFiles\styles + @@ -1966,6 +1972,9 @@ SourceFiles\profile + + SourceFiles\boxes +