rpl::producer<bool> VerifiedValue(
		not_null<PeerData*> peer) {
	if (auto user = peer->asUser()) {
		return Data::PeerFlagValue(user, MTPDuser::Flag::f_verified);
	} else if (auto channel = peer->asChannel()) {
		return Data::PeerFlagValue(
			channel,
			MTPDchannel::Flag::f_verified);
	}
	return rpl::single(false);
}
rpl::producer<QString> PlainAboutValue(
		not_null<PeerData*> peer) {
	if (auto channel = peer->asChannel()) {
		return Notify::PeerUpdateValue(
				channel,
				Notify::PeerUpdate::Flag::AboutChanged
		) | rpl::map([channel] { return channel->about(); });
	} else if (auto user = peer->asUser()) {
		if (user->botInfo) {
			return PlainBioValue(user);
		}
	}
	return rpl::single(QString());
}
rpl::producer<bool> CanAddMemberValue(
		not_null<PeerData*> peer) {
	if (auto chat = peer->asChat()) {
		return Notify::PeerUpdateValue(
			chat,
			Notify::PeerUpdate::Flag::ChatCanEdit
		) | rpl::map([chat] {
			return chat->canEdit();
		});
	} else if (auto channel = peer->asChannel()) {
		return Notify::PeerUpdateValue(
			channel,
			Notify::PeerUpdate::Flag::ChannelRightsChanged
		) | rpl::map([channel] {
			return channel->canAddMembers();
		});
	}
	return rpl::single(false);
}
rpl::producer<int> MembersCountValue(
		not_null<PeerData*> peer) {
	if (auto chat = peer->asChat()) {
		return Notify::PeerUpdateValue(
			peer,
			Notify::PeerUpdate::Flag::MembersChanged
		) | rpl::map([chat] {
			return chat->amIn()
				? std::max(chat->count, int(chat->participants.size()))
				: 0;
		});
	} else if (auto channel = peer->asChannel()) {
		return Notify::PeerUpdateValue(
				channel,
				Notify::PeerUpdate::Flag::MembersChanged
		) | rpl::map([channel] {
			return channel->membersCount();
		});
	}
	Unexpected("User in MembersCountViewer().");
}
SearchResult ParseSearchResult(
		not_null<PeerData*> peer,
		Storage::SharedMediaType type,
		MsgId messageId,
		SparseIdsLoadDirection direction,
		const MTPmessages_Messages &data) {
	auto result = SearchResult();
	result.noSkipRange = MsgRange{ messageId, messageId };

	auto messages = [&] {
		switch (data.type()) {
		case mtpc_messages_messages: {
			auto &d = data.c_messages_messages();
			App::feedUsers(d.vusers);
			App::feedChats(d.vchats);
			result.fullCount = d.vmessages.v.size();
			return &d.vmessages.v;
		} break;

		case mtpc_messages_messagesSlice: {
			auto &d = data.c_messages_messagesSlice();
			App::feedUsers(d.vusers);
			App::feedChats(d.vchats);
			result.fullCount = d.vcount.v;
			return &d.vmessages.v;
		} break;

		case mtpc_messages_channelMessages: {
			auto &d = data.c_messages_channelMessages();
			if (auto channel = peer->asChannel()) {
				channel->ptsReceived(d.vpts.v);
			} else {
				LOG(("API Error: received messages.channelMessages when "
					"no channel was passed! (ParseSearchResult)"));
			}
			App::feedUsers(d.vusers);
			App::feedChats(d.vchats);
			result.fullCount = d.vcount.v;
			return &d.vmessages.v;
		} break;

		case mtpc_messages_messagesNotModified: {
			LOG(("API Error: received messages.messagesNotModified! "
				"(ParseSearchResult)"));
			return (const QVector<MTPMessage>*)nullptr;
		} break;
		}
		Unexpected("messages.Messages type in ParseSearchResult()");
	}();

	if (!messages) {
		return result;
	}

	auto addType = NewMessageExisting;
	result.messageIds.reserve(messages->size());
	for (auto &message : *messages) {
		if (auto item = App::histories().addNewMessage(message, addType)) {
			auto itemId = item->id;
			if ((type == Storage::SharedMediaType::kCount)
				|| item->sharedMediaTypes().test(type)) {
				result.messageIds.push_back(itemId);
			}
			accumulate_min(result.noSkipRange.from, itemId);
			accumulate_max(result.noSkipRange.till, itemId);
		}
	}
	if (messageId && result.messageIds.empty()) {
		result.noSkipRange = [&]() -> MsgRange {
			switch (direction) {
			case SparseIdsLoadDirection::Before: // All old loaded.
				return { 0, result.noSkipRange.till };
			case SparseIdsLoadDirection::Around: // All loaded.
				return { 0, ServerMaxMsgId };
			case SparseIdsLoadDirection::After: // All new loaded.
				return { result.noSkipRange.from, ServerMaxMsgId };
			}
			Unexpected("Direction in ParseSearchResult");
		}();
	}
	return result;
}