mirror of https://github.com/procxx/kepka.git
				
				
				
			Better chats list entries management.
Make unread counts and last message base::optional<>. Remove ChannelHistory.
This commit is contained in:
		
							parent
							
								
									edcaccba1f
								
							
						
					
					
						commit
						a7f67c4bc9
					
				|  | @ -1420,6 +1420,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| 
 | 
 | ||||||
| "lng_feed_group" = "Group in feed"; | "lng_feed_group" = "Group in feed"; | ||||||
| "lng_feed_ungroup" = "Ungroup from feed"; | "lng_feed_ungroup" = "Ungroup from feed"; | ||||||
|  | "lng_feed_channel_added" = "Channel added to your feed."; | ||||||
|  | "lng_feed_channel_removed" = "Channel removed from your feed."; | ||||||
| 
 | 
 | ||||||
| "lng_info_feed_title" = "Feed Info"; | "lng_info_feed_title" = "Feed Info"; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -172,9 +172,10 @@ void ApiWrap::savePinnedOrder() { | ||||||
| 
 | 
 | ||||||
| void ApiWrap::toggleChannelGrouping( | void ApiWrap::toggleChannelGrouping( | ||||||
| 		not_null<ChannelData*> channel, | 		not_null<ChannelData*> channel, | ||||||
| 		bool group) { | 		bool group, | ||||||
|  | 		base::lambda<void()> callback) { | ||||||
| 	if (const auto already = _channelGroupingRequests.take(channel)) { | 	if (const auto already = _channelGroupingRequests.take(channel)) { | ||||||
| 		request(*already).cancel(); | 		request(already->first).cancel(); | ||||||
| 	} | 	} | ||||||
| 	const auto feedId = Data::Feed::kId; | 	const auto feedId = Data::Feed::kId; | ||||||
| 	const auto flags = group | 	const auto flags = group | ||||||
|  | @ -185,15 +186,18 @@ void ApiWrap::toggleChannelGrouping( | ||||||
| 		channel->inputChannel, | 		channel->inputChannel, | ||||||
| 		MTP_int(feedId) | 		MTP_int(feedId) | ||||||
| 	)).done([=](const MTPBool &result) { | 	)).done([=](const MTPBool &result) { | ||||||
| 		_channelGroupingRequests.remove(channel); |  | ||||||
| 		if (group) { | 		if (group) { | ||||||
| 			channel->setFeed(Auth().data().feed(feedId)); | 			channel->setFeed(Auth().data().feed(feedId)); | ||||||
| 		} else { | 		} else { | ||||||
| 			channel->clearFeed(); | 			channel->clearFeed(); | ||||||
| 		} | 		} | ||||||
|  | 		if (const auto data = _channelGroupingRequests.take(channel)) { | ||||||
|  | 			data->second(); | ||||||
|  | 		} | ||||||
| 	}).fail([=](const RPCError &error) { | 	}).fail([=](const RPCError &error) { | ||||||
| 		_channelGroupingRequests.remove(channel); | 		_channelGroupingRequests.remove(channel); | ||||||
| 	}).send(); | 	}).send(); | ||||||
|  | 	_channelGroupingRequests.emplace(channel, requestId, callback); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ApiWrap::sendMessageFail(const RPCError &error) { | void ApiWrap::sendMessageFail(const RPCError &error) { | ||||||
|  | @ -362,6 +366,124 @@ void ApiWrap::requestContacts() { | ||||||
| 	}).send(); | 	}).send(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ApiWrap::requestDialogEntry(not_null<Data::Feed*> feed) { | ||||||
|  | 	if (_dialogFeedRequests.contains(feed)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	_dialogFeedRequests.emplace(feed); | ||||||
|  | 
 | ||||||
|  | 	auto peers = QVector<MTPInputDialogPeer>( | ||||||
|  | 		1, | ||||||
|  | 		MTP_inputDialogPeerFeed(MTP_int(feed->id()))); | ||||||
|  | 	if (feed->channelsLoaded()) { | ||||||
|  | 		const auto &channels = feed->channels(); | ||||||
|  | 		peers.reserve(channels.size() + 1); | ||||||
|  | 		for (const auto history : feed->channels()) { | ||||||
|  | 			peers.push_back(MTP_inputDialogPeer(history->peer->input)); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		request(MTPmessages_GetDialogs( | ||||||
|  | 			MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id), | ||||||
|  | 			MTP_int(feed->id()), | ||||||
|  | 			MTP_int(0), // offset_date
 | ||||||
|  | 			MTP_int(0), // offset_id
 | ||||||
|  | 			MTP_inputPeerEmpty(), // offset_peer
 | ||||||
|  | 			MTP_int(Data::Feed::kChannelsLimit) | ||||||
|  | 		)).done([=](const MTPmessages_Dialogs &result) { | ||||||
|  | 		//	applyFeedChannels(result);
 | ||||||
|  | 		}).send(); | ||||||
|  | 	} | ||||||
|  | 	request(MTPmessages_GetPeerDialogs( | ||||||
|  | 		MTP_vector(std::move(peers)) | ||||||
|  | 	)).done([=](const MTPmessages_PeerDialogs &result) { | ||||||
|  | 		applyPeerDialogs(result); | ||||||
|  | 		_dialogFeedRequests.remove(feed); | ||||||
|  | 	}).fail([=](const RPCError &error) { | ||||||
|  | 		_dialogFeedRequests.remove(feed); | ||||||
|  | 	}).send(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ApiWrap::requestDialogEntry(not_null<History*> history) { | ||||||
|  | 	if (_dialogRequests.contains(history)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	_dialogRequests.emplace(history); | ||||||
|  | 	auto peers = QVector<MTPInputDialogPeer>( | ||||||
|  | 		1, | ||||||
|  | 		MTP_inputDialogPeer(history->peer->input)); | ||||||
|  | 	request(MTPmessages_GetPeerDialogs( | ||||||
|  | 		MTP_vector(std::move(peers)) | ||||||
|  | 	)).done([=](const MTPmessages_PeerDialogs &result) { | ||||||
|  | 		applyPeerDialogs(result); | ||||||
|  | 
 | ||||||
|  | 		if (history->lastMessage()) { | ||||||
|  | 			if (!history->chatsListDate().isNull() | ||||||
|  | 				&& history->loadedAtBottom()) { | ||||||
|  | 				if (const auto channel = history->peer->asChannel()) { | ||||||
|  | 					const auto inviter = channel->inviter; | ||||||
|  | 					if (inviter != 0 | ||||||
|  | 						&& history->chatsListDate() <= channel->inviteDate | ||||||
|  | 						&& channel->amIn()) { | ||||||
|  | 						if (const auto from = App::userLoaded(inviter)) { | ||||||
|  | 							history->insertJoinedMessage(true); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			history->updateChatListExistence(); | ||||||
|  | 		} else { | ||||||
|  | 			if (const auto chat = history->peer->asChat()) { | ||||||
|  | 				if (!chat->haveLeft()) { | ||||||
|  | 					Local::addSavedPeer( | ||||||
|  | 						history->peer, | ||||||
|  | 						history->chatsListDate()); | ||||||
|  | 				} | ||||||
|  | 			} else if (const auto channel = history->peer->asChannel()) { | ||||||
|  | 				const auto inviter = channel->inviter; | ||||||
|  | 				if (inviter != 0 && channel->amIn()) { | ||||||
|  | 					if (const auto from = App::userLoaded(inviter)) { | ||||||
|  | 						history->unloadBlocks(); | ||||||
|  | 						history->addNewerSlice(QVector<MTPMessage>()); | ||||||
|  | 						history->insertJoinedMessage( | ||||||
|  | 							true); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				App::main()->deleteConversation(history->peer, false); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		_dialogRequests.remove(history); | ||||||
|  | 	}).fail([=](const RPCError &error) { | ||||||
|  | 		_dialogRequests.remove(history); | ||||||
|  | 	}).send(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) { | ||||||
|  | 	Expects(dialogs.type() == mtpc_messages_peerDialogs); | ||||||
|  | 
 | ||||||
|  | 	const auto &data = dialogs.c_messages_peerDialogs(); | ||||||
|  | 	App::feedUsers(data.vusers); | ||||||
|  | 	App::feedChats(data.vchats); | ||||||
|  | 	App::feedMsgs(data.vmessages, NewMessageLast); | ||||||
|  | 	for (const auto &dialog : data.vdialogs.v) { | ||||||
|  | 		switch (dialog.type()) { | ||||||
|  | 		case mtpc_dialog: { | ||||||
|  | 			const auto &fields = dialog.c_dialog(); | ||||||
|  | 			if (const auto peerId = peerFromMTP(fields.vpeer)) { | ||||||
|  | 				App::history(peerId)->applyDialog(fields); | ||||||
|  | 			} | ||||||
|  | 		} break; | ||||||
|  | 
 | ||||||
|  | 		case mtpc_dialogFeed: { | ||||||
|  | 			const auto &fields = dialog.c_dialogFeed(); | ||||||
|  | 			const auto feed = Auth().data().feed(fields.vfeed_id.v); | ||||||
|  | 			feed->applyDialog(fields); | ||||||
|  | 		} break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	_session->data().sendHistoryChangeNotifications(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ApiWrap::requestFullPeer(PeerData *peer) { | void ApiWrap::requestFullPeer(PeerData *peer) { | ||||||
| 	if (!peer || _fullPeerRequests.contains(peer)) return; | 	if (!peer || _fullPeerRequests.contains(peer)) return; | ||||||
| 
 | 
 | ||||||
|  | @ -464,7 +586,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt | ||||||
| 				if (auto h = App::historyLoaded(cfrom->id)) { | 				if (auto h = App::historyLoaded(cfrom->id)) { | ||||||
| 					if (auto hto = App::historyLoaded(channel->id)) { | 					if (auto hto = App::historyLoaded(channel->id)) { | ||||||
| 						if (!h->isEmpty()) { | 						if (!h->isEmpty()) { | ||||||
| 							h->clear(true); | 							h->unloadBlocks(); | ||||||
| 						} | 						} | ||||||
| 						if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { | 						if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { | ||||||
| 							App::main()->removeDialog(h); | 							App::main()->removeDialog(h); | ||||||
|  | @ -495,14 +617,12 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt | ||||||
| 		channel->setRestrictedCount(f.has_banned_count() ? f.vbanned_count.v : 0); | 		channel->setRestrictedCount(f.has_banned_count() ? f.vbanned_count.v : 0); | ||||||
| 		channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0); | 		channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0); | ||||||
| 		channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString()); | 		channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString()); | ||||||
| 		if (auto h = App::historyLoaded(channel->id)) { | 		if (const auto history = App::historyLoaded(channel->id)) { | ||||||
| 			if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) { | 			history->applyDialogFields( | ||||||
| 				h->setUnreadCount(f.vunread_count.v); | 				f.vunread_count.v, | ||||||
| 				h->inboxReadBefore = f.vread_inbox_max_id.v + 1; | 				f.vread_inbox_max_id.v, | ||||||
|  | 				f.vread_outbox_max_id.v); | ||||||
| 		} | 		} | ||||||
| 			accumulate_max(h->outboxReadBefore, f.vread_outbox_max_id.v + 1); |  | ||||||
| 		} |  | ||||||
| 		ranges::overload([] {}, [](int a) {}); |  | ||||||
| 		if (f.has_pinned_msg_id()) { | 		if (f.has_pinned_msg_id()) { | ||||||
| 			channel->setPinnedMessageId(f.vpinned_msg_id.v); | 			channel->setPinnedMessageId(f.vpinned_msg_id.v); | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -1063,7 +1183,9 @@ void ApiWrap::unblockParticipant( | ||||||
| 		not_null<ChannelData*> channel, | 		not_null<ChannelData*> channel, | ||||||
| 		not_null<UserData*> user) { | 		not_null<UserData*> user) { | ||||||
| 	const auto kick = KickRequest(channel, user); | 	const auto kick = KickRequest(channel, user); | ||||||
| 	if (_kickRequests.contains(kick)) return; | 	if (_kickRequests.contains(kick)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const auto requestId = request(MTPchannels_EditBanned( | 	const auto requestId = request(MTPchannels_EditBanned( | ||||||
| 		channel->inputChannel, | 		channel->inputChannel, | ||||||
|  | @ -1085,6 +1207,43 @@ void ApiWrap::unblockParticipant( | ||||||
| 	_kickRequests.emplace(kick, requestId); | 	_kickRequests.emplace(kick, requestId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ApiWrap::deleteAllFromUser( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		not_null<UserData*> from) { | ||||||
|  | 	const auto history = App::historyLoaded(channel->id); | ||||||
|  | 	const auto ids = history | ||||||
|  | 		? history->collectMessagesFromUserToDelete(from) | ||||||
|  | 		: QVector<MsgId>(); | ||||||
|  | 	const auto channelId = peerToChannel(channel->id); | ||||||
|  | 	for (const auto msgId : ids) { | ||||||
|  | 		if (const auto item = App::histItemById(channelId, msgId)) { | ||||||
|  | 			item->destroy(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Auth().data().sendHistoryChangeNotifications(); | ||||||
|  | 
 | ||||||
|  | 	deleteAllFromUserSend(channel, from); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ApiWrap::deleteAllFromUserSend( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		not_null<UserData*> from) { | ||||||
|  | 	request(MTPchannels_DeleteUserHistory( | ||||||
|  | 		channel->inputChannel, | ||||||
|  | 		from->inputUser | ||||||
|  | 	)).done([=](const MTPmessages_AffectedHistory &result) { | ||||||
|  | 		const auto offset = applyAffectedHistory(channel, result); | ||||||
|  | 		if (offset > 0) { | ||||||
|  | 			deleteAllFromUserSend(channel, from); | ||||||
|  | 		} else if (const auto history = App::historyLoaded(channel)) { | ||||||
|  | 			if (!history->lastMessageKnown()) { | ||||||
|  | 				Auth().api().requestDialogEntry(history); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}).send(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ApiWrap::requestChannelMembersForAdd( | void ApiWrap::requestChannelMembersForAdd( | ||||||
| 		not_null<ChannelData*> channel, | 		not_null<ChannelData*> channel, | ||||||
| 		base::lambda<void(const MTPchannels_ChannelParticipants&)> callback) { | 		base::lambda<void(const MTPchannels_ChannelParticipants&)> callback) { | ||||||
|  | @ -1505,23 +1664,23 @@ int ApiWrap::onlineTillFromStatus(const MTPUserStatus &status, int currentOnline | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ApiWrap::clearHistory(not_null<PeerData*> peer) { | void ApiWrap::clearHistory(not_null<PeerData*> peer) { | ||||||
| 	auto lastMsgId = MsgId(0); | 	auto deleteTillId = MsgId(0); | ||||||
| 	if (auto history = App::historyLoaded(peer->id)) { | 	if (auto history = App::historyLoaded(peer->id)) { | ||||||
| 		if (history->lastMsg) { | 		if (const auto last = history->lastMessage()) { | ||||||
| 			lastMsgId = history->lastMsg->id; | 			deleteTillId = last->id; | ||||||
| 			Local::addSavedPeer(history->peer, history->lastMsg->date); | 			Local::addSavedPeer(history->peer, last->date); | ||||||
| 		} | 		} | ||||||
| 		history->clear(); | 		history->clear(); | ||||||
| 		history->newLoaded = history->oldLoaded = true; | 		history->newLoaded = history->oldLoaded = true; | ||||||
| 	} | 	} | ||||||
| 	if (auto channel = peer->asChannel()) { | 	if (const auto channel = peer->asChannel()) { | ||||||
| 		if (auto migrated = peer->migrateFrom()) { | 		if (const auto migrated = peer->migrateFrom()) { | ||||||
| 			clearHistory(migrated); | 			clearHistory(migrated); | ||||||
| 		} | 		} | ||||||
| 		if (IsServerMsgId(lastMsgId)) { | 		if (IsServerMsgId(deleteTillId)) { | ||||||
| 			request(MTPchannels_DeleteHistory( | 			request(MTPchannels_DeleteHistory( | ||||||
| 				channel->inputChannel, | 				channel->inputChannel, | ||||||
| 				MTP_int(lastMsgId) | 				MTP_int(deleteTillId) | ||||||
| 			)).send(); | 			)).send(); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -1530,7 +1689,7 @@ void ApiWrap::clearHistory(not_null<PeerData*> peer) { | ||||||
| 			peer->input, | 			peer->input, | ||||||
| 			MTP_int(0) | 			MTP_int(0) | ||||||
| 		)).done([=](const MTPmessages_AffectedHistory &result) { | 		)).done([=](const MTPmessages_AffectedHistory &result) { | ||||||
| 			auto offset = applyAffectedHistory(peer, result); | 			const auto offset = applyAffectedHistory(peer, result); | ||||||
| 			if (offset > 0) { | 			if (offset > 0) { | ||||||
| 				clearHistory(peer); | 				clearHistory(peer); | ||||||
| 			} | 			} | ||||||
|  | @ -1775,6 +1934,94 @@ void ApiWrap::requestParticipantsCountDelayed( | ||||||
| 		[this, channel] { channel->updateFullForced(); }); | 		[this, channel] { channel->updateFullForced(); }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ApiWrap::requestChannelRangeDifference(not_null<History*> history) { | ||||||
|  | 	Expects(history->isChannel()); | ||||||
|  | 
 | ||||||
|  | 	const auto channel = history->peer->asChannel(); | ||||||
|  | 	if (const auto requestId = _rangeDifferenceRequests.take(channel)) { | ||||||
|  | 		request(*requestId).cancel(); | ||||||
|  | 	} | ||||||
|  | 	const auto range = history->rangeForDifferenceRequest(); | ||||||
|  | 	if (!(range.from < range.till) || !channel->pts()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	MTP_LOG(0, ("getChannelDifference { good - " | ||||||
|  | 		"after channelDifferenceTooLong was received, " | ||||||
|  | 		"validating history part }%1").arg(cTestMode() ? " TESTMODE" : "")); | ||||||
|  | 	channelRangeDifferenceSend(channel, range, channel->pts()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ApiWrap::channelRangeDifferenceSend( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		MsgRange range, | ||||||
|  | 		int32 pts) { | ||||||
|  | 	Expects(range.from < range.till); | ||||||
|  | 
 | ||||||
|  | 	const auto limit = range.till - range.from; | ||||||
|  | 	const auto filter = MTP_channelMessagesFilter( | ||||||
|  | 		MTP_flags(0), | ||||||
|  | 		MTP_vector<MTPMessageRange>(1, MTP_messageRange( | ||||||
|  | 			MTP_int(range.from), | ||||||
|  | 			MTP_int(range.till - 1)))); | ||||||
|  | 	const auto requestId = request(MTPupdates_GetChannelDifference( | ||||||
|  | 		MTP_flags(MTPupdates_GetChannelDifference::Flag::f_force), | ||||||
|  | 		channel->inputChannel, | ||||||
|  | 		filter, | ||||||
|  | 		MTP_int(pts), | ||||||
|  | 		MTP_int(limit) | ||||||
|  | 	)).done([=](const MTPupdates_ChannelDifference &result) { | ||||||
|  | 		_rangeDifferenceRequests.remove(channel); | ||||||
|  | 		channelRangeDifferenceDone(channel, range, result); | ||||||
|  | 	}).fail([=](const RPCError &error) { | ||||||
|  | 		_rangeDifferenceRequests.remove(channel); | ||||||
|  | 	}).send(); | ||||||
|  | 	_rangeDifferenceRequests.emplace(channel, requestId); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ApiWrap::channelRangeDifferenceDone( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		MsgRange range, | ||||||
|  | 		const MTPupdates_ChannelDifference &result) { | ||||||
|  | 	auto nextRequestPts = int32(0); | ||||||
|  | 	auto isFinal = true; | ||||||
|  | 
 | ||||||
|  | 	switch (result.type()) { | ||||||
|  | 	case mtpc_updates_channelDifferenceEmpty: { | ||||||
|  | 		const auto &d = result.c_updates_channelDifferenceEmpty(); | ||||||
|  | 		nextRequestPts = d.vpts.v; | ||||||
|  | 		isFinal = d.is_final(); | ||||||
|  | 	} break; | ||||||
|  | 
 | ||||||
|  | 	case mtpc_updates_channelDifferenceTooLong: { | ||||||
|  | 		const auto &d = result.c_updates_channelDifferenceTooLong(); | ||||||
|  | 
 | ||||||
|  | 		App::feedUsers(d.vusers); | ||||||
|  | 		App::feedChats(d.vchats); | ||||||
|  | 
 | ||||||
|  | 		nextRequestPts = d.vpts.v; | ||||||
|  | 		isFinal = d.is_final(); | ||||||
|  | 	} break; | ||||||
|  | 
 | ||||||
|  | 	case mtpc_updates_channelDifference: { | ||||||
|  | 		const auto &d = result.c_updates_channelDifference(); | ||||||
|  | 
 | ||||||
|  | 		App::main()->feedChannelDifference(d); | ||||||
|  | 
 | ||||||
|  | 		nextRequestPts = d.vpts.v; | ||||||
|  | 		isFinal = d.is_final(); | ||||||
|  | 	} break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!isFinal) { | ||||||
|  | 		MTP_LOG(0, ("getChannelDifference { " | ||||||
|  | 			"good - after not final channelDifference was received, " | ||||||
|  | 			"validating history part }%1" | ||||||
|  | 			).arg(cTestMode() ? " TESTMODE" : "")); | ||||||
|  | 		channelRangeDifferenceSend(channel, range, nextRequestPts); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) { | void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) { | ||||||
| 	const QVector<MTPMessage> *v = 0; | 	const QVector<MTPMessage> *v = 0; | ||||||
| 	switch (msgs.type()) { | 	switch (msgs.type()) { | ||||||
|  | @ -3438,7 +3685,11 @@ void ApiWrap::readServerHistory(not_null<History*> history) { | ||||||
| 
 | 
 | ||||||
| void ApiWrap::readServerHistoryForce(not_null<History*> history) { | void ApiWrap::readServerHistoryForce(not_null<History*> history) { | ||||||
| 	const auto peer = history->peer; | 	const auto peer = history->peer; | ||||||
| 	const auto upTo = history->inboxRead(0); | 	const auto upTo = history->readInbox(); | ||||||
|  | 	if (!upTo) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (const auto channel = peer->asChannel()) { | 	if (const auto channel = peer->asChannel()) { | ||||||
| 		if (!channel->amIn()) { | 		if (!channel->amIn()) { | ||||||
| 			return; // no read request for channels that I didn't join
 | 			return; // no read request for channels that I didn't join
 | ||||||
|  |  | ||||||
|  | @ -56,7 +56,10 @@ public: | ||||||
| 	void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0); | 	void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0); | ||||||
| 
 | 
 | ||||||
| 	void savePinnedOrder(); | 	void savePinnedOrder(); | ||||||
| 	void toggleChannelGrouping(not_null<ChannelData*> channel, bool group); | 	void toggleChannelGrouping( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		bool group, | ||||||
|  | 		base::lambda<void()> callback); | ||||||
| 
 | 
 | ||||||
| 	using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>; | 	using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>; | ||||||
| 	void requestMessageData( | 	void requestMessageData( | ||||||
|  | @ -65,6 +68,8 @@ public: | ||||||
| 		RequestMessageDataCallback callback); | 		RequestMessageDataCallback callback); | ||||||
| 
 | 
 | ||||||
| 	void requestContacts(); | 	void requestContacts(); | ||||||
|  | 	void requestDialogEntry(not_null<Data::Feed*> feed); | ||||||
|  | 	void requestDialogEntry(not_null<History*> history); | ||||||
| 
 | 
 | ||||||
| 	void requestFullPeer(PeerData *peer); | 	void requestFullPeer(PeerData *peer); | ||||||
| 	void requestPeer(PeerData *peer); | 	void requestPeer(PeerData *peer); | ||||||
|  | @ -73,6 +78,7 @@ public: | ||||||
| 	void requestBots(not_null<ChannelData*> channel); | 	void requestBots(not_null<ChannelData*> channel); | ||||||
| 	void requestAdmins(not_null<ChannelData*> channel); | 	void requestAdmins(not_null<ChannelData*> channel); | ||||||
| 	void requestParticipantsCountDelayed(not_null<ChannelData*> channel); | 	void requestParticipantsCountDelayed(not_null<ChannelData*> channel); | ||||||
|  | 	void requestChannelRangeDifference(not_null<History*> history); | ||||||
| 
 | 
 | ||||||
| 	void requestChangelog( | 	void requestChangelog( | ||||||
| 		const QString &sinceVersion, | 		const QString &sinceVersion, | ||||||
|  | @ -96,6 +102,9 @@ public: | ||||||
| 	void unblockParticipant( | 	void unblockParticipant( | ||||||
| 		not_null<ChannelData*> channel, | 		not_null<ChannelData*> channel, | ||||||
| 		not_null<UserData*> user); | 		not_null<UserData*> user); | ||||||
|  | 	void deleteAllFromUser( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		not_null<UserData*> from); | ||||||
| 
 | 
 | ||||||
| 	void requestWebPageDelayed(WebPageData *page); | 	void requestWebPageDelayed(WebPageData *page); | ||||||
| 	void clearWebPageRequest(WebPageData *page); | 	void clearWebPageRequest(WebPageData *page); | ||||||
|  | @ -269,6 +278,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	QVector<MTPint> collectMessageIds(const MessageDataRequests &requests); | 	QVector<MTPint> collectMessageIds(const MessageDataRequests &requests); | ||||||
| 	MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); | 	MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); | ||||||
|  | 	void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs); | ||||||
| 
 | 
 | ||||||
| 	void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req); | 	void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req); | ||||||
| 	void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req); | 	void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req); | ||||||
|  | @ -285,10 +295,24 @@ private: | ||||||
| 		int availableCount, | 		int availableCount, | ||||||
| 		const QVector<MTPChannelParticipant> &list); | 		const QVector<MTPChannelParticipant> &list); | ||||||
| 	void resolveWebPages(); | 	void resolveWebPages(); | ||||||
| 	void gotWebPages(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); | 	void gotWebPages( | ||||||
|  | 		ChannelData *channel, | ||||||
|  | 		const MTPmessages_Messages &result, | ||||||
|  | 		mtpRequestId req); | ||||||
| 	void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result); | 	void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result); | ||||||
| 
 | 
 | ||||||
| 	PeerData *notifySettingReceived(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings); | 	void channelRangeDifferenceSend( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		MsgRange range, | ||||||
|  | 		int32 pts); | ||||||
|  | 	void channelRangeDifferenceDone( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		MsgRange range, | ||||||
|  | 		const MTPupdates_ChannelDifference &result); | ||||||
|  | 
 | ||||||
|  | 	PeerData *notifySettingReceived( | ||||||
|  | 		MTPInputNotifyPeer peer, | ||||||
|  | 		const MTPPeerNotifySettings &settings); | ||||||
| 
 | 
 | ||||||
| 	void stickerSetDisenabled(mtpRequestId requestId); | 	void stickerSetDisenabled(mtpRequestId requestId); | ||||||
| 	void stickersSaveOrder(); | 	void stickersSaveOrder(); | ||||||
|  | @ -347,6 +371,11 @@ private: | ||||||
| 	void applyAffectedMessages( | 	void applyAffectedMessages( | ||||||
| 		not_null<PeerData*> peer, | 		not_null<PeerData*> peer, | ||||||
| 		const MTPmessages_AffectedMessages &result); | 		const MTPmessages_AffectedMessages &result); | ||||||
|  | 
 | ||||||
|  | 	void deleteAllFromUserSend( | ||||||
|  | 		not_null<ChannelData*> channel, | ||||||
|  | 		not_null<UserData*> from); | ||||||
|  | 
 | ||||||
| 	void sendMessageFail(const RPCError &error); | 	void sendMessageFail(const RPCError &error); | ||||||
| 	void uploadAlbumMedia( | 	void uploadAlbumMedia( | ||||||
| 		not_null<HistoryItem*> item, | 		not_null<HistoryItem*> item, | ||||||
|  | @ -391,7 +420,7 @@ private: | ||||||
| 		const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback; | 		const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback; | ||||||
| 	base::flat_map< | 	base::flat_map< | ||||||
| 		not_null<ChannelData*>, | 		not_null<ChannelData*>, | ||||||
| 		mtpRequestId> _channelGroupingRequests; | 		std::pair<mtpRequestId,base::lambda<void()>>> _channelGroupingRequests; | ||||||
| 
 | 
 | ||||||
| 	using KickRequest = std::pair< | 	using KickRequest = std::pair< | ||||||
| 		not_null<ChannelData*>, | 		not_null<ChannelData*>, | ||||||
|  | @ -400,6 +429,10 @@ private: | ||||||
| 
 | 
 | ||||||
| 	QMap<ChannelData*, mtpRequestId> _selfParticipantRequests; | 	QMap<ChannelData*, mtpRequestId> _selfParticipantRequests; | ||||||
| 
 | 
 | ||||||
|  | 	base::flat_map< | ||||||
|  | 		not_null<ChannelData*>, | ||||||
|  | 		mtpRequestId> _rangeDifferenceRequests; | ||||||
|  | 
 | ||||||
| 	QMap<WebPageData*, mtpRequestId> _webPagesPending; | 	QMap<WebPageData*, mtpRequestId> _webPagesPending; | ||||||
| 	base::Timer _webPagesTimer; | 	base::Timer _webPagesTimer; | ||||||
| 
 | 
 | ||||||
|  | @ -432,6 +465,8 @@ private: | ||||||
| 
 | 
 | ||||||
| 	mtpRequestId _contactsRequestId = 0; | 	mtpRequestId _contactsRequestId = 0; | ||||||
| 	mtpRequestId _contactsStatusesRequestId = 0; | 	mtpRequestId _contactsStatusesRequestId = 0; | ||||||
|  | 	base::flat_set<not_null<Data::Feed*>> _dialogFeedRequests; | ||||||
|  | 	base::flat_set<not_null<History*>> _dialogRequests; | ||||||
| 
 | 
 | ||||||
| 	base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests; | 	base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -459,7 +459,7 @@ namespace { | ||||||
| 					if (auto h = App::historyLoaded(cdata->id)) { | 					if (auto h = App::historyLoaded(cdata->id)) { | ||||||
| 						if (auto hto = App::historyLoaded(channel->id)) { | 						if (auto hto = App::historyLoaded(channel->id)) { | ||||||
| 							if (!h->isEmpty()) { | 							if (!h->isEmpty()) { | ||||||
| 								h->clear(true); | 								h->unloadBlocks(); | ||||||
| 							} | 							} | ||||||
| 							if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { | 							if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) { | ||||||
| 								App::main()->removeDialog(h); | 								App::main()->removeDialog(h); | ||||||
|  | @ -1019,7 +1019,7 @@ namespace { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void feedInboxRead(const PeerId &peer, MsgId upTo) { | 	void feedInboxRead(const PeerId &peer, MsgId upTo) { | ||||||
| 		if (auto history = App::historyLoaded(peer)) { | 		if (const auto history = App::historyLoaded(peer)) { | ||||||
| 			history->inboxRead(upTo); | 			history->inboxRead(upTo); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -1027,17 +1027,8 @@ namespace { | ||||||
| 	void feedOutboxRead(const PeerId &peer, MsgId upTo, TimeId when) { | 	void feedOutboxRead(const PeerId &peer, MsgId upTo, TimeId when) { | ||||||
| 		if (auto history = App::historyLoaded(peer)) { | 		if (auto history = App::historyLoaded(peer)) { | ||||||
| 			history->outboxRead(upTo); | 			history->outboxRead(upTo); | ||||||
| 			if (history->lastMsg | 			if (const auto user = history->peer->asUser()) { | ||||||
| 				&& history->lastMsg->out() | 				user->madeAction(when); | ||||||
| 				&& history->lastMsg->id <= upTo) { |  | ||||||
| 				if (const auto main = App::main()) { |  | ||||||
| 					main->repaintDialogRow(history, history->lastMsg->id); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			history->updateChatListEntry(); |  | ||||||
| 
 |  | ||||||
| 			if (history->peer->isUser()) { |  | ||||||
| 				history->peer->asUser()->madeAction(when); |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -1055,37 +1046,31 @@ namespace { | ||||||
| 		return &(*i); | 		return &(*i); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds) { | 	void feedWereDeleted( | ||||||
|  | 			ChannelId channelId, | ||||||
|  | 			const QVector<MTPint> &msgsIds) { | ||||||
| 		const auto data = fetchMsgsData(channelId, false); | 		const auto data = fetchMsgsData(channelId, false); | ||||||
| 		if (!data) return; | 		if (!data) return; | ||||||
| 
 | 
 | ||||||
| 		const auto channelHistory = (channelId != NoChannel) | 		const auto affectedHistory = (channelId != NoChannel) | ||||||
| 			? App::history(peerFromChannel(channelId))->asChannelHistory() | 			? App::history(peerFromChannel(channelId)).get() | ||||||
| 			: nullptr; | 			: nullptr; | ||||||
| 
 | 
 | ||||||
| 		base::flat_set<not_null<History*>> historiesToCheck; | 		auto historiesToCheck = base::flat_set<not_null<History*>>(); | ||||||
| 		for (const auto msgId : msgsIds) { | 		for (const auto msgId : msgsIds) { | ||||||
| 			auto j = data->constFind(msgId.v); | 			auto j = data->constFind(msgId.v); | ||||||
| 			if (j != data->cend()) { | 			if (j != data->cend()) { | ||||||
| 				const auto h = (*j)->history(); | 				const auto history = (*j)->history(); | ||||||
| 				(*j)->destroy(); | 				(*j)->destroy(); | ||||||
| 				if (!h->lastMsg) { | 				if (!history->lastMessageKnown()) { | ||||||
| 					historiesToCheck.emplace(h); | 					historiesToCheck.emplace(history); | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else if (affectedHistory) { | ||||||
| 				if (channelHistory) { | 				affectedHistory->unknownMessageDeleted(msgId.v); | ||||||
| 					if (channelHistory->unreadCount() > 0 |  | ||||||
| 						&& msgId.v >= channelHistory->inboxReadBefore) { |  | ||||||
| 						channelHistory->setUnreadCount( |  | ||||||
| 							channelHistory->unreadCount() - 1); |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if (main()) { |  | ||||||
| 		for (const auto history : historiesToCheck) { | 		for (const auto history : historiesToCheck) { | ||||||
| 				main()->checkPeerHistory(history->peer); | 			Auth().api().requestDialogEntry(history); | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1222,10 +1207,6 @@ namespace { | ||||||
| 		return ::histories.findOrInsert(peer); | 		return ::histories.findOrInsert(peer); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	not_null<History*> historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead, int32 maxOutboxRead) { |  | ||||||
| 		return ::histories.findOrInsert(peer, unreadCnt, maxInboxRead, maxOutboxRead); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	History *historyLoaded(const PeerId &peer) { | 	History *historyLoaded(const PeerId &peer) { | ||||||
| 		if (!peer) { | 		if (!peer) { | ||||||
| 			return nullptr; | 			return nullptr; | ||||||
|  |  | ||||||
|  | @ -148,7 +148,6 @@ namespace App { | ||||||
| 
 | 
 | ||||||
| 	Histories &histories(); | 	Histories &histories(); | ||||||
| 	not_null<History*> history(const PeerId &peer); | 	not_null<History*> history(const PeerId &peer); | ||||||
| 	not_null<History*> historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead, int32 maxOutboxRead); |  | ||||||
| 	History *historyLoaded(const PeerId &peer); | 	History *historyLoaded(const PeerId &peer); | ||||||
| 	HistoryItem *histItemById(ChannelId channelId, MsgId itemId); | 	HistoryItem *histItemById(ChannelId channelId, MsgId itemId); | ||||||
| 	inline not_null<History*> history(const PeerData *peer) { | 	inline not_null<History*> history(const PeerData *peer) { | ||||||
|  |  | ||||||
|  | @ -568,7 +568,7 @@ void DeleteMessagesBox::deleteAndClear() { | ||||||
| 					MTP_vector<MTPint>(1, MTP_int(_ids[0].msg)))); | 					MTP_vector<MTPint>(1, MTP_int(_ids[0].msg)))); | ||||||
| 		} | 		} | ||||||
| 		if (_deleteAll && _deleteAll->checked()) { | 		if (_deleteAll && _deleteAll->checked()) { | ||||||
| 			App::main()->deleteAllFromUser( | 			Auth().api().deleteAllFromUser( | ||||||
| 				_moderateInChannel, | 				_moderateInChannel, | ||||||
| 				_moderateFrom); | 				_moderateFrom); | ||||||
| 		} | 		} | ||||||
|  | @ -583,13 +583,13 @@ void DeleteMessagesBox::deleteAndClear() { | ||||||
| 		if (auto item = App::histItemById(itemId)) { | 		if (auto item = App::histItemById(itemId)) { | ||||||
| 			auto history = item->history(); | 			auto history = item->history(); | ||||||
| 			auto wasOnServer = (item->id > 0); | 			auto wasOnServer = (item->id > 0); | ||||||
| 			auto wasLast = (history->lastMsg == item); | 			auto wasLast = (history->lastMessage() == item); | ||||||
| 			item->destroy(); | 			item->destroy(); | ||||||
| 
 | 
 | ||||||
| 			if (wasOnServer) { | 			if (wasOnServer) { | ||||||
| 				idsByPeer[history->peer].push_back(MTP_int(itemId.msg)); | 				idsByPeer[history->peer].push_back(MTP_int(itemId.msg)); | ||||||
| 			} else if (wasLast) { | 			} else if (wasLast) { | ||||||
| 				App::main()->checkPeerHistory(history->peer); | 				Auth().api().requestDialogEntry(history); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -372,6 +372,7 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(not_null<Pee | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) { | void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) { | ||||||
|  | 	Auth().api().requestDialogEntry(App::history(row->peer())); | ||||||
| 	Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId); | 	Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -72,8 +72,10 @@ void Feed::registerOne(not_null<ChannelData*> channel) { | ||||||
| 	if (!base::contains(_channels, history)) { | 	if (!base::contains(_channels, history)) { | ||||||
| 		const auto invisible = (_channels.size() < 2); | 		const auto invisible = (_channels.size() < 2); | ||||||
| 		_channels.push_back(history); | 		_channels.push_back(history); | ||||||
| 		if (history->lastMsg) { | 		if (history->lastMessageKnown()) { | ||||||
| 			updateLastMessage(history->lastMsg); | 			recountLastMessage(); | ||||||
|  | 		} else if (_channelsLoaded) { | ||||||
|  | 			_parent->session().api().requestDialogEntry(history); | ||||||
| 		} | 		} | ||||||
| 		_parent->session().storage().remove( | 		_parent->session().storage().remove( | ||||||
| 			Storage::FeedMessagesInvalidate(_id)); | 			Storage::FeedMessagesInvalidate(_id)); | ||||||
|  | @ -96,8 +98,10 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) { | ||||||
| 	if (i != end(_channels)) { | 	if (i != end(_channels)) { | ||||||
| 		const auto visible = (_channels.size() > 1); | 		const auto visible = (_channels.size() > 1); | ||||||
| 		_channels.erase(i, end(_channels)); | 		_channels.erase(i, end(_channels)); | ||||||
| 		if (_lastMessage && _lastMessage->history() == history) { | 		if (const auto last = lastMessage()) { | ||||||
| 			messageRemoved(_lastMessage); | 			if (last->history() == history) { | ||||||
|  | 				recountLastMessage(); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		_parent->session().storage().remove( | 		_parent->session().storage().remove( | ||||||
| 			Storage::FeedMessagesRemoveAll(_id, channel->bareId())); | 			Storage::FeedMessagesRemoveAll(_id, channel->bareId())); | ||||||
|  | @ -115,8 +119,10 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Feed::updateLastMessage(not_null<HistoryItem*> item) { | void Feed::updateLastMessage(not_null<HistoryItem*> item) { | ||||||
| 	if (justSetLastMessage(item)) { | 	if (justUpdateLastMessage(item)) { | ||||||
| 		setChatsListDate(_lastMessage->date); | 		if (_lastMessage && *_lastMessage) { | ||||||
|  | 			setChatsListDate((*_lastMessage)->date); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -207,8 +213,11 @@ void Feed::setChannels(std::vector<not_null<ChannelData*>> channels) { | ||||||
| 	_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels); | 	_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Feed::justSetLastMessage(not_null<HistoryItem*> item) { | bool Feed::justUpdateLastMessage(not_null<HistoryItem*> item) { | ||||||
| 	if (_lastMessage && item->position() <= _lastMessage->position()) { | 	if (!_lastMessage) { | ||||||
|  | 		return false; | ||||||
|  | 	} else if (*_lastMessage | ||||||
|  | 		&& item->position() <= (*_lastMessage)->position()) { | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	_lastMessage = item; | 	_lastMessage = item; | ||||||
|  | @ -216,36 +225,106 @@ bool Feed::justSetLastMessage(not_null<HistoryItem*> item) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Feed::messageRemoved(not_null<HistoryItem*> item) { | void Feed::messageRemoved(not_null<HistoryItem*> item) { | ||||||
| 	if (_lastMessage == item) { | 	if (lastMessage() == item) { | ||||||
|  | 		_lastMessage = base::none; | ||||||
| 		recountLastMessage(); | 		recountLastMessage(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Feed::historyCleared(not_null<History*> history) { | void Feed::historyCleared(not_null<History*> history) { | ||||||
| 	if (_lastMessage->history() == history) { | 	if (const auto last = lastMessage()) { | ||||||
| 		recountLastMessage(); | 		if (last->history() == history) { | ||||||
|  | 			messageRemoved(last); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Feed::recountLastMessage() { | void Feed::recountLastMessage() { | ||||||
|  | 	for (const auto history : _channels) { | ||||||
|  | 		if (!history->lastMessageKnown()) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	_lastMessage = nullptr; | 	_lastMessage = nullptr; | ||||||
| 	for (const auto history : _channels) { | 	for (const auto history : _channels) { | ||||||
| 		if (const auto last = history->lastMsg) { | 		if (const auto last = history->lastMessage()) { | ||||||
| 			justSetLastMessage(last); | 			justUpdateLastMessage(last); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if (_lastMessage) { | 	updateChatsListDate(); | ||||||
| 		setChatsListDate(_lastMessage->date); | } | ||||||
|  | 
 | ||||||
|  | void Feed::updateChatsListDate() { | ||||||
|  | 	if (_lastMessage && *_lastMessage) { | ||||||
|  | 		setChatsListDate((*_lastMessage)->date); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) { | HistoryItem *Feed::lastMessage() const { | ||||||
| 	_unreadCount = unreadCount; | 	return _lastMessage ? *_lastMessage : nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Feed::lastMessageKnown() const { | ||||||
|  | 	return !!_lastMessage; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Feed::applyDialog(const MTPDdialogFeed &data) { | ||||||
|  | 	const auto addChannel = [&](ChannelId channelId) { | ||||||
|  | 		if (const auto channel = App::channelLoaded(channelId)) { | ||||||
|  | 			channel->setFeed(this); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 	for (const auto &channelId : data.vfeed_other_channels.v) { | ||||||
|  | 		addChannel(channelId.v); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_lastMessage = nullptr; | ||||||
|  | 	if (const auto peerId = peerFromMTP(data.vpeer)) { | ||||||
|  | 		if (const auto channelId = peerToChannel(peerId)) { | ||||||
|  | 			addChannel(channelId); | ||||||
|  | 			const auto fullId = FullMsgId(channelId, data.vtop_message.v); | ||||||
|  | 			if (const auto item = App::histItemById(fullId)) { | ||||||
|  | 				justUpdateLastMessage(item); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	updateChatsListDate(); | ||||||
|  | 
 | ||||||
|  | 	setUnreadCounts( | ||||||
|  | 		data.vunread_count.v, | ||||||
|  | 		data.vunread_muted_count.v); | ||||||
|  | 	if (data.has_read_max_position()) { | ||||||
|  | 		setUnreadPosition(FeedPositionFromMTP(data.vread_max_position)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) { | ||||||
|  | 	_unreadCount = unreadNonMutedCount + unreadMutedCount; | ||||||
| 	_unreadMutedCount = unreadMutedCount; | 	_unreadMutedCount = unreadMutedCount; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Feed::setUnreadPosition(const MessagePosition &position) { | void Feed::setUnreadPosition(const MessagePosition &position) { | ||||||
|  | 	if (_unreadPosition.current() < position) { | ||||||
| 		_unreadPosition = position; | 		_unreadPosition = position; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Feed::unreadCountChanged( | ||||||
|  | 		base::optional<int> unreadCountDelta, | ||||||
|  | 		int mutedCountDelta) { | ||||||
|  | 	if (!_unreadCount) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (unreadCountDelta) { | ||||||
|  | 		*_unreadCount = std::max(*_unreadCount + *unreadCountDelta, 0); | ||||||
|  | 		_unreadMutedCount = snap( | ||||||
|  | 			_unreadMutedCount + mutedCountDelta, | ||||||
|  | 			0, | ||||||
|  | 			*_unreadCount); | ||||||
|  | 		updateChatListEntry(); | ||||||
|  | 	} else { | ||||||
|  | 		_parent->session().api().requestDialogEntry(this); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MessagePosition Feed::unreadPosition() const { | MessagePosition Feed::unreadPosition() const { | ||||||
|  | @ -265,15 +344,15 @@ bool Feed::shouldBeInChatList() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int Feed::chatListUnreadCount() const { | int Feed::chatListUnreadCount() const { | ||||||
| 	return _unreadCount; | 	return _unreadCount ? *_unreadCount : 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Feed::chatListMutedBadge() const { | bool Feed::chatListMutedBadge() const { | ||||||
| 	return _unreadCount <= _unreadMutedCount; | 	return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| HistoryItem *Feed::chatsListItem() const { | HistoryItem *Feed::chatsListItem() const { | ||||||
| 	return _lastMessage; | 	return lastMessage(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const QString &Feed::chatsListName() const { | const QString &Feed::chatsListName() const { | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position); | ||||||
| class Feed : public Dialogs::Entry { | class Feed : public Dialogs::Entry { | ||||||
| public: | public: | ||||||
| 	static constexpr auto kId = 1; | 	static constexpr auto kId = 1; | ||||||
|  | 	static constexpr auto kChannelsLimit = 1000; | ||||||
| 
 | 
 | ||||||
| 	Feed(FeedId id, not_null<Data::Session*> parent); | 	Feed(FeedId id, not_null<Data::Session*> parent); | ||||||
| 
 | 
 | ||||||
|  | @ -43,11 +44,18 @@ public: | ||||||
| 	void messageRemoved(not_null<HistoryItem*> item); | 	void messageRemoved(not_null<HistoryItem*> item); | ||||||
| 	void historyCleared(not_null<History*> history); | 	void historyCleared(not_null<History*> history); | ||||||
| 
 | 
 | ||||||
| 	void setUnreadCounts(int unreadCount, int unreadMutedCount); | 	void applyDialog(const MTPDdialogFeed &data); | ||||||
|  | 	void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount); | ||||||
| 	void setUnreadPosition(const MessagePosition &position); | 	void setUnreadPosition(const MessagePosition &position); | ||||||
|  | 	void unreadCountChanged( | ||||||
|  | 		base::optional<int> unreadCountDelta, | ||||||
|  | 		int mutedCountDelta); | ||||||
| 	MessagePosition unreadPosition() const; | 	MessagePosition unreadPosition() const; | ||||||
| 	rpl::producer<MessagePosition> unreadPositionChanges() const; | 	rpl::producer<MessagePosition> unreadPositionChanges() const; | ||||||
| 
 | 
 | ||||||
|  | 	HistoryItem *lastMessage() const; | ||||||
|  | 	bool lastMessageKnown() const; | ||||||
|  | 
 | ||||||
| 	bool toImportant() const override; | 	bool toImportant() const override; | ||||||
| 	bool shouldBeInChatList() const override; | 	bool shouldBeInChatList() const override; | ||||||
| 	int chatListUnreadCount() const override; | 	int chatListUnreadCount() const override; | ||||||
|  | @ -73,7 +81,8 @@ public: | ||||||
| private: | private: | ||||||
| 	void indexNameParts(); | 	void indexNameParts(); | ||||||
| 	void recountLastMessage(); | 	void recountLastMessage(); | ||||||
| 	bool justSetLastMessage(not_null<HistoryItem*> item); | 	bool justUpdateLastMessage(not_null<HistoryItem*> item); | ||||||
|  | 	void updateChatsListDate(); | ||||||
| 
 | 
 | ||||||
| 	FeedId _id = 0; | 	FeedId _id = 0; | ||||||
| 	not_null<Data::Session*> _parent; | 	not_null<Data::Session*> _parent; | ||||||
|  | @ -83,10 +92,10 @@ private: | ||||||
| 	QString _name; | 	QString _name; | ||||||
| 	base::flat_set<QString> _nameWords; | 	base::flat_set<QString> _nameWords; | ||||||
| 	base::flat_set<QChar> _nameFirstLetters; | 	base::flat_set<QChar> _nameFirstLetters; | ||||||
| 	HistoryItem *_lastMessage = nullptr; | 	base::optional<HistoryItem*> _lastMessage; | ||||||
| 
 | 
 | ||||||
| 	rpl::variable<MessagePosition> _unreadPosition; | 	rpl::variable<MessagePosition> _unreadPosition; | ||||||
| 	int _unreadCount = 0; | 	base::optional<int> _unreadCount; | ||||||
| 	int _unreadMutedCount = 0; | 	int _unreadMutedCount = 0; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -849,13 +849,22 @@ void ChannelData::setAvailableMinId(MsgId availableMinId) { | ||||||
| 		if (auto history = App::historyLoaded(this)) { | 		if (auto history = App::historyLoaded(this)) { | ||||||
| 			history->clearUpTill(availableMinId); | 			history->clearUpTill(availableMinId); | ||||||
| 		} | 		} | ||||||
|  | 		if (_pinnedMessageId <= _availableMinId) { | ||||||
|  | 			_pinnedMessageId = MsgId(0); | ||||||
|  | 			Notify::peerUpdatedDelayed( | ||||||
|  | 				this, | ||||||
|  | 				Notify::PeerUpdate::Flag::ChannelPinnedChanged); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ChannelData::setPinnedMessageId(MsgId messageId) { | void ChannelData::setPinnedMessageId(MsgId messageId) { | ||||||
|  | 	messageId = (messageId > _availableMinId) ? messageId : MsgId(0); | ||||||
| 	if (_pinnedMessageId != messageId) { | 	if (_pinnedMessageId != messageId) { | ||||||
| 		_pinnedMessageId = messageId; | 		_pinnedMessageId = messageId; | ||||||
| 		Notify::peerUpdatedDelayed(this, Notify::PeerUpdate::Flag::ChannelPinnedChanged); | 		Notify::peerUpdatedDelayed( | ||||||
|  | 			this, | ||||||
|  | 			Notify::PeerUpdate::Flag::ChannelPinnedChanged); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1726,76 +1726,32 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const auto history = App::historyFromDialog( | 	const auto history = App::history(peerId); | ||||||
| 		peerId, | 	history->applyDialog(dialog); | ||||||
| 		dialog.vunread_count.v, |  | ||||||
| 		dialog.vread_inbox_max_id.v, |  | ||||||
| 		dialog.vread_outbox_max_id.v); |  | ||||||
| 	history->setUnreadMentionsCount(dialog.vunread_mentions_count.v); |  | ||||||
| 	const auto peer = history->peer; |  | ||||||
| 	if (const auto channel = peer->asChannel()) { |  | ||||||
| 		if (dialog.has_pts()) { |  | ||||||
| 			channel->ptsReceived(dialog.vpts.v); |  | ||||||
| 		} |  | ||||||
| 		if (!channel->amCreator()) { |  | ||||||
| 			const auto topMsgId = FullMsgId( |  | ||||||
| 				peerToChannel(channel->id), |  | ||||||
| 				dialog.vtop_message.v); |  | ||||||
| 			if (const auto topMsg = App::histItemById(topMsgId)) { |  | ||||||
| 				if (topMsg->date <= date(channel->date)) { |  | ||||||
| 					Auth().api().requestSelfParticipant(channel); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	App::main()->applyNotifySetting( |  | ||||||
| 		MTP_notifyPeer(dialog.vpeer), |  | ||||||
| 		dialog.vnotify_settings, |  | ||||||
| 		history); |  | ||||||
| 
 | 
 | ||||||
| 	if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) { | 	if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) { | ||||||
| 		addSavedPeersAfter(history->chatsListDate()); | 		addSavedPeersAfter(history->chatsListDate()); | ||||||
| 	} | 	} | ||||||
| 	_contactsNoDialogs->del(history); | 	_contactsNoDialogs->del(history); | ||||||
| 	if (peer->migrateFrom()) { | 	if (const auto from = history->peer->migrateFrom()) { | ||||||
| 		if (const auto historyFrom = App::historyLoaded(peer->migrateFrom())) { | 		if (const auto historyFrom = App::historyLoaded(from)) { | ||||||
| 			removeDialog(historyFrom); | 			removeDialog(historyFrom); | ||||||
| 		} | 		} | ||||||
| 	} else if (peer->migrateTo() && peer->migrateTo()->amIn()) { | 	} else if (const auto to = history->peer->migrateTo()) { | ||||||
|  | 		if (to->amIn()) { | ||||||
| 			removeDialog(history); | 			removeDialog(history); | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 	if (dialog.has_draft() && dialog.vdraft.type() == mtpc_draftMessage) { |  | ||||||
| 		const auto &draft = dialog.vdraft.c_draftMessage(); |  | ||||||
| 		Data::applyPeerCloudDraft(peerId, draft); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) { | void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) { | ||||||
| 	const auto peerId = peerFromMTP(dialog.vpeer); |  | ||||||
| 	const auto feedId = dialog.vfeed_id.v; | 	const auto feedId = dialog.vfeed_id.v; | ||||||
| 	const auto feed = Auth().data().feed(feedId); | 	const auto feed = Auth().data().feed(feedId); | ||||||
| 	const auto addChannel = [&](ChannelId channelId) { | 	feed->applyDialog(dialog); | ||||||
| 		if (const auto channel = App::channelLoaded(channelId)) { | 
 | ||||||
| 			channel->setFeed(feed); |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 	if (peerId && peerIsChannel(peerId)) { |  | ||||||
| 		addChannel(peerToChannel(peerId)); |  | ||||||
| 	} |  | ||||||
| 	for (const auto &channelId : dialog.vfeed_other_channels.v) { |  | ||||||
| 		addChannel(channelId.v); |  | ||||||
| 	} |  | ||||||
| 	feed->setUnreadCounts( |  | ||||||
| 		dialog.vunread_count.v, |  | ||||||
| 		dialog.vunread_muted_count.v); |  | ||||||
| 	if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) { | 	if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) { | ||||||
| 		addSavedPeersAfter(feed->chatsListDate()); | 		addSavedPeersAfter(feed->chatsListDate()); | ||||||
| 	} | 	} | ||||||
| 	if (dialog.has_read_max_position()) { |  | ||||||
| 		feed->setUnreadPosition( |  | ||||||
| 			Data::FeedPositionFromMTP(dialog.vread_max_position)); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DialogsInner::addSavedPeersAfter(const QDateTime &date) { | void DialogsInner::addSavedPeersAfter(const QDateTime &date) { | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "base/flags.h" | #include "base/flags.h" | ||||||
| 
 | 
 | ||||||
| class History; | class History; | ||||||
| class ChannelHistory; |  | ||||||
| class HistoryBlock; | class HistoryBlock; | ||||||
| class HistoryItem; | class HistoryItem; | ||||||
| class HistoryMessage; | class HistoryMessage; | ||||||
|  | @ -50,12 +49,7 @@ enum NewMessageType : char { | ||||||
| 
 | 
 | ||||||
| class Histories { | class Histories { | ||||||
| public: | public: | ||||||
| 	using Map = QHash<PeerId, History*>; | 	Histories(); | ||||||
| 	Map map; |  | ||||||
| 
 |  | ||||||
| 	Histories() : _a_typings(animation(this, &Histories::step_typings)) { |  | ||||||
| 		_selfDestructTimer.setCallback([this] { checkSelfDestructItems(); }); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	void registerSendAction( | 	void registerSendAction( | ||||||
| 		not_null<History*> history, | 		not_null<History*> history, | ||||||
|  | @ -64,20 +58,16 @@ public: | ||||||
| 		TimeId when); | 		TimeId when); | ||||||
| 	void step_typings(TimeMs ms, bool timer); | 	void step_typings(TimeMs ms, bool timer); | ||||||
| 
 | 
 | ||||||
| 	History *find(const PeerId &peerId); | 	History *find(PeerId peerId) const; | ||||||
| 	not_null<History*> findOrInsert(const PeerId &peerId); | 	not_null<History*> findOrInsert(PeerId peerId); | ||||||
| 	not_null<History*> findOrInsert( |  | ||||||
| 		const PeerId &peerId, |  | ||||||
| 		int unreadCount, |  | ||||||
| 		MsgId maxInboxRead, |  | ||||||
| 		MsgId maxOutboxRead); |  | ||||||
| 
 | 
 | ||||||
| 	void clear(); | 	void clear(); | ||||||
| 	void remove(const PeerId &peer); | 	void remove(const PeerId &peer); | ||||||
| 
 | 
 | ||||||
| 	HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type); | 	HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type); | ||||||
| 
 | 
 | ||||||
| 	typedef QMap<History*, TimeMs> TypingHistories; // when typing in this history started
 | 	// When typing in this history started.
 | ||||||
|  | 	typedef QMap<History*, TimeMs> TypingHistories; | ||||||
| 	TypingHistories typing; | 	TypingHistories typing; | ||||||
| 	BasicAnimation _a_typings; | 	BasicAnimation _a_typings; | ||||||
| 
 | 
 | ||||||
|  | @ -114,6 +104,8 @@ public: | ||||||
| private: | private: | ||||||
| 	void checkSelfDestructItems(); | 	void checkSelfDestructItems(); | ||||||
| 
 | 
 | ||||||
|  | 	std::unordered_map<PeerId, std::unique_ptr<History>> _map; | ||||||
|  | 
 | ||||||
| 	int _unreadFull = 0; | 	int _unreadFull = 0; | ||||||
| 	int _unreadMuted = 0; | 	int _unreadMuted = 0; | ||||||
| 	base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated; | 	base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated; | ||||||
|  | @ -136,29 +128,24 @@ public: | ||||||
| 	History(const History &) = delete; | 	History(const History &) = delete; | ||||||
| 	History &operator=(const History &) = delete; | 	History &operator=(const History &) = delete; | ||||||
| 
 | 
 | ||||||
| 	ChannelId channelId() const { | 	ChannelId channelId() const; | ||||||
| 		return peerToChannel(peer->id); | 	bool isChannel() const; | ||||||
| 	} | 	bool isMegagroup() const; | ||||||
| 	bool isChannel() const { |  | ||||||
| 		return peerIsChannel(peer->id); |  | ||||||
| 	} |  | ||||||
| 	bool isMegagroup() const { |  | ||||||
| 		return peer->isMegagroup(); |  | ||||||
| 	} |  | ||||||
| 	ChannelHistory *asChannelHistory(); |  | ||||||
| 	const ChannelHistory *asChannelHistory() const; |  | ||||||
| 
 |  | ||||||
| 	not_null<History*> migrateToOrMe() const; | 	not_null<History*> migrateToOrMe() const; | ||||||
| 	History *migrateFrom() const; | 	History *migrateFrom() const; | ||||||
|  | 	MsgRange rangeForDifferenceRequest() const; | ||||||
|  | 	HistoryService *insertJoinedMessage(bool unread); | ||||||
|  | 	void checkJoinedMessage(bool createUnread = false); | ||||||
| 
 | 
 | ||||||
| 	bool isEmpty() const { | 	bool isEmpty() const; | ||||||
| 		return blocks.empty(); |  | ||||||
| 	} |  | ||||||
| 	bool isDisplayedEmpty() const; | 	bool isDisplayedEmpty() const; | ||||||
| 	bool hasOrphanMediaGroupPart() const; | 	bool hasOrphanMediaGroupPart() const; | ||||||
| 	bool removeOrphanMediaGroupPart(); | 	bool removeOrphanMediaGroupPart(); | ||||||
|  | 	QVector<MsgId> collectMessagesFromUserToDelete( | ||||||
|  | 		not_null<UserData*> user) const; | ||||||
| 
 | 
 | ||||||
| 	void clear(bool leaveItems = false); | 	void clear(); | ||||||
|  | 	void unloadBlocks(); | ||||||
| 	void clearUpTill(MsgId availableMinId); | 	void clearUpTill(MsgId availableMinId); | ||||||
| 
 | 
 | ||||||
| 	void applyGroupAdminChanges(const base::flat_map<UserId, bool> &changes); | 	void applyGroupAdminChanges(const base::flat_map<UserId, bool> &changes); | ||||||
|  | @ -184,18 +171,17 @@ public: | ||||||
| 	void newItemAdded(not_null<HistoryItem*> item); | 	void newItemAdded(not_null<HistoryItem*> item); | ||||||
| 
 | 
 | ||||||
| 	int countUnread(MsgId upTo); | 	int countUnread(MsgId upTo); | ||||||
| 	MsgId inboxRead(MsgId upTo); | 	MsgId readInbox(); | ||||||
| 	MsgId inboxRead(HistoryItem *wasRead); | 	void inboxRead(MsgId upTo); | ||||||
| 	MsgId outboxRead(MsgId upTo); | 	void inboxRead(not_null<const HistoryItem*> wasRead); | ||||||
| 	MsgId outboxRead(HistoryItem *wasRead); | 	void outboxRead(MsgId upTo); | ||||||
|  | 	void outboxRead(not_null<const HistoryItem*> wasRead); | ||||||
|  | 	bool isServerSideUnread(not_null<const HistoryItem*> item) const; | ||||||
|  | 	MsgId loadAroundId() const; | ||||||
| 
 | 
 | ||||||
| 	int unreadCount() const { | 	int unreadCount() const; | ||||||
| 		return _unreadCount; |  | ||||||
| 	} |  | ||||||
| 	void setUnreadCount(int newUnreadCount); | 	void setUnreadCount(int newUnreadCount); | ||||||
| 	bool mute() const { | 	bool mute() const; | ||||||
| 		return _mute; |  | ||||||
| 	} |  | ||||||
| 	bool changeMute(bool newMute); | 	bool changeMute(bool newMute); | ||||||
| 	void addUnreadBar(); | 	void addUnreadBar(); | ||||||
| 	void destroyUnreadBar(); | 	void destroyUnreadBar(); | ||||||
|  | @ -212,46 +198,34 @@ public: | ||||||
| 	bool isReadyFor(MsgId msgId); // has messages for showing history at msgId
 | 	bool isReadyFor(MsgId msgId); // has messages for showing history at msgId
 | ||||||
| 	void getReadyFor(MsgId msgId); | 	void getReadyFor(MsgId msgId); | ||||||
| 
 | 
 | ||||||
| 	void setLastMessage(HistoryItem *msg); | 	HistoryItem *lastMessage() const; | ||||||
| 	void fixLastMessage(bool wasAtBottom); | 	bool lastMessageKnown() const; | ||||||
|  | 	void unknownMessageDeleted(MsgId messageId); | ||||||
|  | 	void applyDialogTopMessage(MsgId topMessageId); | ||||||
|  | 	void applyDialog(const MTPDdialog &data); | ||||||
|  | 	void applyDialogFields( | ||||||
|  | 		int unreadCount, | ||||||
|  | 		MsgId maxInboxRead, | ||||||
|  | 		MsgId maxOutboxRead); | ||||||
| 
 | 
 | ||||||
| 	MsgId minMsgId() const; | 	MsgId minMsgId() const; | ||||||
| 	MsgId maxMsgId() const; | 	MsgId maxMsgId() const; | ||||||
| 	MsgId msgIdForRead() const; | 	MsgId msgIdForRead() const; | ||||||
| 
 | 
 | ||||||
| 	void resizeToWidth(int newWidth); | 	void resizeToWidth(int newWidth); | ||||||
|  | 	int height() const; | ||||||
| 
 | 
 | ||||||
| 	void removeNotification(HistoryItem *item) { | 	void itemRemoved(not_null<HistoryItem*> item); | ||||||
| 		if (!notifies.isEmpty()) { | 	void itemVanished(not_null<HistoryItem*> item); | ||||||
| 			for (auto i = notifies.begin(), e = notifies.end(); i != e; ++i) { |  | ||||||
| 				if ((*i) == item) { |  | ||||||
| 					notifies.erase(i); |  | ||||||
| 					break; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	HistoryItem *currentNotification() { |  | ||||||
| 		return notifies.isEmpty() ? 0 : notifies.front(); |  | ||||||
| 	} |  | ||||||
| 	bool hasNotification() const { |  | ||||||
| 		return !notifies.isEmpty(); |  | ||||||
| 	} |  | ||||||
| 	void skipNotification() { |  | ||||||
| 		if (!notifies.isEmpty()) { |  | ||||||
| 			notifies.pop_front(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	void popNotification(HistoryItem *item) { |  | ||||||
| 		if (!notifies.isEmpty() && notifies.back() == item) notifies.pop_back(); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	bool hasPendingResizedItems() const { | 	HistoryItem *currentNotification(); | ||||||
| 		return _flags & Flag::f_has_pending_resized_items; | 	bool hasNotification() const; | ||||||
| 	} | 	void skipNotification(); | ||||||
|  | 	void popNotification(HistoryItem *item); | ||||||
|  | 
 | ||||||
|  | 	bool hasPendingResizedItems() const; | ||||||
| 	void setHasPendingResizedItems(); | 	void setHasPendingResizedItems(); | ||||||
| 
 | 
 | ||||||
| 	void paintDialog(Painter &p, int32 w, bool sel) const; |  | ||||||
| 	bool mySendActionUpdated(SendAction::Type type, bool doing); | 	bool mySendActionUpdated(SendAction::Type type, bool doing); | ||||||
| 	bool paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, style::color color, TimeMs ms); | 	bool paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, style::color color, TimeMs ms); | ||||||
| 
 | 
 | ||||||
|  | @ -345,15 +319,9 @@ public: | ||||||
| 	// Still public data.
 | 	// Still public data.
 | ||||||
| 	std::deque<std::unique_ptr<HistoryBlock>> blocks; | 	std::deque<std::unique_ptr<HistoryBlock>> blocks; | ||||||
| 
 | 
 | ||||||
| 	int height() const; |  | ||||||
| 	int32 msgCount = 0; |  | ||||||
| 	MsgId inboxReadBefore = 1; |  | ||||||
| 	MsgId outboxReadBefore = 1; |  | ||||||
| 
 |  | ||||||
| 	not_null<PeerData*> peer; | 	not_null<PeerData*> peer; | ||||||
| 	bool oldLoaded = false; | 	bool oldLoaded = false; | ||||||
| 	bool newLoaded = true; | 	bool newLoaded = true; | ||||||
| 	HistoryItem *lastMsg = nullptr; |  | ||||||
| 	HistoryItem *lastSentMsg = nullptr; | 	HistoryItem *lastSentMsg = nullptr; | ||||||
| 
 | 
 | ||||||
| 	typedef QList<HistoryItem*> NotifyQueue; | 	typedef QList<HistoryItem*> NotifyQueue; | ||||||
|  | @ -379,7 +347,17 @@ public: | ||||||
| 
 | 
 | ||||||
| 	Text cloudDraftTextCache; | 	Text cloudDraftTextCache; | ||||||
| 
 | 
 | ||||||
| protected: | private: | ||||||
|  | 	friend class HistoryBlock; | ||||||
|  | 
 | ||||||
|  | 	enum class Flag { | ||||||
|  | 		f_has_pending_resized_items = (1 << 0), | ||||||
|  | 	}; | ||||||
|  | 	using Flags = base::flags<Flag>; | ||||||
|  | 	friend inline constexpr auto is_flag_type(Flag) { | ||||||
|  | 		return true; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	// when this item is destroyed scrollTopItem just points to the next one
 | 	// when this item is destroyed scrollTopItem just points to the next one
 | ||||||
| 	// and scrollTopOffset remains the same
 | 	// and scrollTopOffset remains the same
 | ||||||
| 	// if we are at the bottom of the window scrollTopItem == nullptr and
 | 	// if we are at the bottom of the window scrollTopItem == nullptr and
 | ||||||
|  | @ -389,7 +367,6 @@ protected: | ||||||
| 	// helper method for countScrollState(int top)
 | 	// helper method for countScrollState(int top)
 | ||||||
| 	void countScrollTopItem(int top); | 	void countScrollTopItem(int top); | ||||||
| 
 | 
 | ||||||
| 	void clearOnDestroy(); |  | ||||||
| 	HistoryItem *addNewToLastBlock(const MTPMessage &msg, NewMessageType type); | 	HistoryItem *addNewToLastBlock(const MTPMessage &msg, NewMessageType type); | ||||||
| 
 | 
 | ||||||
| 	// this method just removes a block from the blocks list
 | 	// this method just removes a block from the blocks list
 | ||||||
|  | @ -429,25 +406,18 @@ protected: | ||||||
| 		return _buildingFrontBlock != nullptr; | 		return _buildingFrontBlock != nullptr; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| private: |  | ||||||
| 	friend class HistoryBlock; |  | ||||||
| 
 |  | ||||||
| 	enum class Flag { |  | ||||||
| 		f_has_pending_resized_items = (1 << 0), |  | ||||||
| 	}; |  | ||||||
| 	using Flags = base::flags<Flag>; |  | ||||||
| 	friend inline constexpr auto is_flag_type(Flag) { |  | ||||||
| 		return true; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	void mainViewRemoved( | 	void mainViewRemoved( | ||||||
| 		not_null<HistoryBlock*> block, | 		not_null<HistoryBlock*> block, | ||||||
| 		not_null<Element*> view); | 		not_null<Element*> view); | ||||||
|  | 	void removeNotification(not_null<HistoryItem*> item); | ||||||
| 
 | 
 | ||||||
| 	QDateTime adjustChatListDate() const override; | 	QDateTime adjustChatListDate() const override; | ||||||
| 	void changedInChatListHook(Dialogs::Mode list, bool added) override; | 	void changedInChatListHook(Dialogs::Mode list, bool added) override; | ||||||
| 	void changedChatListPinHook() override; | 	void changedChatListPinHook() override; | ||||||
| 
 | 
 | ||||||
|  | 	void setInboxReadTill(MsgId upTo); | ||||||
|  | 	void setOutboxReadTill(MsgId upTo); | ||||||
|  | 
 | ||||||
| 	void applyMessageChanges( | 	void applyMessageChanges( | ||||||
| 		not_null<HistoryItem*> item, | 		not_null<HistoryItem*> item, | ||||||
| 		const MTPMessage &original); | 		const MTPMessage &original); | ||||||
|  | @ -455,14 +425,13 @@ private: | ||||||
| 		not_null<HistoryItem*> item, | 		not_null<HistoryItem*> item, | ||||||
| 		const MTPDmessageService &data); | 		const MTPDmessageService &data); | ||||||
| 
 | 
 | ||||||
| 	// After adding a new history slice check the lastMsg and newLoaded.
 | 	// After adding a new history slice check the lastMessage and newLoaded.
 | ||||||
| 	void checkLastMsg(); | 	void checkLastMessage(); | ||||||
|  | 	void setLastMessage(HistoryItem *item); | ||||||
| 
 | 
 | ||||||
| 	// Add all items to the unread mentions if we were not loaded at bottom and now are.
 | 	// Add all items to the unread mentions if we were not loaded at bottom and now are.
 | ||||||
| 	void checkAddAllToUnreadMentions(); | 	void checkAddAllToUnreadMentions(); | ||||||
| 
 | 
 | ||||||
| 	template <int kSharedMediaTypeCount> |  | ||||||
| 	void addToSharedMedia(std::vector<MsgId> (&medias)[kSharedMediaTypeCount], bool force); |  | ||||||
| 	void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items); | 	void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items); | ||||||
| 	void addEdgesToSharedMedia(); | 	void addEdgesToSharedMedia(); | ||||||
| 
 | 
 | ||||||
|  | @ -480,14 +449,18 @@ private: | ||||||
| 
 | 
 | ||||||
| 	Flags _flags = 0; | 	Flags _flags = 0; | ||||||
| 	bool _mute = false; | 	bool _mute = false; | ||||||
| 	int _unreadCount = 0; |  | ||||||
| 	int _width = 0; | 	int _width = 0; | ||||||
| 	int _height = 0; | 	int _height = 0; | ||||||
| 	Element *_unreadBarView = nullptr; | 	Element *_unreadBarView = nullptr; | ||||||
| 	Element *_firstUnreadView = nullptr; | 	Element *_firstUnreadView = nullptr; | ||||||
|  | 	HistoryService *_joinedMessage = nullptr; | ||||||
| 
 | 
 | ||||||
|  | 	base::optional<MsgId> _inboxReadBefore; | ||||||
|  | 	base::optional<MsgId> _outboxReadBefore; | ||||||
|  | 	base::optional<int> _unreadCount; | ||||||
| 	base::optional<int> _unreadMentionsCount; | 	base::optional<int> _unreadMentionsCount; | ||||||
| 	base::flat_set<MsgId> _unreadMentions; | 	base::flat_set<MsgId> _unreadMentions; | ||||||
|  | 	base::optional<HistoryItem*> _lastMessage; | ||||||
| 
 | 
 | ||||||
| 	// A pointer to the block that is currently being built.
 | 	// A pointer to the block that is currently being built.
 | ||||||
| 	// We hold this pointer so we can destroy it while building
 | 	// We hold this pointer so we can destroy it while building
 | ||||||
|  | @ -517,37 +490,6 @@ private: | ||||||
| 
 | 
 | ||||||
|  }; |  }; | ||||||
| 
 | 
 | ||||||
| class ChannelHistory : public History { |  | ||||||
| public: |  | ||||||
| 	using History::History; |  | ||||||
| 
 |  | ||||||
| 	void messageDetached(not_null<HistoryItem*> message); |  | ||||||
| 
 |  | ||||||
| 	void getRangeDifference(); |  | ||||||
| 	void getRangeDifferenceNext(int32 pts); |  | ||||||
| 
 |  | ||||||
| 	HistoryService *insertJoinedMessage(bool unread); |  | ||||||
| 	void checkJoinedMessage(bool createUnread = false); |  | ||||||
| 	const QDateTime &maxReadMessageDate(); |  | ||||||
| 
 |  | ||||||
| 	~ChannelHistory(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	friend class History; |  | ||||||
| 
 |  | ||||||
| 	void checkMaxReadMessageDate(); |  | ||||||
| 	void cleared(bool leaveItems); |  | ||||||
| 
 |  | ||||||
| 	QDateTime _maxReadMessageDate; |  | ||||||
| 
 |  | ||||||
| 	HistoryService *_joinedMessage = nullptr; |  | ||||||
| 
 |  | ||||||
| 	MsgId _rangeDifferenceFromId, _rangeDifferenceToId; |  | ||||||
| 	int32 _rangeDifferencePts; |  | ||||||
| 	mtpRequestId _rangeDifferenceRequestId; |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class HistoryBlock { | class HistoryBlock { | ||||||
| public: | public: | ||||||
| 	using Element = HistoryView::Element; | 	using Element = HistoryView::Element; | ||||||
|  | @ -559,7 +501,6 @@ public: | ||||||
| 
 | 
 | ||||||
| 	std::vector<std::unique_ptr<Element>> messages; | 	std::vector<std::unique_ptr<Element>> messages; | ||||||
| 
 | 
 | ||||||
| 	void clear(bool leaveItems = false); |  | ||||||
| 	void remove(not_null<Element*> view); | 	void remove(not_null<Element*> view); | ||||||
| 	void refreshView(not_null<Element*> view); | 	void refreshView(not_null<Element*> view); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -178,11 +178,15 @@ HistoryInner::HistoryInner( | ||||||
| 	}, lifetime()); | 	}, lifetime()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryInner::messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages) { | void HistoryInner::messagesReceived( | ||||||
|  | 		PeerData *peer, | ||||||
|  | 		const QVector<MTPMessage> &messages) { | ||||||
| 	if (_history && _history->peer == peer) { | 	if (_history && _history->peer == peer) { | ||||||
| 		_history->addOlderSlice(messages); | 		_history->addOlderSlice(messages); | ||||||
| 	} else if (_migrated && _migrated->peer == peer) { | 	} else if (_migrated && _migrated->peer == peer) { | ||||||
| 		bool newLoaded = (_migrated && _migrated->isEmpty() && !_history->isEmpty()); | 		const auto newLoaded = _migrated | ||||||
|  | 			&& _migrated->isEmpty() | ||||||
|  | 			&& !_history->isEmpty(); | ||||||
| 		_migrated->addOlderSlice(messages); | 		_migrated->addOlderSlice(messages); | ||||||
| 		if (newLoaded) { | 		if (newLoaded) { | ||||||
| 			_migrated->addNewerSlice(QVector<MTPMessage>()); | 			_migrated->addNewerSlice(QVector<MTPMessage>()); | ||||||
|  |  | ||||||
|  | @ -148,19 +148,7 @@ void HistoryItem::invalidateChatsListEntry() { | ||||||
| 
 | 
 | ||||||
| void HistoryItem::finishEditionToEmpty() { | void HistoryItem::finishEditionToEmpty() { | ||||||
| 	finishEdition(-1); | 	finishEdition(-1); | ||||||
| 
 | 	_history->itemVanished(this); | ||||||
| 	_history->removeNotification(this); |  | ||||||
| 	if (auto channel = history()->peer->asChannel()) { |  | ||||||
| 		if (channel->pinnedMessageId() == id) { |  | ||||||
| 			channel->clearPinnedMessage(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if (history()->lastKeyboardId == id) { |  | ||||||
| 		history()->clearLastKeyboard(); |  | ||||||
| 	} |  | ||||||
| 	if ((!out() || isPost()) && unread() && history()->unreadCount() > 0) { |  | ||||||
| 		history()->setUnreadCount(history()->unreadCount() - 1); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryItem::isMediaUnread() const { | bool HistoryItem::isMediaUnread() const { | ||||||
|  | @ -244,7 +232,7 @@ void HistoryItem::destroy() { | ||||||
| 	if (isLogEntry()) { | 	if (isLogEntry()) { | ||||||
| 		Assert(!mainView()); | 		Assert(!mainView()); | ||||||
| 	} else { | 	} else { | ||||||
| 		// All this must be done for all items manually in History::clear(false)!
 | 		// All this must be done for all items manually in History::clear()!
 | ||||||
| 		eraseFromUnreadMentions(); | 		eraseFromUnreadMentions(); | ||||||
| 		if (IsServerMsgId(id)) { | 		if (IsServerMsgId(id)) { | ||||||
| 			if (const auto types = sharedMediaTypes()) { | 			if (const auto types = sharedMediaTypes()) { | ||||||
|  | @ -256,32 +244,7 @@ void HistoryItem::destroy() { | ||||||
| 		} else { | 		} else { | ||||||
| 			Auth().api().cancelLocalItem(this); | 			Auth().api().cancelLocalItem(this); | ||||||
| 		} | 		} | ||||||
| 
 | 		_history->itemRemoved(this); | ||||||
| 		const auto wasAtBottom = history->loadedAtBottom(); |  | ||||||
| 		history->removeNotification(this); |  | ||||||
| 
 |  | ||||||
| 		removeMainView(); |  | ||||||
| 
 |  | ||||||
| 		if (history->lastMsg == this) { |  | ||||||
| 			history->fixLastMessage(wasAtBottom); |  | ||||||
| 		} |  | ||||||
| 		if (history->lastKeyboardId == id) { |  | ||||||
| 			history->clearLastKeyboard(); |  | ||||||
| 		} |  | ||||||
| 		if ((!out() || isPost()) && unread() && history->unreadCount() > 0) { |  | ||||||
| 			history->setUnreadCount(history->unreadCount() - 1); |  | ||||||
| 		} |  | ||||||
| 		if (const auto channel = history->peer->asChannel()) { |  | ||||||
| 			if (channel->pinnedMessageId() == id) { |  | ||||||
| 				channel->clearPinnedMessage(); |  | ||||||
| 			} |  | ||||||
| 			if (const auto feed = channel->feed()) { |  | ||||||
| 				// Must be after histroy->lastMsg is cleared.
 |  | ||||||
| 				// Otherwise feed last message will be this value again.
 |  | ||||||
| 				feed->messageRemoved(this); |  | ||||||
| 				// #TODO feeds unread
 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	delete this; | 	delete this; | ||||||
| } | } | ||||||
|  | @ -295,9 +258,6 @@ void HistoryItem::refreshMainView() { | ||||||
| 
 | 
 | ||||||
| void HistoryItem::removeMainView() { | void HistoryItem::removeMainView() { | ||||||
| 	if (const auto view = mainView()) { | 	if (const auto view = mainView()) { | ||||||
| 		if (const auto channelHistory = _history->asChannelHistory()) { |  | ||||||
| 			channelHistory->messageDetached(this); |  | ||||||
| 		} |  | ||||||
| 		Auth().data().notifyHistoryChangeDelayed(_history); | 		Auth().data().notifyHistoryChangeDelayed(_history); | ||||||
| 		view->removeFromBlock(); | 		view->removeFromBlock(); | ||||||
| 	} | 	} | ||||||
|  | @ -551,11 +511,11 @@ bool HistoryItem::unread() const { | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (id > 0) { | 		if (IsServerMsgId(id)) { | ||||||
| 			if (id < history()->outboxReadBefore) { | 			if (!history()->isServerSideUnread(this)) { | ||||||
| 				return false; | 				return false; | ||||||
| 			} | 			} | ||||||
| 			if (auto user = history()->peer->asUser()) { | 			if (const auto user = history()->peer->asUser()) { | ||||||
| 				if (user->botInfo) { | 				if (user->botInfo) { | ||||||
| 					return false; | 					return false; | ||||||
| 				} | 				} | ||||||
|  | @ -568,8 +528,8 @@ bool HistoryItem::unread() const { | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (id > 0) { | 	if (IsServerMsgId(id)) { | ||||||
| 		if (id < history()->inboxReadBefore) { | 		if (!history()->isServerSideUnread(this)) { | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 		return true; | 		return true; | ||||||
|  | @ -587,24 +547,6 @@ bool HistoryItem::isEmpty() const { | ||||||
| 		&& !Has<HistoryMessageLogEntryOriginal>(); | 		&& !Has<HistoryMessageLogEntryOriginal>(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| HistoryItem *HistoryItem::previousItem() const { |  | ||||||
| 	if (const auto view = mainView()) { |  | ||||||
| 		if (const auto previous = view->previousInBlocks()) { |  | ||||||
| 			return previous->data(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nullptr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| HistoryItem *HistoryItem::nextItem() const { |  | ||||||
| 	if (const auto view = mainView()) { |  | ||||||
| 		if (const auto next = view->nextInBlocks()) { |  | ||||||
| 			return next->data(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nullptr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| QString HistoryItem::notificationText() const { | QString HistoryItem::notificationText() const { | ||||||
| 	auto getText = [this]() { | 	auto getText = [this]() { | ||||||
| 		if (_media) { | 		if (_media) { | ||||||
|  |  | ||||||
|  | @ -254,9 +254,6 @@ public: | ||||||
| 
 | 
 | ||||||
| 	MessageGroupId groupId() const; | 	MessageGroupId groupId() const; | ||||||
| 
 | 
 | ||||||
| 	HistoryItem *previousItem() const; |  | ||||||
| 	HistoryItem *nextItem() const; |  | ||||||
| 
 |  | ||||||
| 	virtual std::unique_ptr<HistoryView::Element> createView( | 	virtual std::unique_ptr<HistoryView::Element> createView( | ||||||
| 		not_null<HistoryView::ElementDelegate*> delegate) = 0; | 		not_null<HistoryView::ElementDelegate*> delegate) = 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2256,9 +2256,7 @@ void HistoryWidget::historyToDown(History *history) { | ||||||
| void HistoryWidget::unreadCountChanged(not_null<History*> history) { | void HistoryWidget::unreadCountChanged(not_null<History*> history) { | ||||||
| 	if (history == _history || history == _migrated) { | 	if (history == _history || history == _migrated) { | ||||||
| 		updateHistoryDownVisibility(); | 		updateHistoryDownVisibility(); | ||||||
| 		_historyDown->setUnreadCount( | 		_historyDown->setUnreadCount(_history->chatListUnreadCount()); | ||||||
| 			_history->unreadCount() |  | ||||||
| 			+ (_migrated ? _migrated->unreadCount() : 0)); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2402,9 +2400,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages | ||||||
| 		if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation(); | 		if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation(); | ||||||
| 	} else if (_firstLoadRequest == requestId) { | 	} else if (_firstLoadRequest == requestId) { | ||||||
| 		if (toMigrated) { | 		if (toMigrated) { | ||||||
| 			_history->clear(true); | 			_history->unloadBlocks(); | ||||||
| 		} else if (_migrated) { | 		} else if (_migrated) { | ||||||
| 			_migrated->clear(true); | 			_migrated->unloadBlocks(); | ||||||
| 		} | 		} | ||||||
| 		addMessagesToFront(peer, *histList); | 		addMessagesToFront(peer, *histList); | ||||||
| 		_firstLoadRequest = 0; | 		_firstLoadRequest = 0; | ||||||
|  | @ -2421,9 +2419,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages | ||||||
| 		historyLoaded(); | 		historyLoaded(); | ||||||
| 	} else if (_delayedShowAtRequest == requestId) { | 	} else if (_delayedShowAtRequest == requestId) { | ||||||
| 		if (toMigrated) { | 		if (toMigrated) { | ||||||
| 			_history->clear(true); | 			_history->unloadBlocks(); | ||||||
| 		} else if (_migrated) { | 		} else if (_migrated) { | ||||||
| 			_migrated->clear(true); | 			_migrated->unloadBlocks(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		_delayedShowAtRequest = 0; | 		_delayedShowAtRequest = 0; | ||||||
|  | @ -2506,15 +2504,15 @@ void HistoryWidget::firstLoadMessages() { | ||||||
| 	auto offset = 0; | 	auto offset = 0; | ||||||
| 	auto loadCount = kMessagesPerPage; | 	auto loadCount = kMessagesPerPage; | ||||||
| 	if (_showAtMsgId == ShowAtUnreadMsgId) { | 	if (_showAtMsgId == ShowAtUnreadMsgId) { | ||||||
| 		if (_migrated && _migrated->unreadCount()) { | 		if (const auto around = _migrated ? _migrated->loadAroundId() : 0) { | ||||||
| 			_history->getReadyFor(_showAtMsgId); | 			_history->getReadyFor(_showAtMsgId); | ||||||
| 			from = _migrated->peer; | 			from = _migrated->peer; | ||||||
| 			offset = -loadCount / 2; | 			offset = -loadCount / 2; | ||||||
| 			offsetId = _migrated->inboxReadBefore; | 			offsetId = around; | ||||||
| 		} else if (_history->unreadCount()) { | 		} else if (const auto around = _history->loadAroundId()) { | ||||||
| 			_history->getReadyFor(_showAtMsgId); | 			_history->getReadyFor(_showAtMsgId); | ||||||
| 			offset = -loadCount / 2; | 			offset = -loadCount / 2; | ||||||
| 			offsetId = _history->inboxReadBefore; | 			offsetId = around; | ||||||
| 		} else { | 		} else { | ||||||
| 			_history->getReadyFor(ShowAtTheEndMsgId); | 			_history->getReadyFor(ShowAtTheEndMsgId); | ||||||
| 		} | 		} | ||||||
|  | @ -2651,13 +2649,13 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { | ||||||
| 	auto offset = 0; | 	auto offset = 0; | ||||||
| 	auto loadCount = kMessagesPerPage; | 	auto loadCount = kMessagesPerPage; | ||||||
| 	if (_delayedShowAtMsgId == ShowAtUnreadMsgId) { | 	if (_delayedShowAtMsgId == ShowAtUnreadMsgId) { | ||||||
| 		if (_migrated && _migrated->unreadCount()) { | 		if (const auto around = _migrated ? _migrated->loadAroundId() : 0) { | ||||||
| 			from = _migrated->peer; | 			from = _migrated->peer; | ||||||
| 			offset = -loadCount / 2; | 			offset = -loadCount / 2; | ||||||
| 			offsetId = _migrated->inboxReadBefore; | 			offsetId = around; | ||||||
| 		} else if (_history->unreadCount()) { | 		} else if (const auto around = _history->loadAroundId()) { | ||||||
| 			offset = -loadCount / 2; | 			offset = -loadCount / 2; | ||||||
| 			offsetId = _history->inboxReadBefore; | 			offsetId = around; | ||||||
| 		} else { | 		} else { | ||||||
| 			loadCount = kMessagesPerPageFirst; | 			loadCount = kMessagesPerPageFirst; | ||||||
| 		} | 		} | ||||||
|  | @ -3630,8 +3628,17 @@ bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::isBotStart() const { | bool HistoryWidget::isBotStart() const { | ||||||
| 	if (!_peer || !_peer->isUser() || !_peer->asUser()->botInfo || !_canSendMessages) return false; | 	const auto user = _peer ? _peer->asUser() : nullptr; | ||||||
| 	return !_peer->asUser()->botInfo->startToken.isEmpty() || (_history->isEmpty() && !_history->lastMsg); | 	if (!user | ||||||
|  | 		|| !user->botInfo | ||||||
|  | 		|| !_canSendMessages) { | ||||||
|  | 		return false; | ||||||
|  | 	} else if (!user->botInfo->startToken.isEmpty()) { | ||||||
|  | 		return true; | ||||||
|  | 	} else if (_history->isEmpty() && !_history->lastMessage()) { | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::isBlocked() const { | bool HistoryWidget::isBlocked() const { | ||||||
|  | @ -5126,14 +5133,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { | ||||||
| 		if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { | 		if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { | ||||||
| 			_scroll->keyPressEvent(e); | 			_scroll->keyPressEvent(e); | ||||||
| 		} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) { | 		} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) { | ||||||
| 			if (_history && _history->lastMsg && !_editMsgId) { | 			replyToNextMessage(); | ||||||
| 				if (const auto item = App::histItemById(_history->channelId(), _replyToId)) { |  | ||||||
| 					if (const auto next = item->nextItem()) { |  | ||||||
| 						Ui::showPeerHistory(_peer, next->id); |  | ||||||
| 						replyToMessage(next); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} else if (e->key() == Qt::Key_Up) { | 	} else if (e->key() == Qt::Key_Up) { | ||||||
| 		if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { | 		if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { | ||||||
|  | @ -5145,17 +5145,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { | ||||||
| 			} | 			} | ||||||
| 			_scroll->keyPressEvent(e); | 			_scroll->keyPressEvent(e); | ||||||
| 		} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) { | 		} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) { | ||||||
| 			if (_history && _history->lastMsg && !_editMsgId) { | 			replyToPreviousMessage(); | ||||||
| 				if (const auto item = App::histItemById(_history->channelId(), _replyToId)) { |  | ||||||
| 					if (const auto previous = item->previousItem()) { |  | ||||||
| 						Ui::showPeerHistory(_peer, previous->id); |  | ||||||
| 						replyToMessage(previous); |  | ||||||
| 					} |  | ||||||
| 				} else if (const auto previous = _history->lastMsg) { |  | ||||||
| 					Ui::showPeerHistory(_peer, previous->id); |  | ||||||
| 					replyToMessage(previous); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { | 	} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { | ||||||
| 		onListEnterPressed(); | 		onListEnterPressed(); | ||||||
|  | @ -5164,6 +5154,45 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HistoryWidget::replyToPreviousMessage() { | ||||||
|  | 	if (!_history || _editMsgId) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	const auto fullId = FullMsgId( | ||||||
|  | 		_history->channelId(), | ||||||
|  | 		_replyToId); | ||||||
|  | 	if (const auto item = App::histItemById(fullId)) { | ||||||
|  | 		if (const auto view = item->mainView()) { | ||||||
|  | 			if (const auto previousView = view->previousInBlocks()) { | ||||||
|  | 				const auto previous = previousView->data(); | ||||||
|  | 				Ui::showPeerHistoryAtItem(previous); | ||||||
|  | 				replyToMessage(previous); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else if (const auto previous = _history->lastMessage()) { | ||||||
|  | 		Ui::showPeerHistoryAtItem(previous); | ||||||
|  | 		replyToMessage(previous); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HistoryWidget::replyToNextMessage() { | ||||||
|  | 	if (!_history || _editMsgId) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	const auto fullId = FullMsgId( | ||||||
|  | 		_history->channelId(), | ||||||
|  | 		_replyToId); | ||||||
|  | 	if (const auto item = App::histItemById(fullId)) { | ||||||
|  | 		if (const auto view = item->mainView()) { | ||||||
|  | 			if (const auto nextView = view->nextInBlocks()) { | ||||||
|  | 				const auto next = nextView->data(); | ||||||
|  | 				Ui::showPeerHistoryAtItem(next); | ||||||
|  | 				replyToMessage(next); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void HistoryWidget::onFieldTabbed() { | void HistoryWidget::onFieldTabbed() { | ||||||
| 	if (!_fieldAutocomplete->isHidden()) { | 	if (!_fieldAutocomplete->isHidden()) { | ||||||
| 		_fieldAutocomplete->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab); | 		_fieldAutocomplete->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab); | ||||||
|  |  | ||||||
|  | @ -538,6 +538,8 @@ private: | ||||||
| 	void applyInlineBotQuery(UserData *bot, const QString &query); | 	void applyInlineBotQuery(UserData *bot, const QString &query); | ||||||
| 
 | 
 | ||||||
| 	void cancelReplyAfterMediaSend(bool lastKeyboardUsed); | 	void cancelReplyAfterMediaSend(bool lastKeyboardUsed); | ||||||
|  | 	void replyToPreviousMessage(); | ||||||
|  | 	void replyToNextMessage(); | ||||||
| 
 | 
 | ||||||
| 	void hideSelectorControlsAnimated(); | 	void hideSelectorControlsAnimated(); | ||||||
| 	int countMembersDropdownHeightMax() const; | 	int countMembersDropdownHeightMax() const; | ||||||
|  |  | ||||||
|  | @ -611,6 +611,9 @@ void Element::clickHandlerPressedChanged( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Element::~Element() { | Element::~Element() { | ||||||
|  | 	if (_data->mainView() == this) { | ||||||
|  | 		_data->clearMainView(); | ||||||
|  | 	} | ||||||
| 	Auth().data().unregisterItemView(this); | 	Auth().data().unregisterItemView(this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -887,21 +887,27 @@ void MainWidget::deleteLayer(FullMsgId itemId) { | ||||||
| void MainWidget::cancelUploadLayer(not_null<HistoryItem*> item) { | void MainWidget::cancelUploadLayer(not_null<HistoryItem*> item) { | ||||||
| 	const auto itemId = item->fullId(); | 	const auto itemId = item->fullId(); | ||||||
| 	Auth().uploader().pause(itemId); | 	Auth().uploader().pause(itemId); | ||||||
| 	Ui::show(Box<ConfirmBox>(lang(lng_selected_cancel_sure_this), lang(lng_selected_upload_stop), lang(lng_continue), base::lambda_guarded(this, [=] { | 	const auto stopUpload = [=] { | ||||||
| 		Ui::hideLayer(); | 		Ui::hideLayer(); | ||||||
| 		if (const auto item = App::histItemById(itemId)) { | 		if (const auto item = App::histItemById(itemId)) { | ||||||
| 			const auto history = item->history(); | 			const auto history = item->history(); | ||||||
| 			const auto wasLast = (history->lastMsg == item); |  | ||||||
| 			item->destroy(); | 			item->destroy(); | ||||||
| 			if (wasLast && !history->lastMsg) { | 			if (!history->lastMessageKnown()) { | ||||||
| 				checkPeerHistory(history->peer); | 				Auth().api().requestDialogEntry(history); | ||||||
| 			} | 			} | ||||||
| 			Auth().data().sendHistoryChangeNotifications(); | 			Auth().data().sendHistoryChangeNotifications(); | ||||||
| 		} | 		} | ||||||
| 		Auth().uploader().unpause(); | 		Auth().uploader().unpause(); | ||||||
| 	}), base::lambda_guarded(this, [] { | 	}; | ||||||
|  | 	const auto continueUpload = [=] { | ||||||
| 		Auth().uploader().unpause(); | 		Auth().uploader().unpause(); | ||||||
| 	}))); | 	}; | ||||||
|  | 	Ui::show(Box<ConfirmBox>( | ||||||
|  | 		lang(lng_selected_cancel_sure_this), | ||||||
|  | 		lang(lng_selected_upload_stop), | ||||||
|  | 		lang(lng_continue), | ||||||
|  | 		stopUpload, | ||||||
|  | 		continueUpload)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::deletePhotoLayer(PhotoData *photo) { | void MainWidget::deletePhotoLayer(PhotoData *photo) { | ||||||
|  | @ -1041,12 +1047,11 @@ void MainWidget::deleteConversation( | ||||||
| 	if (const auto history = App::historyLoaded(peer->id)) { | 	if (const auto history = App::historyLoaded(peer->id)) { | ||||||
| 		Auth().data().setPinnedDialog(history, false); | 		Auth().data().setPinnedDialog(history, false); | ||||||
| 		removeDialog(history); | 		removeDialog(history); | ||||||
| 		if (peer->isMegagroup() && peer->asChannel()->mgInfo->migrateFromPtr) { | 		if (const auto channel = peer->asMegagroup()) { | ||||||
| 			if (auto migrated = App::historyLoaded(peer->asChannel()->mgInfo->migrateFromPtr->id)) { | 			channel->addFlags(MTPDchannel::Flag::f_left); | ||||||
| 				if (migrated->lastMsg) { // return initial dialog
 | 			if (const auto from = channel->mgInfo->migrateFromPtr) { | ||||||
| 					migrated->setLastMessage(migrated->lastMsg); | 				if (const auto migrated = App::historyLoaded(from)) { | ||||||
| 				} else { | 					migrated->updateChatListExistence(); | ||||||
| 					checkPeerHistory(migrated->peer); |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -1059,7 +1064,12 @@ void MainWidget::deleteConversation( | ||||||
| 	} | 	} | ||||||
| 	if (deleteHistory) { | 	if (deleteHistory) { | ||||||
| 		DeleteHistoryRequest request = { peer, false }; | 		DeleteHistoryRequest request = { peer, false }; | ||||||
| 		MTP::send(MTPmessages_DeleteHistory(MTP_flags(0), peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, request)); | 		MTP::send( | ||||||
|  | 			MTPmessages_DeleteHistory( | ||||||
|  | 				MTP_flags(0), | ||||||
|  | 				peer->input, | ||||||
|  | 				MTP_int(0)), | ||||||
|  | 			rpcDone(&MainWidget::deleteHistoryPart, request)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1068,47 +1078,6 @@ void MainWidget::deleteAndExit(ChatData *chat) { | ||||||
| 	MTP::send(MTPmessages_DeleteChatUser(chat->inputChat, App::self()->inputUser), rpcDone(&MainWidget::deleteHistoryAfterLeave, peer), rpcFail(&MainWidget::leaveChatFailed, peer)); | 	MTP::send(MTPmessages_DeleteChatUser(chat->inputChat, App::self()->inputUser), rpcDone(&MainWidget::deleteHistoryAfterLeave, peer), rpcFail(&MainWidget::leaveChatFailed, peer)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::deleteAllFromUser(ChannelData *channel, UserData *from) { |  | ||||||
| 	Assert(channel != nullptr && from != nullptr); |  | ||||||
| 
 |  | ||||||
| 	QVector<MsgId> toDestroy; |  | ||||||
| 	if (auto history = App::historyLoaded(channel->id)) { |  | ||||||
| 		for (const auto &block : history->blocks) { |  | ||||||
| 			for (const auto &message : block->messages) { |  | ||||||
| 				const auto item = message->data(); |  | ||||||
| 				if (item->from() == from && item->canDelete()) { |  | ||||||
| 					toDestroy.push_back(item->id); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		for_const (auto &msgId, toDestroy) { |  | ||||||
| 			if (auto item = App::histItemById(peerToChannel(channel->id), msgId)) { |  | ||||||
| 				item->destroy(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	MTP::send( |  | ||||||
| 		MTPchannels_DeleteUserHistory( |  | ||||||
| 			channel->inputChannel, |  | ||||||
| 			from->inputUser), |  | ||||||
| 		rpcDone(&MainWidget::deleteAllFromUserPart, { channel, from })); |  | ||||||
| 	Auth().data().sendHistoryChangeNotifications(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void MainWidget::deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result) { |  | ||||||
| 	auto &d = result.c_messages_affectedHistory(); |  | ||||||
| 	params.channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v); |  | ||||||
| 
 |  | ||||||
| 	auto offset = d.voffset.v; |  | ||||||
| 	if (offset > 0) { |  | ||||||
| 		MTP::send(MTPchannels_DeleteUserHistory(params.channel->inputChannel, params.from->inputUser), rpcDone(&MainWidget::deleteAllFromUserPart, params)); |  | ||||||
| 	} else if (auto h = App::historyLoaded(params.channel)) { |  | ||||||
| 		if (!h->lastMsg) { |  | ||||||
| 			checkPeerHistory(params.channel); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void MainWidget::addParticipants( | void MainWidget::addParticipants( | ||||||
| 		not_null<PeerData*> chatOrChannel, | 		not_null<PeerData*> chatOrChannel, | ||||||
| 		const std::vector<not_null<UserData*>> &users) { | 		const std::vector<not_null<UserData*>> &users) { | ||||||
|  | @ -1194,99 +1163,6 @@ bool MainWidget::addParticipantsFail( | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::checkPeerHistory(PeerData *peer) { |  | ||||||
| 	auto offsetId = 0; |  | ||||||
| 	auto offsetDate = 0; |  | ||||||
| 	auto addOffset = 0; |  | ||||||
| 	auto limit = 1; |  | ||||||
| 	auto maxId = 0; |  | ||||||
| 	auto minId = 0; |  | ||||||
| 	auto historyHash = 0; |  | ||||||
| 	MTP::send( |  | ||||||
| 		MTPmessages_GetHistory( |  | ||||||
| 			peer->input, |  | ||||||
| 			MTP_int(offsetId), |  | ||||||
| 			MTP_int(offsetDate), |  | ||||||
| 			MTP_int(addOffset), |  | ||||||
| 			MTP_int(limit), |  | ||||||
| 			MTP_int(maxId), |  | ||||||
| 			MTP_int(minId), |  | ||||||
| 			MTP_int(historyHash)), |  | ||||||
| 		rpcDone(&MainWidget::checkedHistory, peer)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &result) { |  | ||||||
| 	const QVector<MTPMessage> *v = 0; |  | ||||||
| 	switch (result.type()) { |  | ||||||
| 	case mtpc_messages_messages: { |  | ||||||
| 		auto &d(result.c_messages_messages()); |  | ||||||
| 		App::feedUsers(d.vusers); |  | ||||||
| 		App::feedChats(d.vchats); |  | ||||||
| 		v = &d.vmessages.v; |  | ||||||
| 	} break; |  | ||||||
| 
 |  | ||||||
| 	case mtpc_messages_messagesSlice: { |  | ||||||
| 		auto &d(result.c_messages_messagesSlice()); |  | ||||||
| 		App::feedUsers(d.vusers); |  | ||||||
| 		App::feedChats(d.vchats); |  | ||||||
| 		v = &d.vmessages.v; |  | ||||||
| 	} break; |  | ||||||
| 
 |  | ||||||
| 	case mtpc_messages_channelMessages: { |  | ||||||
| 		auto &d(result.c_messages_channelMessages()); |  | ||||||
| 		if (peer && peer->isChannel()) { |  | ||||||
| 			peer->asChannel()->ptsReceived(d.vpts.v); |  | ||||||
| 		} else { |  | ||||||
| 			LOG(("API Error: received messages.channelMessages when no channel was passed! (MainWidget::checkedHistory)")); |  | ||||||
| 		} |  | ||||||
| 		App::feedUsers(d.vusers); |  | ||||||
| 		App::feedChats(d.vchats); |  | ||||||
| 		v = &d.vmessages.v; |  | ||||||
| 	} break; |  | ||||||
| 
 |  | ||||||
| 	case mtpc_messages_messagesNotModified: { |  | ||||||
| 		LOG(("API Error: received messages.messagesNotModified! (MainWidget::checkedHistory)")); |  | ||||||
| 	} break; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!v || v->isEmpty()) { |  | ||||||
| 		if (peer->isChat() && !peer->asChat()->haveLeft()) { |  | ||||||
| 			if (const auto history = App::historyLoaded(peer->id)) { |  | ||||||
| 				Local::addSavedPeer(peer, history->chatsListDate()); |  | ||||||
| 			} |  | ||||||
| 		} else if (const auto channel = peer->asChannel()) { |  | ||||||
| 			if (channel->inviter > 0 && channel->amIn()) { |  | ||||||
| 				if (const auto from = App::userLoaded(channel->inviter)) { |  | ||||||
| 					const auto history = App::history(peer->id); |  | ||||||
| 					history->clear(true); |  | ||||||
| 					history->addNewerSlice(QVector<MTPMessage>()); |  | ||||||
| 					history->asChannelHistory()->insertJoinedMessage(true); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} else if (const auto history = App::historyLoaded(peer->id)) { |  | ||||||
| 			deleteConversation(history->peer, false); |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		const auto history = App::history(peer->id); |  | ||||||
| 		if (!history->lastMsg) { |  | ||||||
| 			history->addNewMessage((*v)[0], NewMessageLast); |  | ||||||
| 		} |  | ||||||
| 		if (!history->chatsListDate().isNull() && history->loadedAtBottom()) { |  | ||||||
| 			if (const auto channel = peer->asChannel()) { |  | ||||||
| 				if (channel->inviter > 0 |  | ||||||
| 					&& history->chatsListDate() <= channel->inviteDate |  | ||||||
| 					&& channel->amIn()) { |  | ||||||
| 					if (const auto from = App::userLoaded(channel->inviter)) { |  | ||||||
| 						history->asChannelHistory()->insertJoinedMessage(true); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		history->updateChatListExistence(); |  | ||||||
| 	} |  | ||||||
| 	Auth().data().sendHistoryChangeNotifications(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool MainWidget::sendMessageFail(const RPCError &error) { | bool MainWidget::sendMessageFail(const RPCError &error) { | ||||||
| 	if (MTP::isDefaultHandledError(error)) return false; | 	if (MTP::isDefaultHandledError(error)) return false; | ||||||
| 
 | 
 | ||||||
|  | @ -1562,9 +1438,9 @@ void MainWidget::messagesAffected( | ||||||
| 		ptsUpdateAndApply(data.vpts.v, data.vpts_count.v); | 		ptsUpdateAndApply(data.vpts.v, data.vpts_count.v); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (auto h = App::historyLoaded(peer ? peer->id : 0)) { | 	if (const auto history = App::historyLoaded(peer)) { | ||||||
| 		if (!h->lastMsg) { | 		if (!history->lastMessageKnown()) { | ||||||
| 			checkPeerHistory(peer); | 			Auth().api().requestDialogEntry(history); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -3473,7 +3349,9 @@ void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) { | void MainWidget::gotChannelDifference( | ||||||
|  | 		ChannelData *channel, | ||||||
|  | 		const MTPupdates_ChannelDifference &diff) { | ||||||
| 	_channelFailDifferenceTimeout.remove(channel); | 	_channelFailDifferenceTimeout.remove(channel); | ||||||
| 
 | 
 | ||||||
| 	int32 timeout = 0; | 	int32 timeout = 0; | ||||||
|  | @ -3491,28 +3369,28 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | ||||||
| 
 | 
 | ||||||
| 		App::feedUsers(d.vusers); | 		App::feedUsers(d.vusers); | ||||||
| 		App::feedChats(d.vchats); | 		App::feedChats(d.vchats); | ||||||
| 		auto h = App::historyLoaded(channel->id); | 		auto history = App::historyLoaded(channel->id); | ||||||
| 		if (h) { | 		if (history) { | ||||||
| 			h->setNotLoadedAtBottom(); | 			history->setNotLoadedAtBottom(); | ||||||
| 		} | 		} | ||||||
| 		App::feedMsgs(d.vmessages, NewMessageLast); | 		App::feedMsgs(d.vmessages, NewMessageLast); | ||||||
| 		if (h) { | 		if (history) { | ||||||
| 			if (auto item = App::histItemById(peerToChannel(channel->id), d.vtop_message.v)) { | 			history->applyDialogFields( | ||||||
| 				h->setLastMessage(item); | 				d.vunread_count.v, | ||||||
| 			} | 				d.vread_inbox_max_id.v, | ||||||
| 			if (d.vunread_count.v >= h->unreadCount()) { | 				d.vread_outbox_max_id.v); | ||||||
| 				h->setUnreadCount(d.vunread_count.v); | 			history->applyDialogTopMessage(d.vtop_message.v); | ||||||
| 				h->inboxReadBefore = d.vread_inbox_max_id.v + 1; | 			history->setUnreadMentionsCount(d.vunread_mentions_count.v); | ||||||
| 			} |  | ||||||
| 			h->setUnreadMentionsCount(d.vunread_mentions_count.v); |  | ||||||
| 			if (_history->peer() == channel) { | 			if (_history->peer() == channel) { | ||||||
| 				_history->updateHistoryDownVisibility(); | 				_history->updateHistoryDownVisibility(); | ||||||
| 				_history->preloadHistoryIfNeeded(); | 				_history->preloadHistoryIfNeeded(); | ||||||
| 			} | 			} | ||||||
| 			h->asChannelHistory()->getRangeDifference(); | 			Auth().api().requestChannelRangeDifference(history); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (d.has_timeout()) timeout = d.vtimeout.v; | 		if (d.has_timeout()) { | ||||||
|  | 			timeout = d.vtimeout.v; | ||||||
|  | 		} | ||||||
| 		isFinal = d.is_final(); | 		isFinal = d.is_final(); | ||||||
| 		channel->ptsInit(d.vpts.v); | 		channel->ptsInit(d.vpts.v); | ||||||
| 	} break; | 	} break; | ||||||
|  | @ -3520,39 +3398,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | ||||||
| 	case mtpc_updates_channelDifference: { | 	case mtpc_updates_channelDifference: { | ||||||
| 		auto &d = diff.c_updates_channelDifference(); | 		auto &d = diff.c_updates_channelDifference(); | ||||||
| 
 | 
 | ||||||
| 		App::feedUsers(d.vusers); | 		feedChannelDifference(d); | ||||||
| 		App::feedChats(d.vchats); |  | ||||||
| 
 |  | ||||||
| 		_handlingChannelDifference = true; |  | ||||||
| 		feedMessageIds(d.vother_updates); |  | ||||||
| 
 |  | ||||||
| 		// feed messages and groups, copy from App::feedMsgs
 |  | ||||||
| 		auto h = App::history(channel->id); |  | ||||||
| 		auto &vmsgs = d.vnew_messages.v; |  | ||||||
| 		auto indices = base::flat_map<uint64, int>(); |  | ||||||
| 		for (auto i = 0, l = vmsgs.size(); i != l; ++i) { |  | ||||||
| 			const auto &msg = vmsgs[i]; |  | ||||||
| 			if (msg.type() == mtpc_message) { |  | ||||||
| 				const auto &data = msg.c_message(); |  | ||||||
| 				if (App::checkEntitiesAndViewsUpdate(data)) { // new message, index my forwarded messages to links _overview, already in blocks
 |  | ||||||
| 					LOG(("Skipping message, because it is already in blocks!")); |  | ||||||
| 					continue; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			const auto msgId = idFromMessage(msg); |  | ||||||
| 			indices.emplace((uint64(uint32(msgId)) << 32) | uint64(i), i); |  | ||||||
| 		} |  | ||||||
| 		for (const auto [position, index] : indices) { |  | ||||||
| 			const auto &msg = vmsgs[index]; |  | ||||||
| 			if (channel->id != peerFromMessage(msg)) { |  | ||||||
| 				LOG(("API Error: message with invalid peer returned in channelDifference, channelId: %1, peer: %2").arg(peerToChannel(channel->id)).arg(peerFromMessage(msg))); |  | ||||||
| 				continue; // wtf
 |  | ||||||
| 			} |  | ||||||
| 			h->addNewMessage(msg, NewMessageUnread); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		feedUpdateVector(d.vother_updates, true); |  | ||||||
| 		_handlingChannelDifference = false; |  | ||||||
| 
 | 
 | ||||||
| 		if (d.has_timeout()) timeout = d.vtimeout.v; | 		if (d.has_timeout()) timeout = d.vtimeout.v; | ||||||
| 		isFinal = d.is_final(); | 		isFinal = d.is_final(); | ||||||
|  | @ -3570,55 +3416,16 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::gotRangeDifference( | void MainWidget::feedChannelDifference( | ||||||
| 		ChannelData *channel, | 		const MTPDupdates_channelDifference &data) { | ||||||
| 		const MTPupdates_ChannelDifference &diff) { | 	App::feedUsers(data.vusers); | ||||||
| 	int32 nextRequestPts = 0; | 	App::feedChats(data.vchats); | ||||||
| 	bool isFinal = true; |  | ||||||
| 	switch (diff.type()) { |  | ||||||
| 	case mtpc_updates_channelDifferenceEmpty: { |  | ||||||
| 		auto &d = diff.c_updates_channelDifferenceEmpty(); |  | ||||||
| 		nextRequestPts = d.vpts.v; |  | ||||||
| 		isFinal = d.is_final(); |  | ||||||
| 	} break; |  | ||||||
| 
 |  | ||||||
| 	case mtpc_updates_channelDifferenceTooLong: { |  | ||||||
| 		auto &d = diff.c_updates_channelDifferenceTooLong(); |  | ||||||
| 
 |  | ||||||
| 		App::feedUsers(d.vusers); |  | ||||||
| 		App::feedChats(d.vchats); |  | ||||||
| 
 |  | ||||||
| 		nextRequestPts = d.vpts.v; |  | ||||||
| 		isFinal = d.is_final(); |  | ||||||
| 	} break; |  | ||||||
| 
 |  | ||||||
| 	case mtpc_updates_channelDifference: { |  | ||||||
| 		auto &d = diff.c_updates_channelDifference(); |  | ||||||
| 
 |  | ||||||
| 		App::feedUsers(d.vusers); |  | ||||||
| 		App::feedChats(d.vchats); |  | ||||||
| 
 | 
 | ||||||
| 	_handlingChannelDifference = true; | 	_handlingChannelDifference = true; | ||||||
| 		feedMessageIds(d.vother_updates); | 	feedMessageIds(data.vother_updates); | ||||||
| 		App::feedMsgs(d.vnew_messages, NewMessageUnread); | 	App::feedMsgs(data.vnew_messages, NewMessageUnread); | ||||||
| 		feedUpdateVector(d.vother_updates, true); | 	feedUpdateVector(data.vother_updates, true); | ||||||
| 	_handlingChannelDifference = false; | 	_handlingChannelDifference = false; | ||||||
| 
 |  | ||||||
| 		nextRequestPts = d.vpts.v; |  | ||||||
| 		isFinal = d.is_final(); |  | ||||||
| 	} break; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!isFinal) { |  | ||||||
| 		if (const auto history = App::historyLoaded(channel->id)) { |  | ||||||
| 			MTP_LOG(0, ("getChannelDifference { " |  | ||||||
| 				"good - after not final channelDifference was received, " |  | ||||||
| 				"validating history part }%1" |  | ||||||
| 				).arg(cTestMode() ? " TESTMODE" : "")); |  | ||||||
| 			history->asChannelHistory()->getRangeDifferenceNext( |  | ||||||
| 				nextRequestPts); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool MainWidget::failChannelDifference(ChannelData *channel, const RPCError &error) { | bool MainWidget::failChannelDifference(ChannelData *channel, const RPCError &error) { | ||||||
|  | @ -3985,12 +3792,12 @@ void MainWidget::onSelfParticipantUpdated(ChannelData *channel) { | ||||||
| 			history = App::history(channel); | 			history = App::history(channel); | ||||||
| 		} | 		} | ||||||
| 		if (history->isEmpty()) { | 		if (history->isEmpty()) { | ||||||
| 			checkPeerHistory(channel); | 			Auth().api().requestDialogEntry(history); | ||||||
| 		} else { | 		} else { | ||||||
| 			history->asChannelHistory()->checkJoinedMessage(true); | 			history->checkJoinedMessage(true); | ||||||
| 		} | 		} | ||||||
| 	} else if (history) { | 	} else if (history) { | ||||||
| 		history->asChannelHistory()->checkJoinedMessage(); | 		history->checkJoinedMessage(); | ||||||
| 	} | 	} | ||||||
| 	Auth().data().sendHistoryChangeNotifications(); | 	Auth().data().sendHistoryChangeNotifications(); | ||||||
| } | } | ||||||
|  | @ -4768,10 +4575,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { | ||||||
| 				const auto existing = App::histItemById(channel, newId); | 				const auto existing = App::histItemById(channel, newId); | ||||||
| 				if (existing && !local->mainView()) { | 				if (existing && !local->mainView()) { | ||||||
| 					const auto history = local->history(); | 					const auto history = local->history(); | ||||||
| 					const auto wasLast = (history->lastMsg == local); |  | ||||||
| 					local->destroy(); | 					local->destroy(); | ||||||
| 					if (wasLast && !history->lastMsg) { | 					if (!history->lastMessageKnown()) { | ||||||
| 						checkPeerHistory(history->peer); | 						Auth().api().requestDialogEntry(history); | ||||||
| 					} | 					} | ||||||
| 				} else { | 				} else { | ||||||
| 					if (existing) { | 					if (existing) { | ||||||
|  | @ -5211,7 +5017,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { | ||||||
| 
 | 
 | ||||||
| 	case mtpc_updateChannel: { | 	case mtpc_updateChannel: { | ||||||
| 		auto &d = update.c_updateChannel(); | 		auto &d = update.c_updateChannel(); | ||||||
| 		if (auto channel = App::channelLoaded(d.vchannel_id.v)) { | 		if (const auto channel = App::channelLoaded(d.vchannel_id.v)) { | ||||||
| 			channel->inviter = 0; | 			channel->inviter = 0; | ||||||
| 			if (!channel->amIn()) { | 			if (!channel->amIn()) { | ||||||
| 				if (const auto history = App::historyLoaded(channel->id)) { | 				if (const auto history = App::historyLoaded(channel->id)) { | ||||||
|  |  | ||||||
|  | @ -215,9 +215,6 @@ public: | ||||||
| 		not_null<ChannelData*> channel, | 		not_null<ChannelData*> channel, | ||||||
| 		const RPCError &e); // for multi invite in channels
 | 		const RPCError &e); // for multi invite in channels
 | ||||||
| 
 | 
 | ||||||
| 	void checkPeerHistory(PeerData *peer); |  | ||||||
| 	void checkedHistory(PeerData *peer, const MTPmessages_Messages &result); |  | ||||||
| 
 |  | ||||||
| 	bool sendMessageFail(const RPCError &error); | 	bool sendMessageFail(const RPCError &error); | ||||||
| 
 | 
 | ||||||
| 	Dialogs::IndexedList *contactsList(); | 	Dialogs::IndexedList *contactsList(); | ||||||
|  | @ -288,8 +285,8 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void scheduleViewIncrement(HistoryItem *item); | 	void scheduleViewIncrement(HistoryItem *item); | ||||||
| 
 | 
 | ||||||
| 	void gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff); |  | ||||||
| 	void onSelfParticipantUpdated(ChannelData *channel); | 	void onSelfParticipantUpdated(ChannelData *channel); | ||||||
|  | 	void feedChannelDifference(const MTPDupdates_channelDifference &data); | ||||||
| 
 | 
 | ||||||
| 	// Mayde public for ApiWrap, while it is still here.
 | 	// Mayde public for ApiWrap, while it is still here.
 | ||||||
| 	// Better would be for this to be moved to ApiWrap.
 | 	// Better would be for this to be moved to ApiWrap.
 | ||||||
|  | @ -478,7 +475,6 @@ private: | ||||||
| 	void feedUpdate(const MTPUpdate &update); | 	void feedUpdate(const MTPUpdate &update); | ||||||
| 
 | 
 | ||||||
| 	void deleteHistoryPart(DeleteHistoryRequest request, const MTPmessages_AffectedHistory &result); | 	void deleteHistoryPart(DeleteHistoryRequest request, const MTPmessages_AffectedHistory &result); | ||||||
| 	void deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result); |  | ||||||
| 
 | 
 | ||||||
| 	void updateReceived(const mtpPrime *from, const mtpPrime *end); | 	void updateReceived(const mtpPrime *from, const mtpPrime *end); | ||||||
| 	bool updateFail(const RPCError &e); | 	bool updateFail(const RPCError &e); | ||||||
|  |  | ||||||
|  | @ -593,7 +593,15 @@ void PeerMenuAddChannelMembers(not_null<ChannelData*> channel) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ToggleChannelGrouping(not_null<ChannelData*> channel, bool group) { | void ToggleChannelGrouping(not_null<ChannelData*> channel, bool group) { | ||||||
| 	Auth().api().toggleChannelGrouping(channel, group); | 	const auto callback = [=] { | ||||||
|  | 		Ui::Toast::Show(lang(group | ||||||
|  | 			? lng_feed_channel_added | ||||||
|  | 			: lng_feed_channel_removed)); | ||||||
|  | 	}; | ||||||
|  | 	Auth().api().toggleChannelGrouping( | ||||||
|  | 		channel, | ||||||
|  | 		group, | ||||||
|  | 		callback); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| base::lambda<void()> ClearHistoryHandler(not_null<PeerData*> peer) { | base::lambda<void()> ClearHistoryHandler(not_null<PeerData*> peer) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue